Chapter 22Lesson 01~165 minutes

Tags, Releases, Release CLI, Changelogs, Evidence, Asset Links, and Release-Orchestration Pipelines: Concepts, Architecture, and Mental Model

Model a release as an immutable relationship among commit SHA, tag, verified artifact digest, release record, evidence, asset links, authorization, and consumer verification.

ReleasesTagsEvidenceImmutable identityAuthorization

Learning objectives

  • Explain why a Git tag, a GitLab release record, an artifact, and a deployment are distinct objects.
  • Trace a release from exact commit SHA and verified artifact digest through tag, release record, evidence, asset link, and consumer verification.
  • Describe current release keyword and glab behavior, including the danger of implicit tag creation from a moving default branch.
  • Distinguish base release evidence from additional evidence collection and from your own digest/provenance evidence.
  • Inspect tag/ref/SHA, release identity, changelog/evidence, asset identity, and authorization without mutating the project.

1. The practical problem: “v1.4.0 exists” does not prove what was shipped

Chapter 19 separated deployment-job success, GitLab deployment history, and real target health. Chapter 21 added deployment authorization. Release engineering adds another trust boundary: the version label that humans consume must resolve to the exact commit and exact bytes that were verified. A release page can look complete while its tag points at the wrong commit, its binary was rebuilt later, or an asset link silently points at mutable content.

The safe release question is therefore not “did the release job turn green?” It is “which SHA was tagged, which bytes were approved, which identity created the record, what evidence was captured, and can a consumer independently retrieve and hash the same bytes?”

Core rule: a version is trustworthy only when source identity, artifact identity, publication identity, and consumer verification all converge on the same immutable release candidate.

2. Terms before configuration

Term Meaning here Do not confuse it with
Git tag A repository ref that names a commit (lightweight) or tag object (annotated). A GitLab release record. A tag can exist without a release.
Release GitLab metadata associated with a tag: name, notes, evidence, assets, timestamps, milestones. The artifact bytes themselves.
Release job A CI job using the YAML release keyword. Current GitLab executes release creation through glab. An ordinary script-only job that happens to run on a tag.
glab GitLab CLI. glab release create can create or update a release and should be given an explicit --ref when it may create a tag. Deprecated release-cli.
Release evidence GitLab JSON snapshot collected for a release. Base evidence is generated with the release; some extra evidence capabilities have narrower tiers/offerings. A cryptographic signature or proof that your binary matches source.
Asset link A URL displayed with a release, optionally with a direct asset path/type. Immutable storage automatically. The target can still be mutable.
Changelog Markdown generated from commit titles/trailers or curated by humans. Provenance. Notes describe change; they do not hash the artifact.
Protected tag Rule limiting who may create matching tags and preventing accidental mutation/deletion. A guarantee the release artifact itself is immutable.

3. State model: record all identities before publication

State layer Evidence to capture Why release correctness depends on it
Source/revision CI_PIPELINE_SOURCE, ref, CI_COMMIT_SHA, tag object/target SHA Proves exactly which repository state the release claims to represent.
Compiled configuration Merged YAML, release-job inclusion/rules, image/tool identity Proves which orchestration GitLab actually compiled.
Job/runner Pipeline/job IDs, actor, runner/executor, glab --version Separates configuration intent from execution context.
Identity/trust CI_JOB_TOKEN or narrowly scoped alternative, protected-tag rule Shows who was authorized to create/update the tag/release.
Artifact/evidence Artifact SHA-256, evidence snapshot path, changelog input range Connects release metadata to exact bytes and review evidence.
External/distribution Asset URL, immutable path/digest, consumer re-download digest Proves consumers retrieve the same object you verified.
Governance/recovery Exact tag/release ID, superseding record, deletion decision Makes rollback or cleanup bounded instead of “delete latest”.

4. Mental model: verified candidate → immutable identity → release record → consumer proof

Start with a verified commit and artifact digest. Choose an exact version/tag name and bind that tag to the intended SHA. The release job, glab, or Releases API then creates the GitLab release record. GitLab captures release evidence and displays asset links. A consumer fetches the exact tagged source or immutable asset and verifies its digest. If a problem appears later, publish a new superseding version or delete only the explicitly identified lab release; do not mutate “latest” as if it were an identity.

flowchart LR
  A[Verified commit SHA] --> B[Verified artifact digest]
  B --> C[Exact tag/version]
  C --> D[release keyword / glab / API]
  D --> E[GitLab release record]
  E --> F[Evidence + changelog + asset links]
  F --> G[Consumer fetches exact version]
  G --> H[Digest / provenance verification]
  H --> I[Supersede, deprecate, or bounded cleanup]

Each arrow is a contract. If the tag is implicit, if the asset URL moves, or if cleanup uses “latest,” the chain no longer identifies one reproducible object.

5. Current release-job behavior: declarative, but still stateful

The current CI/CD YAML reference documents release as a job keyword. The job must still have a script; the release action runs after the main script succeeds and before after_script. The runner must have a sufficiently recent glab in PATH. If a release with the same tag already exists, the YAML release action does not silently update it; release creation fails, which is useful protection against accidental rewriting.

release_candidate:
  stage: release
  image: registry.gitlab.com/gitlab-org/cli:latest
  rules:
    - if: '$CI_COMMIT_TAG =~ /^v\d+\.\d+\.\d+(-[0-9A-Za-z.-]+)?$/'
  script:
    - glab --version
    - printf '%s  %s\n' "$ARTIFACT_SHA256" "dist/app.tar.gz"
  release:
    tag_name: "$CI_COMMIT_TAG"
    name: "Release $CI_COMMIT_TAG"
    description: "release-notes.md"

