Skip to content

fix(ci): use PAT in release.yml so release.published fires downstream workflows #130

@laradji

Description

@laradji

Parent: none
Follow-up from: v0.2.0 release on 2026-04-17 where update-package-channels.yml failed to fire on release.published
Depends on: none (requires a new repo secret)

Decision (locked 2026-04-17)

Replace the default GITHUB_TOKEN used by release.yml's publish step with a fine-grained PAT (scope: contents:write) stored as RELEASE_PUBLISH_TOKEN. This restores the release.published event firing and re-enables automatic update-package-channels.yml runs (currently only triggerable manually via the workflow_dispatch escape hatch added in commit 1edd01d).

Why

GitHub's documented anti-recursion rule: events triggered by the default GITHUB_TOKEN do not spawn new workflow runs. release.yml's publish step uses the default token, so when v0.2.0 was published, update-package-channels.yml (on: release: types: [published]) never fired, leaving the Homebrew tap un-bumped until a manual operator intervention.

Until this lands, every release needs the operator to manually gh workflow run update-package-channels.yml -f tag=v<X> after the tag push — duplicative and error-prone.

Acceptance criteria

  • New fine-grained PAT created with repository scope laradji/deadzone only, permission Contents: Read and write, no other permissions
  • PAT stored as repository secret RELEASE_PUBLISH_TOKEN (not GITHUB_TOKEN, which is reserved)
  • .github/workflows/release.yml — identify the step(s) performing the release publish (the job that runs gh release create or softprops/action-gh-release or equivalent) and replace GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} with GH_TOKEN: ${{ secrets.RELEASE_PUBLISH_TOKEN }}
  • Same substitution for any gh release upload invocations in release.yml
  • Do NOT substitute the token used for the artifact build matrix — only the publish step and any subsequent upload/edit against the created release
  • CLAUDE.mdRelease operator secrets section updated with RELEASE_PUBLISH_TOKEN following the same pattern as HOMEBREW_TAP_TOKEN (scope, purpose, rotation/renewal failure mode)
  • Post-merge test: manually dispatch release.yml via workflow_dispatch path against a scratch tag v0.0.0-token-probe, verify update-package-channels.yml does fire on the resulting release publish, then delete the scratch release + tag
  • Once automatic path is verified, the workflow_dispatch trigger in update-package-channels.yml stays (as operator escape hatch) — it does not get removed by this issue

Concrete file pointers

Files to modify:

  • .github/workflows/release.yml — publish step(s) using GH_TOKEN (grep the file for the exact count before editing)
  • CLAUDE.mdRelease operator secrets section, add RELEASE_PUBLISH_TOKEN entry

Files to read as reference — do NOT change:

  • .github/workflows/update-package-channels.yml — the downstream workflow that waits for the event (no code change there; the fix is upstream in release.yml)
  • CLAUDE.md existing HOMEBREW_TAP_TOKEN block — exact format template for the new RELEASE_PUBLISH_TOKEN entry

Test commands (literal, for agent self-check)

  • grep -c 'GITHUB_TOKEN' .github/workflows/release.yml — before + after, to confirm only the intended sites were substituted
  • Scratch-tag probe: gh tag v0.0.0-token-probe && gh push --tags → watch release.yml → watch update-package-channels.yml fire without manual dispatch → clean up: gh release delete v0.0.0-token-probe --yes && git tag -d v0.0.0-token-probe && git push origin :refs/tags/v0.0.0-token-probe

Out of scope (fenced)

  • No changes to the contents: write job-level permission in release.yml — the token scope change is sufficient; the YAML-level permission declaration is orthogonal
  • No removal of workflow_dispatch in update-package-channels.yml — it stays as a manual escape hatch even after automatic path works (for rare cases like backfilling a missed bump, or testing the formula rewrite logic without a real release)
  • No new workflow file — this is strictly a token substitution + docs update
  • No other secrets created — don't touch HOMEBREW_TAP_TOKEN; each PAT stays scoped to one repo target

Related

Metadata

Metadata

Assignees

Labels

P2Normal — clear value, not urgentfeatureNew feature

Projects

No projects

Relationships

None yet

Development

No branches or pull requests

Issue actions