Chapter 22Lesson 05~200 minutes

Checkpoint Lab — Tags, Releases, Release CLI, Changelogs, Evidence, Asset Links, and Release-Orchestration Pipelines

Publish and verify a disposable versioned release from an exact SHA, prove artifact digest and evidence, and safely supersede or remove only that identified release.

CheckpointExact SHADigestEvidence packetSuperseding

Learning objectives

Checkpoint outcomes

  • Publish a disposable release whose tag resolves to a pre-recorded exact SHA.
  • Verify that the released asset digest equals the digest captured before publication.
  • Capture GitLab release/evidence metadata and pipeline/job/tool identity without exposing credentials.
  • Demonstrate a safe superseding/update path and an exact guarded cleanup path.
  • Produce a compact evidence packet another engineer can independently audit.

1. Mission and success criteria

You are the release engineer for a synthetic project. Build candidate A once, record its SHA and digest, publish a disposable version from that exact identity, and prove that a consumer receives the same bytes. Then simulate a metadata correction or publish a superseding lab version. Finally, remove only the identified release record if cleanup is required.

Checkpoint rule: if you cannot state the exact tag, source SHA, artifact SHA-256, release/pipeline/job identity, and cleanup target before a mutation, you are not ready to perform the mutation.

2. Assumptions and preflight

Item Mandatory assumption / evidence
Project Disposable project you own/control, or local faithful simulation.
Tier Mandatory path uses Free-capable release/changelog/protected-tag concepts. No Premium-only feature is required.
Runner/executor Any authorized runner capable of Git/shell; record executor/image if using CI.
CLI Current docs: glab >= 1.58.0 for the release-keyword path. Record exact version.
Credentials Use CI_JOB_TOKEN for supported release API/CLI calls; examples contain no real secrets.
Namespace Only tags matching v0.0.0-ch22-lab.* may be created/deleted by this lab.
git status --short
git rev-parse HEAD
git --version
glab --version || true
printf 'source=%s ref=%s sha=%s pipeline=%s job=%s\n' \
  "${CI_PIPELINE_SOURCE:-local}" "${CI_COMMIT_REF_NAME:-local}" \
  "${CI_COMMIT_SHA:-$(git rev-parse HEAD)}" "${CI_PIPELINE_ID:-local}" "${CI_JOB_ID:-local}"

3. Predict state changes before execution

Prediction Before After expected Independent verification
Tag identity No lab tag One exact LAB_TAG points to TARGET_SHA git rev-parse "$LAB_TAG^{}".
Release record No release for lab tag One release references the same tag/SHA glab release view or Releases API.
Artifact identity Local verified digest exists Consumer download has identical digest sha256sum -c on re-downloaded asset.
Evidence Only local evidence files GitLab evidence path + local manifest/pipeline IDs retained Release API JSON plus evidence packet.
Cleanup No mutation selected At most one exact release record removed; tag removal separate GET exact release/tag after action.

4. Setup exact candidate and evidence manifest

set -eu
mkdir -p release-assets checkpoint-evidence
printf 'chapter22 checkpoint payload\n' > release-assets/checkpoint.txt

git add release-assets/checkpoint.txt
git commit -m "Prepare Chapter 22 checkpoint payload

Changelog: added" || true

TARGET_SHA="$(git rev-parse HEAD)"
ARTIFACT_SHA256="$(sha256sum release-assets/checkpoint.txt | awk '{print $1}')"
LAB_ID="${CI_PIPELINE_ID:-$(date +%Y%m%d%H%M%S)}"
LAB_TAG="v0.0.0-ch22-lab.${LAB_ID}"

printf 'TARGET_SHA=%s\nARTIFACT_SHA256=%s\nLAB_TAG=%s\n' \
  "$TARGET_SHA" "$ARTIFACT_SHA256" "$LAB_TAG" \
  | tee checkpoint-evidence/identity.env

Do not proceed if git status --short reveals unrelated uncommitted content that could confuse the candidate.

5. Bind the version to the exact SHA

case "$LAB_TAG" in v0.0.0-ch22-lab.*) ;; *) exit 2 ;; esac

