Chapter 19Lesson 05~330 minutes

Checkpoint Lab — Parent-Child Pipelines, Multi-Project Pipelines, Trigger Tokens, and Pipeline Composition

Prove source identity, mirrored failure propagation, optional cross-project authorization, credential hygiene, evidence capture, and cleanup in one production-style composition checkpoint.

CheckpointFailure propagationCross-projectAuthorizationEvidenceCleanup

Learning objectives

  • Predict parent/child graph, source values, ref/SHA identity, and failure propagation before execution.
  • Capture independent evidence for trigger jobs and downstream pipelines.
  • Diagnose one blocked cross-project trigger with a least-privilege repair or fixture.
  • Prove no real trigger credential or sensitive variable leaked into logs/source.
  • Remove disposable branches, allowlist entries, optional credentials, and secondary project resources safely.
Availability baseline (verified 2026-08-22 against current GitLab 19.3 documentation). Parent-child pipelines, multi-project pipelines, trigger jobs, trigger:strategy: mirror, downstream inputs, pipeline trigger tokens, the pipeline triggers API, CI_JOB_TOKEN cross-project access controls, and the job-token scope API are available on GitLab Free/Premium/Ultimate across GitLab.com, Self-Managed, and Dedicated. The mandatory path uses one disposable Free project and a local child configuration. A second disposable project is optional. Fetching upstream artifacts with needs:pipeline:job or needs:project is currently Premium/Ultimate, so cross-pipeline artifact retrieval is taught with a fixture/read-only extension rather than required for completion.

1. Checkpoint scenario

Mandatory: one disposable Free project with a parent pipeline and local child configuration. Optional: a second disposable Free project for multi-project composition and a job-token allowlist drill. The checkpoint deliberately creates one child failure, proves strategy: mirror, repairs it, then captures the final graph.

No production environment, registry, package, cloud account, Kubernetes cluster, real secret, persistent runner, or paid feature is required.

2. Preflight and role assumptions

Item Required Verification
Project A Disposable branch ch19/checkpoint; Developer or higher Record project ID/path/default branch.
Runner Tiny eligible runner or fixture path Record runner/job status only; never runner config/token.
Project B (optional) Disposable Free project; user can run target pipelines Record ID/path/ref; separate ownership boundary.
Allowlist mutation (optional) Maintainer/Owner of Project B Capture before-state and exact Project A ID.
Trigger token (optional syntax extension) Not required If created, record token object metadata only and revoke before cleanup.

3. Write the prediction table before running

Event Prediction
Parent push Parent source=push, project=A, ref=ch19/checkpoint, SHA=P.
Child creation Child project=A, same ref/SHA=P, source=parent_pipeline.
First child run Deliberate child exit 7 makes child failed and mirrored trigger failed.
Parent verify job Must not run after failed mirrored trigger under normal stage semantics.
Repair run Child success makes mirrored trigger success and parent verify job runs.
Optional Project B Downstream source=pipeline; project/ref/SHA belong to B, not A.
Blocked job-token probe Before allowlisting A in B, target denies cross-project token access.

4. Create checkpoint parent and child files

.gitlab/ci/ch19-checkpoint-child.yml:

spec:
  inputs:
    fail_mode:
      options: [fail, pass]
      default: fail
---
child_check:
  script:
    - test "$CI_PIPELINE_SOURCE" = "parent_pipeline"
    - printf 'child=%s ref=%s sha=%s\n' "$CI_PROJECT_PATH" "$CI_COMMIT_REF_NAME" "$CI_COMMIT_SHA"
    - |
      if [ "$[[ inputs.fail_mode ]]" = "fail" ]; then
        echo "intentional checkpoint failure"
        exit 7
      fi

Parent fragment:

stages: [inspect, compose, verify]

parent_check:
  stage: inspect
  script:
    - printf 'parent=%s ref=%s sha=%s source=%s\n' "$CI_PROJECT_PATH" "$CI_COMMIT_REF_NAME" "$CI_COMMIT_SHA" "$CI_PIPELINE_SOURCE"

child_gate:
  stage: compose
  trigger:
    include:
      - local: .gitlab/ci/ch19-checkpoint-child.yml
        inputs:
          fail_mode: fail
    strategy: mirror

