Monorepo Turborepo Remote Cache 設計完全ガイド|CI パイプライン・GitHub Actions・2026 実践

Monorepo Turborepo CI/CD GitHub Actions DevOps Remote Cache
結論
  • Monorepo CI のボトルネックは 「毎 PR 全パッケージビルド」 だ。
  • Turborepo の タスクグラフ + Remote Cache + --filter=...[origin/main] で影響範囲だけ実行し、CI 時間を 70% 以上削減できる。
  • 2026 年の定番構成は pnpm workspace + turbo.json pipeline + Vercel/S3 Remote Cache + GitHub Actions matrix 分割
  • OpenAPI CI バリデーション など横断品質ゲートも turbo タスクとして統合し、Required Check 一本化が保守しやすい。

なぜ Monorepo に Turborepo Remote Cache が必要か

フロントエンド、バックエンド API、共有 UI ライブラリ、OpenAPI 生成クライアント——複数パッケージを 1 リポジトリ にまとめる Monorepo は、コード共有と atomic change の恩恵が大きい。反面、CI 時間はパッケージ数に比例して膨らむ。

従来の「各 package.json で npm run build を順番に実行」では、変更していないパッケージまで毎回ビルド してしまう。GitHub Actions の無料枠 2,000 分/月をすぐ消費し、開発者は PR マージを待つ時間が増える。

Turborepo は次の 3 層でこの問題を解く。

  1. タスクグラフ(pipeline)dependsOn: ["^build"] で依存順序を宣言的に定義
  2. ローカルキャッシュ — 同一マシン内で inputs ハッシュが一致すれば再実行をスキップ
  3. Remote Cache — チーム全員と CI runner がキャッシュアーティファクトを S3 / Vercel 経由で共有

Remote Cache が効くと、main ブランチで一度ビルドした packages/ui の dist/ が、別開発者の laptop や PR CI から ネットワーク経由で即座に復元 される。ビルド時間ではなく ダウンロード時間 になる。

Remote Cache なし Remote Cache あり
PR ごとに全 12 パッケージ build 変更 2 パッケージ + 依存 1 件のみ build
CI 18 分、月 4,000 分消費 CI 4 分、キャッシュ warm-up で main は 6 分
ローカルと CI で別々に同じ compile main CI の成果物を feature ブランチが再利用
lint/test も毎回フル実行 inputs 未変更タスクは 0 秒で skip

Monorepo ディレクトリ構成(2026 定番)

以下は Next.js アプリ、Express API、共有 UI、OpenAPI クライアント生成を含む典型的構成だ。

monorepo/
├── apps/
│   ├── web/                 # Next.js 15 フロント
│   ├── api/                 # Node.js API サーバー
│   └── docs/                # Astro / Starlight ドキュメント
├── packages/
│   ├── ui/                  # React コンポーネントライブラリ
│   ├── config-eslint/       # 共有 ESLint 設定
│   ├── config-typescript/   # 共有 tsconfig
│   └── api-client/          # OpenAPI から生成された SDK
├── openapi/
│   └── openapi.yaml         # API 契約の Single Source of Truth
├── turbo.json
├── pnpm-workspace.yaml
├── package.json
└── .github/
    └── workflows/
        ├── ci.yml
        ├── ci-main.yml
        └── release.yml

OpenAPI 仕様の CI バリデーションは OpenAPI CI バリデーション完全ガイド で解説した validate → lint → diff パイプラインを、turbo.jsonvalidate:openapi タスクとして組み込む。Monorepo では API 契約変更が複数 apps に波及するため、merge 前の spec 検証は必須 だ。

turbo.json 設計:ルート pipeline 定義

Turborepo 2.x の turbo.jsonルート 1 ファイル で全 workspace を統治する。以下は本番運用を想定した 完全なルート turbo.json だ。

