Repository navigation
release-age: 7-day minimum release age for npm/pnpm/bun/uv/pip/cargo - #185
Conversation
DJRHails
left a comment
There was a problem hiding this comment.
Automated review — findings posted inline. This PR is already merged, so the P1/P2 fixes go to a follow-up PR off main. P1/P2 threads resolve as their fixes land; P3s are left for the author.
| # `uv tool` and `uvx`; versions already in uv.lock are kept. A project uv.toml | ||
| # or [tool.uv] table overrides this file. Exempt a package with | ||
| # exclude-newer-package = { <name> = false }. | ||
| exclude-newer = "7 days" |
There was a problem hiding this comment.
P1 — correctness / claims vs reality: the user-level exclude-newer does not leave existing locks alone. uv records it in uv.lock, so the "Lockfiles win" bullet in the README and the "versions already in uv.lock are kept" line here are wrong for uv, in both directions:
- On a gated host, every lock made without the gate is out of date. I reproduced this on uv 0.12.23 with a zero-dependency project locked with
UV_NO_CONFIG=1. With this file asUV_CONFIG_FILE, bothuv lock --checkanduv sync --lockedfail withResolving despite existing lockfile due to addition of global exclude newer …/error: The lockfile at uv.lock needs to be updated, but --locked was provided. Plainuv run/uv syncsilently re-resolve and rewrite the lock. - A lock rewritten here breaks hosts without the gate. The re-lock adds
[options] exclude-newer-span = "P7D". RunUV_NO_CONFIG=1 uv lock --checkagainst that lock and it fails the same way. So CI runners, collaborators and fleet hosts that lack~/.config/uv/uv.tomlgo red on--lockedfor any repo last locked on a gated machine. On shared-org repos this keeps rewriting other people's locks back and forth. - The relative span is at least stable: I moved the lock's timestamp two days back and it still passed
--check, so there's no daily churn.
Fix pushed (docs only): the uv.toml comment and README now describe this, with the --frozen / per-project exclude-newer escape hatches. Whether to keep uv gated at user level is your call. The alternatives are dropping uv.toml from symlinks.conf, or gating per repo in [tool.uv] so the setting is committed alongside the lock. I didn't make that design change.
There was a problem hiding this comment.
Docs corrected in 49e496b (#186): the README's "Lockfiles win" bullet and the uv.toml comment now describe the lock interaction and the --frozen / per-repo [tool.uv] options. I'm leaving this thread open because the behaviour itself, a user-level uv gate that invalidates existing locks and makes re-made locks fail --locked on ungated hosts, is a design decision for the author.
| log::success "npm min-release-age=7" | ||
| else | ||
| npm config set min-release-age=7 --location=user | ||
| log::result $? "npm min-release-age=7 -> $(npm config get userconfig)" |
There was a problem hiding this comment.
P3 — exit code: if npm config set (or pnpm config set -g at L48) fails, for example because ~/.npmrc isn't writable, log::result prints ✖ but release_age_failed stays 0. setup.sh then returns 0 and bootstrap logs setup completed ✔, even though the missing-npm and old-version paths deliberately return 1. A one-line || release_age_failed=1 on both log::result calls would make the exit code consistent. Left for the author.
Review Summary#185 was already merged when this review ran, so the fixes are in a follow-up PR, #186.
Verified: I reproduced F1 on uv 0.12.23 with a zero-dependency project: Verdict: request_changes |
- F1 (P2, fixed): release-age.test.sh put /usr/bin on run_setup's PATH, so on a host with a system npm (NodeSource, Debian's npm) the real npm answered the no-npm case and the suite failed; it runs unconditionally as a pre-commit hook, so every commit on such a host was blocked. The sandbox now gets a filtered system bin dir without npm/npx/pnpm/pnpx/ corepack. Origin: f7a6cc7 (#185). - F2 (P3, left for the author): README presents npm config set -g as the way to edit the tracked npmrc, but npm rewrites it without its comments.
What
A new
release-agemodule that makes npm, pnpm, bun, uv, pip and cargo refuse any package version published less than 7 days ago. It is the counterpart ofsfw: sfw blocks versions already known to be malicious, the age gate covers the ones nobody has looked at yet.min-release-age=7(days)setup.sh,npm config set --location=userminimum-release-age=10080(minutes)setup.sh,pnpm config set -g[install] minimumReleaseAge = 604800(s)~/.bunfig.tomlexclude-newer = "7 days"~/.config/uv/uv.toml[global] uploaded-prior-to = P7D~/.config/pip/pip.conf(+~/Library/Application Support/pip/on macOS)[registry] global-min-publish-age = "7 days"~/.cargo/config.tomlsetup.shreads the value back first, so it is idempotent, and it fails loudly with the fix on hosts whose npm (Node 22 bundles npm 10) or pnpm predate the key.rustup update.--cliprofile, the README module and dependency tables, and the lefthook / pre-commit suites.Verification
tests/release-age.test.sh(new, 34 checks): setup.sh against stub npm/pnpm that record every call. Fresh host writes each key once in the user/global location, rerun writes nothing, old npm / old pnpm / missing npm fail with the fix named and still configure the other tool, version floor is inclusive and numeric, each symlinked file carries 7 days in its own unit, uv and cargo load their files.tests/module-deps.test.sh,tests/link-symlinks.test.sh,scripts/check-crypt-patterns.sh: pass.typescript@nextresolved to a build a week older under npm, pnpm and bun,boto3dropped from 1.43.110 to 1.43.107 under uv and pip, and each documented bypass restored the latest. cargo loads the file without a warning. pnpm 10 and pnpm 12 both accept the kebab-case key throughconfig set -g.setup.shrun twice against the real npm and pnpm under a throwaway HOME seeded with a fake auth token: the token line survived, the second run made no writes.via claude