Skip to content

ci: auto-merge Dependabot PRs for bounded-risk groups - #30

Merged
guarzo merged 2 commits into
mainfrom
dependabot-automerge
Aug 3, 2026
Merged

ci: auto-merge Dependabot PRs for bounded-risk groups#30
guarzo merged 2 commits into
mainfrom
dependabot-automerge

Conversation

@guarzo

@guarzo guarzo commented Aug 3, 2026

Copy link
Copy Markdown
Owner

Based on main. Depends on #24 for the groups it names, but does not conflict with it — if this merges first the workflow simply never matches anything.

What changed and why

One new file, .github/workflows/dependabot-auto-merge.yml. It does not merge anything itself: it flips GitHub's native auto-merge flag, and GitHub merges only once main's required checks pass.

Gating is on fetch-metadata's dependency-group output rather than update-type. update-type is documented as "the highest semver change being made by this PR" with no stated behaviour for grouped PRs, so the group name is the claim that is actually specified. Verified against action.yml on the v3 tag — dependency-group is a real output there, and github-token now defaults to ${{ github.token }}, so it is omitted.

Auto-merged: minor-and-patch (restricted to minor/patch by its own update-types), linting, testing, types, and the actions group.

Not auto-merged: react and drizzle — coordinated majors, and a drizzle-orm/drizzle-kit skew surfaces at fly.toml's release_command. Nor any ungrouped PR, which is the next/zod/pg/pg-boss/jose/tsx/typescript major tier. Security updates are ungrouped by design and land there too: they should be merged fast, but by someone who has read the advisory.

