fix(impl-merge): use ADMIN_TOKEN PAT so --admin can bypass main ruleset#5523
fix(impl-merge): use ADMIN_TOKEN PAT so --admin can bypass main ruleset#5523MarkusNeusinger merged 1 commit intomainfrom
Conversation
Follow-up to c662f83 (added --admin to gh pr merge). That alone is not enough: GitHub's main-branch ruleset bypass list contains only RepositoryRole=admin (mode: pull_request). The default GITHUB_TOKEN runs as github-actions[bot] with `write` role — not admin — so the merge API call still returns: GraphQL: Repository rule violations found 3 of 3 required status checks are expected. To bypass, the merge must be authenticated as a user/identity that holds the admin role. Solution: route only the merge step through a repository-admin PAT stored as `ADMIN_TOKEN`. All other steps in impl-merge.yml (and other impl-* workflows) keep using GITHUB_TOKEN. Behavior: - If ADMIN_TOKEN is set: `gh pr merge --admin` runs as the PAT owner (admin), the bypass actor matches, ruleset is bypassed, merge succeeds. - If ADMIN_TOKEN is unset: falls back to GITHUB_TOKEN. Workflow emits a warning at the top of the merge step and the merge still fails with the same ruleset error as before — no regression, just clearer signal in the run log. Setup required after merging this PR: 1. Create a fine-grained PAT (Settings → Developer settings → Personal access tokens → Fine-grained), repository = anyplot, permissions: Contents: Read+Write, Pull requests: Read+Write, Administration: Read+Write, Metadata: Read. 2. Settings → Secrets and variables → Actions → New repository secret named `ADMIN_TOKEN`, value = the PAT. Considered alternatives: - Add github-actions[bot] as bypass actor on the ruleset (would let *any* workflow run bypass main protection — broader blast radius) - Remove the 3 required checks from the ruleset (weakens protection for human PRs as well) - Push from impl-generate via PAT so CI triggers naturally (cleanest semantically but requires changes in 3 workflows + same PAT management overhead) The PAT-only-for-merge approach scopes bypass to one workflow step while leaving everything else under GITHUB_TOKEN.
There was a problem hiding this comment.
Pull request overview
Updates the Impl: Merge GitHub Actions workflow so that the actual merge step can bypass main branch rulesets (required status checks) by using an admin user’s PAT (ADMIN_TOKEN) when available, while keeping the rest of the workflow on GITHUB_TOKEN.
Changes:
- Switch
ghauthentication for the merge step to prefersecrets.ADMIN_TOKENoversecrets.GITHUB_TOKEN. - Add a warning in logs when
ADMIN_TOKENis not configured. - Expand inline documentation around why
--adminrequires an admin-role token.
| # ADMIN_TOKEN: PAT with admin scope from a repo-admin user, used so | ||
| # that `gh pr merge --admin` can bypass the main-branch ruleset | ||
| # (required-status-checks). Falls back to GITHUB_TOKEN if not set so | ||
| # the workflow still runs and fails with a clear ruleset error | ||
| # instead of an opaque auth error. | ||
| GH_TOKEN: ${{ secrets.ADMIN_TOKEN || secrets.GITHUB_TOKEN }} | ||
| PR_NUM: ${{ steps.check.outputs.pr_number }} | ||
| REPOSITORY: ${{ github.repository }} | ||
| HAS_ADMIN_TOKEN: ${{ secrets.ADMIN_TOKEN != '' }} | ||
| run: | | ||
| if [ "$HAS_ADMIN_TOKEN" != "true" ]; then | ||
| echo "::warning::ADMIN_TOKEN secret is not set — merge will fail if main ruleset enforces required status checks. Add a fine-grained PAT with Contents:Write + Pull requests:Write + Administration:Read+Write as repo secret ADMIN_TOKEN." | ||
| fi |
There was a problem hiding this comment.
The inline docs and warning refer to a “PAT with admin scope”, but fine-grained PATs don’t have OAuth-style scopes; what matters is that the token belongs to a user with admin role on the repo and has the specific fine-grained permissions needed for gh pr merge --admin. Updating the wording here will reduce confusion when someone sets up ADMIN_TOKEN later.
| # ADMIN_TOKEN: PAT with admin scope from a repo-admin user, used so | ||
| # that `gh pr merge --admin` can bypass the main-branch ruleset | ||
| # (required-status-checks). Falls back to GITHUB_TOKEN if not set so | ||
| # the workflow still runs and fails with a clear ruleset error | ||
| # instead of an opaque auth error. | ||
| GH_TOKEN: ${{ secrets.ADMIN_TOKEN || secrets.GITHUB_TOKEN }} | ||
| PR_NUM: ${{ steps.check.outputs.pr_number }} | ||
| REPOSITORY: ${{ github.repository }} | ||
| HAS_ADMIN_TOKEN: ${{ secrets.ADMIN_TOKEN != '' }} |
There was a problem hiding this comment.
Using a repo-admin PAT here significantly increases the blast radius of this workflow: any PR that matches the current conditions (label ai-approved + branch name implementation/*) could be merged to main while bypassing required checks. Consider adding an explicit trust gate before using ADMIN_TOKEN (e.g., require the PR author to be your automation bot and/or assert the PR is not cross-repo and the head repo is the same as github.repository), and fail fast if the gate isn’t met.
Summary
Follow-up to #5521 (which added
--admintogh pr merge). That change alone wasn't enough — verified just now: 3 dispatched merges (#5476, #5480, #5481) all failed identically with:Why --admin alone didn't work
The
mainruleset's bypass list contains onlyRepositoryRole admin(mode:pull_request). DefaultGITHUB_TOKENruns asgithub-actions[bot]withwriterole — not admin — so the API rejects the bypass.The fix
Route only the merge step through a repo-admin PAT (
ADMIN_TOKEN). All other steps inimpl-merge.ymland the rest of the impl-* workflows keep usingGITHUB_TOKEN. Bypass scope is therefore exactly one step, not the whole pipeline.- name: Merge PR to main (with retry) if: steps.check.outputs.should_run == 'true' env: - GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + GH_TOKEN: ${{ secrets.ADMIN_TOKEN || secrets.GITHUB_TOKEN }} PR_NUM: ${{ steps.check.outputs.pr_number }} REPOSITORY: ${{ github.repository }} + HAS_ADMIN_TOKEN: ${{ secrets.ADMIN_TOKEN != '' }} run: | + if [ "$HAS_ADMIN_TOKEN" != "true" ]; then + echo "::warning::ADMIN_TOKEN secret is not set..." + fiThe fallback
secrets.ADMIN_TOKEN || secrets.GITHUB_TOKENand the warning preserve the previous behavior ifADMIN_TOKENisn't set yet — workflow still runs, fails with the same ruleset error as before, but the log says clearly what's missing instead of an opaque auth error.Required after merge
anyplotADMIN_TOKENConsidered alternatives
github-actions[bot]as bypass actor on rulesetTest plan
ADMIN_TOKENrepo secretimpl-merge.ymlfor the 3 stuck approved PRs (feat(seaborn): implement marimekko-basic #5476 seaborn, feat(altair): implement marimekko-basic #5480 altair, feat(letsplot): implement marimekko-basic #5481 letsplot)impl:{library}:donelabel🤖 Generated with Claude Code