|$ curl https://forge-ai.dev/api/markdown?path=docs/ci/monorepo
$cat docs/monorepo-ci.md
updated Recently·30 min read·published

Monorepo CI

CI/CDIntermediate🎯Free Tools
Introduction

Monorepos (one repository, many packages) face a CI challenge: running all tests for every change is wasteful. The solution is incremental CI — only build, test, and deploy packages affected by a change, using dependency graphs and caching.

Turborepo
turbo.json
JSON
1// turbo.json — Turborepo pipeline configuration
2{
3 "$schema": "https://turbo.build/schema.json",
4 "tasks": {
5 "build": {
6 "dependsOn": ["^build"],
7 "outputs": ["dist/**", ".next/**"],
8 "inputs": ["src/**", "package.json"]
9 },
10 "test": {
11 "dependsOn": ["build"],
12 "inputs": ["src/**", "tests/**"],
13 "outputs": []
14 },
15 "lint": {
16 "inputs": ["src/**"],
17 "outputs": []
18 },
19 "dev": {
20 "cache": false,
21 "persistent": true
22 }
23 }
24}
turborepo-commands.sh
Bash
1# Turborepo commands
2npx turbo run build # Build all packages
3npx turbo run build --filter=web # Build only web and its dependencies
4npx turbo run test --filter=./packages/ui # Test UI package
5
6# Only run tasks affected by changes (remote cache)
7npx turbo run build test --since=main
8
9# Verbose output to see cache hits
10npx turbo run build --verbose
11
12# Prune packages not needed for deployment
13npx turbo prune web --docker # Output for Docker build
14
15# GitHub Actions with Turborepo
16# - uses: actions/cache@v4
17# with:
18# path: .turbo
19# key: turbo-${{ hashFiles('**/turbo.json') }}-${{ github.sha }}
20# restore-keys: turbo-

info

Turborepo's remote caching shares build artifacts across your team and CI. Configure it with npx turbo login && npx turbo link to get cache hits on CI runs triggered by other team members.
Nx
nx.json
TypeScript
1// nx.json — Nx workspace configuration
2{
3 "tasksRunnerOptions": {
4 "default": {
5 "runner": "nx-cloud",
6 "options": {
7 "cacheDirectory": ".nx/cache",
8 "accessToken": "xxx"
9 }
10 }
11 },
12 "targetDefaults": {
13 "build": {
14 "dependsOn": ["^build"],
15 "inputs": ["production", "^production"]
16 }
17 }
18}
19
20# Nx affected commands — only process changed projects
21npx nx affected --target=build # Build affected projects
22npx nx affected --target=test # Test affected projects
23npx nx affected --target=lint # Lint affected projects
24npx nx affected --target=e2e # E2E test affected projects
25
26# Visualize dependency graph
27npx nx graph
28
29# Run specific project
30npx nx build web
31npx nx test api
32
33# Cloud caching with Nx Cloud
34npx nx connect-to-nx-cloud

best practice

Use nx affected in CI to skip unchanged packages. Nx uses a dependency graph and file hashes to determine what changed — only those packages are built and tested.
GitHub Actions Monorepo CI
monorepo-ci.yml
YAML
1# GitHub Actions: only CI for changed packages
2name: CI
3on:
4 pull_request:
5 branches: [main]
6
7jobs:
8 detect-changes:
9 runs-on: ubuntu-latest
10 outputs:
11 packages: ${{ steps.changes.outputs.packages }}
12 steps:
13 - uses: actions/checkout@v4
14 - uses: dorny/paths-filter@v3
15 id: changes
16 with:
17 filters: |
18 web:
19 - 'apps/web/**'
20 api:
21 - 'apps/api/**'
22 ui:
23 - 'packages/ui/**'
24
25 build-and-test:
26 needs: detect-changes
27 runs-on: ubuntu-latest
28 strategy:
29 matrix:
30 package: ${{ fromJSON(needs.detect-changes.outputs.packages) }}
31 steps:
32 - uses: actions/checkout@v4
33 - run: npm ci
34 - run: npm run build --workspace=${{ matrix.package }}
35 - run: npm test --workspace=${{ matrix.package }}
$Blueprint — Engineering Documentation·Section ID: CI-MR-01·Revision: 1.0