Files
cozystack/docs/release/tag-immutability.md
T
Myasnikov Daniil 35a4f7c5d9 docs(release): split the release document into one page per topic
docs/release.md had grown into one page that mixed the release model,
the CI pipeline, changelogs, signatures, nightlies and the fires seen
at release time. A reader looking for one of them read all of it, and
an edit to one part reflowed the rest. It now indexes one page per
topic under docs/release/.

The pages already describe how fixes reach a release line through the
backport batch, which the following commits add. Workflows, scripts
and tests that named docs/release.md now point at the page that holds
the text they mean.

Assisted-By: LLM
Signed-off-by: Myasnikov Daniil <myasnikovdaniil2001@gmail.com>
2026-11-04 15:03:06 +00:00

3.9 KiB

Tag immutability

Published tags are write-once — once a vX.Y.Z or rc tag is pushed it is never moved or deleted. (Nightlies are not git tags at all — they are GHCR OCI tags, see Nightly builds.) Moving a tag silently poisons the Go module proxy / pkg.go.dev cache for api/apps/v1alpha1/vX.Y.Z and drifts SBOM/provenance toolchains, so the release flow is built so a move is impossible by construction:

File Tag / branch handling
pull-requests-release.yaml Verifies the pinned packages candidate before creating a stable name. Creates vX.Y.Z at the PR merge commit write-once (create if absent, no-op if unchanged, fail loudly if it would move — the merge commit is new, so there is nothing to move). Cuts the api/apps/v1alpha1/<vTAG> Go-submodule tag write-once at the same commit, stable only (never rc/beta/alpha). The release-X.Y maintenance branch is fast-forward-only. Retags the verified packages artifact and rc container digests to the stable image tag (also write-once at the image level).
tags.yaml The release-X.Y.Z-rc.N staging branch is a mutable staging ref (compare-before-force: skipped when unchanged, force+log only when it genuinely moves). Fails fast on a stable vX.Y.Z tag with no pre-existing draft — stable tags come from promote-rc.yaml, never a hand push.
promote-rc.yaml Mutates no stable tags. Before staging, verifies green full E2E from the latest rc tag-push run or a tag-correlated manual published-tag run (unless loudly bypassed). Commits and publishes the rewritten packages tree under a run-specific temporary promotion-* tag, adds a second commit pinning its digest on the release-X.Y.Z branch, generates the changelog there, and opens the promote PR with release but no automatic full-e2e; the PR gate verifies the candidate and finalize repeats that verification before the stable tag and image retag. The staging branch is force-pushed on re-dispatch, but an existing docs/changelogs/vX.Y.Z.md is preserved across the rebuild.

This is the immutable-tag + rc-promotion model from #2677: stable vX.Y.Z is created only by promoting an existing rc, rc tags are write-once, and api/apps/v1alpha1/vX.Y.Z is created only on a stable release. The old nightly auto-release.yaml (which delete-recreated auto-bumped patch tags) has been removed. rc and stable tags accrete permanently; the only churning artifacts are GHCR nightlies and abandoned temporary promotion candidates, which retention.yaml prunes (see Nightly builds) — GHCR storage is the cost vector to watch.

Repo-side enforcement (tag-protection rulesets)

The guarantees above are enforced in the workflows. Repo admins back them with two GitHub tag rulesets targeting v*:

  • Immutability — Restrict updates + Restrict deletions, no bypass. No actor (not even the CI app) can move or delete a shipped v* tag. Safe to enable at any time: nothing in the release flow ever moves or deletes a v* tag, it only creates new ones.
  • Creation control — Restrict creations with the CI app as the sole bypass actor, plus Limit branches/tags updated in a single push = 1. This turns "don't hand-push tags" from convention into hard enforcement: every v* tag — pre-release and stable — can then be created only by CI. Enable this only after cut-prerelease.yaml is in use, since that workflow is what makes CI the sole creator of pre-release tags; enabling it earlier would lock maintainers out of cutting rc/alpha/beta. (The Go-module tag api/apps/v1alpha1/* gets an equivalent pair of rulesets.)