Checkpoint Lab — Artifacts, Reports, Retention, Dependencies, needs:artifacts, and Cross-Job Data Flow
The checkpoint implements “build once, verify everywhere”: one producer emits a synthetic release candidate plus SHA-256 evidence, later jobs consume exactly that artifact, one dependency is deliberately broken, and recovery restores the intended bytes without rebuilding the release candidate.
Learning objectives
- Predict which bytes and metadata each job should receive before executing the checkpoint.
- Build once and verify the same artifact digest across multiple jobs and evidence records.
- Inject a transfer/dependency failure, preserve the failed consumer evidence, and repair only the causal declaration.
- Demonstrate that recovery does not rebuild or silently replace the intended release candidate.
- Produce an evidence packet and bridge retained outputs naturally into Chapter 11 cache correctness.
1. Checkpoint scenario and success criteria
You are preparing a synthetic release candidate. One job may build
it. Two later jobs must independently consume the same bytes:
verify_candidate checks integrity and provenance, while
package_manifest records release metadata. You will
then break one transfer declaration, preserve the failed evidence,
and repair it without rerunning the build step as a substitute.
Success: both consumers verify the producer digest; the deliberate failure is explained by transfer metadata rather than guesswork; the restored pipeline uses the same source commit and build contract; and the evidence packet contains no secret material.
2. Preflight and assumptions
-
Disposable project/branch only:
glci/ch10-checkpoint. - Current GitLab behavior checked 2026-09-11.
-
Any compatible runner is sufficient; container image uses
alpine:3.22.1. - No real credentials, package registry, release, environment, cloud, or Kubernetes target.
- Before the run, record the exact commit SHA containing the checkpoint configuration.
- If a runner is unavailable, perform CI Lint plus a local directory-copy/digest simulation and mark runtime evidence as not observed.
verify_candidate should
receive producer files as soon as
build_candidate succeeds.
Prediction 2: package_manifest should
receive the exact same candidate digest but should not rebuild it.
3. Exact checkpoint configuration
The producer writes a candidate, digest, and producer identity. A
small JUnit job supplies independent typed report evidence. The two
consumers use explicit needs artifact transfer.
stages: [build, verify, package]
default:
image: alpine:3.22.1
build_candidate:
stage: build
script:
- mkdir -p dist evidence
- printf 'candidate-sha=%s\n' "$CI_COMMIT_SHA" > dist/candidate.txt
- sha256sum dist/candidate.txt > evidence/candidate.sha256
- printf 'pipeline=%s\njob=%s\nsha=%s\n' "$CI_PIPELINE_ID" "$CI_JOB_ID" "$CI_COMMIT_SHA" > evidence/producer.txt
artifacts:
name: "candidate-$CI_PIPELINE_ID-$CI_JOB_ID"
paths:
- dist/candidate.txt
- evidence/candidate.sha256
- evidence/producer.txt
expire_in: 7 days
unit_report:
stage: verify
needs: []
script:
- mkdir -p test-results
- printf '%s\n' '<testsuite name="checkpoint" tests="1" failures="0"><testcase classname="artifact" name="contract"/></testsuite>' > test-results/junit.xml
artifacts:
when: always
reports:
junit: test-results/junit.xml
paths:
- test-results/junit.xml
expire_in: 2 days
verify_candidate:
stage: verify
needs:
- job: build_candidate
artifacts: true
script:
- cat evidence/producer.txt
- sha256sum -c evidence/candidate.sha256
- grep -F "candidate-sha=$CI_COMMIT_SHA" dist/candidate.txt
package_manifest:
stage: package
needs:
- job: build_candidate
artifacts: true
- job: verify_candidate
artifacts: false
script:
- sha256sum -c evidence/candidate.sha256
- mkdir -p package
- cp evidence/candidate.sha256 package/release.sha256
- printf 'source_sha=%s\nproducer_job=%s\nconsumer_job=%s\n' "$CI_COMMIT_SHA" "$(sed -n 's/^job=//p' evidence/producer.txt)" "$CI_JOB_ID" > package/manifest.txt
artifacts:
paths:
- package/release.sha256
- package/manifest.txt
expire_in: 7 days
4. Predict the graph and dataflow before execution
Write down the expected state:
-
build_candidateis the only job that createsdist/candidate.txt. -
unit_reportstarts without waiting for build because it hasneeds: []and does not consume candidate data. -
verify_candidatewaits for build and downloads its artifacts. -
package_manifestwaits for the build artifact and the verification gate. It downloads artifacts from the build, but not fromverify_candidate. - The candidate digest in verifier and packager must match the producer manifest exactly.
5. Run the verified baseline and capture evidence
Push the exact checkpoint commit. Record:
- pipeline ID/source/ref/SHA,
- build, report, verifier, and packager job IDs/statuses,
- build artifact name and declared expiry,
- JUnit report presence in GitLab’s test view,
- the producer
candidate.sha256content, - successful digest checks in both consumers,
- final
package/manifest.txtartifact identity.
build job ID + SHA
path + SHA-256 digest
unit_report job ID + JUnit type
job ID + digest PASS
job ID + same digest PASS
7-day candidate/manifest declaration + keep-latest assumption
6. Inject one transfer failure without changing the build script
Change only verify_candidate:
verify_candidate:
stage: verify
needs:
- job: build_candidate
artifacts: false
script:
- sha256sum -c evidence/candidate.sha256
Commit and run. Preserve the failed pipeline ID, failed verifier job ID, and first missing-file message. The producer can still upload its artifact successfully; the failure is in the consumer transfer contract.
7. Diagnose by layer
Prove each statement in order:
- The pipeline was created for the expected source/ref/SHA.
build_candidateexists and succeeded.- The build job has the expected artifact and provenance manifest.
-
verify_candidatehas aneedsedge to the correct producer. - The edge explicitly disables artifact transfer.
- The failed trace is therefore explained without blaming the runner, shell, or artifact expiry.
8. Repair only the causal declaration
Restore artifacts: true. Run the corrected pipeline at
its new commit and verify the new producer/consumer tuple. In a real
release incident where the original intended artifact must be
preserved, recovery would target the original retained artifact by
exact producer identity rather than rebuild. In this teaching
branch, every commit creates a new pipeline identity, so record that
fact explicitly instead of pretending the repaired run is the same
artifact record.
The lesson’s invariant is unchanged: never hide a missing retained release artifact by rebuilding during promotion/deployment.
9. Required evidence packet
| Field | Record |
|---|---|
| Pipeline | ID, source, ref, exact SHA for successful baseline, broken run, and repaired run |
| Compiled config | Relevant artifact/needs declarations |
| Producer | Build job ID/name, runner context, artifact name |
| Artifact | Candidate path, SHA-256 digest, expiry/access assumptions |
| Report | JUnit producer job ID/type and GitLab ingestion observation |
| Consumers | Verifier/packager job IDs and digest results |
| Failure | Broken verifier job ID + first missing-file evidence + artifacts:false cause |
| Limitations | No real release/registry/deployment; free disposable lab only |
10. Verification checklist
- Exactly one job in each run creates the candidate bytes.
- Producer identity includes pipeline/job/SHA.
- Consumers verify SHA-256 before using the file.
- Typed JUnit evidence remains separate from the candidate artifact.
- The deliberate failure is explained by the transfer flag, not by guesswork.
- No cache is used as the authoritative artifact store.
- No consumer rebuilds the candidate.
- Retention is long enough for the checkpoint but intentionally bounded.
11. No-runner local simulation path
If no runner is available, simulate the data contract locally:
rm -rf .ch10-sim && mkdir -p .ch10-sim/producer/dist .ch10-sim/producer/evidence .ch10-sim/consumer
printf 'candidate-sha=SIMULATED_SHA\n' > .ch10-sim/producer/dist/candidate.txt
sha256sum .ch10-sim/producer/dist/candidate.txt > .ch10-sim/producer/evidence/candidate.sha256
cp -R .ch10-sim/producer/dist .ch10-sim/producer/evidence .ch10-sim/consumer/
(cd .ch10-sim/consumer && sha256sum -c evidence/candidate.sha256)
This faithfully demonstrates producer selection, transfer, and digest verification, but it does not prove GitLab artifact retention, UI/API access, report ingestion, or Runner transfer behavior. Mark those fields “not observed.”
12. Cleanup/rollback
- Keep the evidence packet only as long as needed for the course exercise.
- Delete
.ch10-simif created locally. - Delete only the exact disposable branch/project after verifying its identity.
- Do not bulk-delete project artifacts or pipelines as part of this lab.
13. What Chapter 10 adds to the production model
You can now name an artifact producer, retain only intentional outputs, distinguish generic files from typed reports, choose stage-based or DAG transfer deliberately, reason about expiry/access, and prove that consumers received the same bytes by digest. That closes the file-evidence gap in the pipeline state model.
Chapter 11 tackles a neighboring but fundamentally different state system: caches. Caches trade certainty for speed and must be keyed so reuse cannot silently violate correctness. The artifact contract you just built becomes the reference point for understanding why a cache is never the release source of truth.
Knowledge check
What makes “build once” auditable?
One producer identity tied to exact source SHA plus a retained artifact digest that every consumer verifies.
Why did package_manifest use artifacts:false for verify_candidate?
It needs verify_candidate as a control/quality gate but does not need files from that job; candidate files come from build_candidate.
What evidence distinguishes a transfer failure from a build failure?
The producer job succeeded and retained the expected artifact, while the consumer need explicitly disabled artifact transfer and failed on the missing file.
Why must the repaired run be labeled as a new artifact identity?
A new commit/pipeline/job produces a new artifact record; pretending it is the original would erase provenance.
What is the Chapter 11 bridge?
Artifacts are intended outputs/evidence; caches are performance reuse state whose correctness depends on keys and can be missing or stale.
Version and compatibility note
GitLab and GitLab Runner evolve continuously. Treat version-sensitive YAML, runner/executor behavior, APIs, security features, policy controls, tiers, and deprecations as assumptions to verify against the current official GitLab documentation before production use. Preserve the exact project/ref/SHA, compiled configuration, pipeline/job IDs, runner/tool versions, and external target evidence used for any reproducible lab or incident record.
Official references and version notes
Documentation verification date: 2026-09-11. Artifact/report retention, access, API behavior, and cross-job transfer semantics are version-sensitive; verify the deployed GitLab version for Self-Managed/Dedicated installations.
- Job artifacts — creation, paths, expiry, download, artifact browsing, access, latest-success retention, and default previous-stage fetching.
-
CI/CD YAML syntax reference
— authoritative
artifacts,artifacts:access,artifacts:expire_in,dependencies, andneeds:artifactssemantics. - CI/CD artifacts report types — typed report ingestion and report-specific GitLab UI behavior.
- Unit test reports — JUnit report configuration and display.
- Job Artifacts API — artifact archive/file/report download, keep, and delete operations with exact job identity.
- Troubleshooting job artifacts — artifact expiry and upload/report problems.
- Caching in GitLab CI/CD — artifact-versus-cache boundary.
-
Make jobs start earlier with
needs— DAG dependency edges and artifact-transfer interaction.
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.
0x716c4Ab160C4B66F31a28AE2448BfF68fc3a2ef0
Send only Ethereum/ERC-20 compatible assets to this
address.