Skip to content

Pinned CLI installs are not reproducible: ship npm-shrinkwrap.json and drop the unbounded runtime @types/node dependency #1417

Description

@unbraind

Problem

npm install -g @unbrained/pm-cli@<exact version> is not reproducible: the published package has no npm-shrinkwrap.json, so every install re-resolves the transitive tree from caret ranges at install time. Pinning the CLI version pins nothing below it.

On 2026-10-06 this broke every CI job that installs a pinned CLI, with no change on our side:

npm install -g @unbrained/pm-cli@2026.10.5
npm error code ETARGET
npm error notarget No matching version found for @opentelemetry/resources@2.12.0.

A few minutes later the same command failed with E404 GET .../@opentelemetry/sdk-trace/-/sdk-trace-2.12.0.tgz. The OpenTelemetry 2.12.0 release was propagating: the packuments already listed 2.12.0 (published 13:40:53Z; our failing install ran at 13:40:43Z) before the tarballs were served. The packages come in transitively through @sentry/node (pinned at 10.75.1, but its own OpenTelemetry deps use ^ ranges). Every downstream repo that pins @unbrained/pm-cli@X as a test oracle or tool failed at once: Linux, macOS and Windows jobs, required checks included.

Related: dependencies includes "@types/node": ">=22". That is an unbounded range on a type-only package, so a global install pulls whatever @types/node major is newest. Type definitions are not needed at runtime, and an open-ended >= is the least reproducible range there is.

Proposal

  1. Publish npm-shrinkwrap.json with the CLI package (npm honours it for both npm i -g and npx), generated from the release lockfile that CI actually tested. A pinned CLI version then installs exactly the tree that passed the release gates. Bun's bunx/bun add -g ignore shrinkwrap, so also document --frozen-lockfile-equivalent guidance, or accept that Bun floats.
  2. Move @types/node to devDependencies. If consumers of the SDK types need it, use peerDependencies + peerDependenciesMeta.optional, with a bounded range (^22 || ^24 || ^26).
  3. Optional: a release gate that installs the packed tarball with --prefer-offline=false into an empty prefix, and fails if the resolved tree differs from the tested lockfile.

Acceptance

  • npm view @unbrained/pm-cli@<next> _hasShrinkwrap is true.
  • Installing the same CLI version on two different days resolves byte-identical node_modules (same integrity hashes).
  • @types/node is no longer a runtime dependency.

No activity

Activity on this issue will appear here.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions