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>
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 av*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 aftercut-prerelease.yamlis 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 tagapi/apps/v1alpha1/*gets an equivalent pair of rulesets.)