The action is pinned to the v3 major tag, matching how ci.yml pins actions/*. It will keep itself current via the actions group — which is itself on the auto-merge list.

What CI cannot check

actionlint (not currently in CI) on the new file: clean, no output. npm run format:check -> All matched files use Prettier code style!.

The workflow's real behaviour cannot be exercised until a Dependabot PR opens, which will be Monday 06:00 UTC at the earliest. The first one is the test.

Deploy notes

None for the app. Two repository settings must be in place or this does not work — see Flags.

Flags

Two prerequisites are not yet met, and the second one is a live risk.

  1. allow_auto_merge is still false on the repo, so gh pr merge --auto will error and this job will go red on every Dependabot PR until it is enabled. Settings > General > Pull Requests > "Allow auto-merge".

  2. The ruleset on main (id 20330845) is "enforcement": "disabled" and contains only a pull_request rule — no required_status_checks rule at all. Auto-merge waits on required checks only; with none defined it merges the moment the flag is set, before CI reports. Until that ruleset gains required checks and is set to active, this workflow is strictly worse than not having it.

Correction to earlier advice in this thread. I recommended requiring unit tests but leaving e2e advisory. That is not possible as CI is currently written: ci.yml has one job, test / "Tests (unit, then e2e)", which runs both serially — the serialization the original brief asked for. They are a single check run. Requiring it requires e2e too. The three check names to require are exactly:

  • Typecheck, lint & format
  • Next build
  • Tests (unit, then e2e)

Splitting e2e into its own job would be a separate change, and would need the two jobs kept off the shared database.

Remaining gap either way: docker build is not in CI, so a dependency that breaks the image build auto-merges green. Mitigated by there being no CD workflow — .github/workflows/ is ci.yml and uptime.yml only — so a bad bump sits on main until someone runs fly deploy, rather than reaching production.

Summary by CodeRabbit

  • Chores
    • Added automated squash merging for eligible Dependabot dependency updates.
    • Supports minor and patch updates, linting, testing, type-related, and workflow dependencies.

Enables GitHub's native auto-merge on the Dependabot PRs whose risk the
grouping in .github/dependabot.yml already bounds: minor-and-patch,
linting, testing, types, and the github-actions group. The workflow
sets the flag; GitHub merges only after main's required checks pass.

Gates on fetch-metadata's dependency-group output rather than
update-type. update-type is documented as the highest semver change in
the PR, with no stated behaviour for grouped PRs, so the group name is
the claim that is actually specified.

react and drizzle are excluded (coordinated majors), as is every
ungrouped PR — that tier is the next/zod/pg-boss majors and the
security updates, both of which want a human.
@coderabbitai

coderabbitai Bot commented Aug 3, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

You’ve reached a temporary PR review limit under our Fair Usage Limits Policy.

Your recent review volume is higher than typical usage, so adaptive limits are currently applied.

Next review available in: 27 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 17de756c-3566-4072-9a53-1c589e17dfe0

📥 Commits

Reviewing files that changed from the base of the PR and between 8be91b0 and 71bcc01.

📒 Files selected for processing (1)
  • .github/workflows/dependabot-auto-merge.yml
📝 Walkthrough

Walkthrough

Adds a GitHub Actions workflow that enables native squash auto-merge for selected Dependabot dependency groups after repository, actor, permission, and metadata checks.

Changes

Dependabot auto-merge

Layer / File(s) Summary
Workflow gating and merge execution
.github/workflows/dependabot-auto-merge.yml
The workflow validates the repository and actor, grants contents and pull-request write permissions, filters Dependabot groups, retrieves metadata, and runs gh pr merge --auto --squash.

Estimated code review effort: 2 (Simple) | ~10 minutes

Poem

A rabbit checks the workflow gate,
Then lets the trusted patches wait.
Minor, lint, test, types in line,
Actions merge when checks align.
“Squeak!” says Bun, “the flow is fine!”

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes the workflow that auto-merges Dependabot pull requests for bounded-risk dependency groups.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch dependabot-automerge
✨ Simplify code
  • Create PR with simplified code
  • Commit simplified code in branch dependabot-automerge

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 4

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @.github/workflows/dependabot-auto-merge.yml:
- Line 37: Update the dependabot/fetch-metadata action reference in the workflow
to use the resolved commit SHA for the intended v3 release instead of the
mutable v3 tag, and add an inline version comment for readability.
- Around line 24-26: Move the contents and pull-requests write permissions from
the workflow-level configuration into the auto-merge job’s permissions block,
preserving both scopes and the existing job behavior. Use the jobs.auto-merge
definition as the target location and remove the top-level permissions block.
- Line 33: Update the condition in the workflow’s pull-request gate to use
github.event.pull_request.user.login for the author check instead of
github.actor, while preserving the existing repository equality check.
- Around line 39-61: Update the bounded-risk group condition in “Enable
auto-merge for bounded-risk groups” to exclude the “actions” dependency group,
or explicitly document and implement acceptance of the workflow-replacement
risk. Preserve auto-merge for the remaining approved groups.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 9ca715d5-187d-4598-b3c4-922ea16d74a4

📥 Commits

Reviewing files that changed from the base of the PR and between 9047052 and 8be91b0.

📒 Files selected for processing (1)
  • .github/workflows/dependabot-auto-merge.yml

Comment thread .github/workflows/dependabot-auto-merge.yml Outdated
name: Enable auto-merge
# The repository guard keeps this from running in forks, where the actor
# check alone could be satisfied by a branch named to impersonate the bot.
if: github.actor == 'dependabot[bot]' && github.repository == 'guarzo/authGD'

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🔒 Security & Privacy | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🌐 Web query:

Does GitHub recommend github.actor or github.event.pull_request.user.login to gate Dependabot-only workflow steps?

💡 Result:

Yes, GitHub recommends using github.event.pull_request.user.login rather than github.actor to gate Dependabot-only workflow steps [1][2][3]. Using github.actor is generally insecure for security-sensitive gating because it is susceptible to spoofing [1][4]. For example, an attacker can trigger certain events (such as @dependabot commands) that cause dependabot[bot] to appear as the github.actor on a pull request that they control [5]. In contrast, github.event.pull_request.user.login represents the immutable author of the pull request [1][3]. Checking this value ensures that the workflow is only operating on a pull request actually created by Dependabot [1][5]. Security best practices for workflow gating include: - Use github.event.pull_request.user.login == 'dependabot[bot]' to verify the source of the pull request [1][3]. - Treat github.actor as informational rather than a reliable trust boundary [4]. - When possible, use native GitHub features like environment protection rules with required reviewers instead of relying solely on workflow condition strings [4]. While some documentation mentions using github.actor for simple exclusions (e.g., if: github.actor!= 'dependabot[bot]') [6], security auditors and modern security guidelines emphasize that this is not a robust security control [1][4]. If your workflow requires suppressing bot-triggered reruns, it may be necessary to check both values or use more complex logic, but for identifying a "trusted" Dependabot-authored event, github.event.pull_request.user.login is the correct and recommended approach [7][5].

Citations:


🏁 Script executed:

#!/bin/bash
set -eu

file=".github/workflows/dependabot-auto-merge.yml"
if [ -f "$file" ]; then
  cat -n "$file" | sed -n '1,90p'
else
  echo "Missing $file"
fi

printf '\nWorkflow references:\n'
rg -n --hidden -S "dependabot-auto-merge|github\.actor|pull_request\.user\.login|dependabot\[bot\]" .github 2>/dev/null || true

printf '\nRepository status:\n'
git status --short

Repository: guarzo/authGD

Length of output: 3890


🌐 Web query:

site:docs.github.com GitHub Actions Dependabot pull_request.user.login github.actor workflow condition

💡 Result:

To identify if a GitHub Actions workflow was triggered by Dependabot, you can check the github.actor context, which will be set to 'dependabot[bot]' [1][2]. For workflow conditions (such as skipping a job or enabling specific logic), use an expression like the following in your workflow YAML file: if: github.actor!= 'dependabot[bot]' If you specifically need to verify that a pull request was opened by Dependabot, you can use the event payload: if: github.event.pull_request.user.login == 'dependabot[bot]' Key Considerations: - Default Behavior: Workflows triggered by Dependabot for events like pull_request are treated as if they originated from a fork [3][4]. This means they have a read-only GITHUB_TOKEN by default and do not have access to standard repository secrets [1][3]. - Permissions: You can override the default read-only permissions by explicitly defining the permissions key in your workflow, for example: permissions: pull-requests: write issues: write - Context Differences: While github.actor identifies the entity that triggered the workflow run, github.event.pull_request.user.login specifically checks the author of the pull request [1][5]. Using both in combination is common when you need to distinguish between Dependabot-initiated events and other actions [6].

Citations:


🌐 Web query:

site:docs.github.com github.actor pull_request user.login event payload Actions

💡 Result:

In GitHub Actions, github.actor and the user who triggered a pull_request event via the payload are distinct concepts with specific roles [1][2]. 1. github.actor github.actor is a built-in context property that identifies the username of the user who initiated the workflow run [1][2]. - If the workflow is a re-run, github.actor may differ from github.triggering_actor [1][2]. - Importantly, workflow re-runs always execute with the privileges of the original github.actor, regardless of which user triggered the re-run [1][2]. 2. Pull Request Event Payload (github.event) When a workflow is triggered by a pull_request event, you can access detailed information about the event through the github.event context, which contains the webhook payload [3][1]. The user who performed the action on the pull request (e.g., opened, closed, or synchronized it) is found within this payload [4][3]. - Location: The user information is typically located at github.event.sender.login (the user who performed the action) or github.event.pull_request.user.login (the author of the pull request) [3]. - Distinction: Unlike github.actor, which represents the person who "initiated" the workflow (often the person who pushed code or opened the PR), github.event.sender.login identifies the specific individual who triggered the webhook event (e.g., someone who commented on or labeled the PR) [3][1]. Summary Comparison - github.actor: The person who triggered the workflow run [1]. - github.event.sender.login: The person who performed the specific action (like pushing a commit or labeling) that sent the webhook event [3]. - github.event.pull_request.user.login: The author who originally created the pull request [3].

Citations:


🌐 Web query:

zizmor bot-conditions actor context may be spoofable github.actor pull_request

💡 Result:

The zizmor tool's "bot-conditions" audit flags the use of github.actor (and related contexts like github.triggering_actor) as potentially spoofable when used to authorize privileged actions in GitHub Actions workflows [1][2][3]. The core issue is that github.actor does not represent the original author of a pull request; instead, it identifies the last actor to perform an action on the triggering context [1][4]. In the context of a pull_request event, an attacker can exploit this by creating a pull request and then using actions—such as rebasing or force-pushing (e.g., via @dependabot commands)—that cause a trusted bot to become the "last actor" [1][5][4]. Because the workflow's conditional check (e.g., if: github.actor == 'dependabot[bot]') relies on this spoofable field, the attacker can bypass the intended permission boundary and execute privileged code [1][5][6]. To remediate this, zizmor recommends using immutable, event-specific contexts that accurately reflect the pull request's author, such as github.event.pull_request.user.login [1][7][3]. Key points regarding the vulnerability: - Spoofable Contexts: zizmor identifies github.actor, github.triggering_actor, and github.event.pull_request.sender.login as contexts that can be spoofed in this manner [2][3]. - Exploitation: Attackers leverage "confused deputy" attacks where they manipulate the branch history or PR activity to associate a trusted bot's identity with the latest trigger event, tricking the workflow into believing the trusted bot initiated the action [5][4][6]. - Mitigation: Replace the spoofable actor check with a check against the PR author's login, which remains constant regardless of subsequent push or sync events [1][7]. Zizmor provides automatic fixes for this specific audit [1][8].

Citations:


🌐 Web query:

GitHub Actions Dependabot auto merge github.actor github.event.pull_request.user.login security

💡 Result:

Using github.actor to identify Dependabot in GitHub Actions workflows is considered insecure because it can be spoofed [1][2]. The github.actor context represents the account that triggered the specific workflow run, which can change if a user manually re-runs the workflow or pushes a follow-up commit to a Dependabot-created branch [3]. To securely identify Dependabot-created pull requests, you should instead use github.event.pull_request.user.login [1]. This value is immutable and remains set to dependabot[bot] regardless of who triggered the workflow or subsequently interacted with the pull request [1][3]. Best practices for securing Dependabot auto-merge workflows include: 1. Use immutable identifiers: Rely on github.event.pull_request.user.login == 'dependabot[bot]' [1][3]. For additional robustness, some organizations also verify the numeric ID github.event.pull_request.user.id == 49699333 [2]. 2. Avoid over-privileged permissions: By default, workflows triggered by Dependabot have read-only permissions [4][5]. Explicitly define the minimal permissions required (e.g., contents: write and pull-requests: write) at the job or workflow level rather than granting global access [6][7][8]. 3. Validate metadata: Use the dependabot/fetch-metadata action to confirm the update type (e.g., patch or minor) before proceeding [9][6][10]. Avoid auto-merging major updates, as these often contain breaking changes that require human verification [6][11]. 4. Respect branch protection: Use gh pr merge --auto, which does not perform an immediate merge but instructs GitHub to merge the pull request once all required status checks and branch protection rules have passed [2][6][10]. This ensures security checks are not bypassed [6][11]. 5. Use dedicated tokens: If you need to perform actions that the default GITHUB_TOKEN cannot (like triggering other workflows), consider using a GitHub App token instead of a long-lived personal access token [8].

Citations:


Gate on the pull request author

Replace github.actor == 'dependabot[bot]' with github.event.pull_request.user.login == 'dependabot[bot]'. github.actor can reflect a later event actor and is spoofable for privileged workflow conditions.

🧰 Tools
🪛 zizmor (1.28.0)

[error] 33-33: spoofable bot actor check (bot-conditions): actor context may be spoofable

(bot-conditions)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/dependabot-auto-merge.yml at line 33, Update the condition
in the workflow’s pull-request gate to use github.event.pull_request.user.login
for the author check instead of github.actor, while preserving the existing
repository equality check.

Source: Linters/SAST tools

Comment thread .github/workflows/dependabot-auto-merge.yml Outdated
Comment thread .github/workflows/dependabot-auto-merge.yml
…om auto-merge

Review feedback on #30.

Pin dependabot/fetch-metadata to 25dd0e3 (v3.1.0) rather than the v3
moving tag. This departs from ci.yml's major-tag convention on purpose:
this is the only job holding contents: write, so a repointed tag would
run hostile code with write access to the default branch. Both refs
resolve to the same commit today.

Move permissions from workflow level to the job, so a job added later
has to ask for the write token rather than inherit it.

Drop the actions group from the auto-merge list. It edits the workflow
files themselves, including this one — its own pinned action matches
that group's * pattern. CI runs under the configuration being changed,
so green says the new action did not break the build, not that it is
the code its author published. A handful of PRs a year.

Not applied: swapping github.actor for
github.event.pull_request.user.login. That widens the gate rather than
narrowing it — see the PR discussion.
@guarzo

guarzo commented Aug 3, 2026

Copy link
Copy Markdown
Owner Author

Three of four applied. Verification: actionlint clean on the file, npm run format:check -> All matched files use Prettier code style!.

1. Pin fetch-metadata to a SHA — applied.

$ gh api repos/dependabot/fetch-metadata/git/ref/tags/v3      --jq '.object.sha'
25dd0e34f4fe68f24cc83900b1fe3fe149efef98
$ gh api repos/dependabot/fetch-metadata/git/ref/tags/v3.1.0  --jq '.object.sha'
25dd0e34f4fe68f24cc83900b1fe3fe149efef98

Both resolve to the same commit, so the pin costs nothing today. Now dependabot/fetch-metadata@25dd0e34f4fe68f24cc83900b1fe3fe149efef98 # v3.1.0.

Worth noting this departs from ci.yml, which pins actions/* by major tag. The comment says why: this is the only job in the repo holding contents: write, so a repointed tag here runs hostile code with write access to the default branch. The rest of CI has no such token.

2. Move permissions to the job — applied. Functionally identical today (one job), but a job added later now has to ask for the write token rather than inherit it.

3. github.actor -> github.event.pull_request.user.login — not applied.

This one widens the gate rather than narrowing it, so I'd rather not take it without pushback.

The two diverge when a human pushes a commit to a Dependabot branch. pull_request.user.login stays dependabot[bot] — the PR's author never changes — so the condition still matches and auto-merge is enabled on a branch that now contains human-authored commits. github.actor becomes that person, the condition goes false, and nothing is auto-merged. The current form fails closed; the proposed one fails open.

GitHub's documented auto-merge example uses github.actor == 'dependabot[bot]' for this reason. I can add user.login as an additional conjunct if you want belt-and-braces, but as a replacement it's a regression.

4. Drop actions from the auto-merge list — applied, and a good catch.

I had described the actions group keeping this workflow's own action current as a feature in the PR description. It is closer to a hazard: github-actions groups on *, which matches the pinned action in this very file, so the workflow could auto-merge edits to itself.

The asymmetry that makes it different from the other groups: CI runs under the configuration being changed. For linting or testing, green means the bump is fine. For an action bump, green means only that the new version did not break the build — not that it is the code its author published. Automating that merge, with a token that can write to main, is the one bump worth reading. It is a handful of PRs a year.

Auto-merged groups are now minor-and-patch, linting, testing, types.


Two corrections to this PR's description while I'm here:

  • It says "It will keep itself current via the actions group — which is itself on the auto-merge list." No longer true, per finding 4. The action still gets bumped by Dependabot; the PR just needs a human.
  • Under Flags I wrote that the ruleset "contains only a pull_request rule". Wrong — it also had deletion and non_fast_forward. My jq used // empty, which silently collapses any rule with no parameters. The real gap was the missing required checks and enforcement: disabled.

Both prerequisites in that Flags section are now resolved: the ruleset is active with Typecheck, lint & format, Next build and Tests (unit, then e2e) required, and auto-merge is enabled on the repository.

@guarzo
guarzo enabled auto-merge (squash) August 3, 2026 21:38
@guarzo
guarzo disabled auto-merge August 3, 2026 21:44
@guarzo
guarzo merged commit 77fcd03 into main Aug 3, 2026
5 checks passed
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.

1 participant