Summary
Adopt Automattic/dependabot-auto-merge-action so Dependabot security PRs merge themselves once they clear a set of gates, instead of waiting on a human to notice them.
Scope check before anyone starts: this action only acts on PRs that fix a known advisory — gate 1 requires a GHSA ID, falling back to the Dependabot Alerts API for indirect dependencies. It does not touch routine version bumps. The toil documented in docs/dependabot.md § Routine merge workflow — grouped minor/patch PRs, @dependabot rebase churn, lockfile PRs going BEHIND one at a time — is unaffected by this. If reducing that churn is also wanted, it needs a separate mechanism, and gh pr merge --auto --merge (already documented there) is the cheap version.
How it works
Four sequential gates, each configurable:
- Security advisory — PR must fix a known vulnerability (GHSA ID required).
- CVSS severity — score must meet
cvss-threshold (default 7.0). A score of zero, missing, or out of range routes to human review rather than merging.
- Compatibility score — direct dependencies only, default minimum 80%. Indirect dependencies skip this gate because fetch-metadata cannot score them.
- Age — PR must have been open
age-days (default 7) before the scheduled job merges it.
A security-fast-track label bypasses all four and posts an audit comment naming who applied it. PRs failing a gate get sirt-review-required; PRs passing but waiting on age get auto-merge-pending.
What it would actually do here, today
All 9 open Dependabot alerts (7 high, 2 medium) are indirect/transitive and all have a patched version available. Against the default cvss-threshold: 7.0:
- Would clear the CVSS gate:
js-yaml (7.5, two alerts), brace-expansion (7.5, two alerts) — 4 of 9.
- Would route to human review as below threshold:
nanoid (5.9), mysql2 (5.9) — 2 of 9.
- Would route to human review as unscored:
mysql2 (0.0), deepmerge-ts (0.0), postcss (0.0) — 3 of 9. GitHub returns 0.0 for unscored advisories and the action treats that as needs-a-human.
Since every one of these is indirect, gate 3 is skipped and gate 1 leans entirely on the Alerts API fallback, which is what makes the security-events: read permission load-bearing rather than optional.
Precondition worth resolving first: there are currently zero open Dependabot security PRs despite those 9 fixable alerts — the 4 open bot PRs (#171-#174) are all routine bumps. An auto-merger for security PRs is worth nothing if security PRs are never opened. Find out why first: these are transitive under pnpm, and Dependabot may be unable to bump them without the parent package moving. That investigation may turn out to be the higher-value half of this issue.
Prerequisites
All already satisfied, nothing to change:
- Auto-merge enabled on the repo (
allow_auto_merge: true).
- At least one required status check on the default branch —
web, via existing branch protection on main.
- Dependabot security updates enabled.
Proposed workflow
New file .github/workflows/dependabot-auto-merge.yml. Pin to a SHA, not a floating tag — the action's own README says so, and it runs with write permissions:
name: Dependabot auto-merge
on:
pull_request_target:
types: [opened, synchronize, reopened, labeled]
schedule:
- cron: '0 9 * * *'
permissions:
pull-requests: write
contents: write
security-events: read
jobs:
dependabot-auto-merge:
uses: Automattic/dependabot-auto-merge-action/.github/workflows/dependabot-auto-merge.yml@8143c95d871e96dc12cb448aa1dde9c2abae694e # v1.5
permissions:
pull-requests: write
contents: write
security-events: read
with:
event-name: ${{ github.event_name }}
merge-method: merge
merge-method: merge rather than the default squash, to match how Dependabot PRs are merged here today.
Note this is the repo's first pull_request_target workflow — it runs with write access to the base repo against PR head refs. The action's stated mitigation is that it acts only on app/dependabot as author. It is also the first workflow needing contents: write; ci.yml is contents: read. Both are worth a deliberate look rather than a rubber stamp, and the github-actions Dependabot ecosystem is already configured, so the pinned SHA will get its own update PRs.
Decisions to make
cvss-threshold: default 7.0 leaves 5 of the current 9 alerts to a human. Lowering it to around 5.0 would sweep in the two 5.9s, but unscored 0.0 advisories always need a human regardless.
age-days: default is 7. Note dependabot.yml already sets cooldown.default-days: 3 and pnpm-workspace.yaml sets minimumReleaseAge — but security updates ignore cooldown, so the action's age gate is the only aging applied to exactly these PRs. Worth picking deliberately rather than inheriting 7.
- Interaction with strict status checks: docs/dependabot.md notes a lockfile PR goes
BEHIND whenever another PR merges, and strict checks block it until rebased. A scheduled merge job will hit the same wall; decide whether it should trigger @dependabot rebase or just leave it for the next run.
Acceptance
A Dependabot security PR fixing a high-severity advisory gets labeled auto-merge-pending, and merges without human action once CI is green and the age gate passes. One below the CVSS threshold gets sirt-review-required and is left alone. Routine (non-security) Dependabot PRs are untouched, and no non-Dependabot PR is ever labeled or merged by this workflow.
Summary
Adopt Automattic/dependabot-auto-merge-action so Dependabot security PRs merge themselves once they clear a set of gates, instead of waiting on a human to notice them.
Scope check before anyone starts: this action only acts on PRs that fix a known advisory — gate 1 requires a GHSA ID, falling back to the Dependabot Alerts API for indirect dependencies. It does not touch routine version bumps. The toil documented in docs/dependabot.md § Routine merge workflow — grouped minor/patch PRs,
@dependabot rebasechurn, lockfile PRs goingBEHINDone at a time — is unaffected by this. If reducing that churn is also wanted, it needs a separate mechanism, andgh pr merge --auto --merge(already documented there) is the cheap version.How it works
Four sequential gates, each configurable:
cvss-threshold(default7.0). A score of zero, missing, or out of range routes to human review rather than merging.age-days(default 7) before the scheduled job merges it.A
security-fast-tracklabel bypasses all four and posts an audit comment naming who applied it. PRs failing a gate getsirt-review-required; PRs passing but waiting on age getauto-merge-pending.What it would actually do here, today
All 9 open Dependabot alerts (7 high, 2 medium) are indirect/transitive and all have a patched version available. Against the default
cvss-threshold: 7.0:js-yaml(7.5, two alerts),brace-expansion(7.5, two alerts) — 4 of 9.nanoid(5.9),mysql2(5.9) — 2 of 9.mysql2(0.0),deepmerge-ts(0.0),postcss(0.0) — 3 of 9. GitHub returns0.0for unscored advisories and the action treats that as needs-a-human.Since every one of these is indirect, gate 3 is skipped and gate 1 leans entirely on the Alerts API fallback, which is what makes the
security-events: readpermission load-bearing rather than optional.Precondition worth resolving first: there are currently zero open Dependabot security PRs despite those 9 fixable alerts — the 4 open bot PRs (#171-#174) are all routine bumps. An auto-merger for security PRs is worth nothing if security PRs are never opened. Find out why first: these are transitive under pnpm, and Dependabot may be unable to bump them without the parent package moving. That investigation may turn out to be the higher-value half of this issue.
Prerequisites
All already satisfied, nothing to change:
allow_auto_merge: true).web, via existing branch protection onmain.Proposed workflow
New file
.github/workflows/dependabot-auto-merge.yml. Pin to a SHA, not a floating tag — the action's own README says so, and it runs with write permissions:merge-method: mergerather than the defaultsquash, to match how Dependabot PRs are merged here today.Note this is the repo's first
pull_request_targetworkflow — it runs with write access to the base repo against PR head refs. The action's stated mitigation is that it acts only onapp/dependabotas author. It is also the first workflow needingcontents: write;ci.ymliscontents: read. Both are worth a deliberate look rather than a rubber stamp, and thegithub-actionsDependabot ecosystem is already configured, so the pinned SHA will get its own update PRs.Decisions to make
cvss-threshold: default7.0leaves 5 of the current 9 alerts to a human. Lowering it to around5.0would sweep in the two 5.9s, but unscored0.0advisories always need a human regardless.age-days: default is 7. Notedependabot.ymlalready setscooldown.default-days: 3andpnpm-workspace.yamlsetsminimumReleaseAge— but security updates ignore cooldown, so the action's age gate is the only aging applied to exactly these PRs. Worth picking deliberately rather than inheriting 7.BEHINDwhenever another PR merges, and strict checks block it until rebased. A scheduled merge job will hit the same wall; decide whether it should trigger@dependabot rebaseor just leave it for the next run.Acceptance
A Dependabot security PR fixing a high-severity advisory gets labeled
auto-merge-pending, and merges without human action once CI is green and the age gate passes. One below the CVSS threshold getssirt-review-requiredand is left alone. Routine (non-security) Dependabot PRs are untouched, and no non-Dependabot PR is ever labeled or merged by this workflow.