git tag -a "$LAB_TAG" "$TARGET_SHA" -m "Chapter 22 disposable checkpoint"
RESOLVED_SHA="$(git rev-parse "$LAB_TAG^{}")"
printf 'resolved=%s expected=%s\n' "$RESOLVED_SHA" "$TARGET_SHA" \
  | tee checkpoint-evidence/tag-resolution.txt
test "$RESOLVED_SHA" = "$TARGET_SHA"

Real GitLab path: after review, push only the exact lab tag. Local simulation: retain the local tag and continue; the evidence packet must state that no server-side release was created.

6. Generate or simulate bounded changelog notes

PREVIOUS_REF="${PREVIOUS_REF:-$(git rev-list --max-parents=0 HEAD)}"
VERSION="${LAB_TAG#v}"

if command -v glab >/dev/null 2>&1 && [ -n "${CI_API_V4_URL:-}" ]; then
  glab changelog generate \
    --from "$PREVIOUS_REF" \
    --to "$TARGET_SHA" \
    --version "$VERSION" > checkpoint-evidence/release-notes.md
else
  {
    printf '## %s (local simulation)\n\n' "$VERSION"
    git log "$PREVIOUS_REF..$TARGET_SHA" --format='- %h %s'
  } > checkpoint-evidence/release-notes.md
fi
printf '\nSource SHA: `%s`\nArtifact SHA-256: `%s`\n' \
  "$TARGET_SHA" "$ARTIFACT_SHA256" >> checkpoint-evidence/release-notes.md

7. Publish the exact disposable release

Use one publication mechanism. This checkpoint uses glab because it makes exact --ref and later metadata update behavior visible. In CI, enable job-token auto-login.

export GLAB_ENABLE_CI_AUTOLOGIN=true

# Guard that the tag target is still the verified candidate.
test "$(git rev-parse "$LAB_TAG^{}")" = "$TARGET_SHA"

RAW_URL="${CI_PROJECT_URL}/-/raw/${TARGET_SHA}/release-assets/checkpoint.txt"
ASSET_JSON=$(printf '[{"name":"checkpoint.txt","url":"%s","link_type":"other"}]' "$RAW_URL")

glab release create "$LAB_TAG" \
  --ref "$TARGET_SHA" \
  --name "Disposable Chapter 22 $LAB_TAG" \
  --notes-file checkpoint-evidence/release-notes.md \
  --assets-links "$ASSET_JSON" \
  --repo "$CI_PROJECT_PATH"

If the tag is already pushed and exact, --ref remains useful documentation of intent. If the release already exists unexpectedly, stop and inspect it; do not treat an update as equivalent to first publication without understanding the existing state.

8. Verify server-side release/evidence identity

GLAB_ENABLE_CI_AUTOLOGIN=true \
  glab release view "$LAB_TAG" --repo "$CI_PROJECT_PATH" \
  | tee checkpoint-evidence/release-view.txt

curl --fail --silent --show-error \
  --header "JOB-TOKEN: $CI_JOB_TOKEN" \
  "$CI_API_V4_URL/projects/$CI_PROJECT_ID/releases/$LAB_TAG" \
  | tee checkpoint-evidence/release.json

# Extract/display only non-secret fields if jq is available.
if command -v jq >/dev/null 2>&1; then
  jq '{tag_name,name,commit:.commit.id,evidence_file_path:.assets.evidence_file_path}' \
    checkpoint-evidence/release.json
fi

Verify that the API’s commit identity and the tag resolution agree with TARGET_SHA. Record the evidence file path. Do not claim the evidence snapshot proves external asset immutability.

9. Consumer verification: re-download and hash

curl --fail --location "$RAW_URL" -o checkpoint-evidence/downloaded.txt
printf '%s  %s\n' "$ARTIFACT_SHA256" checkpoint-evidence/downloaded.txt \
  > checkpoint-evidence/expected-download.sha256
sha256sum -c checkpoint-evidence/expected-download.sha256

This is the checkpoint’s strongest end-to-end assertion: a consumer using the immutable asset coordinate obtains the same bytes that were hashed before publication.

10. Metadata correction or superseding release

First demonstrate that metadata can change without changing the tag/SHA. For example, add a clarification to the notes with glab release create on the same tag (which updates existing release information) or the Releases API PUT. Confirm afterward that the resolved tag SHA and artifact digest are unchanged.

