docs(release): tags become vYYYY.MM.DD.HHMM, and stop telling people to move them
The tag is now the artifact's own version name with a `v` in front, so `v2026.09.10.1432` and `2026.09.10.1432` are one string. Nothing has to reconcile what the tag claims against what the APK reports, and minting one is arithmetic on the tagged commit's timestamp rather than a lookup. The substantive change is the prose. release.yml's header instructed the reader to `git push -f origin vYYYY.MM.DD` on a same-day re-cut. That is the operation the family rulebook forbids outright, and it has incidents behind it — moving a same-day tag forward once took a published release down with it. Anyone who had installed from that tag was holding something it no longer pointed at. With HHMM there is nothing left for mutability to buy: every tag is unique by construction, so a second release the same day is not a collision to resolve, just another tag. The old instruction is recorded as retired rather than deleted. Someone who remembers it should learn it was withdrawn and why, not find it silently absent and assume they misremembered. README contradicted itself inside one sentence — "immutable per-day release tags ... a same-day re-cut moves the tag forward" — and now says which it is, plus a note that pre-2026-09-10 tags keep the old shape and still work. Transition wrinkle, deliberately left for step 3: the non-tag :latest path reconstructs the bundled APK's name from the latest release's commit timestamp, which for the one existing old-shape release yields 2026.09.09.1828 while that APK actually declares 2026.09.09.1895. It fails SAFE — 1828 compares lower, so no false update is offered — and it self-corrects at the first new-scheme release. Step 3 removes the reconstruction entirely by having the sidecar carry recorded values instead of derived ones. Step 2 of 5 — Scribe task #3809, milestone #390. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SQ31KQpYbStyK5y58UmPLH
This commit is contained in:
@@ -2,15 +2,30 @@ name: release
|
||||
|
||||
# Builds and pushes the minstrel container image to the Gitea registry.
|
||||
#
|
||||
# push to main → :main and :latest (latest-release APK bundled)
|
||||
# push tag vYYYY.MM.DD → :vYYYY.MM.DD and :latest (freshly-built APK bundled)
|
||||
# workflow_dispatch → manual trigger (same rules based on the ref)
|
||||
# push to main → :main and :latest (latest-release APK bundled)
|
||||
# push tag vYYYY.MM.DD.HHMM → :vYYYY.MM.DD.HHMM and :latest (fresh APK bundled)
|
||||
# workflow_dispatch → manual trigger (same rules based on the ref)
|
||||
#
|
||||
# Release model: per-day CalVer tags (no trailing patch digit). The day's
|
||||
# tag is intentionally mutable — if a second release happens the same day,
|
||||
# move the tag with `git push -f origin vYYYY.MM.DD` and the image tag of
|
||||
# the same name gets overwritten. :latest is updated by every main push
|
||||
# AND every tag push, so it always reflects the newest blessed image.
|
||||
# Release model: the tag IS the artifact's version name with a `v` in front.
|
||||
# `v2026.09.10.1432` and `2026.09.10.1432` are the same string, derived from
|
||||
# the tagged commit's UTC timestamp — so there is no mismatch to reconcile
|
||||
# between what the tag says and what the APK reports, and nothing to look up
|
||||
# when minting one.
|
||||
#
|
||||
# TAGS ARE IMMUTABLE. Never move, retarget or delete a published tag. A
|
||||
# same-day second release is not a collision — HHMM makes every tag unique
|
||||
# by construction, so the answer is simply another tag.
|
||||
#
|
||||
# This block used to say the opposite: that the per-day tag was
|
||||
# "intentionally mutable" and that a same-day re-cut should
|
||||
# `git push -f origin vYYYY.MM.DD`. That instruction is what the family
|
||||
# rulebook now forbids outright, and it has incidents behind it — moving a
|
||||
# same-day tag forward once took a published release down with it. Anyone
|
||||
# installing from a tag is holding something the tag no longer points at,
|
||||
# which is a worse failure than an extra row in the tag list.
|
||||
#
|
||||
# :latest is updated by every main push AND every tag push, so it always
|
||||
# reflects the newest blessed image.
|
||||
#
|
||||
# APK pipeline: on tag pushes the android-release job builds + signs the
|
||||
# Android APK and uploads it as a workflow artifact. The image-release
|
||||
@@ -41,9 +56,10 @@ on:
|
||||
- '**/*.md'
|
||||
workflow_dispatch:
|
||||
|
||||
# Force-moving the per-day tag (or rapidly re-pushing to main) should
|
||||
# supersede the in-flight build — the operator explicitly wants the
|
||||
# later commit to win.
|
||||
# A rapid re-push to main should supersede the in-flight build — the
|
||||
# operator explicitly wants the later commit to win. Tags no longer enter
|
||||
# into this: they are immutable and unique, so no tag build can ever be
|
||||
# superseded by another run on the same ref.
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
@@ -290,8 +306,8 @@ jobs:
|
||||
else
|
||||
# Main is the protected, post-PR-merge branch. Treat it as the
|
||||
# rolling stable channel — every main push moves :latest.
|
||||
# Pinned consumers can target :vYYYY.MM.DD; everyone else
|
||||
# gets the newest main.
|
||||
# Pinned consumers can target :vYYYY.MM.DD.HHMM, which never
|
||||
# moves; everyone else gets the newest main.
|
||||
echo "args=-t ${IMAGE}:main -t ${IMAGE}:latest" >> "$GITHUB_OUTPUT"
|
||||
echo "version=main" >> "$GITHUB_OUTPUT"
|
||||
echo "::notice::Main-branch build: :main + :latest"
|
||||
|
||||
Reference in New Issue
Block a user