{
  "$schema": "https://turbo.build/schema.json",
  "ui": "tui",
  "globalDependencies": [
    ".env",
    "tsconfig.json",
    "pnpm-lock.yaml"
  ],
  "globalEnv": [
    "NODE_ENV",
    "CI",
    "VERCEL",
    "VERCEL_ENV"
  ],
  "globalPassThroughEnv": [
    "npm_lifecycle_event",
    "TURBO_TEAM",
    "TURBO_TOKEN",
    "TURBO_API",
    "TURBO_REMOTE_CACHE_SIGNATURE_KEY"
  ],
  "tasks": {
    "build": {
      "dependsOn": ["^build"],
      "inputs": ["$TURBO_DEFAULT$", ".env*"],
      "outputs": ["dist/**", ".next/**", "!.next/cache/**", "build/**"],
      "env": [
        "NEXT_PUBLIC_API_URL",
        "NEXT_PUBLIC_APP_ENV"
      ]
    },
    "dev": {
      "cache": false,
      "persistent": true
    },
    "lint": {
      "dependsOn": ["^build"],
      "inputs": ["$TURBO_DEFAULT$", "../../packages/config-eslint/**"]
    },
    "typecheck": {
      "dependsOn": ["^build"],
      "inputs": ["$TURBO_DEFAULT$", "tsconfig.json", "tsconfig.*.json"]
    },
    "test": {
      "dependsOn": ["^build"],
      "inputs": ["$TURBO_DEFAULT$", "**/*.test.ts", "**/*.test.tsx", "**/*.spec.ts"],
      "outputs": ["coverage/**"],
      "env": ["VITEST_POOL", "TZ"]
    },
    "test:e2e": {
      "dependsOn": ["build"],
      "inputs": ["$TURBO_DEFAULT$", "e2e/**", "playwright.config.ts"],
      "outputs": ["playwright-report/**", "test-results/**"],
      "env": ["PLAYWRIGHT_BROWSERS_PATH", "BASE_URL"],
      "cache": true
    },
    "validate:openapi": {
      "dependsOn": [],
      "inputs": [
        "../../openapi/**",
        "../../.spectral.yaml",
        "../../scripts/validate-openapi.mjs"
      ],
      "outputs": [],
      "cache": true
    },
    "generate:api-client": {
      "dependsOn": ["validate:openapi"],
      "inputs": ["../../openapi/openapi.yaml", "orval.config.ts"],
      "outputs": ["src/generated/**"]
    },
    "clean": {
      "cache": false
    }
  }
}

設計上のポイント

キー意図
dependsOn: ["^build"]依存 workspace の build 完了後に実行(キャレット ^ は upstream)
inputsハッシュ対象ファイル。未変更ならキャッシュヒット
outputsRemote Cache にアップロードする成果物パス
env値が変わるとキャッシュミス。公開 env のみ列挙
cache: falsedev サーバー等、キャッシュ不要タスク
env の過剰列挙に注意

envDATABASE_URLJWT_SECRET を入れると、値が runner ごとに異なり 常にキャッシュミス になる。シークレットはタスク内で参照するだけにし、inputs/env には含めない。build 成果物に secrets が混入しないよう outputs も慎重に設計する。

apps/web 専用 turbo.json(Package Configurations)

Turborepo 2.x の Package Configurations を使うと、パッケージ単位で pipeline を上書きできる。Next.js アプリ apps/web/turbo.json の完全例:

{
  "$schema": "https://turbo.build/schema.json",
  "extends": ["//"],
  "tasks": {
    "build": {
      "dependsOn": ["^build", "generate:api-client"],
      "inputs": [
        "$TURBO_DEFAULT$",
        "next.config.ts",
        "postcss.config.mjs",
        "tailwind.config.ts"
      ],
      "outputs": [".next/**", "!.next/cache/**"],
      "env": [
        "NEXT_PUBLIC_API_URL",
        "NEXT_PUBLIC_ANALYTICS_ID",
        "NEXT_PUBLIC_APP_ENV"
      ]
    },
    "test:e2e": {
      "dependsOn": ["build"],
      "inputs": ["e2e/**", "playwright.config.ts"],
      "outputs": ["playwright-report/**"],
      "env": ["BASE_URL", "E2E_AUTH_TOKEN"],
      "passThroughEnv": ["PLAYWRIGHT_BROWSERS_PATH"]
    }
  }
}

