From 4b1171781fefe29a5269f3838b2f4091511e7c1b Mon Sep 17 00:00:00 2001 From: d-morrison Date: Thu, 6 Aug 2026 18:36:06 -0700 Subject: [PATCH 1/3] start: bump-dev-version auto-merge WORKFLOW_TOKEN guard (closes #409) From 4241e0947eebc15a50aaa5c9b3baa576d32765e7 Mon Sep 17 00:00:00 2001 From: d-morrison Date: Thu, 6 Aug 2026 18:38:51 -0700 Subject: [PATCH 2/3] fix(bump-dev-version): warn when auto-merge can't complete without WORKFLOW_TOKEN (closes #409) --- .github/workflows/bump-dev-version.yml | 34 ++++++++++++++++++- ...-version-auto-merge-token-warning.fixed.md | 10 ++++++ examples/bump-dev-version.yml | 9 +++-- website/reference/bump-dev-version.qmd | 20 ++++++++--- 4 files changed, 66 insertions(+), 7 deletions(-) create mode 100644 changelog.d/bump-dev-version-auto-merge-token-warning.fixed.md diff --git a/.github/workflows/bump-dev-version.yml b/.github/workflows/bump-dev-version.yml index a65656b9..f7a4d820 100644 --- a/.github/workflows/bump-dev-version.yml +++ b/.github/workflows/bump-dev-version.yml @@ -38,7 +38,11 @@ on: default on: the diff is always exactly one mechanical line and needs no review. Requires the repo to have Settings -> General -> "Allow auto-merge" enabled, and "Allow GitHub Actions to create and approve - pull requests" for the PR-open step itself. + pull requests" for the PR-open step itself. In a repo with required + status checks it also requires WORKFLOW_TOKEN: a GITHUB_TOKEN-authored + PR does not trigger the pull_request runs those checks need, so + without the token the required checks never report and auto-merge + blocks forever (see the warning step below). type: boolean default: true dry-run: @@ -85,6 +89,34 @@ jobs: old-version: ${{ steps.bump.outputs.old-version }} new-version: ${{ steps.bump.outputs.new-version }} steps: + - name: Warn if auto-merge cannot complete without WORKFLOW_TOKEN + # A GITHUB_TOKEN-authored PR does not trigger `pull_request` workflow + # runs -- GitHub suppresses them to prevent recursion -- so in a repo + # with required status checks those contexts never report, and native + # auto-merge blocks forever (the bump PR sits open on every merge until + # a human intervenes). WORKFLOW_TOKEN (a PAT or App token) authors the + # PR as a real user, whose `pull_request` runs are not suppressed, so + # the required checks report and auto-merge can complete. This warns + # rather than fails because the combination is only unreachable when the + # repo actually has required checks: a repo without them auto-merges + # fine on GITHUB_TOKEN, and failing here would break that legitimate + # config. Runs first, before any work, so the signal is visible up + # front rather than as a PR that silently never merges. + if: ${{ !inputs.dry-run && inputs.auto-merge }} + shell: bash + env: + WORKFLOW_TOKEN: ${{ secrets.WORKFLOW_TOKEN }} + run: | + if [ -n "$WORKFLOW_TOKEN" ]; then + exit 0 + fi + msg="auto-merge is enabled but WORKFLOW_TOKEN is not set. In a repo with required status checks, the bump PR is authored by the integrated GITHUB_TOKEN, whose pull_request runs GitHub suppresses -- so the required checks never report and native auto-merge blocks forever (the PR sits open on every merge). Set WORKFLOW_TOKEN (a PAT or App token) so the PR is authored by a real user whose checks run, or set auto-merge: false to merge the bump PR by hand." + echo "::warning title=bump-dev-version::$msg" + { + echo "> [!WARNING]" + echo "> $msg" + } >> "$GITHUB_STEP_SUMMARY" + - name: Check out repository uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: diff --git a/changelog.d/bump-dev-version-auto-merge-token-warning.fixed.md b/changelog.d/bump-dev-version-auto-merge-token-warning.fixed.md new file mode 100644 index 00000000..b15fc9bf --- /dev/null +++ b/changelog.d/bump-dev-version-auto-merge-token-warning.fixed.md @@ -0,0 +1,10 @@ +- **`bump-dev-version` warns when `auto-merge` cannot complete** (#409). + A `GITHUB_TOKEN`-authored bump PR does not trigger the `pull_request` runs + that required status checks need, so in a repo with required checks native + `auto-merge` waits on conditions that never arrive and the PR sits open on + every merge. + The reusable workflow now emits a `::warning::` at the start of its run when + `auto-merge` is on and `WORKFLOW_TOKEN` is unset, and the caller stub and + reference page document that `WORKFLOW_TOKEN` is effectively required in that + case (it authors the PR as a real user, whose `pull_request` runs are not + suppressed). diff --git a/examples/bump-dev-version.yml b/examples/bump-dev-version.yml index e55ff12d..22dc2647 100644 --- a/examples/bump-dev-version.yml +++ b/examples/bump-dev-version.yml @@ -22,6 +22,11 @@ jobs: # description-path: DESCRIPTION # default # auto-merge: true # default; set false to review by hand secrets: - # Set only if the bot must push to a protected branch; otherwise omit - # and the push falls back to GITHUB_TOKEN. + # Effectively required whenever the repo has required status checks and + # auto-merge is on (the default): a GITHUB_TOKEN-authored bump PR does not + # trigger the pull_request runs those checks need, so the required checks + # never report and auto-merge blocks forever. Set a PAT or App token here + # to author the PR as a real user. You can omit it only if the repo has no + # required checks, or you set auto-merge: false -- then the push falls + # back to GITHUB_TOKEN. WORKFLOW_TOKEN: ${{ secrets.WORKFLOW_TOKEN }} diff --git a/website/reference/bump-dev-version.qmd b/website/reference/bump-dev-version.qmd index bc378079..8202a9e9 100644 --- a/website/reference/bump-dev-version.qmd +++ b/website/reference/bump-dev-version.qmd @@ -28,14 +28,26 @@ message. | `description-path` | string | `DESCRIPTION` | Path to the DESCRIPTION file to bump. | | `base-branch` | string | `main` | Base branch to open the bump PR against. | | `pr-branch` | string | `automated/bump-dev-version` | Head branch for the automation PR. | -| `auto-merge` | boolean | `true` | Enable GitHub's native auto-merge (squash) on the bump PR. | +| `auto-merge` | boolean | `true` | Enable GitHub's native auto-merge (squash) on the bump PR. In a repo with required status checks, needs `WORKFLOW_TOKEN` (see Secrets). | | `dry-run` | boolean | `false` | Bump but skip opening/merging a PR. Not yet exercised by this repo's own CI (its `dev-version` selftest job calls the `bump-dev-version` composite directly instead of this reusable workflow -- see the bootstrapping-gap note in `CLAUDE.md`); meant for a consumer to smoke-test the bump logic. | ## Secrets -| Secret | Required | Description | -| ---------------- | -------- | -------------------------------------------------------------- | -| `WORKFLOW_TOKEN` | no | PAT or App token; needed only to push to a protected branch. | +| Secret | Required | Description | +| ---------------- | ------------- | -------------------------------------------------------------- | +| `WORKFLOW_TOKEN` | conditional | PAT or App token that authors the bump PR as a real user. Effectively required when the repo has required status checks and `auto-merge` is on; otherwise optional. | + +Without `WORKFLOW_TOKEN`, the bump PR is opened by the integrated +`GITHUB_TOKEN`. GitHub does not trigger `pull_request` workflow runs for a +`GITHUB_TOKEN`-authored PR (it suppresses them to prevent recursive runs), so +in a repo with required status checks those checks never report and native +`auto-merge` blocks forever -- the PR sits open on every merge until a human +intervenes. Setting `WORKFLOW_TOKEN` (a PAT or App token) authors the PR as a +real user, whose `pull_request` runs are not suppressed, so the required checks +report and `auto-merge` completes. The reusable workflow emits a warning at the +start of its run when `auto-merge` is on and `WORKFLOW_TOKEN` is missing. Omit +the token only if the repo has no required checks, or set `auto-merge: false` +and merge the bump PR by hand. ## Outputs From b1c57e75a6975eb7d1356d89bc6441d264662ab3 Mon Sep 17 00:00:00 2001 From: d-morrison Date: Thu, 6 Aug 2026 18:59:36 -0700 Subject: [PATCH 3/3] review round 1: fix WORKFLOW_TOKEN doc accuracy (two reasons; auto-merge:false is not an escape); sweep README + permissions --- .github/workflows/bump-dev-version.yml | 2 +- README.md | 8 +++--- examples/bump-dev-version.yml | 20 +++++++++------ website/permissions.qmd | 12 ++++++--- website/reference/bump-dev-version.qmd | 35 +++++++++++++++++--------- 5 files changed, 50 insertions(+), 27 deletions(-) diff --git a/.github/workflows/bump-dev-version.yml b/.github/workflows/bump-dev-version.yml index f7a4d820..ebb8cd35 100644 --- a/.github/workflows/bump-dev-version.yml +++ b/.github/workflows/bump-dev-version.yml @@ -110,7 +110,7 @@ jobs: if [ -n "$WORKFLOW_TOKEN" ]; then exit 0 fi - msg="auto-merge is enabled but WORKFLOW_TOKEN is not set. In a repo with required status checks, the bump PR is authored by the integrated GITHUB_TOKEN, whose pull_request runs GitHub suppresses -- so the required checks never report and native auto-merge blocks forever (the PR sits open on every merge). Set WORKFLOW_TOKEN (a PAT or App token) so the PR is authored by a real user whose checks run, or set auto-merge: false to merge the bump PR by hand." + msg="auto-merge is enabled but WORKFLOW_TOKEN is not set. In a repo with required status checks, the bump PR is authored by the integrated GITHUB_TOKEN, whose pull_request runs GitHub suppresses -- so the required checks never report and native auto-merge blocks forever (the PR sits open on every merge). Required checks block manual merges too, so turning off auto-merge does not help. Set WORKFLOW_TOKEN (a PAT or App token) so the PR is authored by a real user whose checks run and report." echo "::warning title=bump-dev-version::$msg" { echo "> [!WARNING]" diff --git a/README.md b/README.md index e1a92615..ce4d39f3 100644 --- a/README.md +++ b/README.md @@ -150,9 +150,11 @@ that need to write must have the **caller** grant it on the calling job: - `bump-dev-version` (opens/auto-merges a PR) → grant `contents: write`, `pull-requests: write`, and the same "Allow GitHub Actions to create and approve pull requests" setting as above; enable "Allow auto-merge" too for - its default `auto-merge: true`. Add a `WORKFLOW_TOKEN` only to push to a - protected branch. `version-check` (read-only) → only `pull-requests: read`, - `contents: read`. + its default `auto-merge: true`. Add a `WORKFLOW_TOKEN` to push to a protected + branch, and -- when `auto-merge` runs against required status checks -- to let + the bump PR's checks run so it can merge at all (a `GITHUB_TOKEN`-authored PR's + checks never report; see the reference page). `version-check` (read-only) → + only `pull-requests: read`, `contents: read`. The stubs in [`examples/`](examples) already include the right `permissions:` blocks — copy them as-is. diff --git a/examples/bump-dev-version.yml b/examples/bump-dev-version.yml index 22dc2647..a7781569 100644 --- a/examples/bump-dev-version.yml +++ b/examples/bump-dev-version.yml @@ -22,11 +22,17 @@ jobs: # description-path: DESCRIPTION # default # auto-merge: true # default; set false to review by hand secrets: - # Effectively required whenever the repo has required status checks and - # auto-merge is on (the default): a GITHUB_TOKEN-authored bump PR does not - # trigger the pull_request runs those checks need, so the required checks - # never report and auto-merge blocks forever. Set a PAT or App token here - # to author the PR as a real user. You can omit it only if the repo has no - # required checks, or you set auto-merge: false -- then the push falls - # back to GITHUB_TOKEN. + # A PAT or App token, needed for two independent reasons: + # 1. To push the bump commit when pr-branch is protected by a ruleset + # that blocks the integrated GITHUB_TOKEN. Without it, checkout and + # open-sync-pr fall back to GITHUB_TOKEN (the original reason -- the + # sibling open-sync-pr stubs document only this one). + # 2. To let auto-merge (the default) complete when the repo has required + # status checks. A GITHUB_TOKEN-authored bump PR does not trigger the + # pull_request runs those checks need, so they never report and the + # merge is blocked forever. Authoring the PR with a real user's token + # makes its checks run. Required checks block manual merges too, so + # turning off auto-merge does not avoid this. + # Omit the token only when neither applies: an unprotected pr-branch and + # no required checks. WORKFLOW_TOKEN: ${{ secrets.WORKFLOW_TOKEN }} diff --git a/website/permissions.qmd b/website/permissions.qmd index 64fe371c..43b81e16 100644 --- a/website/permissions.qmd +++ b/website/permissions.qmd @@ -55,10 +55,14 @@ already include the right `permissions:` blocks; copy them as-is. - **`WORKFLOW_TOKEN`** (a PAT or GitHub App token with `contents:write` + `workflows:write`). The integrated `GITHUB_TOKEN` cannot push changes to files under `.github/workflows/`; GitHub rejects them without the `workflows` scope. - Add this secret if `claude` will edit workflow files, or to push to a - protected branch. Unlike `GITHUB_TOKEN`, a PAT or App-token push **does** - trigger other `push`-based workflows, so enabling it can set off extra CI - runs. + Add this secret if `claude` will edit workflow files, to push to a protected + branch, or -- for `bump-dev-version` with its default `auto-merge` in a repo + with required status checks -- to let the bump PR's checks run so it can merge + (a `GITHUB_TOKEN`-authored PR's `pull_request` checks never report, so the + merge stays blocked; see its + [reference page](reference/bump-dev-version.qmd)). Unlike `GITHUB_TOKEN`, a + PAT or App-token push **does** trigger other `push`-based workflows, so + enabling it can set off extra CI runs. When the secret is absent and `claude` does edit a workflow file, the rejected push is reported as an error naming this secret, and the commits are posted to the thread as a `git format-patch` so they survive the run. diff --git a/website/reference/bump-dev-version.qmd b/website/reference/bump-dev-version.qmd index 8202a9e9..d1514f98 100644 --- a/website/reference/bump-dev-version.qmd +++ b/website/reference/bump-dev-version.qmd @@ -35,19 +35,30 @@ message. | Secret | Required | Description | | ---------------- | ------------- | -------------------------------------------------------------- | -| `WORKFLOW_TOKEN` | conditional | PAT or App token that authors the bump PR as a real user. Effectively required when the repo has required status checks and `auto-merge` is on; otherwise optional. | - -Without `WORKFLOW_TOKEN`, the bump PR is opened by the integrated -`GITHUB_TOKEN`. GitHub does not trigger `pull_request` workflow runs for a +| `WORKFLOW_TOKEN` | conditional | PAT or App token. Needed to push the bump commit when `pr-branch` is protected, and to let `auto-merge` complete when the repo has required status checks (it authors the PR as a real user, so its checks run). Optional when neither applies. | + +`WORKFLOW_TOKEN` covers two independent needs. +First, the checkout and `open-sync-pr` push steps use it to push the bump +commit; without it they fall back to the integrated `GITHUB_TOKEN`, which +cannot push to a `pr-branch` a ruleset protects. +This is the reason the sibling `open-sync-pr` stubs +([`bump-submodule`](bump-submodule.qmd), +[`sync-shared-fragments`](sync-shared-fragments.qmd), +[`sync-upstream`](sync-upstream.qmd)) document, and it still applies here. +Second, it lets `auto-merge` complete in a repo with required status checks. +GitHub does not trigger `pull_request` workflow runs for a `GITHUB_TOKEN`-authored PR (it suppresses them to prevent recursive runs), so -in a repo with required status checks those checks never report and native -`auto-merge` blocks forever -- the PR sits open on every merge until a human -intervenes. Setting `WORKFLOW_TOKEN` (a PAT or App token) authors the PR as a -real user, whose `pull_request` runs are not suppressed, so the required checks -report and `auto-merge` completes. The reusable workflow emits a warning at the -start of its run when `auto-merge` is on and `WORKFLOW_TOKEN` is missing. Omit -the token only if the repo has no required checks, or set `auto-merge: false` -and merge the bump PR by hand. +those required checks never report and the merge is blocked forever -- the PR +sits open on every merge. +Required checks block manual merges too, so turning off `auto-merge` does not +avoid this; the genuine fixes are to set `WORKFLOW_TOKEN` (so the PR is authored +by a real user whose checks run), force-merge with branch-protection bypass +rights, or re-trigger the checks with a real-user event (a manual push to the +bump branch, or closing and reopening the PR). +The reusable workflow emits a warning at the start of its run when `auto-merge` +is on and `WORKFLOW_TOKEN` is missing. +Omit the token only when `pr-branch` is unprotected and the repo has no required +checks. ## Outputs