How to Set Up CI for a Monorepo With Changed-Path Filters in GitHub Actions
dorny/paths-filter detects which packages changed in a PR so you only build and test what was touched.
A detect job runs dorny/paths-filter to set per-package outputs, then each package job declares needs: detect with an if that checks its filter output. Unchanged packages are skipped, saving minutes.
Steps
- Add a
detectjob that runsdorny/paths-filterwith one filter per package. - Expose the filter results as job outputs.
- Gate each package job with
if: needs.detect.outputs.<pkg> == 'true'. - Install, lint, and test inside each package job.
Workflow
.github/workflows/ci.yml
name: CI
on:
pull_request:
jobs:
detect:
runs-on: ubuntu-latest
outputs:
api: ${{ steps.filter.outputs.api }}
web: ${{ steps.filter.outputs.web }}
steps:
- uses: actions/checkout@v4
- uses: dorny/paths-filter@v3
id: filter
with:
filters: |
api:
- 'packages/api/**'
web:
- 'packages/web/**'
api:
needs: detect
if: needs.detect.outputs.api == 'true'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 20
cache: 'npm'
- run: npm ci --workspace packages/api
- run: npm run lint --workspace packages/api
- run: npm test --workspace packages/apiGotchas
- A skipped required check can block merges; add an aggregate job that succeeds when all needed jobs pass or skip.
- On
pushto a base branch, paths-filter compares against the previous commit, not a PR base.
Related guides
How to Set Up CI for Node.js (pnpm) With GitHub ActionsSet up GitHub Actions CI for a pnpm project: install pnpm with pnpm/action-setup, let setup-node cache the pn…
How to Set Up CI for Node.js (npm) With GitHub ActionsSet up GitHub Actions CI for a Node.js project that uses npm, with setup-node caching the npm store, npm ci f…