extends: ["//"] はルート turbo.json を継承する記法だ。アプリ固有の next.config.ts を inputs に追加することで、設定変更時だけ build キャッシュが無効化される。

packages/ui 共有ライブラリの turbo.json

UI ライブラリは 最もキャッシュヒット率が高い パッケージになりやすい。変更頻度が低く、複数 apps から dependsOn: ["^build"] で参照されるためだ。

{
  "$schema": "https://turbo.build/schema.json",
  "extends": ["//"],
  "tasks": {
    "build": {
      "dependsOn": [],
      "inputs": ["src/**", "tsconfig.json", "tsup.config.ts"],
      "outputs": ["dist/**"],
      "env": []
    },
    "test": {
      "dependsOn": [],
      "inputs": ["src/**", "**/*.test.tsx", "vitest.config.ts"],
      "outputs": ["coverage/**"]
    }
  }
}

packages/ui/dist/** が Remote Cache に載ると、apps/webapps/docs の build が compile なしでリンク できる。

Remote Cache アーキテクチャ

Remote Cache のデータフローは次のとおり。

sequenceDiagram
  participant Dev as 開発者 laptop
  participant CI as GitHub Actions
  participant Turbo as Turborepo CLI
  participant RC as Remote Cache (Vercel/S3)

  Dev->>Turbo: turbo run build
  Turbo->>Turbo: inputs ハッシュ計算
  Turbo->>RC: GET cache/{hash}
  alt キャッシュヒット
    RC-->>Turbo: artifacts (dist, .next)
    Turbo-->>Dev: 復元完了 (skip build)
  else キャッシュミス
    Turbo->>Turbo: タスク実行
    Turbo->>RC: PUT cache/{hash}
    Turbo-->>Dev: ビルド完了
  end
  CI->>Turbo: turbo run build --filter=...[origin/main]
  Turbo->>RC: GET (Dev が upload した hash)
  RC-->>Turbo: ヒット → CI 時間短縮

Vercel Remote Cache(最速セットアップ)

Vercel アカウントとリンクしていなくても、Standalone Remote Cache として利用できる。

# ローカル初回セットアップ
pnpm dlx turbo login
pnpm dlx turbo link

# 確認
pnpm turbo run build --summarize

GitHub Secrets に登録する値:

Secret説明
TURBO_TOKENVercel ダッシュボード → Settings → Tokens
TURBO_TEAMチーム slug(team_xxx 形式)

セルフホスト S3 Remote Cache

データ residency 要件がある組織向け。Turborepo の Remote Cache API は HTTP ベースで、community プロキシまたは @turbo/gen テンプレートで S3 バックエンドを構築できる。

# 環境変数(GitHub Actions / ローカル共通)
export TURBO_API="https://turbo-cache.internal.example.com"
export TURBO_TOKEN="Bearer eyJhbGciOiJIUzI1NiIs..."
export TURBO_TEAM="acme-corp"
export TURBO_REMOTE_CACHE_SIGNATURE_KEY="base64-encoded-32-bytes-key"

S3 バケットポリシーでは CI OIDC ロールからの PutObject/GetObject のみ 許可し、パブリックアクセスは Block する。

1

[object Object]

2

[object Object]

3

[object Object]

4

[object Object]

5

[object Object]

pnpm workspace 設定

ルート pnpm-workspace.yaml

packages:
  - "apps/*"
  - "packages/*"

ルート package.json

{
  "name": "acme-monorepo",
  "private": true,
  "packageManager": "[email protected]",
  "scripts": {
    "build": "turbo run build",
    "dev": "turbo run dev",
    "lint": "turbo run lint",
    "test": "turbo run test",
    "typecheck": "turbo run typecheck",
    "validate:openapi": "turbo run validate:openapi",
    "ci": "turbo run lint typecheck test build validate:openapi --filter=...[origin/main]"
  },
  "devDependencies": {
    "turbo": "^2.5.0",
    "typescript": "^5.8.0"
  },
  "engines": {
    "node": ">=20.0.0",
    "pnpm": ">=9.0.0"
  }
}

GitHub Actions:PR CI ワークフロー(完全版)

以下は pull_request 時 に影響パッケージだけ lint / test / build する .github/workflows/ci.yml の完全例だ。

name: CI

on:
  pull_request:
    branches: [main]
    paths-ignore:
      - "**/*.md"
      - "docs/**"

