Workspaces & Monorepos
Using pastoralist in workspace and monorepo environments
Using pastoralist in workspace and monorepo environments
Pastoralist works with workspace and monorepo setups. This guide covers how to track root-level overrides while still showing which workspace packages depend on them.
Pastoralist updates one target package.json, usually the workspace root. When
depPaths is configured, it also reads workspace package manifests so the root
appendix can show which packages still need each override.
You can also run it against an individual workspace package with --path when
that package owns its own override field.
Pastoralist provides multiple ways to configure workspace scanning in monorepos:
Configure dependency paths directly in your package.json for workspace
tracking:
{
"name": "my-monorepo",
"workspaces": ["packages/*", "apps/*"],
"overrides": {
"lodash": "4.17.21"
},
"pastoralist": {
"depPaths": "workspace"
},
"scripts": {
"postinstall": "pastoralist"
}
}
Using "workspace" string - Pastoralist automatically uses all packages defined in your workspaces field. The appendix only appears in the root; workspace packages stay clean.
Using array of paths - Specify custom paths to scan:
{
"pastoralist": {
"depPaths": ["packages/app-a/package.json", "packages/app-b/package.json"]
}
}
After running pastoralist, your root package.json will contain:
{
"overrides": {
"lodash": "4.17.21"
},
"pastoralist": {
"depPaths": "workspace",
"appendix": {
"[email protected]": {
"dependents": {
"app-a": "lodash@^4.17.0",
"app-b": "lodash@^4.17.0",
"package-c": "lodash@^4.17.0"
},
"ledger": {
"addedDate": "2026-05-30T00:00:00.000Z",
"source": "manual"
}
}
}
}
}
The workspace packages (packages/*/package.json and apps/*/package.json) remain clean without any pastoralist appendix.
Specify paths at runtime:
# Scan specific paths
pastoralist --depPaths "packages/*/package.json" "apps/*/package.json"
# CLI flags override package.json configuration
pastoralist --depPaths "packages/app-a/package.json"
Pastoralist offers guided configuration for monorepo setups:
# Initialize with guided setup
pastoralist init
The initializer can:
workspaces entries from package.jsondepPaths: "workspace" or custom package globspackage.json or a supported config file# Run on the root package.json
pastoralist
This will manage overrides in your root package.json, which affect all workspaces.
# Run on a specific workspace package
pastoralist --path packages/app-a/package.json
# Or navigate to the package
cd packages/app-a
pastoralist
Most monorepos use root-level overrides that apply to all workspaces:
{
"name": "my-monorepo",
"workspaces": ["packages/*"],
"overrides": {
"lodash": "4.17.21",
"react": "18.2.0"
}
}
Run pastoralist at the root:
pastoralist
Some packages may need their own overrides:
{
"name": "legacy-app",
"overrides": {
"react": "17.0.2"
}
}
Run pastoralist for this package:
pastoralist --path packages/legacy-app/package.json
Most workspaces should avoid running Pastoralist separately in every package.
Keep shared overrides at the root and let depPaths read workspace manifests:
{
"workspaces": ["packages/*", "apps/*"],
"pastoralist": {
"depPaths": "workspace"
},
"scripts": {
"pastoralist": "pastoralist"
}
}
Use --path only for workspace packages that intentionally own their own
override field.
Keep all overrides in the root package.json and use depPaths configuration:
{
"workspaces": ["packages/*", "apps/*"],
"overrides": {
"lodash": "4.17.21"
},
"pastoralist": {
"depPaths": "workspace"
},
"scripts": {
"postinstall": "pastoralist"
}
}
Allow packages to manage their own overrides only when those overrides are package-specific:
{
"overrides": {
"react": "17.0.2"
},
"scripts": {
"pastoralist": "pastoralist --path package.json"
}
}
Combine root overrides with package-specific ones:
Root overrides can hold shared security patches:
{
"overrides": {
"minimist": "1.2.8"
}
}
Package overrides can hold feature-specific constraints:
{
"overrides": {
"react": "17.0.2"
}
}
{
"name": "my-npm-workspace",
"workspaces": ["packages/*", "apps/*"],
"pastoralist": {
"depPaths": "workspace"
},
"scripts": {
"check-overrides": "pastoralist --dry-run"
}
}
# pnpm-workspace.yaml
packages:
- "packages/*"
- "apps/*"
{
"pastoralist": {
"depPaths": "workspace"
},
"scripts": {
"check-overrides": "pastoralist --dry-run"
}
}
{
"private": true,
"workspaces": {
"packages": ["packages/*"]
},
"pastoralist": {
"depPaths": "workspace"
},
"scripts": {
"check-overrides": "pastoralist --dry-run"
}
}
Ensure overrides are valid in CI:
- name: Validate overrides
run: |
npx pastoralist
git diff --exit-code package.json
Symptom: Workspace packages don't respect root overrides
Solution: Ensure you're using a package manager that supports workspace overrides:
Symptom: Same override tracked in multiple package.json files
Solution: If the override is shared, move it to the root package and use
depPaths: "workspace". If each package owns a different override, separate
appendixes are expected.
Symptom: Pastoralist takes long to run across many packages
Solution: First prefer depPaths: "workspace" so one root run reads the
workspace manifests. If you must scan packages individually, make sure your file
search excludes node_modules:
# Using GNU parallel for package-owned override fields
find . -name "node_modules" -prune -o -name "package.json" -print | \
parallel "pastoralist --path {}"
find . -name "package.json" -not -path "*/node_modules/*" \
-exec jq '.overrides // {}' {} \; | jq -s 'add'
{
"name": "my-monorepo",
"workspaces": ["packages/*", "apps/*"],
"overrides": {
"lodash": "4.17.21"
},
"pastoralist": {
"depPaths": "workspace"
},
"scripts": {
"postinstall": "pastoralist"
}
}
{
"pastoralist": {
"depPaths": ["packages/app-a/package.json", "packages/app-b/package.json"]
}
}
{
"overrides": {
"lodash": "4.17.21"
},
"pastoralist": {
"depPaths": "workspace",
"appendix": {
"[email protected]": {
"dependents": {
"app-a": "lodash@^4.17.0",
"app-b": "lodash@^4.17.0",
"package-c": "lodash@^4.17.0"
},
"ledger": {
"addedDate": "2026-05-30T00:00:00.000Z",
"source": "manual"
}
}
}
}
}
# Scan specific paths
pastoralist --depPaths "packages/*/package.json" "apps/*/package.json"
# CLI flags override package.json configuration
pastoralist --depPaths "packages/app-a/package.json"
# Initialize with guided setup
pastoralist init
# Run on the root package.json
pastoralist
# Run on a specific workspace package
pastoralist --path packages/app-a/package.json
# Or navigate to the package
cd packages/app-a
pastoralist
{
"name": "my-monorepo",
"workspaces": ["packages/*"],
"overrides": {
"lodash": "4.17.21",
"react": "18.2.0"
}
}
pastoralist
{
"name": "legacy-app",
"overrides": {
"react": "17.0.2"
}
}
pastoralist --path packages/legacy-app/package.json
{
"workspaces": ["packages/*", "apps/*"],
"pastoralist": {
"depPaths": "workspace"
},
"scripts": {
"pastoralist": "pastoralist"
}
}
{
"workspaces": ["packages/*", "apps/*"],
"overrides": {
"lodash": "4.17.21"
},
"pastoralist": {
"depPaths": "workspace"
},
"scripts": {
"postinstall": "pastoralist"
}
}
{
"overrides": {
"react": "17.0.2"
},
"scripts": {
"pastoralist": "pastoralist --path package.json"
}
}
{
"overrides": {
"minimist": "1.2.8"
}
}
{
"overrides": {
"react": "17.0.2"
}
}
{
"name": "my-npm-workspace",
"workspaces": ["packages/*", "apps/*"],
"pastoralist": {
"depPaths": "workspace"
},
"scripts": {
"check-overrides": "pastoralist --dry-run"
}
}
# pnpm-workspace.yaml
packages:
- "packages/*"
- "apps/*"
{
"pastoralist": {
"depPaths": "workspace"
},
"scripts": {
"check-overrides": "pastoralist --dry-run"
}
}
{
"private": true,
"workspaces": {
"packages": ["packages/*"]
},
"pastoralist": {
"depPaths": "workspace"
},
"scripts": {
"check-overrides": "pastoralist --dry-run"
}
}
- name: Validate overrides
run: |
npx pastoralist
git diff --exit-code package.json
# Using GNU parallel for package-owned override fields
find . -name "node_modules" -prune -o -name "package.json" -print | \
parallel "pastoralist --path {}"
find . -name "package.json" -not -path "*/node_modules/*" \
-exec jq '.overrides // {}' {} \; | jq -s 'add'