Pre-release tags (vX.Y.Z-alpha.N / -beta.N / -rc.N) were cut by a manual
`git push origin HEAD:refs/tags/<tag>`. That leaves the immutable-tag work in
#2677 only partly enforceable: a repo ruleset cannot restrict `v*` tag
*creation* to CI while still letting maintainers hand-push pre-releases (fnmatch
has no way to match "v* except v*-rc.*"), so "do not hand-cut stable tags" stays
a matter of discipline backed only by an in-workflow guard.
Add cut-prerelease.yaml — a workflow_dispatch that becomes the sole entry point
for creating a pre-release tag:
- Generates the CozyStack CI app token (same app as promote-rc.yaml) and pushes
the tag as the app, so tags.yaml fires (a tag pushed with the default
GITHUB_TOKEN would not trigger it) and the push-event base_ref is populated
(tags.yaml's Get-base-branch step needs it).
- Validates ^v\d+\.\d+\.\d+-(alpha|beta|rc)\.\d+$ and rejects stable vX.Y.Z —
stable tags are cut write-once by the promote flow at a PR merge commit.
- Enforces dispatch from main or the matching release-X.Y branch, and refuses to
move an existing tag (write-once).
- Pushes HEAD:refs/tags/<tag> from the branch tip — the exact form
docs/release.md prescribes — on ubuntu-latest (no build runner needed).
With every pre-release cut by CI, repo admins can then enable a "Restrict
creations" tag ruleset on `v*` with the CI app as the sole bypass actor, turning
tag-creation discipline into hard enforcement.
Also update tags.yaml's two human-facing error messages and docs/release.md (the
RC and patch flows, the workflow list, and a new repo-side tag-ruleset
enforcement note) to point at the workflow instead of a manual push.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Signed-off-by: Myasnikov Daniil <myasnikovdaniil2001@gmail.com>
Promotion mutated the registry at dispatch time — retagging ~37 images to
vX.Y.Z (+:latest), publishing the stable cozy-installer chart — before the
promote PR's e2e ran and before any merge. An abandoned promotion then left
stable-named bytes for a version that never shipped, and the (correct) image
write-once check wedged any later re-promotion to the same version.
Move every irreversible registry side effect into pull-requests-release.yaml
finalize, which runs only after the promote PR merged (= after its e2e passed),
making promotion atomic. promote-rc.yaml now only stages the release-X.Y.Z
branch, drafts the stable release, and opens the PR; an abandoned promotion
leaves nothing but an overwritable draft + branch.
Folded into the same restructure:
- Cut the api/apps/v1alpha1/vX.Y.Z Go-module tag in finalize, at the stable
tag's commit. It was gated in tags.yaml on `release_exists==false &&
is_stable`, unsatisfiable under promotion (the draft always pre-exists), so
promoted stables never got their submodule tag — a regression for Go
consumers. Finalize is where the stable tag is born, so it cuts the matching
submodule tag write-once (stable only).
- Gate :latest by max-semver. promote-retag.sh now moves :latest only when
MOVE_LATEST=1 (default off); finalize passes it the same make_latest decision
the release gets, so a patch on an older line moves neither the release's
`latest` nor the images' :latest. Added MOVE_LATEST bats coverage.
- Stamp platformVersion into the published stable chart's default values
(uncommitted, at package time) so `helm --install --version X.Y.Z` reports the
stable version in telemetry instead of the rc version baked into the operator
image. Not committed to the branch — that would leak platformVersion=vX.Y.Z
onto main and mis-stamp the next rc.
- promote-rc: serialize all promotions (static concurrency group, not per-rc);
label the PR full-e2e so the full suite runs; scope the rc->stable sed to the
depth-2 values.yaml glob plus the kubernetes .tag files (the build's own write
set); tolerate a leftover draft so re-promotion is not wedged.
- finalize: assert the PR author is cozystack-ci[bot], and run on an ephemeral
oracle-vm runner (needs skopeo/helm).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Signed-off-by: Myasnikov Daniil <myasnikovdaniil2001@gmail.com>
nightly.yaml publishes each build under 0.0.0-nightly.<date>.<run-id>.<attempt>
(the run-id + attempt suffix makes every nightly tag immutable), but the docs
still showed the old date-only 0.0.0-nightly.<YYYYMMDD> form in the mirror-stage
description and both the helm-install and oras-pull pin examples. Update them to
the full form; the floating `nightly` example is unchanged.
Assisted-By: Claude <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Signed-off-by: Myasnikov Daniil <myasnikovdaniil2001@gmail.com>
"What CI does on a tag push" only described one of the four triggers;
promote-rc.yaml uses manual dispatch and pull-requests-release.yaml fires
on PR merge. Rename to "What CI does during the release process" so
maintainers find the right entry point regardless of which workflow they
are debugging.
Assisted-By: Claude <noreply@anthropic.com>
Signed-off-by: Myasnikov Daniil <myasnikovdaniil2001@gmail.com>
Address review feedback from coderabbitai on docs/release.md: the Regular and
Patch sections still told maintainers to push vX.Y.Z to start the build, while
the rest of the doc said the stable tag is created write-once at the merge
commit and stable comes only from rc promotion (with a 'can also push directly'
escape hatch). Rewrite both sections to the single promotion model — push an rc,
promote it via promote-rc.yaml, merge the promote PR, finalize creates the
write-once tag — drop the direct-push escape hatch, and add promote-rc.yaml to
the workflow list.
Assisted-By: Claude <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Signed-off-by: Myasnikov Daniil <myasnikovdaniil2001@gmail.com>
Replace the nightly-rc-tag description with the new model: a nightly is
an installable copy of main on GHCR (mirror OCIR->GHCR by digest +
re-published packages artifact, cozy-installer chart and nocloud disk),
validated by the full e2e suite, with no GitHub release and no Go-module
tag. Document the helm install path and the dated/floating tag scheme,
and update the Tag-immutability section and workflow link list.
Assisted-By: Claude <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Signed-off-by: Myasnikov Daniil <myasnikovdaniil2001@gmail.com>
The release doc still described the removed force-retag flow and auto-release.yaml
cron. Update it to the model this PR ships: stable tags are created write-once at
the PR merge commit (never force-moved), the maintenance branch is
fast-forward-only, stable patches come only from rc promotion (nightly-rc.yaml +
promote-rc.yaml + retention.yaml), and the api/apps/v1alpha1 Go-module tag is cut
only for stable. Replaces the "Automated patch tag" and "Force-retagging"
sections (the latter becomes "Tag immutability") and refreshes the See-also list.
Signed-off-by: Myasnikov Daniil <myasnikovdaniil2001@gmail.com>
Address review feedback from lexfrei on docs/release.md: the Patch Releases section described only the manual process, so add an "Automated Patch Releases" subsection covering the nightly cron, the new-commits trigger, the 2-newest-lines support window, and the manual tag-push escape hatch for EOL lines.
Assisted-By: Claude <noreply@anthropic.com>
Signed-off-by: Myasnikov Daniil <myasnikovdaniil2001@gmail.com>