ci: auto-merge Dependabot dev-dependency updates - #93
Conversation
Nothing in package.json ships, so a patch or minor bump to the dev toolchain only needs a green CI run, not a review. Enable GitHub auto-merge for those PRs and for Actions bumps; leave majors and any production dependency for a human. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
There was a problem hiding this comment.
Pull request overview
Adds a GitHub Actions workflow that enables GitHub auto-merge for eligible Dependabot PRs, so routine dev-toolchain and GitHub Actions dependency bumps can merge automatically once existing required checks/rules pass.
Changes:
- Introduces a
pull_request_targetworkflow that runs only for Dependabot-authored PRs targetingmain. - Uses
dependabot/fetch-metadata@v3outputs to gate auto-merge to semver patch/minor updates for dev/indirect deps (and GitHub Actions updates). - Enables auto-merge via
gh pr merge --auto --squashwithout checking out code or running repo scripts.
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
The explicit permissions block already raises Dependabot's read-only GITHUB_TOKEN to what auto-merge needs, which is the pattern GitHub's own auto-merge example uses. pull_request_target bought nothing and made "never check out the PR" a comment-enforced invariant on a privileged run; plain pull_request makes that failure mode structurally impossible. Also pass github-token to fetch-metadata explicitly, per the same example. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 1 out of 1 changed files in this pull request and generated no new comments.
Suppressed comments (1)
.github/workflows/dependabot-auto-merge.yml:32
- The workflow grants
contents: write/pull-requests: writeat the workflow level. Even though the only job is gated to Dependabot, scoping elevated permissions to the job (as other workflows here do) reduces blast radius if future jobs/steps are added or refactored.
Suggestion: keep workflow-level permissions minimal (e.g. contents: read) and move the write permissions onto the auto-merge job.
permissions:
contents: write
pull-requests: write
jobs:
auto-merge:
name: "Enable auto-merge"
runs-on: ubuntu-latest
if: github.actor == 'dependabot[bot]' && github.event.pull_request.user.login == 'dependabot[bot]'
steps:
Require the head branch to live in this repo rather than a fork, and skip draft PRs. GitHub refuses auto-merge on a draft, so the gh call would fail the job red for no reason; the ready_for_review trigger brings the PR back once it is undrafted. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 1 out of 1 changed files in this pull request and generated no new comments.
Suppressed comments (2)
.github/workflows/dependabot-auto-merge.yml:64
dependabot/fetch-metadataoutputs include hyphens (e.g.update-type). In GitHub Actions expressions, outputs with hyphens must be accessed with bracket syntax (steps.metadata.outputs['update-type']). Using dot notation here will be parsed incorrectly and theifcondition will never behave as intended.
(steps.metadata.outputs.update-type == 'version-update:semver-patch' ||
steps.metadata.outputs.update-type == 'version-update:semver-minor') &&
(steps.metadata.outputs.dependency-type == 'direct:development' ||
steps.metadata.outputs.dependency-type == 'indirect' ||
steps.metadata.outputs.package-ecosystem == 'github_actions')
.github/workflows/dependabot-auto-merge.yml:31
- Workflow-level
permissionsapplies to all jobs. Since this workflow is security-sensitive (write token) and may grow over time, it’s safer to scope the write permissions to the singleauto-mergejob (principle of least privilege).
permissions:
contents: write
pull-requests: write
Adds
.github/workflows/dependabot-auto-merge.ymlso routine dependency bumps stop needing a hand-review. Nothing inpackage.jsonships — the extension has no runtime dependencies, so everything indevDependenciesis build/test tooling — which makes a green CI run the entire signal for a bump like #92 (playwright 1.62.0 → 1.62.1, web-ext 10.5.0 → 10.6.0).The workflow only turns on GitHub's auto-merge. It never merges anything itself: the branch ruleset still holds the PR until Prettier, Vitest, and Pack pass.
What auto-merges
A Dependabot PR against
mainwhere both hold:update-typeisversion-update:semver-patchorversion-update:semver-minor, anddependency-typeisdirect:developmentorindirect, or the ecosystem isgithub_actions.Both of this repo's Dependabot configs are grouped, so a PR usually carries several updates.
dependabot/fetch-metadatareports the worst case across a group — the highest semver bump and the most production-facing dependency type — so a single major, or a single production dependency anywhere in the group, fails the check and the whole PR waits.indirectis included because this repo declares no production dependencies at all, so a transitive bump can only come from the dev toolchain. Actions get their own clause because Dependabot labels them as production dependencies.What still requires a human
actions/checkout@v7 → v8-style ones.direct:production— which today can only happen if the extension ever gains a real runtime dependency.dependabot[bot].Security shape
Dependabot-triggered runs get a read-only
GITHUB_TOKENby default, which can't enable auto-merge. The explicitpermissions: { contents: write, pull-requests: write }block raises it to what's needed — that's the documented fix, and it's what GitHub's own auto-merge example does on a plainon: pull_requesttrigger.An earlier revision of this PR used
pull_request_target. It didn't need to, and it was worse:pull_request_targetruns privileged in base-branch context, which would make "this workflow must never check out the PR" a load-bearing invariant enforced only by a comment. A future maintainer adding an innocuousactions/checkoutstep would turn it into an RCE vector against acontents: writetoken. Onpull_requestthat failure mode is structurally impossible, and for Dependabot PRs there's no behavioral difference — the branch is cut frommain, so the workflow file that runs is identical either way.Either way the workflow checks nothing out, installs nothing, and runs no repo scripts; the only step touching the PR is
gh pr merge --auto --squash. Testing the proposed code stays withci.yml(confirmed running on Dependabot PRs — #92 has passing Prettier / Vitest / Chromium / Pack runs).GITHUB_TOKEN-initiated merges don't re-trigger workflows, but that's irrelevant here: auto-merge waits on the CI run that Dependabot's own push already started.Known limitation: Copilot review threads can stall auto-merge
The "Require resolved feedback and checks" ruleset sets
required_review_thread_resolution: trueand runscopilot_code_reviewon push, with an empty bypass-actor list. If Copilot opens a review thread on a Dependabot PR, GitHub auto-merge will sit there until someone resolves it — auto-merge respects every ruleset requirement, and thread resolution is one.I deliberately did not touch the ruleset. Possible follow-ups if this turns out to bite in practice:
Notes
dependabot/fetch-metadata@v3, notv2. v3 is the current major (v3.1.0); its only breaking change is requiring the Node 24 Actions runtime. This matches the repo's convention of pinning latest major tags, and avoids Dependabot immediately opening a v2 → v3 PR.package-ecosystemis matched asgithub_actions(underscore), not thegithub-actionsspelling used independabot.yml— the action derives that output from the branch name (dependabot/github_actions/actions-…). Verified against ci: bump actions/setup-node from 6 to 7 in the actions group #89.dependency-typeis matched asindirect;indirect:developmentisn't a value the action emits..github/dependabot.yml.Validation
npm run format:check— clean.npm test— 496 tests across 16 files, all passing.permissions, jobif, and stepifverified against the parsed tree.package.json,package-lock.json, orsrc/.main, so correctness review is the only gate available here.