You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Rotate HOMEBREW_TAP_TOKEN: the secret is dead and blocks unattended releases #84
The v0.6.6 release run failed with '401 Bad credentials' from goreleaser, meaning the HOMEBREW_TAP_TOKEN repository secret (used as goreleaser's GITHUB_TOKEN in .github/workflows/release.yml) is expired or revoked. No release workflow had run since v0.6.0, so this went unnoticed; releases v0.6.1 through v0.6.5 are bare tags with no binaries.
v0.6.6 was published manually (goreleaser run locally with an operator token) after creating the previously missing yaklabco/homebrew-dot tap repository. Formula/dot.rb now exists there at 0.6.6.
Action needed (requires an interactive PAT creation, so cannot be automated):
Create a token with contents write access to yaklabco/dot and yaklabco/homebrew-dot.
gh secret set HOMEBREW_TAP_TOKEN --repo yaklabco/dot
Optional: backfill binaries for v0.6.1 through v0.6.5 by dispatching the release workflow per tag once the secret works, though goreleaser rebuilds from the tag so older tags will build with whatever toolchain go.mod pins.
The live tap token was in pass all along (yaklab.org/homebrew-tap-token); verified it still has push permission on yaklabco/homebrew-dot and re-set the HOMEBREW_TAP_TOKEN Actions secret from it (piped pass -> gh secret set, value never displayed). Leaving this open until the next real release run proves the workflow_dispatch path end to end; the v0.7.0 release will be that test.
HOMEBREW_TAP_TOKEN rotated 2026-08-12: replaced the dead secret with a fine-grained PAT verified against the API to hold push+admin on both yaklabco/dot and yaklabco/homebrew-dot. The unattended workflow_dispatch release path remains unproven until the next release runs; keeping this open until v0.7.0 ships through it. The optional binary backfill for v0.6.1-v0.6.5 also remains outstanding.
Token choice verified by differential probes: both stored candidates pass the exact compare call that 401d in the last release run, but the org-scoped PAT also holds yaklabco org administration, so the least-privileged repo-scoped PAT stays as HOMEBREW_TAP_TOKEN. Note: it expires 2026-12-30, so re-rotation is due late December.
Status after the v0.7.0 attempt (2026-08-12): the rotated fine-grained PAT gets further than the dead secret did, but the workflow_dispatch release run still failed at publish with PATCH /repos/yaklabco/dot/releases/...: 403 Resource not accessible by personal access token.
Root cause of the bad earlier verification: GET /repos/{owner}/{repo} returns the authenticated user s role-derived permissions, not the fine-grained token s own grants, so the push+admin probe proved nothing about the token. The changelog compare succeeded because the token has Contents: read; it evidently lacks Contents: write, which the release PATCH, asset uploads, and the tap formula push all require.
Fix (needs the token owner, in GitHub settings): edit the fine-grained PAT to grant Contents: Read and write for both yaklabco/dot and yaklabco/homebrew-dot (or mint a classic PAT with repo scope) and update the HOMEBREW_TAP_TOKEN secret. Then verify with a real write (a no-op release PATCH), not the /repos permissions field.
v0.7.0 itself shipped via local goreleaser (11 assets, tap formula updated, checksums verified), so this issue stays open solely for the unattended path. The workflow_dispatch trigger itself works; two runs today also flaked with socket hang up downloading tooling before reaching goreleaser, cleared by gh run rerun --failed.
Resolved 2026-08-12. HOMEBREW_TAP_TOKEN now holds the purpose-built homebrew-tap-goreleaser fine-grained token (expires 2027-03-02), scoped to yaklabco/dot + yaklabco/homebrew-tap with Contents: Read and write.
Verified by real writes, not the misleading GET /repos permissions field:
no-op release PATCH on the v0.7.0 release: 200
blob create on yaklabco/dot: 201
blob create on yaklabco/homebrew-tap: 201
changelog compare v0.6.6...v0.7.0: 200
That is every operation goreleaser performs (release publish, asset upload, formula push, changelog), so the workflow_dispatch release path should now run unattended; the next release proves it live. Note the tap moved to yaklabco/homebrew-tap in #99 and homebrew-dot is archived.
History for the record: the original failure chain was (1) the secret held a dead value, (2) an interim rotation used a token whose fine-grained grants lacked Contents: write (its apparent push+admin came from the user role, not the token), and (3) the purpose-built token had only ever been granted homebrew-tap while the config pointed at homebrew-dot. All three are now aligned.
The optional binary backfill for the bare v0.6.1-v0.6.5 tags remains undone; open a separate issue if wanted.
Activity
jamesainslie commentedon Jul 28, 2026
The live tap token was in pass all along (yaklab.org/homebrew-tap-token); verified it still has push permission on yaklabco/homebrew-dot and re-set the HOMEBREW_TAP_TOKEN Actions secret from it (piped pass -> gh secret set, value never displayed). Leaving this open until the next real release run proves the workflow_dispatch path end to end; the v0.7.0 release will be that test.
jamesainslie commentedon Aug 12, 2026
HOMEBREW_TAP_TOKEN rotated 2026-08-12: replaced the dead secret with a fine-grained PAT verified against the API to hold push+admin on both yaklabco/dot and yaklabco/homebrew-dot. The unattended workflow_dispatch release path remains unproven until the next release runs; keeping this open until v0.7.0 ships through it. The optional binary backfill for v0.6.1-v0.6.5 also remains outstanding.
jamesainslie commentedon Aug 12, 2026
Token choice verified by differential probes: both stored candidates pass the exact compare call that 401d in the last release run, but the org-scoped PAT also holds yaklabco org administration, so the least-privileged repo-scoped PAT stays as HOMEBREW_TAP_TOKEN. Note: it expires 2026-12-30, so re-rotation is due late December.
jamesainslie commentedon Aug 12, 2026
Status after the v0.7.0 attempt (2026-08-12): the rotated fine-grained PAT gets further than the dead secret did, but the workflow_dispatch release run still failed at publish with
PATCH /repos/yaklabco/dot/releases/...: 403 Resource not accessible by personal access token.Root cause of the bad earlier verification:
GET /repos/{owner}/{repo}returns the authenticated user s role-derived permissions, not the fine-grained token s own grants, so the push+admin probe proved nothing about the token. The changelog compare succeeded because the token has Contents: read; it evidently lacks Contents: write, which the release PATCH, asset uploads, and the tap formula push all require.Fix (needs the token owner, in GitHub settings): edit the fine-grained PAT to grant Contents: Read and write for both
yaklabco/dotandyaklabco/homebrew-dot(or mint a classic PAT withreposcope) and update the HOMEBREW_TAP_TOKEN secret. Then verify with a real write (a no-op release PATCH), not the /repos permissions field.v0.7.0 itself shipped via local goreleaser (11 assets, tap formula updated, checksums verified), so this issue stays open solely for the unattended path. The workflow_dispatch trigger itself works; two runs today also flaked with
socket hang updownloading tooling before reaching goreleaser, cleared bygh run rerun --failed.jamesainslie commentedon Aug 12, 2026
Resolved 2026-08-12. HOMEBREW_TAP_TOKEN now holds the purpose-built homebrew-tap-goreleaser fine-grained token (expires 2027-03-02), scoped to
yaklabco/dot+yaklabco/homebrew-tapwith Contents: Read and write.Verified by real writes, not the misleading
GET /repospermissions field:yaklabco/dot: 201yaklabco/homebrew-tap: 201That is every operation goreleaser performs (release publish, asset upload, formula push, changelog), so the
workflow_dispatchrelease path should now run unattended; the next release proves it live. Note the tap moved toyaklabco/homebrew-tapin #99 andhomebrew-dotis archived.History for the record: the original failure chain was (1) the secret held a dead value, (2) an interim rotation used a token whose fine-grained grants lacked Contents: write (its apparent push+admin came from the user role, not the token), and (3) the purpose-built token had only ever been granted
homebrew-tapwhile the config pointed athomebrew-dot. All three are now aligned.The optional binary backfill for the bare v0.6.1-v0.6.5 tags remains undone; open a separate issue if wanted.