Skip to content

yowainwright/pastoralist

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

1,261 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

npm version ci codecov

Pastoralist helps you maintain node module overrides and address security issues in dependencies.

  1. Pastoralist tracks, secures, and cleans up your overrides, resolutions, and patches.
  2. Pastoralist can help resolve dependency security alerts by creating overrides for vulnerable packages.

TL;DR

One command. Three features.

npm i pastoralist -D && pastoralist --init
  • Tracks why each override exists
  • Scans for security vulnerabilities
  • Removes unused overrides

Table of Contents


What are overrides and resolutions?

Package manager overrides and resolutions let you control exact dependency versions in your node_modules.

Package managers (npm, yarn, pnpm, bun) use these overrides to fix:

  • Security vulnerabilities in nested dependencies
  • Bugs in transitive dependencies
  • Version conflicts

Read more: npm overrides, yarn resolutions, pnpm overrides, bun overrides


The Problem

You add overrides to fix a security alert or broken dependency:

"overrides": {
  "lodash": "4.17.21"  // Why? What for? No idea anymore.
}

Six months later, you have no clue:

flowchart LR
    You[You] --> Question1{Why is this here?}
    Question1 --> Question2{What uses it?}
    Question2 --> Question3{Still needed?}
    Question3 --> Stuck[🀷 Just leave it...]

    style You fill:#e3f2fd
    style Stuck fill:#ffebee
Loading

You end up with ghost overrides haunting your package.json forever.

Try it β†’


The Solution

Pastoralist automatically documents every override:

"overrides": {
  "trim": "^0.0.3"
},
"pastoralist": {
  "appendix": {
    "trim@^0.0.3": {
      "dependents": {
        "remark-parse": "4.0.0"  // ← Ah! remark-parse needs this
      }
    }
  }
}

No more mysteries. Every override is tracked.

flowchart LR
    Install[npm install] --> Auto[Pastoralist runs]
    Auto --> Track[Tracks deps]
    Auto --> Scan[Scans security]
    Auto --> Clean[Cleans unused]
    Track --> Done[βœ“ Done]
    Scan --> Done
    Clean --> Done

    style Install fill:#e3f2fd
    style Auto fill:#f3e5f5
    style Done fill:#e8f5e9
Loading

Automatic cleanup:

When trim is no longer needed, Pastoralist removes it automatically:

"overrides": {},      // ← Cleaned up automatically
"pastoralist": {
  "appendix": {} //  ← Cleaned up automatically
}

Automatic security checks:

Run once with --checkSecurity enabled:

"pastoralist": {
  "security": {
    "enabled": true,
    "provider": "osv"
  }
}

Pastoralist tracks the fix:

"overrides": {
  "lodash": "4.17.21"  // ← Auto-fixed CVE-2021-23337
},
"pastoralist": {
  "appendix": {
    "lodash@4.17.21": {
      "dependents": {"my-app": "lodash@^4.17.0"},
      "ledger": {
        "reason": "Security vulnerability CVE-2021-23337",
        "securityProvider": "osv"
      }
    }
  }
}

Security fixes are tracked with full context.


What Pastoralist Automates

1. Override Tracking

Pastoralist documents every override, including nested dependencies.

"overrides": {
  "pg": {
    "pg-types": "^4.0.1"  // Nested override
  }
}
flowchart TD
    Start([Nested override detected]) --> Parse[Parse parent dependency]
    Parse --> Track[Track in appendix]
    Track --> End([Full dependency chain visible])

    style Start fill:#e3f2fd
    style End fill:#e8f5e9
    style Parse fill:#f3e5f5
Loading

Result:

"pastoralist": {
  "appendix": {
    "pg-types@^4.0.1": {
      "dependents": {
        "my-app": "pg@^8.13.1 (nested override)"
      }
    }
  }
}

2. Security Checks

Enable security scanning with your preferred provider.

"pastoralist": {
  "security": {
    "enabled": true,
    "provider": "osv",
    "severityThreshold": "medium"
  }
}
flowchart TD
    Start([npm install]) --> Scan[Request vulnerability reports]
    Scan --> Detect{Vulnerabilities found?}
    Detect -->|Yes| Fix[Auto-generate override]
    Detect -->|No| Done[βœ“ Done]
    Fix --> Done

    style Start fill:#e3f2fd
    style Fix fill:#fff3cd
    style Done fill:#e8f5e9
