# Dependabot auto-merge × GitHub Actions automation guide: safely auto-merging only patch/minor with fetch-metadata

> An implementation guide to safely auto-merging Dependabot PRs with GitHub Actions. Faithful to the official documentation (as of June 2026), it explains, with copy-paste real code: all outputs of dependabot/fetch-metadata@v3, conditional branching by update-type, gh pr merge --auto, the token model of a read-only GITHUB_TOKEN and Dependabot secrets, using pull_request vs. pull_request_target, and how to build safety valves with required checks and branch protection.

- Published: 2026-06-28
- Author: 友田 陽大
- Tags: Dependabot, GitHub Actions, CI/CD, DevSecOps, 自動化, セキュリティ
- URL: https://tomodahinata.com/en/blog/dependabot-auto-merge-github-actions-automation-guide
- Category: Dependabot & dependency automation
- Pillar guide: https://tomodahinata.com/en/blog/dependabot-production-guide

## Key points

- The iron rule of auto-merge is 'auto for patch/minor only, human for major.' Branch on dependabot/fetch-metadata@v3's update-type output, and reserve a merge after required checks pass with gh pr merge --auto.
- The GITHUB_TOKEN of a pull_request workflow triggered by Dependabot is read-only by default. For auto-merge, explicitly declare permissions (contents: write / pull-requests: write) on the workflow side.
- A Dependabot workflow normally can't access Actions secrets. If you need secrets, use the Dependabot secret store. Avoid the dangerous pattern of checking out & running untrusted code with pull_request_target.
- The safety valves of auto-merge are CI (required status checks) and branch protection. Only by making tests, types, lint, and build required does unattended merging become 'safe automation.'
- fetch-metadata has 14 outputs like update-type / dependency-type / dependency-group / compatibility-score. With these, you can build a delicate auto-merge policy according to group PRs, production/dev dependencies, and the compatibility score.

---

Once you bring in Dependabot, the next thing you'll surely want is **auto-merge.** Manually merging an `@types/node` patch every time is a waste of time — but **auto-merging anything** is an accident that pushes breaking changes into production unattended. The right answer is the division of labor: **"auto-merge only the things with a low probability of breaking (patch/minor), conditioned on passing CI, and have a human review major."**

This article, as a topic of the [Dependabot production-operations guide](/blog/dependabot-production-guide), explains **safe auto-merge with GitHub Actions** at the implementation level. Since there are many security pitfalls here, it handles the **token model and event triggers** especially carefully.

> **Rules for this article**: action names, outputs, and token behavior are based on the **GitHub official documentation and the `dependabot/fetch-metadata` README (as of June 2026).** Since auto-merge directly connects to security, always confirm the latest in the [official automation guide](https://docs.github.com/en/code-security/dependabot/working-with-dependabot/automating-dependabot-with-github-actions) before production. **Don't hardcode secrets other than `GITHUB_TOKEN`** in the workflow.

---

## 1. The big picture: auto-merge is a "reservation"

GitHub's auto-merge isn't "merge now" but a **reservation** to "merge when all required checks turn green." So even with auto-merge enabled, **if tests fail it isn't merged.** This property is the core of safety.

```text
Dependabot がPRを開く
   └─▶ ワークフロー起動（actor が dependabot[bot] か検査）
          └─▶ fetch-metadata で update-type 等を取得
                 └─▶ patch/minor なら gh pr merge --auto を実行（=予約）
                        └─▶ 必須チェック（テスト/型/lint/ビルド）が green
                               └─▶ 自動マージ ✅   （赤なら止まる ⛔）
```

As a premise, enable **"Allow auto-merge"** in the repository settings (Settings → General → Pull Requests).

---

## 2. The minimal implementation: auto-merge patch/minor

First, a working minimal form. The points are to trigger on the **`pull_request` event** and **explicitly declare write permissions on the workflow side.**

```yaml
# .github/workflows/dependabot-auto-merge.yml
name: Dependabot auto-merge
on: pull_request

permissions:
  contents: write          # マージに必要
  pull-requests: write     # PR操作に必要

jobs:
  auto-merge:
    runs-on: ubuntu-latest
    # Dependabot 以外のPRでは何もしない（必須のガード）
    if: github.actor == 'dependabot[bot]'
    steps:
      - name: Fetch Dependabot metadata
        id: meta
        uses: dependabot/fetch-metadata@v3
        with:
          github-token: "${{ secrets.GITHUB_TOKEN }}"

      - name: Enable auto-merge for patch & minor
        if: |
          steps.meta.outputs.update-type == 'version-update:semver-patch' ||
          steps.meta.outputs.update-type == 'version-update:semver-minor'
        run: gh pr merge --auto --merge "$PR_URL"
        env:
          PR_URL: ${{ github.event.pull_request.html_url }}
          GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
```

Three key points:

1. **`if: github.actor == 'dependabot[bot]'`** — a guard that doesn't run on non-Dependabot PRs. **Omitting this is dangerous as it reacts to third-party PRs too.**
2. **Explicit `permissions:`** — the `GITHUB_TOKEN` of a `pull_request` workflow triggered by Dependabot is **read-only by default.** Explicitly grant `contents: write` and `pull-requests: write` for auto-merge (below).
3. **Conditional branching on `update-type`** — only `version-update:semver-patch` / `semver-minor`. Major (`semver-major`) is intentionally excluded and routed to a human.

---

## 3. The token model: this is the heart of security

Most accidents in auto-merge are **insufficient understanding of tokens.** Grasp the official behavior accurately.

- **The `GITHUB_TOKEN` of a `pull_request` workflow triggered by Dependabot is read-only by default.** And it **can't access normal Actions secrets.** This is a design to **prevent a compromised dependency from stealing secrets via CI.**
- If you need writes, **declare `permissions:` within the workflow** and elevate only the necessary scope (for Dependabot's `pull_request` event, the permissions declared in the workflow are respected).
- If the workflow **needs secrets** (e.g., a test accessing a private resource), put them in the **Dependabot secret store** (Settings → Secrets and variables → **Dependabot**). It's **separate from Actions secrets.**

> Mnemonic: "CI that runs on a Dependabot PR starts **read-only & with no secrets.**" So auto-merge needs an explicit `permissions`, and secrets go in the Dependabot-dedicated store. Miss these two and you stall on "no permission" or "the secret is empty."

### 3.1 pull_request or pull_request_target

The `dependabot/fetch-metadata` README also has an example using `pull_request_target`, but **the safe default is `pull_request`.** The reason:

- **`pull_request_target` runs with a token that can read/write in the base repository's context.** Here, **checking out and running the PR's code (= including the updated dependency) with `actions/checkout`** creates a path for **a compromised dependency to touch a write-permissioned token or secrets.**
- On the other hand, **only metadata retrieval + `gh pr merge --auto` (= an operation that doesn't run code)** can sufficiently achieve the goal with `pull_request` + explicit `permissions`, and is safer.

Conclusion: **use `pull_request` for auto-merge that doesn't involve building or arbitrary code execution.** If you absolutely need `pull_request_target`, **don't check out and run the PR's head**, and use it with an understanding of the trust boundary.

> When using a merge queue: you can't add a PR to the queue with the built-in `GITHUB_TOKEN`. You need to authenticate the workflow with a **PAT or a GitHub App token** (official note).

---

## 4. Build a "delicate policy" with all fetch-metadata outputs

`dependabot/fetch-metadata@v3` has 14 outputs. Using them, you can build a **more delicate auto-merge policy** than "patch/minor only."

| Output | Use example |
| --- | --- |
| `update-type` | Granularity judgment with `version-update:semver-patch/minor/major` |
| `dependency-type` | `direct:production` / `direct:development` / `indirect` |
| `dependency-names` | Limit to specific packages (e.g., `rails`) |
| `dependency-group` | Branch on whether it's a group PR (groups setting) |
| `package-ecosystem` | Per-ecosystem policy (github-actions auto, etc.) |
| `directory` | Per-directory policy for monorepos |
| `previous-version` / `new-version` | Log/judge by version diff |
| `compatibility-score` | Threshold judgment by compatibility score |
| `alert-state` / `ghsa-id` / `cvss` | Treat security updates separately |
| `maintainer-changes` | Route to human review if there's a maintainer change |
| `target-branch` | Per-target-branch policy |
| `updated-dependencies-json` | Raw data for machine processing |

### 4.1 Practical example: dev dependencies up to minor, production dependencies patch only

```yaml
      - name: Auto-merge policy
        # 開発依存は patch/minor を自動、本番の direct 依存は patch のみ自動
        if: |
          (steps.meta.outputs.dependency-type == 'direct:development' &&
            (steps.meta.outputs.update-type == 'version-update:semver-patch' ||
             steps.meta.outputs.update-type == 'version-update:semver-minor')) ||
          (steps.meta.outputs.dependency-type == 'direct:production' &&
            steps.meta.outputs.update-type == 'version-update:semver-patch')
        run: gh pr merge --auto --merge "$PR_URL"
        env:
          PR_URL: ${{ github.event.pull_request.html_url }}
          GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
```

Design philosophy: **the closer to production, the more cautious.** `direct:production` minor goes to human review, and `direct:development` (test/build tools) is loosened a bit more — "automation proportional to risk."

### 4.2 Always have a human look at a maintainer change

```yaml
      - name: Flag maintainer changes for human review
        if: steps.meta.outputs.maintainer-changes == 'true'
        run: gh pr comment "$PR_URL" --body "⚠️ メンテナ変更あり。サプライチェーン観点で人間レビュー必須。"
        env:
          PR_URL: ${{ github.event.pull_request.html_url }}
          GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
```

**A maintainer change can be a sign of a dependency hijack.** Looking at `maintainer-changes`, excluding it from auto-merge and putting human eyes on it is a cheap and effective defense.

---

## 5. Build safety valves: required checks × branch protection

Auto-merge is "merge if green." In other words, **"what you make the green condition" is the safety itself.** Auto-merge without a safety valve is just Russian roulette.

### 5.1 Run real CI even on Dependabot PRs

```yaml
# .github/workflows/ci.yml（Dependabot PR でも走る通常CI）
name: CI
on: pull_request

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: "22"
          cache: "npm"
      - run: npm ci
      - run: npm run lint
      - run: npm run typecheck
      - run: npm test
      - run: npm run build
```

> Note: this CI **installs and runs the updated dependency with `npm ci`.** Dependabot CI running on `pull_request` has a **read-only token and no secrets**, so even if a malicious dependency sneaks in, the damage is minimized. Designing so that **integration tests needing secrets don't run on Dependabot PRs** is more robust.

### 5.2 Make it required with branch protection

In `main`'s branch protection (or Rulesets), set the following:

- **Require `lint` / `typecheck` / `test` / `build`** in **Require status checks to pass before merging.**
- **Require branches to be up to date before merging** (optional, in consultation with `rebase-strategy`).
- With this, `gh pr merge --auto` **won't merge until all required checks turn green** = the safety valve works.

"Before bringing in auto-merge, first prepare **trustworthy required checks**" — don't reverse the order.

---

## 6. Combining with group PRs and cooldown

The `groups` / `cooldown` you built in the [complete configuration guide](/blog/dependabot-yml-configuration-complete-guide) mesh cleanly with auto-merge.

- **Bundle patch/minor into one PR with groups → auto-merge that group PR**: the review target shrinks to just "major and substantive updates." You can judge a group PR with the `dependency-group` output.
- **Let fresh versions age with cooldown → auto-merge what comes out after aging**: lower the regression risk before automatically incorporating.

```yaml
      - name: Auto-merge grouped patch/minor PRs
        if: |
          steps.meta.outputs.dependency-group != '' &&
          steps.meta.outputs.update-type != 'version-update:semver-major'
        run: gh pr merge --auto --squash "$PR_URL"
        env:
          PR_URL: ${{ github.event.pull_request.html_url }}
          GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
```

(Choose per your operation, such as `--squash` to fold a group PR's history into one commit.)

---

## 7. Antipattern collection

- ❌ **No `if: github.actor` guard** → reacts to third-party PRs. Always include the required guard.
- ❌ **Auto-merge major too** → breaking changes to production unattended. patch/minor-limited is the iron rule.
- ❌ **Auto-merge with no required checks** → no safety valve. A set with branch protection.
- ❌ **Check out & run PR code with `pull_request_target`** → a path for a write token/secrets to touch a malicious dependency. If no code execution, `pull_request`.
- ❌ **Depend on Actions secrets** → Dependabot CI can't touch them. Use the Dependabot secret store.
- ❌ **`write-all` for `permissions` globally** → a least-privilege violation. Narrow to just `contents` / `pull-requests`.

---

## 8. FAQ

**Q. I enabled auto-merge but it doesn't merge.**
A. `--auto` merges "when required checks turn green." It doesn't proceed if checks are pending/failing, or if "Allow auto-merge" or branch-protection required checks aren't set.

**Q. I get a `Resource not accessible by integration` error.**
A. Dependabot's `pull_request` workflow is read-only by default. Explicitly declare `permissions: { contents: write, pull-requests: write }` in the workflow.

**Q. I'd like to auto everything including major.**
A. Not recommended. It's not worth the risk of incorporating breaking changes unattended. Exclude major with `fetch-metadata`'s `update-type` and route it to human review.

**Q. I use a merge queue.**
A. Since you can't add to the queue with the built-in `GITHUB_TOKEN`, authenticate the workflow with a PAT or a GitHub App token.

**Q. Is it OK to auto-merge security updates too?**
A. If CI (especially tests) is sufficient, auto-merging patch security updates is highly effective. But also consider a policy of **looking at `alert-state` / `cvss` and having a human confirm the critical ones.** For the big picture of vulnerability response, go to the [alerts/security-updates guide](/blog/dependabot-security-updates-alerts-vulnerability-management-guide).