If release:tag_name names a tag that does not yet exist, GitLab can create that tag. The safer production pattern is to make the ref relationship explicit: either trigger from a pre-created protected tag or provide an exact release:ref / glab --ref based on a recorded SHA.

6. glab and API identity: exact ref first, token second

glab release create can create a new release or update an existing one. Current CLI docs warn that if the tag does not exist and no --ref is supplied, the new tag can be created from the latest state of the default branch. That convenience is unacceptable when a release candidate was already verified at another SHA.

LAB_TAG="v0.0.0-ch22-lab.${CI_PIPELINE_ID}"
EXACT_SHA="$CI_COMMIT_SHA"
GLAB_ENABLE_CI_AUTOLOGIN=true \
  glab release create "$LAB_TAG" \
  --ref "$EXACT_SHA" \
  --name "Disposable $LAB_TAG" \
  --notes-file release-notes.md

The Releases API accepts CI_JOB_TOKEN in a JOB-TOKEN header. For glab in CI, the recommended job-token path is GLAB_ENABLE_CI_AUTOLOGIN=true. Do not set GITLAB_TOKEN to the value of CI_JOB_TOKEN; that variable is sent as a private token and the Releases API does not interpret it as a job token.

7. Evidence: GitLab snapshot plus your own byte identity

When a release is created, GitLab generates release evidence JSON. Treat that as a useful audit snapshot, not as a substitute for an artifact digest or signature. Your evidence packet should include the release’s tag/SHA, pipeline and job IDs, tool version, the exact artifact SHA-256, the release/evidence URL, and the final consumer-side digest check.

Evidence What it proves What it does not prove
Release evidence JSON Snapshot of GitLab data associated with the release. That an external binary has not changed after publication.
SHA-256 manifest Exact bytes of one artifact. Who authorized publication.
Protected-tag rule Who may create matching tag refs. That all release notes/assets are correct.
Pipeline/job IDs Execution lineage and timestamps. External consumer retrieved the intended object.
Consumer re-hash Downloaded bytes equal the published digest. Source-to-binary reproducibility unless the build is independently reproducible.

8. Changelogs: describe the change range explicitly

GitLab changelogs are Free-tier and can be generated from commit titles and Git trailers. glab changelog generate has existed since glab 1.30.0. For release automation, prefer explicit --from, --to, and --version inputs rather than relying on ambient HEAD or whichever tag happens to be considered latest.

PREVIOUS_TAG="v1.3.0"
TARGET_SHA="$CI_COMMIT_SHA"
TARGET_VERSION="1.4.0"

glab changelog generate \
  --from "$PREVIOUS_TAG" \
  --to "$TARGET_SHA" \
  --version "$TARGET_VERSION" \
  > release-notes.md

Generated notes are deterministic only to the extent that their input range and changelog configuration are pinned. Human curation can improve communication, but should not silently change the commit/artifact identity that the release represents.

9. Asset links: a link is not an immutable artifact

Release asset links may point to HTTP, HTTPS, or FTP resources. Their metadata can be stable while their target content changes. Direct job-artifact links are particularly fragile because job artifacts can expire or be manually removed. Prefer immutable package/container coordinates or another content-addressed/versioned store; Chapter 23 develops that distribution model in detail.

Safe lab pattern: link to content addressed by the exact commit SHA (for example, a committed synthetic file at a raw SHA URL) and publish its SHA-256 next to the link. Never call a moving URL such as /latest/ the release identity.

10. Inspect first: read-only checks before creating or changing anything

# Repository identity
git rev-parse HEAD
git tag --points-at HEAD
git show-ref --tags --dereference

# In CI: print identifiers, not secrets
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"

glab --version
GLAB_ENABLE_CI_AUTOLOGIN=true glab release list --repo "$CI_PROJECT_PATH"

Do not dump the environment to “see what token is present.” Inspect only non-secret identifiers and authorization results.

11. Common mistakes and safer patterns

Mistake Why it breaks release integrity Safer pattern
Create missing tag without explicit ref A CLI may tag the moving default branch instead of the verified candidate. Pre-create the tag or pass the exact SHA with --ref.
Rebuild during release Bytes can differ from the tested artifact. Promote/link the already verified artifact and its digest.
Use latest for cleanup Ordering is not identity; you may delete an unrelated release. Require the exact lab tag and guard its prefix/SHA.
Use broad PAT by default Credential can mutate unrelated project state. Prefer CI_JOB_TOKEN where the Releases API supports it; otherwise use the narrowest project identity.
Treat release evidence as artifact integrity Evidence snapshots are metadata, not a content-address guarantee. Publish and independently verify artifact digests/provenance.

12. Summary and evidence checklist

  • Record the exact SHA before creating a tag or release.
  • Bind the tag to that SHA explicitly.
  • Reuse the already verified artifact; do not rebuild for publication.
  • Record artifact digest, pipeline/job IDs, release tag, creator identity, evidence URL, and asset coordinates.
  • Use protected tags to narrow who can create release refs.
  • Let consumers fetch an exact version and verify its digest.
  • Supersede or delete by exact tag—not by “latest”.

Knowledge check

Why is glab release create v1.4.0 risky if v1.4.0 does not exist yet?

Does GitLab release evidence replace an artifact SHA-256?

What is the current recommended CI authentication path for glab with a job token?

A release asset URL is stable. Does that prove the file behind it is immutable?

Why should cleanup not select the “latest” release?

Next lesson

Guided hands-on workflow and core operations

Create a disposable pre-release tag from an exact SHA, generate bounded release notes, add a safe immutable asset link, inspect release/evidence metadata, and clean up only that lab release.

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. Base release evidence is available on all tiers; the API action to collect additional evidence snapshots is documented for Premium/Ultimate on Self-Managed/Dedicated, and inclusion of some report artifacts has narrower tier requirements.

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.