Loading

Supported providers:

  • OSV (default) - No auth required
  • GitHub - Requires token and permissions (see below)
  • Snyk [EXPERIMENTAL] - Requires CLI and token
  • Socket [EXPERIMENTAL] - Requires CLI and token

GitHub Provider Setup

When using the GitHub provider in CI workflows, add the vulnerability-alerts: read permission:

permissions:
  contents: write
  vulnerability-alerts: read

You must also enable Dependabot alerts in your repository: Settings > Code security and analysis > Dependabot alerts.

If permissions are insufficient, Pastoralist will warn and continue (your workflow won't fail).

Try Security Scanning β†’

3. Cleanup

When dependencies are removed, Pastoralist removes their overrides.

flowchart TD
    Start([Dependency removed or updated]) --> Check[Check if override still needed]
    Check --> Remove[Auto-remove from overrides & appendix]
    Remove --> Done[βœ“ Cleaned up]

    style Start fill:#e3f2fd
    style Remove fill:#f3e5f5
    style Done fill:#e8f5e9
Loading

Pastoralist also detects overrides that no package depends on and labels them as (unused override). To remove them:

pastoralist --remove-unused

Try Cleanup β†’

4. Patch Tracking

Works with patch-package. Links patches to overrides and warns about unused patches.

"pastoralist": {
  "appendix": {
    "lodash@4.17.21": {
      "dependents": {"my-app": "lodash@^4.17.0"},
      "patches": ["patches/lodash+4.17.21.patch"]  // ← Auto-tracked
    }
  }
}

Try Patches β†’


How Pastoralist Works

You: Add an override when needed.

Pastoralist: Tracks, scans, and cleans up.

flowchart LR
    You[You add override] --> Install[npm install]
    Install --> Pastor[Pastoralist runs]
    Pastor --> Track[Tracks it]
    Pastor --> Scan[Scans it]
    Pastor --> Clean[Cleans if unused]
    Track --> Chill[Back to coding]
    Scan --> Chill
    Clean --> Chill

    style You fill:#e3f2fd
    style Pastor fill:#f3e5f5
    style Chill fill:#e8f5e9
Loading

Add it to your postinstall script and forget about it:

"scripts": {
  "postinstall": "pastoralist"
}

For detailed architecture, code flows, and user journeys, see Architecture and User Journeys

Key Notes

  • You control what goes into overrides/resolutions
  • Pastoralist handles tracking, security, and cleanup
  • Runs on every install via postinstall hook

Using Pastoralist with Workspaces and Monorepos

Pastoralist provides enhanced support for monorepo scenarios where overrides are defined at the root but dependencies exist in workspace packages.

Auto-Detection

If your package.json has a workspaces field, Pastoralist automatically scans workspace packages:

{
  "workspaces": ["packages/*", "apps/*"],
  "overrides": {
    "lodash": "4.17.21"
  }
}

Run pastoralist and it automatically scans all workspace packages. No configuration needed.

Try Monorepo β†’

Manual Configuration

For explicit control, configure workspace scanning:

{
  "pastoralist": {
    "depPaths": "workspace"
  }
}

Or specify custom paths:

{
  "pastoralist": {
    "depPaths": ["packages/*/package.json", "apps/*/package.json"]
  }
}

Monorepo Override Tracking

When you have overrides at the root for packages only installed in workspace packages, Pastoralist tracks them properly:

// Root package.json with overrides for workspace packages
{
  "overrides": {
    "lodash": "4.17.21"  // Used by workspace packages, not root
  },
  "pastoralist": {
    "overridePaths": {
      "packages/app-a/package.json": {
        "lodash@4.17.21": {
          "dependents": {
            "app-a": "lodash@^4.17.0"
          }
        }
      }
    }
  }
}

Configuration Options

  1. Interactive Configuration - Let Pastoralist guide you through setup:
# Initialize with interactive prompts
pastoralist --init

# Or use --interactive when overrides are detected
pastoralist --interactive

When Pastoralist detects overrides for packages not in root dependencies, it will:

  • Prompt you to configure workspace paths
  • Offer to auto-detect common monorepo structures
  • Allow you to specify custom paths
  • Optionally save the configuration to your package.json
  1. Using depPaths CLI Flag - Specify paths to scan for package.json files:
pastoralist --depPaths "packages/*/package.json" "apps/*/package.json"
  1. Using depPaths in package.json - Configure dependency paths directly in your package.json:
"pastoralist": {
  "depPaths": "workspace"  // Automatically uses all workspaces
}

// OR specify custom paths
"pastoralist": {
  "depPaths": ["packages/*/package.json", "apps/*/package.json"]
}

When using depPaths: "workspace", Pastoralist will automatically scan all packages defined in your workspaces field. This is the recommended approach for most monorepos as it keeps your configuration in sync with your workspace structure.

Benefits of using depPaths configuration:

  • Single source of truth in package.json
  • No need to remember CLI flags
  • Works automatically with postinstall scripts
  • Appendix only appears in root package.json (workspace packages remain clean)
  1. Using overridePaths/resolutionPaths - Configure in your package.json:
"pastoralist": {
  "overridePaths": {  // or "resolutionPaths" for yarn
    "packages/app-a/package.json": { /* appendix for app-a */ },
    "packages/app-b/package.json": { /* appendix for app-b */ }
  }
}

This configuration ensures that:

  • Overrides for packages not in root dependencies are preserved
  • Each workspace package's usage is tracked separately
  • The appendix correctly maps overrides to their actual consumers

Configuration

Pastoralist supports multiple configuration methods to fit your project's needs. Configuration can be defined in external files or directly in your package.json.

Configuration Files

Pastoralist searches for configuration files in this order (first found wins):

  1. .pastoralistrc (JSON format)
  2. .pastoralistrc.json
  3. pastoralist.json
  4. pastoralist.config.js
  5. pastoralist.config.ts

Example .pastoralistrc.json:

{
  "checkSecurity": true,
  "depPaths": "workspaces",
  "security": {
    "provider": "osv",
    "severityThreshold": "medium"
  }
}

Example pastoralist.config.js:

module.exports = {
  checkSecurity: true,
  depPaths: ["packages/*/package.json", "apps/*/package.json"],
  security: {
    provider: "osv",
    severityThreshold: "high",
    excludePackages: ["@types/*"],
  },
};

Example pastoralist.config.ts:

import { PastoralistConfig } from "pastoralist";

const config: PastoralistConfig = {
  checkSecurity: true,
  depPaths: "workspaces",
  security: {
    provider: "osv",
    severityThreshold: "critical",
  },
};

export default config;

Configuration Priority

When both external config files and package.json configuration exist:

  1. External config provides base settings
  2. package.json overrides top-level fields
  3. Nested objects (like security) are deep merged

Example:

// .pastoralistrc.json
{
  "checkSecurity": true,
  "depPaths": "workspaces",
  "security": {
    "provider": "osv",
    "severityThreshold": "medium"
  }
}

// package.json
{
  "pastoralist": {
    "security": {
      "severityThreshold": "high"  // Overrides "medium" from .pastoralistrc.json
    }
  }
}

// Effective config:
{
  "checkSecurity": true,
  "depPaths": "workspaces",
  "security": {
    "provider": "osv",
    "severityThreshold": "high"  // From package.json
  }
}

Configuration Options

Option Type Description
checkSecurity boolean Enable security vulnerability scanning
depPaths "workspace" | "workspaces" | string[] Paths to scan for dependencies in monorepos
appendix object Auto-generated dependency tracking (managed by Pastoralist)
overridePaths object Manual override tracking for specific paths
resolutionPaths object Manual resolution tracking for specific paths
security object Security scanning configuration (see below)

Security Configuration

Option Type Description
enabled boolean Enable/disable security checks
provider "osv" | "github" | "snyk" | "socket" Security provider: osv (free, recommended), github (requires token, includes transitive deps), snyk [EXPERIMENTAL], socket [EXPERIMENTAL]
autoFix boolean Automatically apply security fixes
interactive boolean Use interactive mode for security fixes
securityProviderToken string API token for providers that require auth
severityThreshold "low" | "medium" | "high" | "critical" Minimum severity level to report
excludePackages string[] Packages to exclude from security checks
hasWorkspaceSecurityChecks boolean Include workspace packages in scans

Security Tracking in Appendix

When security vulnerabilities are detected and fixed, Pastoralist tracks complete vulnerability information in the appendix ledger:

"pastoralist": {
  "appendix": {
    "lodash@4.17.21": {
      "dependents": {
        "my-app": "lodash@^4.17.0"
      },
      "ledger": {
        "addedDate": "2024-01-15T10:30:00.000Z",
        "reason": "Security vulnerability CVE-2021-23337",
        "securityChecked": true,
        "securityCheckDate": "2024-01-15T10:30:00.000Z",
        "securityProvider": "osv",
        "cve": "CVE-2021-23337",
        "severity": "high",
        "description": "Command injection in lodash",
        "url": "https://nvd.nist.gov/vuln/detail/CVE-2021-23337"
      }
    }
  }
}

The ledger tracks:

  • addedDate: When the override was first added
  • reason: Why the override was needed
  • securityChecked: Whether a security check was performed
  • securityCheckDate: When the last security check occurred
  • securityProvider: Which provider detected the vulnerability
  • cve: CVE identifier (if applicable)
  • severity: Vulnerability severity level (low, medium, high, critical)
  • description: Brief description of the vulnerability
  • url: Link to full vulnerability details

This complete audit trail lets you understand exactly which security issues were fixed and provides full context for future reference.

Best Practices

  1. Use external config files for shared settings across teams
  2. Use package.json for project-specific overrides
  3. Commit config files to version control
  4. Use depPaths: "workspaces" for most monorepos
  5. Enable security checks in CI/CD pipelines with --checkSecurity

Setup

Okay! Hopefully the breakdowns above were clear enough on why you might want to use Pastoralist!

Please submit a pull request or issue if it wasn't!

Now for the super simple setup!

  1. Install
npm install pastoralist --save-dev
# pastoralist does not expect to be a dependency! It's a tool!!!
  1. run
pastoralist
# => That's it! Check out your package.json
  1. (recommended) add Pastoralist to a postInstall script
// package.json
{
  "scripts": {
    "postinstall": "pastoralist"
  }
}

Additional Commands

Preview changes without writing to package.json:

pastoralist --dry-run

This shows exactly what Pastoralist would change without modifying any files.

Try Dry Run β†’

Quiet mode for CI:

pastoralist --quiet --checkSecurity

Minimal output for CI pipelines. Exits with code 1 if vulnerabilities found, 0 if clean.

Try Quiet Mode β†’

Show summary metrics:

pastoralist --summary

Displays metrics table with packages scanned, vulnerabilities blocked, and overrides managed.

Try Summary β†’

Add postinstall hook:

pastoralist --setup-hook

Adds pastoralist to your postinstall script automatically.

Try Setup Hook β†’

Remove unused overrides:

pastoralist --remove-unused

Removes overrides that no package depends on. When unused overrides are detected, Pastoralist displays a notice suggesting this flag.


GitHub Action

Pastoralist provides a GitHub Action for CI/CD integration.

Quick Start

name: Override Check
on: [pull_request]

jobs:
  check:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: yowainwright/pastoralist@v1
        with:
          mode: check
          fail-on-security: true

Scheduled Security Maintenance

name: Override Maintenance
on:
  schedule:
    - cron: "0 0 * * 1" # Weekly on Monday

jobs:
  maintain:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: yowainwright/pastoralist@v1
        with:
          mode: pr
          check-security: true
          fail-on-security: true
          pr-title: "chore(deps): update dependency overrides"

Modes

Mode Description
check Validate only - reports issues without modifying files
update Modify package.json (default) - you handle commits
pr Create pull request with changes

See ACTION.md for full documentation including all inputs, outputs, and examples.


Thanks

Shout out to Bryant Cabrera and the infamous Mardin for all the fun conversation, insights, and pairing around this topic.


Made by @yowainwright for fun with passion! O'Sassy, 2022

About

A CLI for automatically shepherding package.json overrides πŸ‘©πŸ½β€πŸŒΎ

Topics

Resources

License

Code of conduct

Security policy

Stars

Watchers

Forks

Packages

 
 
 

Contributors