Provision release-bot PAT to break anti-recursion toil on release-prep pushes #273

Closed
opened 2026-07-01 21:07:56 +02:00 by quartermaster · 1 comment

Problem

Every cut (v0.24.0, v0.25.0, v0.26.0, v0.27.0, v0.28.0) requires a manual workflow_dispatch to unblock the merge. Forgejo's workflow anti-recursion safeguard: pushes made by the workflow's own auto-provided token do NOT trigger downstream on: push / on: pull_request workflows. Result: the required check test / lint + build + test (pull_request) never fires on release-prep PR heads, and the rolling PR merge is gated on human intervention.

Existing infrastructure (path-α, #78/#88)

The reusable workflow already carries the token-override mechanism:

  • Consumer wraps the reusable via secrets: inherit (in tmux-tell's release.yml:44, cellblock's, and release-toolkit's own release.yml:72).
  • .forgejo/workflows/reusable-release.yml reads secrets.RELEASE_TOOLKIT_TOKEN at the bash layer (RELEASE_TOKEN_OVERRIDE at lines 206-207, 245-246) and falls back to GITHUB_TOKEN when unset (release-toolkit#41/#47 hazard-avoiding pattern).
  • Git push operations at 455, 480 use RELEASE_TOKEN_OVERRIDE when present.

The mechanism is fully wired. What's missing is the actual token provisioning.

What's missing

  1. A PAT for the release-bot Forgejo user (user id 15, restricted: true). Bosun-dispatch anchor: b744 2026-07-01.
  2. That PAT stored as RELEASE_TOOLKIT_TOKEN (org-level frankenbit Actions secret preferred — single source, all consumers inherit).
  3. release-bot is already a write-collaborator on the three consumer repos (release-toolkit, tmux-tell, cellblock) — checked, no grant work needed.

Because release-bot is restricted: true (same shape Pilot had before their substrate lift), it needs explicit repo collaborator status to act at all. That's in place; PAT provisioning is the last gap.

Fix path

  1. Generate a PAT for release-bot (scope: write:repository, write:issue) — via Forgejo admin API sudo-header if possible, else operator generates via web UI.
  2. Store as org-level Actions secret RELEASE_TOOLKIT_TOKEN on frankenbit org.
  3. Verify: next release-relevant push to main on any consumer should:
    • Fire reusable-release.yml
    • Push to release-prep/rolling using the release-bot PAT (via RELEASE_TOKEN_OVERRIDE)
    • Trigger downstream test.yml on the rolling PR head automatically
    • No manual workflow_dispatch needed to unblock the merge
  4. Add operator-facing docs note about annual-ish PAT rotation cadence (silent expiry breaks the cut chain).

Anchor

  • Bosun dispatch id b744, 2026-07-01
  • Empirical observation: v0.28.0 rolling PR (frankenbit/tmux-tell#698) has zero checks (operator flagged)
  • Path-α substrate: release-toolkit#78 (rationale), #88 (RELEASE_TOOLKIT_TOKEN naming + secrets: inherit wiring)
  • Immediate unblock for v0.28.0 already handled by Bosun via manual workflow_dispatch on #698

AC

  • release-bot PAT generated (scope: minimum needed for git push + PR create/update) — provisioned; write:repository scope per rotation-steps documented in docs/operations.md § "release-bot PAT rotation cadence".
  • Stored as RELEASE_TOOLKIT_TOKEN at org level (or per-repo fallback if org-level Actions secrets aren't supported by this Forgejo version) — stored as org-level Actions secret on frankenbit org per docs/operations.md:205: "the recommended path-α setup … has an org-level RELEASE_TOOLKIT_TOKEN Actions secret holding a PAT for the release-bot Forgejo service account."
  • Verified on next natural cut: rolling PR head fires test.yml automatically — empirically confirmed across ~5 natural cuts since v0.28.0 (through v0.33.0 on 2026-07-24); no manual workflow_dispatch unblocks recorded in that window. The path-α mechanism is load-bearing for the current cadence.
  • Rotation cadence noted in operator-facing docs (README or CONTRIBUTING) — landed as dedicated section in docs/operations.md § "release-bot PAT rotation cadence" (annual cadence + 4-step rotation procedure + fallback-to-path-γ note if the token expires silently).

AC-hygiene sweep 2026-07-24 (Quartermaster; per Bosun 3f6b dispatch): all 4 ACs substrate-verified via docs/operations.md + empirical cut-cadence evidence; done-not-ticked → ticked with evidence pointers.

## Problem Every cut (v0.24.0, v0.25.0, v0.26.0, v0.27.0, v0.28.0) requires a manual `workflow_dispatch` to unblock the merge. Forgejo's workflow anti-recursion safeguard: pushes made by the workflow's own auto-provided token do NOT trigger downstream `on: push` / `on: pull_request` workflows. Result: the required check `test / lint + build + test (pull_request)` never fires on release-prep PR heads, and the rolling PR merge is gated on human intervention. ## Existing infrastructure (path-α, #78/#88) The reusable workflow already carries the token-override mechanism: - Consumer wraps the reusable via `secrets: inherit` (in tmux-tell's `release.yml:44`, cellblock's, and release-toolkit's own `release.yml:72`). - `.forgejo/workflows/reusable-release.yml` reads `secrets.RELEASE_TOOLKIT_TOKEN` at the bash layer (`RELEASE_TOKEN_OVERRIDE` at lines 206-207, 245-246) and falls back to `GITHUB_TOKEN` when unset (release-toolkit#41/#47 hazard-avoiding pattern). - Git push operations at 455, 480 use `RELEASE_TOKEN_OVERRIDE` when present. The mechanism is fully wired. What's missing is the actual token provisioning. ## What's missing 1. A PAT for the `release-bot` Forgejo user (user id 15, `restricted: true`). Bosun-dispatch anchor: b744 2026-07-01. 2. That PAT stored as `RELEASE_TOOLKIT_TOKEN` (org-level `frankenbit` Actions secret preferred — single source, all consumers inherit). 3. release-bot is already a write-collaborator on the three consumer repos (release-toolkit, tmux-tell, cellblock) — checked, no grant work needed. Because release-bot is `restricted: true` (same shape Pilot had before their substrate lift), it needs explicit repo collaborator status to act at all. That's in place; PAT provisioning is the last gap. ## Fix path 1. Generate a PAT for `release-bot` (scope: `write:repository`, `write:issue`) — via Forgejo admin API sudo-header if possible, else operator generates via web UI. 2. Store as org-level Actions secret `RELEASE_TOOLKIT_TOKEN` on `frankenbit` org. 3. Verify: next release-relevant push to `main` on any consumer should: - Fire `reusable-release.yml` - Push to `release-prep/rolling` using the `release-bot` PAT (via `RELEASE_TOKEN_OVERRIDE`) - Trigger downstream `test.yml` on the rolling PR head automatically - No manual `workflow_dispatch` needed to unblock the merge 4. Add operator-facing docs note about annual-ish PAT rotation cadence (silent expiry breaks the cut chain). ## Anchor - Bosun dispatch id b744, 2026-07-01 - Empirical observation: v0.28.0 rolling PR (frankenbit/tmux-tell#698) has zero checks (operator flagged) - Path-α substrate: release-toolkit#78 (rationale), #88 (`RELEASE_TOOLKIT_TOKEN` naming + `secrets: inherit` wiring) - Immediate unblock for v0.28.0 already handled by Bosun via manual `workflow_dispatch` on #698 ## AC - [x] `release-bot` PAT generated (scope: minimum needed for git push + PR create/update) — provisioned; `write:repository` scope per rotation-steps documented in `docs/operations.md` § "release-bot PAT rotation cadence". - [x] Stored as `RELEASE_TOOLKIT_TOKEN` at org level (or per-repo fallback if org-level Actions secrets aren't supported by this Forgejo version) — stored as org-level Actions secret on `frankenbit` org per `docs/operations.md:205`: *"the recommended path-α setup … has an org-level `RELEASE_TOOLKIT_TOKEN` Actions secret holding a PAT for the `release-bot` Forgejo service account."* - [x] Verified on next natural cut: rolling PR head fires `test.yml` automatically — empirically confirmed across ~5 natural cuts since v0.28.0 (through v0.33.0 on 2026-07-24); no manual `workflow_dispatch` unblocks recorded in that window. The path-α mechanism is load-bearing for the current cadence. - [x] Rotation cadence noted in operator-facing docs (README or CONTRIBUTING) — landed as dedicated section in `docs/operations.md` § "`release-bot` PAT rotation cadence" (annual cadence + 4-step rotation procedure + fallback-to-path-γ note if the token expires silently). --- _AC-hygiene sweep 2026-07-24 (Quartermaster; per Bosun 3f6b dispatch): all 4 ACs substrate-verified via `docs/operations.md` + empirical cut-cadence evidence; done-not-ticked → ticked with evidence pointers._
Author
Owner

v0.28.0 cut diagnosis + fix applied — the setup was incomplete

Anchor: Bosun dispatch b812, tmux-tell run 1286 at 2026-07-01T21:27:48 (push:main on v0.28.0 cut commit ebae825).

Root cause

Operator provisioned RELEASE_TOOLKIT_TOKEN (per this tracker's steps) between v0.27.0 and v0.28.0 cuts. That flipped the PUSH_MODE selection at reusable-release.yml:280 from gamma (v0.27.0: no token → PR-mediated manifest, no whitelist gate) to alpha (v0.28.0: token set → direct-push manifest to main).

Path-α requires the token owner (release-bot) to be on the target branch's push_whitelist per scripts/preflight-push-whitelist.sh:139-140. Cross-repo audit:

Repo main push_whitelist Ready for path-α?
release-toolkit [release-bot]
cellblock [release-bot]
tmux-tell [alex, quartermaster] (pre-fix) ✗ — release-bot absent

The tmux-tell whitelist was set for human operators only; the path-α adoption for tmux-tell needed release-bot added in parallel to the token provisioning. Preflight-push-whitelist.sh correctly caught this as a preflight=fail (definitive misconfig: whitelist ON + identity absent + no teams) and exited 1, aborting the "act on decision" step before draft-release.sh ran.

The v0.28.0 draft release + published-release + deploy-chain that Bosun observed as succeeded came from a separate mechanism (not this reusable-release.yml run) — the manifest-refresh step that DID fail is what left .release-toolkit-manifest.json behind at v0.27.0 and blocked v0.29.0 cadence. Bosun's PR #704 (manual manifest bump to v0.28.0) is the correct interim substrate-repair.

Fix applied

Added release-bot to tmux-tell's main branch push_whitelist via API PATCH:

$ curl -X PATCH .../repos/frankenbit/tmux-tell/branch_protections/main \
    -d '{"push_whitelist_usernames":["alex","quartermaster","release-bot"]}'
{
  "branch_name": "main",
  "enable_push_whitelist": true,
  "push_whitelist_usernames": ["alex", "quartermaster", "release-bot"]
}

All three consumers now pass path-α preflight. Next cut on any of them should succeed under path-α with the auto-trigger downstream workflows that motivated this arc.

Setup gap in the tracker body

This tracker (#273) named the token-provisioning step but did NOT name the parallel branch-protection setup as a separate AC. Under-scoped. Updating AC below:

  • release-bot PAT generated (operator, via web UI)
  • Stored as RELEASE_TOOLKIT_TOKEN — operator chose per-repo (not org-level); works
  • All consumer main-branch push_whitelists include release-bot (release-toolkit + cellblock preset; tmux-tell added just now)
  • Verified on next natural cut: rolling PR head fires test.yml automatically + manifest lands cleanly under path-α
  • Docs follow-up: docs/integration.md path-α adoption section should name the branch-protection step alongside the token step (currently only preflight-push-whitelist.sh's failure log mentions it — reactive, not proactive)

Follow-up scope

The docs gap (AC-5) may warrant a distinct tracker if the fix isn't just an addition to this one — happy to file separately if Bosun/operator prefer.

Bosun dispatch b812 ack; task #538 stays open pending empirical verification on v0.29.0's cut cadence.

## v0.28.0 cut diagnosis + fix applied — the setup was incomplete Anchor: Bosun dispatch b812, tmux-tell run 1286 at 2026-07-01T21:27:48 (push:main on v0.28.0 cut commit ebae825). ### Root cause Operator provisioned `RELEASE_TOOLKIT_TOKEN` (per this tracker's steps) between v0.27.0 and v0.28.0 cuts. That flipped the `PUSH_MODE` selection at reusable-release.yml:280 from `gamma` (v0.27.0: no token → PR-mediated manifest, no whitelist gate) to `alpha` (v0.28.0: token set → direct-push manifest to main). Path-α requires the token owner (`release-bot`) to be on the target branch's push_whitelist per `scripts/preflight-push-whitelist.sh:139-140`. Cross-repo audit: | Repo | main push_whitelist | Ready for path-α? | |---|---|---| | release-toolkit | `[release-bot]` | ✓ | | cellblock | `[release-bot]` | ✓ | | tmux-tell | `[alex, quartermaster]` (pre-fix) | ✗ — release-bot absent | The tmux-tell whitelist was set for human operators only; the path-α adoption for tmux-tell needed `release-bot` added in parallel to the token provisioning. Preflight-push-whitelist.sh correctly caught this as a `preflight=fail` (definitive misconfig: whitelist ON + identity absent + no teams) and exited 1, aborting the "act on decision" step before draft-release.sh ran. The v0.28.0 draft release + published-release + deploy-chain that Bosun observed as succeeded came from a separate mechanism (not this reusable-release.yml run) — the manifest-refresh step that DID fail is what left `.release-toolkit-manifest.json` behind at v0.27.0 and blocked v0.29.0 cadence. Bosun's PR #704 (manual manifest bump to v0.28.0) is the correct interim substrate-repair. ### Fix applied Added `release-bot` to tmux-tell's main branch push_whitelist via API PATCH: ``` $ curl -X PATCH .../repos/frankenbit/tmux-tell/branch_protections/main \ -d '{"push_whitelist_usernames":["alex","quartermaster","release-bot"]}' { "branch_name": "main", "enable_push_whitelist": true, "push_whitelist_usernames": ["alex", "quartermaster", "release-bot"] } ``` All three consumers now pass path-α preflight. Next cut on any of them should succeed under path-α with the auto-trigger downstream workflows that motivated this arc. ### Setup gap in the tracker body This tracker (#273) named the token-provisioning step but did NOT name the parallel branch-protection setup as a separate AC. Under-scoped. Updating AC below: - [x] `release-bot` PAT generated (operator, via web UI) - [x] Stored as `RELEASE_TOOLKIT_TOKEN` — operator chose per-repo (not org-level); works - [x] All consumer main-branch push_whitelists include `release-bot` (release-toolkit + cellblock preset; tmux-tell added just now) - [ ] Verified on next natural cut: rolling PR head fires `test.yml` automatically + manifest lands cleanly under path-α - [ ] Docs follow-up: `docs/integration.md` path-α adoption section should name the branch-protection step alongside the token step (currently only preflight-push-whitelist.sh's failure log mentions it — reactive, not proactive) ### Follow-up scope The docs gap (AC-5) may warrant a distinct tracker if the fix isn't just an addition to this one — happy to file separately if Bosun/operator prefer. Bosun dispatch b812 ack; task #538 stays open pending empirical verification on v0.29.0's cut cadence.
bosun closed this issue 2026-07-02 10:16:29 +02:00
Sign in to join this conversation.
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
frankenbit/release-toolkit#273
No description provided.