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.
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.
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
- If any persistent trigger token was created, revoke it first.
- Remove Project A from Project B's job-token allowlist and verify absence.
- Remove synthetic downstream trigger jobs/config from Project A.
- Preserve pipeline IDs/manifests needed for learning evidence.
- Delete only the disposable branch.
- 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?
Parent and child API records show the same project ID, ref, and
SHA; child source is parent_pipeline.
Why is the first failed child pipeline preserved?
It proves strategy: mirror failure propagation and
keeps the original cause/status evidence intact.
What is the least-privilege repair for the optional blocked job-token trigger?
Add only the required source project/group to the target allowlist while retaining user/ref permissions; do not disable the allowlist globally.
What should be revoked before project/history cleanup if an optional trigger token was created?
The trigger token itself. Credential containment comes before deleting the string or project.
Why is no cross-pipeline artifact fetched in the mandatory checkpoint?
Current explicit upstream artifact retrieval with
needs:pipeline:job/needs:project is
Premium/Ultimate, while the chapter must remain Free-compatible.
What new production capability does this chapter add?
Explicit, auditable pipeline composition across configuration/project boundaries with known source identity, status strategy, input policy, token/permission trust, and cleanup.
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
- GitLab Docs — Downstream pipelines
- GitLab Docs — Pipeline architectures
- GitLab Docs — CI/CD YAML trigger reference
- GitLab Docs — Trigger pipelines with the API
- GitLab Docs — CI/CD job token
- GitLab Docs — Job token scope API
- GitLab Docs — Fine-grained job-token permissions
- GitLab Docs — Pipelines API
- GitLab Docs — Jobs API / trigger jobs
- GitLab Docs — CI/CD inputs
- GitLab Docs — Troubleshooting downstream pipelines
- GitLab Docs — Job artifacts troubleshooting
- GitLab Docs — Pipeline security
- GitLab Docs — Roles and permissions
- GitLab 19.3 — What’s new
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.
0x716c4Ab160C4B66F31a28AE2448BfF68fc3a2ef0Send only Ethereum/ERC-20 compatible assets to this
address.