concurrency:
  group: ci-${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
  cancel-in-progress: true

env:
  TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
  TURBO_TEAM: ${{ secrets.TURBO_TEAM }}
  FORCE_COLOR: 3
  CI: true

jobs:
  changes:
    name: Detect affected packages
    runs-on: ubuntu-latest
    outputs:
      has-changes: ${{ steps.filter.outputs.changes }}
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0
      - uses: pnpm/action-setup@v4
        with:
          version: 9
      - uses: actions/setup-node@v4
        with:
          node-version: 20
          cache: pnpm
      - name: Install dependencies
        run: pnpm install --frozen-lockfile
      - name: Check affected
        id: filter
        run: |
          AFFECTED=$(pnpm turbo run build --filter=...[origin/main] --dry-run=json | jq '.tasks | length')
          echo "Affected tasks: $AFFECTED"
          if [ "$AFFECTED" -gt 0 ]; then
            echo "changes=true" >> "$GITHUB_OUTPUT"
          else
            echo "changes=false" >> "$GITHUB_OUTPUT"
          fi

  quality:
    name: Lint, Test, Build
    needs: changes
    if: needs.changes.outputs.has-changes == 'true'
    runs-on: ubuntu-latest
    timeout-minutes: 30
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0

      - uses: pnpm/action-setup@v4
        with:
          version: 9

      - uses: actions/setup-node@v4
        with:
          node-version: 20
          cache: pnpm

      - name: Install dependencies
        run: pnpm install --frozen-lockfile

      - name: Turbo lint + typecheck + test + build
        run: |
          pnpm turbo run lint typecheck test build validate:openapi \
            --filter=...[origin/main] \
            --summarize \
            --continue

      - name: Upload Turbo Run Summary
        if: always()
        uses: actions/upload-artifact@v4
        with:
          name: turbo-run-summary
          path: .turbo/runs
          retention-days: 14

      - name: Report cache hit rate
        if: always()
        run: |
          if [ -d .turbo/runs ]; then
            CACHED=$(find .turbo/runs -name "*.json" -exec jq -r '.tasks[] | select(.cache.status == "HIT") | .taskId' {} \; 2>/dev/null | wc -l)
            TOTAL=$(find .turbo/runs -name "*.json" -exec jq -r '.tasks[] | .taskId' {} \; 2>/dev/null | wc -l)
            echo "Cache hits: $CACHED / $TOTAL"
          fi

--filter=...[origin/main] の読み方

フィルタ意味
--filter=webweb パッケージのみ
--filter=web...web とその依存(downstream)
--filter=...webweb に依存する全パッケージ(upstream)
--filter=...[origin/main]main との git diff から影響パッケージを自動算出

PR CI では ...[origin/main] が最頻出。OpenAPI 変更 → packages/api-clientapps/web という依存チェーンも Turborepo が自動追跡する。

GitHub Actions:main ブランチ キャッシュ warm-up

main への merge 後に フルビルド して Remote Cache を seed する。feature ブランチ PR が main の最新キャッシュを引き継げる。

name: CI Main (Cache Warm-up)

on:
  push:
    branches: [main]
    paths-ignore:
      - "**/*.md"

concurrency:
  group: ci-main-${{ github.sha }}
  cancel-in-progress: false

env:
  TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
  TURBO_TEAM: ${{ secrets.TURBO_TEAM }}
  CI: true

jobs:
  warm-cache:
    name: Full build and cache upload
    runs-on: ubuntu-latest
    timeout-minutes: 45
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0

      - uses: pnpm/action-setup@v4
        with:
          version: 9

      - uses: actions/setup-node@v4
        with:
          node-version: 20
          cache: pnpm

      - name: Install dependencies
        run: pnpm install --frozen-lockfile

      - name: Full turbo pipeline
        run: |
          pnpm turbo run lint typecheck test build validate:openapi generate:api-client \
            --summarize

      - name: Upload summary artifact
        if: always()
        uses: actions/upload-artifact@v4
        with:
          name: turbo-main-summary-${{ github.sha }}
          path: .turbo/runs
          retention-days: 30

GitHub Actions:matrix 分割で E2E 並列化

E2E は重いので shard 分割 + Remote Cache を組み合わせる。apps/web/turbo.jsontest:e2e タスクを利用。

name: E2E

on:
  pull_request:
    branches: [main]
    paths:
      - "apps/web/**"
      - "packages/ui/**"

env:
  TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
  TURBO_TEAM: ${{ secrets.TURBO_TEAM }}
  CI: true

jobs:
  e2e:
    name: Playwright shard ${{ matrix.shard }}
    runs-on: ubuntu-latest
    timeout-minutes: 25
    strategy:
      fail-fast: false
      matrix:
        shard: [1, 2, 3, 4]
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0

      - uses: pnpm/action-setup@v4
        with:
          version: 9

      - uses: actions/setup-node@v4
        with:
          node-version: 20
          cache: pnpm

      - name: Install dependencies
        run: pnpm install --frozen-lockfile

      - name: Install Playwright browsers
        run: pnpm exec playwright install --with-deps chromium
        working-directory: apps/web

      - name: Build (cached)
        run: pnpm turbo run build --filter=web

      - name: Run E2E shard
        run: |
          pnpm turbo run test:e2e --filter=web -- \
            --shard=${{ matrix.shard }}/4
        env:
          BASE_URL: http://127.0.0.1:3000

      - name: Upload Playwright report
        if: failure()
        uses: actions/upload-artifact@v4
        with:
          name: playwright-report-shard-${{ matrix.shard }}
          path: apps/web/playwright-report
          retention-days: 7

build ステップが Remote Cache ヒットすれば、4 shard すべてが 同一 cached .next からスタートできる。

GitHub Actions:OpenAPI バリデーション統合

OpenAPI CI バリデーション で構築した validate / lint / diff を Monorepo タスクとして統合する。openapi/ 変更時のみ走らせる dedicated workflow:

name: OpenAPI CI

on:
  pull_request:
    paths:
      - "openapi/**"
      - ".spectral.yaml"
      - "scripts/validate-openapi.mjs"
      - "packages/api-client/**"

env:
  TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
  TURBO_TEAM: ${{ secrets.TURBO_TEAM }}
  CI: true

jobs:
  openapi:
    name: Validate OpenAPI spec
    runs-on: ubuntu-latest
    timeout-minutes: 10
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0

      - uses: pnpm/action-setup@v4
        with:
          version: 9

      - uses: actions/setup-node@v4
        with:
          node-version: 20
          cache: pnpm

      - name: Install dependencies
        run: pnpm install --frozen-lockfile

      - name: Validate, lint, diff OpenAPI
        run: pnpm turbo run validate:openapi --summarize

      - name: Regenerate API client (dry check)
        run: pnpm turbo run generate:api-client --summarize

      - name: Check for uncommitted generated files
        run: |
          if ! git diff --exit-code packages/api-client/src/generated; then
            echo "Generated API client is out of date. Run pnpm turbo run generate:api-client locally."
            exit 1
          fi

validate:openapi タスクの inputs に openapi/** を指定しているため、spec ファイルが変わらなければ CI スキップ される。

packages/api-client の turbo.json

OpenAPI から SDK を生成するパッケージ。validate タスク完了後のみ実行。

{
  "$schema": "https://turbo.build/schema.json",
  "extends": ["//"],
  "tasks": {
    "generate:api-client": {
      "dependsOn": ["validate:openapi"],
      "inputs": [
        "../../openapi/openapi.yaml",
        "orval.config.ts",
        "package.json"
      ],
      "outputs": ["src/generated/**"],
      "cache": true
    },
    "build": {
      "dependsOn": ["generate:api-client"],
      "inputs": ["src/**", "tsconfig.json", "tsup.config.ts"],
      "outputs": ["dist/**"]
    }
  }
}

GitHub Actions:リリース workflow

タグ push 時に キャッシュを使いつつ 本番 build + publish する完全例。

name: Release

on:
  push:
    tags:
      - "v*.*.*"

permissions:
  contents: read
  id-token: write

env:
  TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
  TURBO_TEAM: ${{ secrets.TURBO_TEAM }}
  CI: true

jobs:
  release:
    name: Build and publish packages
    runs-on: ubuntu-latest
    timeout-minutes: 60
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0

      - uses: pnpm/action-setup@v4
        with:
          version: 9

      - uses: actions/setup-node@v4
        with:
          node-version: 20
          cache: pnpm
          registry-url: https://registry.npmjs.org

      - name: Install dependencies
        run: pnpm install --frozen-lockfile

      - name: Build all packages
        run: pnpm turbo run build validate:openapi --summarize

      - name: Publish packages
        run: pnpm turbo run publish-packages
        env:
          NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}

      - name: Upload build artifacts
        uses: actions/upload-artifact@v4
        with:
          name: release-build-${{ github.ref_name }}
          path: |
            apps/web/.next
            packages/ui/dist
            packages/api-client/dist
          retention-days: 90

キャッシュヒット率の最適化

Remote Cache の効果は ヒット率 に直結する。2026 年の実務で効く調整をまとめる。

1. inputs を最小限かつ正確に

{
  "tasks": {
    "build": {
      "inputs": ["src/**", "package.json", "tsconfig.json", "vite.config.ts"],
      "outputs": ["dist/**"]
    }
  }
}

README.mdCHANGELOG.md を inputs に含めると、ドキュメント修正だけで build キャッシュが全失効する。

2. globalDependencies の慎重な運用

ルート package.json の devDependencies 更新(turbo バージョンアップ等)は 全タスクキャッシュを無効化 する。Renovate PR では --force 再ビルドを許容し、merge 後に warm-up job で seed し直す。

3. --summarize で可視化

pnpm turbo run build --summarize
# .turbo/runs/<id>.json に HIT/MISS 一覧

週次で .turbo/runs を集計し、MISS 率が高いタスク の env/inputs を監査する。

4. ロックファイルの統一

pnpm の pnpm-lock.yamlglobalDependencies に含める。npm / yarn 混在は hash 不一致の温床。

キャッシュミス原因 対策
env に DATABASE_URL を列挙 env から削除、runtime のみ参照
outputs に .next/cache を含む !.next/cache/** で除外
各 package で Node バージョン不一致 .node-version + engines 統一
generate タスクが非決定的 OpenAPI generator の version 固定
PR で fetch-depth: 1 fetch-depth: 0 で filter 正確化

Turborepo と Nx / Lage の比較

観点TurborepoNxLage
学習曲線低(turbo.json のみ)中〜高(プラグインエコシステム)
Remote CacheVercel / セルフホストNx CloudAzure Blob
JS/TS 特化強い強い(フレームワーク統合)Microsoft 内部向け
CI 連携--filter が簡潔affected コマンドpipeline 定義
2026 トレンドpnpm + Turbo 定番エンタープライズ Java/TS限定的

新規 JS/TS Monorepo なら Turborepo + pnpm が初期コスト最小。Java/Kotlin 混在なら Nx を検討。

セキュリティチェックリスト

本番前必須確認

Remote Cache は ビルド成果物を外部ストレージに置く 設計だ。以下を merge 前に確認する。

  1. TURBO_TOKEN — 最小スコープ、90 日ローテーション、漏洩時は即 revoke
  2. outputs 監査dist/.env.production や source map が含まれないか
  3. 署名キーTURBO_REMOTE_CACHE_SIGNATURE_KEY で artifact tampering 検知
  4. PR fork 対策 — fork PR では Secrets を渡さない(pull_request_target 禁止)
  5. S3 暗号化 — SSE-KMS、バケットポリシーで IP/VPC 制限

fork PR 向け CI では Remote Cache read-only に留め、write は github.ref == 'refs/heads/main' の job のみに限定する。

ローカル開発ワークフロー

# 初回
pnpm install
pnpm dlx turbo login
pnpm dlx turbo link

# 日常開発(web と依存だけ)
pnpm turbo run dev --filter=web

# PR 前チェック(origin/main との差分のみ)
git fetch origin main
pnpm turbo run lint typecheck test build validate:openapi --filter=...[origin/main]

# キャッシュ状態確認
pnpm turbo run build --dry-run
pnpm turbo run build --summarize

main CI が warm-up 済みなら、ローカルの --filter=...[origin/main]大半 HIT になり、ラップトップのファンが静かになる。

トラブルシューティング

キャッシュヒットしない

# 詳細ログ
TURBO_LOG_VERBOSITY=2 pnpm turbo run build --force

# ハッシュ差分の原因調査
pnpm turbo run build --dry-run=json | jq '.tasks[] | {task: .taskId, hash: .hash, inputs: .inputs}'

よくある原因:pnpm-lock.yaml 変更、Node バージョン差、globalEnvVERCEL がローカルのみ設定されている。

Remote Cache 接続エラー

Error: Failed to connect to Remote Cache
  • TURBO_TOKEN 期限切れ → 再発行
  • 企業プロキシ → HTTPS_PROXY 設定
  • セルフホスト TURBO_API の TLS 証明書 → runner に CA 追加

--filter=...[origin/main] が空

fetch-depth: 0 未設定で shallow clone だと git diff が不完全。actions/checkout@v4 で fetch-depth: 0 を必ず指定する。

2026 年の Monorepo CI ベストプラクティスまとめ

1

[object Object]

2

[object Object]

3

[object Object]

4

[object Object]

5

[object Object]

6

[object Object]

Monorepo の CI は 「全部走らせない」 が鉄則だ。Turborepo Remote Cache は、その方針を インフラとして強制 する。

  • turbo.json — タスク依存と inputs/outputs をコード化
  • Remote Cache — チーム + CI で成果物共有
  • --filter=...[origin/main] — git diff ベースの部分実行
  • GitHub Actions — concurrency、cancellation、matrix 分割
  • OpenAPI 連携OpenAPI CI バリデーション と generate タスク統合

API 契約、UI コンポーネント、Web アプリが 1 リポジトリに共存する時代、ビルドパイプラインの設計 が開発速度を決める。Turborepo Remote Cache は 2026 年時点で 最もコスト対効果の高い Monorepo CI 基盤 の一つだ。

関連記事

この記事は インフラ・DNS テーマの一環です。あわせて読むと理解が深まる関連記事をまとめました。

トピック記事
DNSが反映されたか確認する方法DNSが反映されたか確認する方法|nslookupとdigコマンドの使い方
HTTPSレコードとSVCBレコードの完全解説HTTPSレコードとSVCBレコードの完全解説|ALPN・ECH・HTTP/3のDNS設定
CNAME lookup・DNS check の確認手順とよくある失敗CNAME lookup・DNS check の確認手順とよくある失敗
SSL証明書の有効期限を確認する方法SSL証明書の有効期限を確認する方法|opensslコマンドと注意点
.htaccessでリダイレクトをかける方法.htaccessでリダイレクトをかける方法|301と302の使い分け
robots.txtの書き方と役割robots.txtの書き方と役割|クローラーを正しく制御する方法
AIクローラー対応 robots.txt・llms.txt 設定ガイド 2026AIクローラー対応 robots.txt・llms.txt 設定ガイド 2026|GPTBot・ClaudeBot・Google-Extended・GEO戦略
ブログの SEO と JSON-LD 構造化データ入門ブログの SEO と JSON-LD 構造化データ入門
依存関係脆弱性管理とSBOM CycloneDX完全ガイド依存関係脆弱性管理とSBOM CycloneDX完全ガイド|npm audit・OSV・Dependabot・サプライチェーンセキュリティ2026
Redisキャッシュスタンピード防止ガイドRedisキャッシュスタンピード防止ガイド|Singleflight・Mutex・SET NX・確率的早期失効
Git Worktree 並行開発ワークフロー完全ガイドGit Worktree 並行開発ワークフロー完全ガイド|複数ブランチ同時作業・IDE設定・CI連携(2026)
JSONの「Unexpected token」エラー原因と対処法JSONの「Unexpected token」エラー原因と対処法