fix(ci): Dependabot auto-merge via GitHub App with ruleset bypass - #71
Conversation
The GITHUB_TOKEN runs as github-actions[bot] (integration 15368) which is not in the repository ruleset bypass list. Auto-merge silently stalls because the merge actor cannot bypass the protect-branches ruleset. Switch to GH_ADMIN_PAT so merges execute as a repo admin with bypass permissions, and add a PR approval step. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
This PR updates the Dependabot auto-merge GitHub Actions workflow to unblock merges that are currently stalled due to branch ruleset bypass restrictions when running as github-actions[bot].
Changes:
- Switch
ghauthentication fromsecrets.GITHUB_TOKENto an admin-scopedsecrets.GH_ADMIN_PATfor auto-merge operations. - Add an explicit
gh pr review --approvestep before enabling auto-merge. - Remove the previous “immediate merge fallback” logic.
| env: | ||
| PR_URL: ${{ github.event.pull_request.html_url }} | ||
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | ||
| GH_TOKEN: ${{ secrets.GH_ADMIN_PAT }} |
There was a problem hiding this comment.
GH_TOKEN: ${{ secrets.GH_ADMIN_PAT }} is used in a workflow triggered by on: pull_request from Dependabot. GitHub Actions does not provide repository secrets to workflows triggered by Dependabot on pull_request, so GH_ADMIN_PAT will be empty and the gh pr review/merge commands will fail to authenticate. Consider switching this workflow to pull_request_target (keeping the Dependabot actor guard) so the secret is available, or use a non-secret approach such as adding the GitHub Actions integration to the ruleset bypass list so GITHUB_TOKEN can merge without a PAT.
The GITHUB_TOKEN runs as github-actions[bot] (integration 15368) which is not in the repository ruleset bypass list. Auto-merge silently stalls because the merge actor cannot bypass the protect-branches ruleset. Create a dedicated GitHub App with contents:write and pull_requests:write permissions. The workflow generates short-lived tokens via actions/create-github-app-token, and the app is added to the ruleset bypass list so auto-merge can execute. Includes setup script: scripts/setup-dependabot-app.sh Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The GitHub App manifest flow had HTML encoding issues, so the app was created manually. Replace the setup script with a practical per-repo version that stores secrets and updates the ruleset bypass list for any repo in the org where the app is already installed. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The ruleset bypass list grants the app admin-level merge permissions, but --auto still respects ruleset rules. Use --admin to bypass the ruleset directly, matching how the app's bypass_mode is configured. Tested: PRs #68 and #69 merged successfully via app token + --admin. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Dependabot-triggered pull_request events do not have access to repository secrets. Switch to pull_request_target which runs in the context of the base branch and can access APP_ID and APP_PRIVATE_KEY. This is safe because the workflow only targets dependabot[bot] PRs and does not checkout any PR code. Addresses Copilot review comment on PR #71. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix(ci): use admin PAT for Dependabot auto-merge to bypass ruleset The GITHUB_TOKEN runs as github-actions[bot] (integration 15368) which is not in the repository ruleset bypass list. Auto-merge silently stalls because the merge actor cannot bypass the protect-branches ruleset. Switch to GH_ADMIN_PAT so merges execute as a repo admin with bypass permissions, and add a PR approval step. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(ci): use GitHub App for Dependabot auto-merge to bypass ruleset The GITHUB_TOKEN runs as github-actions[bot] (integration 15368) which is not in the repository ruleset bypass list. Auto-merge silently stalls because the merge actor cannot bypass the protect-branches ruleset. Create a dedicated GitHub App with contents:write and pull_requests:write permissions. The workflow generates short-lived tokens via actions/create-github-app-token, and the app is added to the ruleset bypass list so auto-merge can execute. Includes setup script: scripts/setup-dependabot-app.sh Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(ci): replace manifest-flow script with per-repo setup script The GitHub App manifest flow had HTML encoding issues, so the app was created manually. Replace the setup script with a practical per-repo version that stores secrets and updates the ruleset bypass list for any repo in the org where the app is already installed. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(ci): use --admin merge instead of --auto for ruleset bypass The ruleset bypass list grants the app admin-level merge permissions, but --auto still respects ruleset rules. Use --admin to bypass the ruleset directly, matching how the app's bypass_mode is configured. Tested: PRs #68 and #69 merged successfully via app token + --admin. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(ci): use pull_request_target for Dependabot secret access Dependabot-triggered pull_request events do not have access to repository secrets. Switch to pull_request_target which runs in the context of the base branch and can access APP_ID and APP_PRIVATE_KEY. This is safe because the workflow only targets dependabot[bot] PRs and does not checkout any PR code. Addresses Copilot review comment on PR #71. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * feat(deploy): create Gmail labels from Step 3 Config UI Allow users to create new Gmail labels directly from the configuration form instead of switching to Gmail. Adds "+ New Label" button next to both trigger and processed label dropdowns with inline input, API creation via gmail.labels scope, automatic list refresh, and auto-selection of the newly created label. Closes #78 * chore(ci): apply prettier/eslint auto-fixes * fix(deploy): surface sign-in error and remove redundant catch-rethrow Address PR review feedback: throw a user-friendly error when accessToken is missing instead of silently returning null, and remove the no-op catch-rethrow wrapper. * chore(ci): apply prettier/eslint auto-fixes * fix(deploy): address PR review comments - Fix JSDoc to match actual throw behavior (no longer returns null) - Throw on missing labelName instead of returning null silently - Escape IDs via escapeHtml() in inline onclick handlers for defense in depth - Expose showNewLabelInput, hideNewLabelInput, handleCreateLabel on window for consistency with browseForFile and confirmSetupDone - Consolidate createGmailLabel import to top of test file * fix(deploy): show New Label button in text-input fallback mode The + New Label button was only shown when Gmail labels were loaded (select dropdown mode). Now it also appears in the text-input fallback that renders when labels haven't loaded yet or when the user has no existing labels. Also handles auto-filling text inputs after creation. * chore(ci): apply prettier/eslint auto-fixes * feat(deploy): replace inline label buttons with New Label modal Redesign the label creation UX per user feedback: - Single "New Label" button at the top of the Gmail to Drive config section - Modal popup that creates both the trigger label and its -archived counterpart in one action - Live preview showing both labels as user types - Auto-selects both labels in the config dropdowns after creation - Supports Enter key, Escape to close, and overlay click to dismiss * chore(ci): apply prettier/eslint auto-fixes * fix(deploy): address CodeRabbit review — modal close helper, idempotent label creation - Remove escapeHtml from thrown error messages (rendered via textContent) - Add shared closeModal helper with isCreating guard to prevent closing during label creation and fix leaked keydown listener - Make two-label creation idempotent: treat 409/duplicate as success, add labels to userGmailLabels on each step, so retries after partial failure work correctly - Surface isDuplicate and status on error objects for caller detection Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Don Petry <don.petry@gmail.com> Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com> Co-authored-by: DJ <dj@Rachels-Air.localdomain>
Problem
Dependabot PRs #68, #69, #70 had auto-merge enabled and all checks passing, yet remained in
BLOCKEDmerge state indefinitely. Investigation revealed auto-merge has never worked — all past Dependabot PRs were merged manually.Root cause
The
protect-branchesruleset grants bypass only to RepositoryRole 5 (admin) and Integration 29110. The workflow usedGITHUB_TOKEN, which runs asgithub-actions[bot](Integration 15368) — not in the bypass list. GitHub's auto-merge executes as the token's actor, so it silently stalled against the ruleset.Secondary bug
The immediate-merge fallback compared
mergeableto"true", butgh pr view --json mergeablereturns"MERGEABLE"— so the fallback never fired either.Solution
Created an org-wide GitHub App (
dependabot-automerge-petry, ID: 3167543) that:actions/create-github-app-token@v1--adminto bypass the rulesetKey finding during testing
--auto(GitHub's auto-merge) still respects ruleset rules even when the actor is in the bypass list. Only--admin(immediate admin merge) triggers the bypass. This means the workflow merges immediately after checks pass rather than waiting for auto-merge.Changes
.github/workflows/dependabot-automerge.yml— replacedGITHUB_TOKEN+--autowith GitHub App token +--adminscripts/setup-dependabot-app.sh— per-repo setup script for deploying to other org reposVerified
app/dependabot-automerge-petryapp/dependabot-automerge-petry🤖 Generated with Claude Code