From 03e1a8c5b74bb82fd94990a0fed9d45442ccee43 Mon Sep 17 00:00:00 2001 From: dan-claude-bot Date: Sun, 19 Jul 2026 16:04:22 +0000 Subject: [PATCH] =?UTF-8?q?feat:=20the=20release=20re-arms=20main=20?= =?UTF-8?q?=E2=80=94=20the=20-dev=20bump=20folds=20into=20the=20release=20?= =?UTF-8?q?act?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Operator decision: the post-release bump PR is ceremony debris — a derivable one-liner with no judgment for a review to add. After tag + publish, the same job computes X.Y.(Z+1)-dev and pushes it to main directly (a GITHUB_TOKEN push fires no workflows: no recursion, no red run); if branch protection refuses, the step opens the bump PR itself, loudly. #98 is the last hand-made bump. Co-Authored-By: Claude Fable 5 --- .github/workflows/release.yml | 31 +++++++++++++++++++++++++++++++ test/release.sh | 8 +++++++- 2 files changed, 38 insertions(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 84ad328..fe2ad40 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -146,6 +146,37 @@ jobs: ver="$(cat VERSION)" gh api "repos/$GITHUB_REPOSITORY/git/refs" -f "ref=refs/tags/$ver" -f "sha=$MERGE_SHA" gh release create "$ver" --verify-tag --title "$ver" --notes-file "$RUNNER_TEMP/notes.md" + # The post-release bump, folded into the release act (#96 followup — + # operator decision: a mechanical one-liner deserves no PR of its + # own). X.Y.(Z+1)-dev is arithmetic, not judgment: derived, committed + # straight to main with this job's token. A GITHUB_TOKEN push fires + # no workflows (anti-recursion), so the bump triggers neither this + # door nor a red run; should branch protection ever refuse the direct + # push, the step opens the bump PR itself and says so, loudly, + # instead of leaving main armed to impersonate the release. + - name: bump main to the next -dev — the release re-arms main itself + if: steps.decide.outputs.ceremony == 'yes' + env: + GH_TOKEN: ${{ github.token }} + run: | + ver="$(cat VERSION)" + next="$(printf '%s' "$ver" | awk -F. '{ printf "%s.%s.%s-dev", $1, $2, $3 + 1 }')" + git config user.name "github-actions[bot]" + git config user.email "github-actions[bot]@users.noreply.github.com" + git fetch origin main + git checkout -B main origin/main + printf '%s\n' "$next" > VERSION + git add VERSION + git commit -m "chore: bump main to $next — a dev install must not impersonate $ver" + if ! git push origin main; then + echo "direct push refused (branch protection?) — opening the bump PR instead" >&2 + git checkout -b "chore/bump-$next" + git push origin "chore/bump-$next" + gh pr create -R "$GITHUB_REPOSITORY" --head "chore/bump-$next" \ + --title "chore: bump main to $next" \ + --body "The post-release re-arm, opened by release.yml because the direct push was refused. One file, one line." \ + --label release + fi # The tag door (#83) — the manual fallback and backfill, unchanged. Gated # to the push event so a closed PR (the trigger above) never runs it diff --git a/test/release.sh b/test/release.sh index f5976d9..8e8a5e9 100644 --- a/test/release.sh +++ b/test/release.sh @@ -170,8 +170,14 @@ check "release.yml: decide — post-release-window work no-ops green" 0 "" \ grep -qF "release-flow work merged in the post-release window" "$RY" check "release.yml: decide — bare, unchanged, never released refuses to guess" 0 "" \ grep -qF "Refusing to guess" "$RY" -check "release.yml: decide gates every later merge-door step on ceremony=yes" 0 "3" \ +check "release.yml: decide gates every later merge-door step on ceremony=yes" 0 "4" \ grep -cF "if: steps.decide.outputs.ceremony == 'yes'" "$RY" +# The release re-arms main itself: the post-release -dev bump is arithmetic, +# not judgment, so it rides the same job — direct push, PR fallback. +check "release.yml: the release bumps main to the next -dev itself" 0 "" \ + grep -qF "bump main to the next -dev" "$RY" +check "release.yml: ...with a PR fallback when the direct push is refused" 0 "" \ + grep -qF "opening the bump PR instead" "$RY" # --------------------------------------------------------------------------- # latest_release_tag — extracted from install.sh (the source-the-pure-function