DEV Community

Sulthon Zainul Habib
Sulthon Zainul Habib

Posted on • Originally published at github.com

I Turned npm outdated into a CI Gate — Here's How

You run npm outdated and see a list of stale packages. But your CI doesn't care. It passes anyway. Dependencies drift until something explodes in production. There's no built-in way to fail the build when versions drift too far.## The Problemnpm outdated lists outdated dependencies, but:- No exit codes — CI cannot gate builds on the result- No threshold configuration — you can't say "fail if >2 minors behind"- No distinction between prod and dev dependencies in many workflows- Manual updates become a fire drill instead of a controlled processA typical scenario: Your team wants to stay current with security patches, but you can't update everything. You need a rule: "No production dependency more than 2 minor versions behind latest." npm outdated can't enforce that.## The SolutionI built npm-outdated-check to turn npm outdated into a first-class CI citizen with:- Semantic version thresholding (major/minor/patch drift limits)- Meaningful exit codes (0 = pass, 1 = violation, 2 = config error, 3 = network error)- Configurable via CLI flags or a .npm-outdated-check.json config file- Production/dev dependency filtering- Multiple output formats (text, table, JSON)## How It WorksThe tool reads your package.json, queries the npm registry for each dependency, calculates the semantic version difference, and flags anything that exceeds your thresholds.Key implementation details:1. Registry fetching: Hit the npm registry endpoint for each package and extract the dist-tags.latest version2. Semver diff: Use semver to parse coerce(current) and parse(latest), then compute major/minor/patch differences3. Violation logic: A package violates if any diff exceeds its configured maxMajor/maxMinor/maxPatch4. Exit codes: CI reads the exit code and fails the build when violations existSample threshold calculation:

typescriptconst majorDiff = latest.major - current.major;const minorDiff = latest.minor - current.minor;const patchDiff = latest.patch - current.patch;const isViolation = majorDiff > config.maxMajor || minorDiff > config.minorDiff || patchDiff > config.maxPatch;

Getting StartedInstall globally or as a dev dependency:


bashnpm install -D npm-outdated-check

Run it in CI with sensible defaults (major=0, minor=2, patch=5):

bashnpx npm-outdated-check

Add it to GitHub Actions:

yamlname: Dependency Checkon: [push, pull_request]jobs: outdated-check: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: actions/setup-node@v4 with: node-version: '18' - run: npm install - run: npx npm-outdated-check --max-minor 3

If a dependency is 4 minor versions behind, CI fails and you get notified.## Why This Matters- Controlled updates: Set thresholds to avoid surprise breaking changes- Security posture: Enforce staying within N patch versions of latest- Team consistency: Config rules checked automatically in CI- Zero config: Works out of the box with smart defaults## What's NextRoadmap items include:- Configurable notification channels (Slack, email)- Automated PR generation for outdated packages- Support for Yarn and pnpm lockfiles- Monorepo workspace awareness## Links- GitHub: https://github.com/sulthonzh/npm-outdated-check- Try it: npm install -D npm-outdated-check

Top comments (0)