verify_after_child:
  stage: verify
  script:
    - echo "mirrored child succeeded"

5. First run: preserve the deliberate failure

git switch -c ch19/checkpoint
# Add the checkpoint CI files after validating them.
git add .gitlab-ci.yml .gitlab/ci/ch19-checkpoint-child.yml
git commit -m "lab: add ch19 checkpoint composition"
git push -u origin ch19/checkpoint

Capture the parent ID, trigger-job ID, child ID, and child failed job ID. Do not retry. Confirm verify_after_child did not run.

6. Prove failure propagation with APIs

PROJECT_A_ID="12345678"
PARENT_FAIL_ID="9201"

glab api "projects/$PROJECT_A_ID/pipelines/$PARENT_FAIL_ID"   --jq '{id,ref,sha,status,source,user:.user.username}'

glab api "projects/$PROJECT_A_ID/pipelines/$PARENT_FAIL_ID/trigger_jobs"   --jq '.[] | {id,name,status,downstream_pipeline:(.downstream_pipeline|{id,project_id,ref,sha,status})}'

The trigger job must fail because strategy: mirror reflects the child. This is the checkpoint proof that status propagation is configured, not assumed.

7. Repair by changing only the synthetic input

Change fail_mode: fail to fail_mode: pass, validate, commit, and push. Do not change strategy or add allow_failure.

git add .gitlab-ci.yml
git commit -m "lab: repair child checkpoint input"
git push

Capture the new parent/child IDs. Compare project/ref/SHA/source/status and prove verify_after_child now runs.

8. Optional second-project composition

Create Project B only if you want the live cross-project extension. Give it a tiny config that accepts source_project as a non-secret input and prints only project/ref/SHA/source.

spec:
  inputs:
    source_project:
      type: string
---
downstream_check:
  script:
    - test "$CI_PIPELINE_SOURCE" = "pipeline"
    - printf 'target=%s ref=%s sha=%s\n' "$CI_PROJECT_PATH" "$CI_COMMIT_REF_NAME" "$CI_COMMIT_SHA"
    - printf 'requested_by_project=%s\n' "$[[ inputs.source_project ]]"

Add a trigger:project job in A with strategy: mirror and that typed input. Verify B's pipeline appears both as an upstream graph card and in B's pipeline list.

9. Blocked downstream trigger drill

Live two-project path: add the CI_JOB_TOKEN probe from Lesson 2 while A is not on B's job-token allowlist. Preserve the non-success HTTP response/body. Fixture-only path: use this synthetic evidence and diagnose it exactly as if live:

$ curl --fail-with-body --request POST --header "JOB-TOKEN: [MASKED]" --form ref=main ...
curl: (22) The requested URL returned error: 404
{"message":"404 Not Found"}

Target allowlist before:
[]
Pipeline user target membership: Developer
Target ref: main exists

Diagnosis: membership/ref are sufficient; the missing inbound allowlist entry is the least-privilege gap.

10. Repair only the required authorization edge

On a disposable B and with Maintainer/Owner authority, add exactly Project A to B's job-token allowlist. Do not switch B to “all groups and projects.” Re-run the probe while the job is active and confirm a new B pipeline with source=pipeline.

After verification, remove the exact A entry from B. This proves the trust edge can be created and revoked independently of project membership.

11. Trigger-token checkpoint: syntax, leak response, optional revocation

The required lab does not create a trigger token. State the operational rule anyway: if an optional external-integration extension created one, revoke it now before deleting projects or cleaning Git history. Record only token description/creator/prefix metadata, never the full value.

A pipeline created with a trigger token would report CI_PIPELINE_SOURCE=trigger and CI_PIPELINE_TRIGGERED=true, which distinguishes it from the pipeline source produced by GitLab-to-GitLab downstream composition.

12. Verify variable/input boundary

Inspect job logs for only the declared non-secret input. Verify no environment dump, CI_JOB_TOKEN, trigger token, protected variable, or unrelated pipeline variable was printed. If you used trigger:forward in an extension, document exactly which class of variables was forwarded and why.

13. Artifact boundary exercise