For a true code/artifact correction, do not move the old public tag. Create a new candidate, new digest, and new version/tag such as $LAB_TAG.1 in the disposable namespace, then mark the old notes as superseded if desired.

11. Intentional failure: authorization or wrong cleanup target

Choose one: (A) in an authorized disposable GitLab project, attempt a protected-tag/release operation as an ineligible actor and preserve the denial; or (B) locally set BAD_TAG=v1.0.0 and prove the cleanup guard refuses it. Option B is mandatory-safe and requires no second account.

BAD_TAG="v1.0.0"
case "$BAD_TAG" in
  v0.0.0-ch22-lab.*) echo "unexpectedly allowed"; exit 1 ;;
  *) echo "PASS: cleanup guard rejected non-lab tag $BAD_TAG" ;;
esac

12. Bounded cleanup / rollback

Preserve the evidence packet before cleanup. Then delete only the exact release record created by this lab. Remember: the Releases API does not delete the associated Git tag.

case "$LAB_TAG" in v0.0.0-ch22-lab.*) ;; *) exit 2 ;; esac
test "$(git rev-parse "$LAB_TAG^{}")" = "$TARGET_SHA"

curl --fail --request DELETE \
  --header "JOB-TOKEN: $CI_JOB_TOKEN" \
  "$CI_API_V4_URL/projects/$CI_PROJECT_ID/releases/$LAB_TAG"

# Verify release record is gone (expect not-found response).
# Keep or separately delete the lab tag according to your lab policy.
# Never delete any tag outside the exact chapter namespace.

If you choose to keep the release for course evidence, skip deletion and document that choice. Cleanup must be deliberate, not automatic destruction of useful evidence.

13. Required evidence packet

File / record Must contain
identity.env Exact tag, source SHA, artifact SHA-256.
tag-resolution.txt Resolved tag target equals source SHA.
release-notes.md Bounded change description + immutable identity fields.
release.json Release tag/name/commit/evidence path; no tokens.
Pipeline/job record Pipeline source/ref/SHA, pipeline ID, job ID, actor, runner/executor/image/tool version.
expected-download.sha256 Consumer-side verification of exact bytes.
Authorization/failure evidence Denied actor or cleanup-guard refusal, if applicable.
Assumptions note Tier/offering, simulation vs live path, limitations, cleanup decision.

14. Final verification checklist

  • Release tag was created from or verified against TARGET_SHA.
  • Artifact digest was captured before release publication.
  • Release publication used an explicit tag/ref and recorded tool/token class.
  • Release API commit identity matches tag/source identity.
  • Asset coordinate is immutable/versioned and re-download hash matches.
  • No secret was printed or embedded in notes/URLs.
  • Any superseding release has a new identity rather than a moved production tag.
  • Cleanup targets only the exact Chapter 22 lab release/tag namespace.

Knowledge check

Which two predictions must be proven independently before calling the release reproducible?

Why is the release evidence JSON not sufficient by itself?

If the release notes are wrong but the artifact/tag are correct, what layer should you change?

If a real artifact defect is found after publication, should you force-move the existing production tag?

What does deleting the exact release record through the Releases API leave behind?

15. What Chapter 22 adds to the production operating model

You now have a release contract that links source SHA, tested artifact digest, version/tag, publication identity, GitLab evidence, asset coordinates, consumer verification, authorization, and recovery. That closes the gap between “pipeline produced something” and “a versioned object can be independently verified later.”

Chapter 23 extends the same contract into GitLab’s Container Registry, Package Registry, Generic Packages, and Dependency Proxy, where immutable versions and digests become the durable distribution layer behind release records.

Next chapter

Container Registry, Package Registry, Generic Packages, Dependency Proxy, and Artifact Promotion

Move from release metadata and links to versioned distribution systems: authenticate explicitly, publish packages/images, verify digests, promote by immutable identity, and apply retention without destroying provenance.

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. Releases, changelogs, the Releases API, release links, base release evidence, and role-based protected tags are documented across Free/Premium/Ultimate unless a narrower tier is explicitly called out. The current YAML release flow uses glab; current docs require glab 1.58.0 or later. release-cli was deprecated in GitLab 18.0 and is planned for removal in 20.0. The checkpoint requires only Free-capable release primitives or a local simulation. Paid/admin-only release governance is optional and must remain inside an authorized disposable project.

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.