Skip to content

feat: add unnecessary-release-age-exclude rule - #162

Open
maastrich wants to merge 2 commits into
QuiiBz:mainfrom
maastrich:feat/unnecessary-release-age-exclude
Open

feat: add unnecessary-release-age-exclude rule#162
maastrich wants to merge 2 commits into
QuiiBz:mainfrom
maastrich:feat/unnecessary-release-age-exclude

Conversation

@maastrich

@maastrich maastrich commented Aug 10, 2026

Copy link
Copy Markdown

What

New warning rule unnecessary-release-age-exclude: reports minimum release age exclusions that don't unblock anything anymore. These are usually added to unblock one urgent upgrade, then forgotten - and they permanently disable the cooldown that protects against compromised releases.

Package manager File Minimum release age Exclusion list
pnpm pnpm-workspace.yaml minimumReleaseAge (minutes) minimumReleaseAgeExclude
Yarn .yarnrc.yml npmMinimalAgeGate (minutes) npmPreapprovedPackages
Bun bunfig.toml minimumReleaseAge (seconds) minimumReleaseAgeExcludes

Two cases are reported:

  • exclusions pinning versions already past the gate (typescript@5.4.5) - those versions are published, so they will never be gated again;
  • exclusions without a version whose package hasn't published anything for at least 10× the gate. Active packages are never reported: they would be gated again on their next release.

Patterns (@myorg/*) and ranges (nx@^21.0.0) are skipped - they can match versions that aren't published yet. npm isn't supported (no exclusion list), nor is the pnpm 10.x .npmrc form.

$ sherif --check-release-age

⚠️ warning unnecessary-release-age-exclude
  │ minimumReleaseAge: 1440
  │ minimumReleaseAgeExclude:   ← pnpm-workspace.yaml bypasses the minimum release age...
  -  - 'typescript@5.4.5'   ← published 851 days ago, past the minimum release age
  -  - 'left-pad'   ← nothing published for 3045 days, currently bypasses nothing

This needs network access, and I know that's a paradigm change

Every other rule is a pure function of files on disk. This one queries the npm registry, because publish dates exist nowhere locally:

  • lockfiles (pnpm-lock.yaml, yarn.lock, package-lock.json, bun.lock) only store resolved versions and integrity hashes;
  • package manager metadata caches strip the time field;
  • abbreviated registry metadata (application/vnd.npm.install-v1+json) doesn't include it either.

So the rule fetches the full registry document, which is what package managers themselves do to enforce the gate.

Because of that, the rule is opt-in: it only runs with --check-release-age (or "checkReleaseAge": true in the config). A plain sherif run stays fully offline, and existing users see no change. The GitHub Action needs no new input - the flag goes through the existing args.

Further guards, on top of the flag:

  • no request unless a config above defines an age and a non-empty exclusion list;
  • -r unnecessary-release-age-exclude short-circuits before any request;
  • custom registry= in the root .npmrc disables the rule, @scope:registry= excludes that scope - nothing is ever sent to a private registry;
  • unreachable registry (offline, sandboxed CI) reports nothing and doesn't change the exit code.

Cost: ureq (rustls/webpki-roots) + toml, binary goes from 1.9M to 3.1M. The musl cross-build was verified locally.

If this isn't wanted behaviour for Sherif, say so and I'll close the PR - no hard feelings.

Tests

Fixtures for each config format, unit tests for parsing/date/verdict logic, a test asserting the rule stays off without the flag, and snapshot tests for the message. cargo fmt, cargo clippy --tests -- -D warnings and cargo test -- --test-threads=1 (60 tests) all pass. README documents the rule, the flag and the network requirement.

🤖 Generated with Claude Code

maastrich and others added 2 commits August 10, 2026 16:11
Reports minimum release age exclusions that don't unblock anything
anymore, for pnpm (`pnpm-workspace.yaml`), Yarn (`.yarnrc.yml`) and Bun
(`bunfig.toml`).

Two cases are reported: exclusions pinning versions that are already
past the gate, and exclusions without a version whose package has been
silent for at least ten times the gate. Patterns and version ranges are
skipped, as they can match versions that aren't published yet.

Publish dates aren't available on disk - lockfiles only store resolved
versions and integrity hashes, and metadata caches strip the `time`
field - so this rule fetches the registry document of the excluded
packages. It only runs when such a configuration exists, never queries
a custom registry, and reports nothing if the registry is unreachable.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The rule queries the npm registry, so it now requires the
`--check-release-age` flag (or the `checkReleaseAge` config field). A
plain `sherif` run stays fully offline.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant