Chapter 23Lesson 05~210 minutes

Checkpoint Lab — Container Registry, Package Registry, Generic Packages, Dependency Proxy, and Artifact Promotion

Publish and consume one disposable artifact by immutable identity, verify producer SHA and digest, diagnose one authorization or mutable-tag failure, and clean up only the identified lab object.

CheckpointImmutable identityEvidence packetPromotionCleanup

Learning objectives

Checkpoint goals

  • Publish a disposable Generic Package from an exact SHA and prove its checksum.
  • Consume it by exact version, not by a moving alias.
  • Record identity/authentication/pipeline evidence without secrets.
  • Demonstrate one intentional denial or mutable-reference failure and diagnose the correct layer.
  • Document same-bytes promotion and exact cleanup/retention choice.

1. Scenario

You own a synthetic CLI payload built in a disposable GitLab project. A staging consumer and a production manifest must refer to the same verified bytes. You will publish version 0.0.0-ch23-lab.<pipeline-id>, verify it from a clean download, create a promotion manifest, and then diagnose either a cross-project authorization denial or an intentionally mutable alias mismatch.

Safety boundary: the checkpoint may mutate only the disposable package namespace glci-ch23-demo with a guarded 0.0.0-ch23-lab. version. Container publication is optional. No production registry, broad PAT, or privileged shared runner is required.

2. Assumptions and preflight

Item Mandatory path assumption
GitLab tier Free-compatible Generic Package workflow.
Runner Any runner able to execute shell + curl + SHA-256 tool; no privileged Docker required.
Identity Same-project CI_JOB_TOKEN for package publish/download.
Data Synthetic text only.
External systems None. Dependency Proxy/container portions may be inspected/simulated.
set -eu
git status --short
git rev-parse HEAD
printf 'source=%s ref=%s sha=%s pipeline=%s job=%s\n' \
  "$CI_PIPELINE_SOURCE" "$CI_COMMIT_REF_NAME" "$CI_COMMIT_SHA" \
  "$CI_PIPELINE_ID" "$CI_JOB_ID"

PACKAGE_NAME="glci-ch23-demo"
PACKAGE_VERSION="0.0.0-ch23-lab.${CI_PIPELINE_ID}"
case "$PACKAGE_VERSION" in 0.0.0-ch23-lab.*) ;; *) exit 2;; esac

3. Predict before mutation

Prediction How you will verify independently
A new exact package version will exist and be tied to this producer pipeline/SHA. Package coordinate/API/UI + evidence file containing producer pipeline/job/SHA.
Consumer bytes will equal producer bytes. Independent SHA-256 after download.
Promotion will not rebuild. Staging/production manifest contains same exact package version + checksum.
Unauthorized cross-project request will remain denied until target authorization is changed. Preserved HTTP status + target allowlist/role inspection.

4. Exact checkpoint pipeline

stages: [build, publish, verify, promote]

default:
  image: alpine:3.20

build_payload:
  stage: build
  script:
    - mkdir -p dist evidence
    - printf 'chapter=23\nsha=%s\npipeline=%s\n' "$CI_COMMIT_SHA" "$CI_PIPELINE_ID" > dist/payload.txt
    - sha256sum dist/payload.txt | tee evidence/producer.sha256
  artifacts:
    paths: [dist/, evidence/]
    expire_in: 7 days

publish_package:
  stage: publish
  needs: [build_payload]
  script:
    - export PACKAGE_NAME="glci-ch23-demo"
    - export PACKAGE_VERSION="0.0.0-ch23-lab.${CI_PIPELINE_ID}"
    - export URL="${CI_API_V4_URL}/projects/${CI_PROJECT_ID}/packages/generic/${PACKAGE_NAME}/${PACKAGE_VERSION}/payload.txt"
    - curl --fail --location --header "JOB-TOKEN: ${CI_JOB_TOKEN}" --upload-file dist/payload.txt "$URL"

verify_package:
  stage: verify
  needs: [build_payload, publish_package]
  script:
    - export PACKAGE_NAME="glci-ch23-demo"
    - export PACKAGE_VERSION="0.0.0-ch23-lab.${CI_PIPELINE_ID}"
    - export URL="${CI_API_V4_URL}/projects/${CI_PROJECT_ID}/packages/generic/${PACKAGE_NAME}/${PACKAGE_VERSION}/payload.txt"
    - curl --fail --location --header "JOB-TOKEN: ${CI_JOB_TOKEN}" --output payload.downloaded "$URL"
    - test "$(sha256sum payload.downloaded | cut -d' ' -f1)" = "$(cut -d' ' -f1 evidence/producer.sha256)"

promote_manifest:
  stage: promote
  needs: [build_payload, verify_package]
  script:
    - mkdir -p promotion
    - printf 'package=glci-ch23-demo\nversion=0.0.0-ch23-lab.%s\nsha256=%s\n' "$CI_PIPELINE_ID" "$(cut -d' ' -f1 evidence/producer.sha256)" > promotion/staging.env
    - cp promotion/staging.env promotion/production-candidate.env
    - diff -u promotion/staging.env promotion/production-candidate.env
  artifacts:
    paths: [promotion/]
    expire_in: 7 days

The checkpoint uses Alpine as a small execution image; for strict supply-chain environments, replace the moving image tag with an organization-approved image digest and record it in the evidence packet. This runner image is tooling, not the artifact being promoted.

5. Expected observations

Step Expected evidence What it proves
Build producer.sha256 + source SHA Exact bytes were identified before publication.
Publish Successful upload for unique version Registry state changed at a bounded coordinate.
Verify Downloaded hash equals producer hash Consumer receives the same bytes.
Promote Staging and production-candidate manifests identical Promotion changed eligibility/reference, not bytes.

6. Inject exactly one failure

Option A — Authorization denial

Attempt a read from a private disposable target project that has not allowlisted the source project. Preserve the denial, inspect target job-token settings and actor permission, then either leave it intentionally denied or make the smallest authorized allowlist change. Do not use a broad PAT.

Option B — Mutable alias mismatch

Locally create an alias file named latest.pointer that first points to the checkpoint checksum, then intentionally change it to another synthetic checksum while the immutable version remains unchanged. Diagnose that the alias is mutable and restore consumers to the exact version/checksum rather than “fixing” the package.

7. Optional container extension

If the project has a safe container-capable runner, build the scratch image from Lesson 2, push tag ch23-lab-$CI_PIPELINE_ID, capture its repository digest, then create staging/production manifests that record the same digest. The checkpoint passes without this extension.

8. Required evidence packet

Evidence Required fields
source.env Pipeline source/ref/SHA, pipeline ID, job IDs; no secrets.
producer.sha256 Producer checksum.
Package coordinate Project, package name, exact version, filename.
Publication result HTTP/API success and producer job identity.
consumer.sha256 Independent downloaded checksum.
Authorization evidence Token class, source/target project, allowlist assumption, denial status if exercised.
Promotion manifest Exact version/checksum or image digest for stage/prod candidate.
Retention/cleanup note Keep/delete decision and exact guarded object identity.
Assumptions GitLab offering/tier, runner/executor/image/tool versions, optional features used.

9. Cleanup / rollback

For maximum auditability, you may retain the lab package until its evidence artifact expires. If deleting it, locate the exact package record corresponding to PACKAGE_NAME + PACKAGE_VERSION, record its ID, assert the version prefix, and delete only that record. If request forwarding is enabled, document dependency-confusion implications before deleting a package that downstream clients might request.

case "$PACKAGE_VERSION" in
  0.0.0-ch23-lab.*) ;;
  *) echo 'Refusing cleanup outside Chapter 23 lab namespace' >&2; exit 2 ;;
esac
printf 'approved cleanup coordinate=%s/%s\n' "$PACKAGE_NAME" "$PACKAGE_VERSION"
# Resolve one exact package ID through authorized UI/API, review it, then delete only that ID.

10. Verification checklist

  • Source SHA and producer pipeline/job IDs are recorded.
  • Package version is unique and guarded.
  • Producer checksum exists before publication.
  • Consumer downloads the exact version.
  • Consumer checksum equals producer checksum.
  • Promotion manifests reference identical bytes.
  • No credential value appears in traces/artifacts.
  • Any authorization failure is explained by allowlist/permission evidence.
  • Cleanup targets one exact lab object or is intentionally skipped.

Knowledge check

Which two independent facts are the minimum proof that the registry did not change the artifact?

Why does the production-candidate manifest copy the staging coordinate instead of rebuilding?

An unauthorized cross-project download fails. Which configuration should you inspect before tokens?

If a human-readable tag moves but the digest remains stored, what should the deployment use?

Why might you intentionally skip cleanup?

11. What Chapter 23 adds to the production operating model

You now have a distribution contract linking source SHA, producer pipeline, authentication identity, registry/package coordinate, checksum or manifest digest, consumer verification, same-bytes promotion, and retention. That means a release or deployment can point to a durable object whose bytes can be independently reproduced and audited later.

Chapter 24 builds on this evidence discipline by teaching test reports, coverage, JUnit, code-quality and browser/accessibility feedback as structured pipeline evidence rather than unstructured log text.

Next chapter

Test Reports, Coverage, JUnit, Code Quality, Browser Performance, Accessibility, and Pipeline Feedback

Move from distributing exact build outputs to ingesting structured quality evidence, preserving report identity and distinguishing job success from report parsing and merge-request feedback.

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-12. Generic Packages, Package Registry, Container Registry, Dependency Proxy, protected packages, and protected container tags are documented for Free/Premium/Ultimate unless noted otherwise. Current immutable container-tag rules are Ultimate-only. Dependency Proxy is group-level and supports Docker Hub images. The checkpoint is fully completable on Free with Generic Packages and CI_JOB_TOKEN. Container publication, protected-package/tag administration, external repository managers, and Ultimate tag immutability remain optional extensions.

Current assumptions used in this chapter: Version-sensitive YAML, Runner/executor behavior, APIs, security features, policy controls, tiers, and deprecations must be verified against the exact GitLab, GitLab Runner, tool, and external-system versions used in production. Preserve the exact project/ref/SHA, compiled configuration, pipeline/job IDs, runner/tool versions, and external target evidence used for reproducible work.

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
0x716c4Ab160C4B66F31a28AE2448BfF68fc3a2ef0 Send only Ethereum/ERC-20 compatible assets to this address.