Predict before reading the answer: does the child automatically receive parent_check artifacts because the trigger uses mirror? No. Status strategy does not transfer artifacts. The current explicit upstream artifact retrieval features are Premium/Ultimate, so this checkpoint remains Free by verifying IDs/SHAs and typed inputs rather than cross-pipeline bytes.

14. Build an evidence manifest

CH19 CHECKPOINT EVIDENCE
Project A: <id/path>
Failed parent: <id> ref=<ref> sha=<sha> source=push status=failed
Failed child: <id> same project/ref/sha source=parent_pipeline status=failed
Failed trigger job: <id> strategy=mirror status=failed
Repaired parent: <id> status=success
Repaired child: <id> source=parent_pipeline status=success
Project B (optional): <id/path/ref/sha> source=pipeline
Blocked job-token response: <HTTP status/body, no token>
Allowlist before/after: <entry absent -> present -> absent>
Trigger token: not created OR <metadata only, revoked=true>
Secrets printed: none
External side effects: none

15. Cleanup in dependency order

  1. If any persistent trigger token was created, revoke it first.
  2. Remove Project A from Project B's job-token allowlist and verify absence.
  3. Remove synthetic downstream trigger jobs/config from Project A.
  4. Preserve pipeline IDs/manifests needed for learning evidence.
  5. Delete only the disposable branch.
  6. If Project B exists solely for this lab and you intentionally choose destructive cleanup, delete it last through the normal GitLab project-deletion workflow.
# Verify no disposable source allowlist entry remains.
glab api "projects/$PROJECT_B_ID/job_token_scope/allowlist" --paginate   --jq '.[] | {id,path_with_namespace}'

# Delete only the disposable Git branch after evidence capture.
git switch main
git ls-remote --heads origin refs/heads/ch19/checkpoint
git push origin --delete ch19/checkpoint
git branch -D ch19/checkpoint

16. Final verification checklist

  • Parent/child same project/ref/SHA was predicted and independently proven.
  • Child source was parent_pipeline.
  • Mirrored child failure propagated to trigger job and blocked later parent stage.
  • Repair changed the synthetic failure input, not the status policy.
  • Optional downstream project showed its own project/ref/SHA and source pipeline.
  • Blocked job-token access was diagnosed from target allowlist + user permission evidence.
  • Any temporary allowlist entry was removed; any optional trigger token was revoked.
  • No token or sensitive variable value appeared in source/logs/output.
  • No paid artifact-transfer feature was required.

17. What Chapter 19 adds to the production operating model

You can now compose delivery graphs without collapsing their trust boundaries. You know which downstream pipeline represents the same source revision, which represents another project's revision, which identity created it, when upstream status should depend on it, and how to authorize/revoke cross-project automation without a blanket credential.

Chapter 20 moves from where pipelines are split to how CI configuration itself is reused and distributed: includes, templates, CI/CD components, the Component Catalog, and maintainable reusable pipeline architecture.

Knowledge check

What evidence proves parent-child identity rather than merely UI linkage?

Why is the first failed child pipeline preserved?

What is the least-privilege repair for the optional blocked job-token trigger?

What should be revoked before project/history cleanup if an optional trigger token was created?

Why is no cross-pipeline artifact fetched in the mandatory checkpoint?

What new production capability does this chapter add?

Checkpoint summary

You proved a child pipeline's source and same-SHA identity, deliberately tested mirrored failure propagation, repaired it without hiding the failure, optionally crossed a project boundary, diagnosed a blocked job-token trigger, and removed the temporary trust edge. Pipeline composition is now a governed dependency graph rather than an opaque chain of “trigger another build” actions.

Official references

Next chapter

Includes, templates, CI/CD components, and reusable architecture

Chapter 20 builds reusable configuration contracts on top of the composition model, with versioning, immutable references, inputs, component trust, and catalog governance.

Keep the academy open

Support free, practical DevOps education.

Every lesson is designed to remain readable in a browser, downloadable from GitHub, and usable without a paid learning platform. Contributions help expand and maintain the curriculum.

Ethereum / ERC-20
0x716c4Ab160C4B66F31a28AE2448BfF68fc3a2ef0Send only Ethereum/ERC-20 compatible assets to this address.