From 7a20c554411452d01319552108fa5a20a4904c63 Mon Sep 17 00:00:00 2001 From: Bryan Van Deusen Date: Thu, 27 Aug 2026 21:56:52 -0400 Subject: [PATCH] ci: publish a per-artifact date tag on main builds (milestone 313 step 3) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Each main build now also publishes :YYYY.M.D — the date of the commit that artifact's shipped files last changed in. Purely additive; :main, :latest, :c- and the dev path are untouched, so reverting this commit reverts the behaviour. main push -> :main, :latest, :c-, :2026.8.27 dev push -> :dev Per artifact, so an image whose files did not change keeps the tag it already had. On this commit the agent reads 2026.7.17 while web and ml read 2026.8.27 — six weeks apart, from one push. Step 4 turns that into not rebuilding it. Day precision, and a second main build the same day replaces the first. Operator's call, and the reasoning is theirs: same-day work is not something worth pinning. A rollback goes to a day, not to the fourth merge of a Tuesday afternoon. It also makes retention mean "the last N days" rather than "the last N pushes". CALVER is computed inside the main branch rather than at the top of the step, and hard-fails when empty. There is no `set -e` here, so an unconditional assignment that failed would have left it empty and published the tag `fabledcurator:` — an invalid name, from a step that still reported success. It is also simply unused on the dev and tag paths. Fixed a stale comment while in this block rather than leaving it for step 7: it claimed release tags carry "no `.N` per family release-posture rule". Rule 148 was amended on 2026-08-24 to REQUIRE the suffix, after the ban caused a same-day tag to be retargeted and a release deleted to make room (note 2813). FC's own tags already carry suffixes; only the comment was asserting the superseded rule. Verified before pushing: the derivation holds across 200 commits of real history — a derived revision always touches its own path set, the version never decreases along any parent->child edge, and web tracks all three extension-only commits in the log. That last one is the direction that would serve stale bytes on a pin. --- .forgejo/workflows/build.yml | 68 +++++++++++++++++++++++++++++++++--- 1 file changed, 64 insertions(+), 4 deletions(-) diff --git a/.forgejo/workflows/build.yml b/.forgejo/workflows/build.yml index a641f19..cd61c3e 100644 --- a/.forgejo/workflows/build.yml +++ b/.forgejo/workflows/build.yml @@ -446,7 +446,10 @@ jobs: run: | # Three trigger shapes: # refs/tags/v… → tag-push: opt-in milestone label (vYY.MM.DD, - # no `.N` per family release-posture rule). + # plus `.N` when the day already carries a tag — + # family rule 148, amended 2026-08-24 after a + # same-day tag was retargeted and a release + # deleted to make room, note 2813). # Publish ONLY the immutable version tag; # don't touch :latest (the main-push build # for the merge commit already did that). @@ -468,6 +471,15 @@ jobs: # everywhere). Operator-flagged 2026-06-01 after first :c- # main-push build failed at this step. SHORT_SHA=$(printf '%s' "$GITHUB_SHA" | cut -c1-7) + # The pinnable tag (milestone 313 step 3): YYYY.M.D of the commit + # THIS artifact's shipped files last changed in. Day precision is + # deliberate — same-day work is not something worth pinning, so a + # second main build the same day replaces the first rather than + # accumulating a tag nobody would roll back to. + # + # Derived per artifact, so an image whose files did not change keeps + # the tag it already had: the agent reads 2026.7.17 today while web + # reads 2026.8.27. Step 4 uses that to stop rebuilding it at all. # `channel` is baked into the image as FC_CHANNEL and reported by # /api/extension/manifest (milestone 271 step 7). A tag-push counts as # `main`: a vYY.MM.DD tag is cut from main, so that image is a @@ -477,7 +489,17 @@ jobs: echo "tags=git.fabledsword.com/bvandeusen/fabledcurator:${TAG_NAME}" >> "$GITHUB_OUTPUT" echo "channel=main" >> "$GITHUB_OUTPUT" elif [ "${GITHUB_REF##*/}" = "main" ]; then - echo "tags=git.fabledsword.com/bvandeusen/fabledcurator:main,git.fabledsword.com/bvandeusen/fabledcurator:latest,git.fabledsword.com/bvandeusen/fabledcurator:c-${SHORT_SHA}" >> "$GITHUB_OUTPUT" + CALVER=$(sh scripts/artifacts.sh tag web) + # Guarded, and computed only on this path. There is no `set -e` in + # this step, so a failed derivation would otherwise leave CALVER + # empty and publish the tag `fabledcurator:` — an invalid + # name, from a green step. An empty pin must never reach the + # registry. + if [ -z "$CALVER" ]; then + echo "ERROR: could not derive a web version tag" >&2 + exit 1 + fi + echo "tags=git.fabledsword.com/bvandeusen/fabledcurator:main,git.fabledsword.com/bvandeusen/fabledcurator:latest,git.fabledsword.com/bvandeusen/fabledcurator:c-${SHORT_SHA},git.fabledsword.com/bvandeusen/fabledcurator:${CALVER}" >> "$GITHUB_OUTPUT" echo "channel=main" >> "$GITHUB_OUTPUT" else echo "tags=git.fabledsword.com/bvandeusen/fabledcurator:dev" >> "$GITHUB_OUTPUT" @@ -569,11 +591,30 @@ jobs: # everywhere). Operator-flagged 2026-06-01 after first :c- # main-push build failed at this step. SHORT_SHA=$(printf '%s' "$GITHUB_SHA" | cut -c1-7) + # The pinnable tag (milestone 313 step 3): YYYY.M.D of the commit + # THIS artifact's shipped files last changed in. Day precision is + # deliberate — same-day work is not something worth pinning, so a + # second main build the same day replaces the first rather than + # accumulating a tag nobody would roll back to. + # + # Derived per artifact, so an image whose files did not change keeps + # the tag it already had: the agent reads 2026.7.17 today while web + # reads 2026.8.27. Step 4 uses that to stop rebuilding it at all. if [ "${GITHUB_REF#refs/tags/}" != "${GITHUB_REF}" ]; then TAG_NAME="${GITHUB_REF#refs/tags/}" echo "tags=git.fabledsword.com/bvandeusen/fabledcurator-ml:${TAG_NAME}" >> "$GITHUB_OUTPUT" elif [ "${GITHUB_REF##*/}" = "main" ]; then - echo "tags=git.fabledsword.com/bvandeusen/fabledcurator-ml:main,git.fabledsword.com/bvandeusen/fabledcurator-ml:latest,git.fabledsword.com/bvandeusen/fabledcurator-ml:c-${SHORT_SHA}" >> "$GITHUB_OUTPUT" + CALVER=$(sh scripts/artifacts.sh tag ml) + # Guarded, and computed only on this path. There is no `set -e` in + # this step, so a failed derivation would otherwise leave CALVER + # empty and publish the tag `fabledcurator-ml:` — an invalid + # name, from a green step. An empty pin must never reach the + # registry. + if [ -z "$CALVER" ]; then + echo "ERROR: could not derive a ml version tag" >&2 + exit 1 + fi + echo "tags=git.fabledsword.com/bvandeusen/fabledcurator-ml:main,git.fabledsword.com/bvandeusen/fabledcurator-ml:latest,git.fabledsword.com/bvandeusen/fabledcurator-ml:c-${SHORT_SHA},git.fabledsword.com/bvandeusen/fabledcurator-ml:${CALVER}" >> "$GITHUB_OUTPUT" else echo "tags=git.fabledsword.com/bvandeusen/fabledcurator-ml:dev" >> "$GITHUB_OUTPUT" fi @@ -636,11 +677,30 @@ jobs: id: tag run: | SHORT_SHA=$(printf '%s' "$GITHUB_SHA" | cut -c1-7) + # The pinnable tag (milestone 313 step 3): YYYY.M.D of the commit + # THIS artifact's shipped files last changed in. Day precision is + # deliberate — same-day work is not something worth pinning, so a + # second main build the same day replaces the first rather than + # accumulating a tag nobody would roll back to. + # + # Derived per artifact, so an image whose files did not change keeps + # the tag it already had: the agent reads 2026.7.17 today while web + # reads 2026.8.27. Step 4 uses that to stop rebuilding it at all. if [ "${GITHUB_REF#refs/tags/}" != "${GITHUB_REF}" ]; then TAG_NAME="${GITHUB_REF#refs/tags/}" echo "tags=git.fabledsword.com/bvandeusen/fabledcurator-agent:${TAG_NAME}" >> "$GITHUB_OUTPUT" elif [ "${GITHUB_REF##*/}" = "main" ]; then - echo "tags=git.fabledsword.com/bvandeusen/fabledcurator-agent:main,git.fabledsword.com/bvandeusen/fabledcurator-agent:latest,git.fabledsword.com/bvandeusen/fabledcurator-agent:c-${SHORT_SHA}" >> "$GITHUB_OUTPUT" + CALVER=$(sh scripts/artifacts.sh tag agent) + # Guarded, and computed only on this path. There is no `set -e` in + # this step, so a failed derivation would otherwise leave CALVER + # empty and publish the tag `fabledcurator-agent:` — an invalid + # name, from a green step. An empty pin must never reach the + # registry. + if [ -z "$CALVER" ]; then + echo "ERROR: could not derive a agent version tag" >&2 + exit 1 + fi + echo "tags=git.fabledsword.com/bvandeusen/fabledcurator-agent:main,git.fabledsword.com/bvandeusen/fabledcurator-agent:latest,git.fabledsword.com/bvandeusen/fabledcurator-agent:c-${SHORT_SHA},git.fabledsword.com/bvandeusen/fabledcurator-agent:${CALVER}" >> "$GITHUB_OUTPUT" else echo "tags=git.fabledsword.com/bvandeusen/fabledcurator-agent:dev" >> "$GITHUB_OUTPUT" fi