Skip to content

fix(impl-merge): use ADMIN_TOKEN PAT so --admin can bypass main ruleset#5523

Merged
MarkusNeusinger merged 1 commit intomainfrom
fix/impl-merge-admin-pat
Apr 29, 2026
Merged

fix(impl-merge): use ADMIN_TOKEN PAT so --admin can bypass main ruleset#5523
MarkusNeusinger merged 1 commit intomainfrom
fix/impl-merge-admin-pat

Conversation

@MarkusNeusinger
Copy link
Copy Markdown
Owner

Summary

Follow-up to #5521 (which added --admin to gh pr merge). That change alone wasn't enough — verified just now: 3 dispatched merges (#5476, #5480, #5481) all failed identically with:

GraphQL: Repository rule violations found
3 of 3 required status checks are expected.
(mergePullRequest)

Why --admin alone didn't work

The main ruleset's bypass list contains only RepositoryRole admin (mode: pull_request). Default GITHUB_TOKEN runs as github-actions[bot] with write role — not admin — so the API rejects the bypass.

gh api repos/MarkusNeusinger/anyplot/rulesets/10578859 --jq '.bypass_actors'
# [{"actor_id":5,"actor_type":"RepositoryRole","bypass_mode":"pull_request"}]

The fix

Route only the merge step through a repo-admin PAT (ADMIN_TOKEN). All other steps in impl-merge.yml and the rest of the impl-* workflows keep using GITHUB_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..."
+          fi

The fallback secrets.ADMIN_TOKEN || secrets.GITHUB_TOKEN and the warning preserve the previous behavior if ADMIN_TOKEN isn'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

  1. Create 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. Set secret: Settings → Secrets and variables → Actions → New repository secret
    • Name: ADMIN_TOKEN
    • Value: the PAT

Considered alternatives

Option Verdict
Add github-actions[bot] as bypass actor on ruleset broader blast radius — every workflow run could bypass main
Remove the 3 required checks from ruleset weakens protection for human PRs too
Push from impl-generate via PAT so CI triggers naturally cleanest semantically but needs PAT in 3 workflows + same maintenance overhead
Scope PAT to merge step only (this PR) smallest blast radius, matches the actual permission gap

Test plan

🤖 Generated with Claude Code

Copilot AI review requested due to automatic review settings April 29, 2026 11:13
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.
@MarkusNeusinger MarkusNeusinger merged commit 92f4bd6 into main Apr 29, 2026
9 checks passed
@MarkusNeusinger MarkusNeusinger deleted the fix/impl-merge-admin-pat branch April 29, 2026 11:14
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 gh authentication for the merge step to prefer secrets.ADMIN_TOKEN over secrets.GITHUB_TOKEN.
  • Add a warning in logs when ADMIN_TOKEN is not configured.
  • Expand inline documentation around why --admin requires an admin-role token.

Comment on lines +181 to +193
# 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
Copy link

Copilot AI Apr 29, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copilot uses AI. Check for mistakes.
Comment on lines +181 to +189
# 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 != '' }}
Copy link

Copilot AI Apr 29, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants