# Dependabot でモノレポを回す：Turborepo / pnpm workspaces を directories・groups で破綻させない設計

> モノレポ（Turborepo / pnpm・npm・yarn workspaces / Nx）で Dependabot を破綻させずに運用する設計ガイド。公式ドキュメント（2026年6月時点）に忠実に、directories のグロブで1エントリに集約する方法、ワークスペースとロックファイルの関係、groups と group-by: dependency-name でディレクトリ横断にPRを束ねる方法、パッケージ別ポリシー、影響範囲だけテストするCIとの連携までを、コピペできる実コードで解説します。

- 公開日: 2026-06-28
- 著者: 友田 陽大
- タグ: Dependabot, モノレポ, Turborepo, 依存関係管理, GitHub Actions, DevSecOps
- URL: https://tomodahinata.com/blog/dependabot-monorepo-turborepo-pnpm-workspaces-directories-groups-guide
- カテゴリ: Dependabot・依存関係の自動更新
- 総合ガイド: https://tomodahinata.com/blog/dependabot-production-guide

## 要点

- モノレポで Dependabot が破綻する正体は『PR数 ≈ パッケージ数 × 依存数』の爆発。directories のグロブ（/apps/*・/packages/**）で1エントリに集約し、groups で束ねるのが基本設計
- pnpm/npm/yarn workspaces はロックファイルがリポジトリ・ルートに1つ。Dependabot はルートのロックファイルを解決するので、まずルート1エントリで全ワークスペースの外部依存を更新できる
- groups で『型定義』『テスト系』『その他 minor/patch』を束ね、group-by: dependency-name で複数アプリの同一依存（例 react）を1PRに統合。レビュー対象を major と中身のある変更に集中させる
- 内部パッケージ（workspace:* 参照）は Dependabot の更新対象外。だから外部依存の追従に集中でき、ignore で内部依存を除外する必要はない
- アプリは積極更新・共有ライブラリは保守的、と per-package ポリシーを使い分ける。cooldown でリリース直後を寝かせ、影響パッケージだけテストするCI（Turborepo の filter）と auto-merge で回す

---

モノレポに Dependabot を素朴に入れると、**月曜の朝に数十本のPRが並ぶ**——そして誰も見なくなります。原因は明確で、モノレポでは更新対象が「**パッケージ数 × 依存数**」に膨らむからです。けれどこれは設計で解けます。**`directories` で集約し、`groups` で束ね、`cooldown` で寝かせ、auto-merge で patch/minor を流す**——この4点で、巨大モノレポでも依存更新は静かに自動化されます。

この記事は[Dependabot 本番運用ガイド](/blog/dependabot-production-guide)の各論として、**モノレポ特有の設計**にフォーカスします。Turborepo / pnpm workspaces を主な題材にしますが、npm/yarn workspaces・Nx でも考え方は同じです。

> **この記事のルール**：設定キー・挙動は **GitHub 公式ドキュメント（2026年6月時点）** に基づきます。`directories`（グロブ）・`group-by` は比較的新しい機能です。本番前に必ず[公式の設定リファレンス](https://docs.github.com/en/code-security/dependabot/working-with-dependabot/dependabot-options-reference)で最新を確認してください。

---

## 0. なぜモノレポで破綻するのか

```text
通常リポジトリ:   1 つの package.json → 依存 N 個 → 最大 N 本のPR
モノレポ(素朴):  M 個の package.json → 依存 N 個ずつ → 最大 M×N 本のPR
```

`apps/` に5つ、`packages/` に10、それぞれが数十の依存を持てば、容易に**3桁のPR**になります。これを「頻度を下げる」で誤魔化すと、今度は脆弱性対応が遅れます。**正しい解は集約と束ね**です。順に組み立てます。

---

## 1. directories のグロブで1エントリに集約

かつてはディレクトリの数だけ `updates` エントリを書いていました。いまは **`directories`（複数形）＋グロブ**で1エントリにまとめられます。

```yaml
version: 2
updates:
  - package-ecosystem: "npm"
    directories:
      - "/"            # ルート（ワークスペース定義・共有devDeps）
      - "/apps/*"      # 各アプリ
      - "/packages/**" # 共有パッケージ（再帰）
    schedule:
      interval: "weekly"
```

`*` は1階層、`**` は再帰。これで**新しいアプリ/パッケージを足しても設定変更が不要**になります（ETC：変更が一箇所に閉じる）。

---

## 2. ワークスペースとロックファイルの関係（ここが肝）

モノレポの依存解決は**ロックファイルの位置**で決まります。

- **pnpm workspaces**：`pnpm-lock.yaml` は**リポジトリ・ルートに1つ**。全ワークスペースの依存がここに解決されます。
- **npm / yarn workspaces**：同様に**ルートに単一のロックファイル**（`package-lock.json` / `yarn.lock`）。

つまり多くのモノレポでは、**まずルート（`/`）の1エントリ**だけで、全ワークスペースの**外部依存**を更新できます。各 `apps/*` の `package.json` は「どの外部依存を使うか」を宣言しますが、解決の真実源はルートのロックファイルです。

```yaml
# pnpm/npm/yarn workspaces：まずはルート1エントリで十分なことが多い
updates:
  - package-ecosystem: "npm"
    directory: "/"
    schedule:
      interval: "weekly"
```

> 個別アプリで**異なる更新ポリシー**（頻度・グループ・ignore）を分けたいときに初めて `directories` で展開します。**YAGNI**：最初からアプリ単位に割らず、必要になってから割るのが運用を軽く保つコツです。

---

## 3. groups でPRを束ねる

集約しても、束ねなければPRは依存数だけ出ます。`groups` で**意味のある単位**にまとめます。

```yaml
updates:
  - package-ecosystem: "npm"
    directory: "/"
    schedule:
      interval: "weekly"
    groups:
      types:
        patterns: ["@types/*"]
      testing:
        patterns: ["vitest", "@testing-library/*", "playwright", "msw"]
      linting:
        patterns: ["eslint*", "prettier", "@typescript-eslint/*"]
      # 上記以外の minor/patch を1本に（major は個別PRのまま）
      minor-and-patch:
        update-types: ["minor", "patch"]
        exclude-patterns: ["@types/*", "vitest", "eslint*"]
```

これで「型」「テスト」「lint」「その他 minor/patch」の**4〜5本**に集約され、レビュー対象が劇的に減ります。`groups` の全オプションは[設定完全ガイド](/blog/dependabot-yml-configuration-complete-guide#4-groups複数の更新を1つのprにまとめる)へ。

---

## 4. group-by: dependency-name でディレクトリ横断に統合

アプリ単位で `directories` を割っている場合、「**全アプリの `react` 更新がバラバラにPR化**」されると本末転倒です。`group-by: dependency-name` で**ディレクトリをまたいで同一依存をまとめます**。

```yaml
  - package-ecosystem: "npm"
    directories: ["/apps/*"]
    schedule:
      interval: "weekly"
    groups:
      react-across-apps:
        patterns: ["react", "react-dom", "next"]
        group-by: "dependency-name"   # 全 app の react/next 更新を1PRに
```

フレームワークのバージョンを**モノレポ全体で揃える**運用（バージョンスキューの防止）と相性が良い設定です。

---

## 5. 内部パッケージは更新対象外（だから楽）

モノレポでは内部パッケージを `"@acme/ui": "workspace:*"` のように参照します。**この `workspace:*` 参照は Dependabot の更新対象になりません**——内部パッケージのバージョンはモノレポ自身が管理するものだからです。

つまり Dependabot は**外部依存の追従だけに集中**します。「内部依存を ignore しなきゃ」と心配する必要はありません。内部パッケージ間の整合は、Turborepo/Nx のビルドグラフや changesets など**別のツール**の仕事です（SRP：責務を混ぜない）。

---

## 6. パッケージ別ポリシー（リスクに比例した更新）

すべてを同じ強度で更新する必要はありません。**本番に近いほど慎重に**割り当てます。

```yaml
updates:
  # アプリ：積極的に追従（minor/patch は groups でまとめ auto-merge 前提）
  - package-ecosystem: "npm"
    directories: ["/apps/*"]
    schedule:
      interval: "weekly"
    groups:
      app-minor-patch:
        update-types: ["minor", "patch"]
    ignore:
      - dependency-name: "*"
        update-types: ["version-update:semver-major"]   # major は人間が棚卸し

  # 共有ライブラリ：保守的（月次・cooldown 長め）
  - package-ecosystem: "npm"
    directories: ["/packages/**"]
    schedule:
      interval: "monthly"
    cooldown:
      default-days: 14
```

共有ライブラリの破壊的変更は**全アプリに波及**します。だから libs は頻度を落とし `cooldown` を長めに。アプリは速く小さく追従。この使い分けが、**安定性と鮮度のトレードオフ**を地に足のついた形で解きます。

---

## 7. CI と auto-merge：影響範囲だけ回す

モノレポで全パッケージをフルテストすると、Dependabot PR のたびに CI 時間（＝[Actions 課金](/blog/dependabot-production-guide#3-どこで動くのか課金はどうなるか)）を浪費します。**影響パッケージだけ**テストします。

```yaml
# .github/workflows/ci.yml（抜粋）— Turborepo の filter で影響範囲のみ
- run: pnpm install --frozen-lockfile
- run: pnpm turbo run lint test build --filter="...[origin/main]"
```

`--filter="...[origin/main]"` は「**変更の影響を受けるパッケージとその依存元だけ**」を回す指定。Dependabot PR の差分に応じて必要最小限のテストになり、速く・安く・確実になります。

その上で[auto-merge](/blog/dependabot-auto-merge-github-actions-automation-guide)を組み合わせ、**グループ化された patch/minor を CI 条件付きで自動マージ**します。

```yaml
      - name: Auto-merge grouped patch/minor
        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 }}
```

---

## 8. モノレポ運用チェックリスト

- [ ] **ルート1エントリ**から始める（ワークスペースのロックファイルはルート単一）
- [ ] アプリ別ポリシーが要るときだけ `directories` で展開（YAGNI）
- [ ] `groups` で型/テスト/lint/その他 minor-patch を束ねる
- [ ] アプリ横断のバージョン統一は `group-by: dependency-name`
- [ ] 共有ライブラリは**月次＋長め cooldown**、アプリは**週次＋積極追従**
- [ ] CI は**影響範囲のみ**（Turborepo/Nx の filter）で時間とコストを最小化
- [ ] グループ化 patch/minor は **auto-merge**、major は人間レビュー

---

## 9. FAQ

**Q. apps と packages で全部 package.json があります。エントリも分けるべき？**
A. まずは**ルート1エントリ**で十分なことが多いです（ワークスペースのロックファイルはルート単一）。頻度やグループを**個別に変えたくなってから** `directories` で割ってください。

**Q. 全アプリの react がバラバラにPR化されます。**
A. `directories` を割っているなら `group-by: dependency-name` で同一依存をディレクトリ横断にまとめてください。バージョンスキューも防げます。

**Q. 内部パッケージ（workspace:*）も更新されますか？**
A. されません。Dependabot は外部依存だけを見ます。内部整合は changesets や Turborepo/Nx の仕事です。

**Q. PRごとに全パッケージのCIが回って遅い・高いです。**
A. Turborepo/Nx の filter で**影響範囲だけ**テストしてください。Dependabot は標準ランナーで無料ですが、CI 自体は通常通り Actions 分を消費します。

**Q. pnpm の `pnpm-lock.yaml` は正しく更新されますか？**
A. はい。Dependabot はルートのロックファイルを解決して更新します。ロックファイルは必ずコミットしてください。
