From 7f18cb24362a0665c75175bb2f4400c6f7f4bb33 Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 22 Sep 2026 11:51:51 -0500 Subject: [PATCH] feat(ci): advance v1 automatically once the gate is green on main MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Nothing moved v1 when main advanced, so a merged change was inert until someone remembered to retag by hand — it happened on 2026-09-22 (PR #25 merged, v1 stayed on the previous release) and was only caught because a person asked whether the tag had moved. Option 1 from gitdan-actions#27 (automate it) over option 2 (fail loud while it lags): a `release-tag` job, gated with `needs: selftest` so a broken build never reaches it, force-moves v1 to the pushed commit using the run's built-in GITHUB_TOKEN. If that token turns out not to have write access, the push fails and the job goes red in the Actions UI — a loud failure either way, not the silent one this replaces. Whether the token actually has write access here is unverified short of a real merge; that merge is the next step for this branch. README's Versioning section documented the old manual step as a deliberate decision "never something a merge does by itself" — that claim is now false, so it's rewritten to describe the automated job and keeps the manual command as the recovery path for when the job can't push. Co-Authored-By: Claude Sonnet 5 Claude-Session: https://claude.ai/code/session_01UkSjXXtU6JYcN2vPntWhfb --- .gitea/workflows/ci.yaml | 30 ++++++++++++++++++++++++++++++ README.md | 26 ++++++++++++++++---------- 2 files changed, 46 insertions(+), 10 deletions(-) diff --git a/.gitea/workflows/ci.yaml b/.gitea/workflows/ci.yaml index ac594f0..70053c0 100644 --- a/.gitea/workflows/ci.yaml +++ b/.gitea/workflows/ci.yaml @@ -101,3 +101,33 @@ jobs: # file. - name: Selftests run: bash scripts/selftest.sh + + release-tag: + name: move v1 to main + # `needs:` is what makes this "after the gate is green" rather than + # merely "after a push": a failed selftest skips this job outright, so + # v1 can never advance onto a broken build. The `if:` restricts it to an + # actual push to main -- a pull_request run targeting main shares this + # workflow but has no ref worth tagging. + needs: selftest + if: ${{ github.event_name == 'push' && github.ref == 'refs/heads/main' }} + runs-on: ubuntu-latest + timeout-minutes: 2 + # Requests write access from the run's built-in token (see README's + # Versioning section for what's actually verified about it). Without + # this the checkout below still succeeds -- it's the push that would be + # rejected, which is a red job, not a silent no-op. + permissions: + contents: write + steps: + - uses: actions/checkout@v4 + with: + token: ${{ secrets.GITHUB_TOKEN }} + + # Lightweight tag, matching what v1 already is (`git cat-file -t v1` + # reports `commit`, not `tag`) -- no identity needed to move it, only + # to push it. + - name: Force v1 to this commit + run: | + git tag -f v1 "${{ github.sha }}" + git push --force origin v1 diff --git a/README.md b/README.md index ef66069..9b916af 100644 --- a/README.md +++ b/README.md @@ -634,13 +634,20 @@ entry, another permission — is a `v2`, not a `v1` move. Everything else moves `v1`: correctness fixes, new optional inputs, and anything internal to `scripts/`. -**Moving the tag is a release step, and it is the operator's.** Merging to -`main` ships nothing to anybody. `v1` is a lightweight tag and does not follow -a branch, so until it is re-pointed every consumer keeps fetching the commit it -already named, whatever `main` now says. The gap is deliberate: re-pointing -`v1` changes what another repository's CI executes on its next run, so it is a -decision taken once, knowingly, after the merge — never something a merge does -by itself. +**Moving the tag is automatic, gated on the same build that gates a PR.** A +`release-tag` job in `.gitea/workflows/ci.yaml` runs on every push to `main`, +`needs: selftest`, and force-moves `v1` to that commit once selftest +succeeds — a broken build never reaches it, so `v1` can't advance onto one. +It pushes with the run's built-in `GITHUB_TOKEN`; if that token turns out not +to have write access, the push step fails and the job goes red in the Actions +UI. That's a loud failure, not the silent one this replaced: `v1` stays put, +and nobody has to notice on their own that it lagged. + +This used to be a manual step, treated as a deliberate release decision taken +once, knowingly, after the merge — in practice it was still forgotten +(gitdan-actions#27): PR #25 merged to `main` and `v1` stayed on the previous +release until someone asked whether it had moved. The manual form below is +now the recovery path, for when the automated job can't push: ```bash git fetch origin @@ -651,9 +658,8 @@ git ls-remote --tags origin v1 # must equal git rev-parse origin/main **Downstream** are emowheel, which pins `cargo-cache@v1` and `cargo-cache-publish@v1` across its CI workflow, and zemyna, migrating to the -same pin. Both pick a move up on their next run with no change on their side, -which is the whole point of the moving pointer and also the reason the move is -not automatic. +same pin. Both pick a move up automatically on their next run with no change +on their side, which is the whole point of the moving pointer. ---