Skip to content

fix(release): honour engines.npm — setup-node ships the Node-bundled npm - #525

Merged
rubenvdlinde merged 1 commit into
mainfrom
fix/release-honour-engines-npm
Aug 20, 2026
Merged

fix(release): honour engines.npm — setup-node ships the Node-bundled npm#525
rubenvdlinde merged 1 commit into
mainfrom
fix/release-honour-engines-npm

Conversation

@rubenvdlinde

Copy link
Copy Markdown
Contributor

The failure

launchpad and portaliq releases fail on npm ci:

npm error code EUSAGE
npm error `npm ci` can only install packages when your package.json and
npm error package-lock.json ... are in sync.
npm error Missing: vite@8.2.2 from lock file

The lock is not out of sync. The identical commit installs cleanly under
npm 11 — npm ci --dry-run, exit 0, "added 1416 packages".

The cause

setup-node installs the npm bundled with the Node it selects. Node 22
bundles npm 10. Both apps declare engines.npm: ^11.0.0 and ship an .npmrc
using min-release-age — a supply-chain cooldown (“do not install anything
published less than N days ago”) that exists only on npm 11+.

On npm 10 that setting is silently ignored, resolution differs, and npm ci
rejects a perfectly good lock.

The dangerous part is the error text: it names the lock, so the obvious fix is
to regenerate it. That would have re-broken the cooldown policy — the lock
is deliberately "behind" because packages inside the cooldown window are
excluded — while papering over a toolchain mismatch that would resurface on
the next dependency bump.

The fix

After setup-node, read engines.npm from package.json and install that
npm. A repo declaring no engines.npm keeps the bundled npm and is
unaffected, so this is a no-op for every app that is currently green.

Blast radius

Fleet-wide workflow, but the step is conditional on a field the repo itself
declares. Of the 18 core apps, only launchpad and portaliq declare
engines.npm today — the other 16 hit the exit 0 path with their bundled
npm unchanged.

Verified the edited file still parses as YAML and the release job is intact.

@rubenvdlinde
rubenvdlinde merged commit 06a2dbe into main Aug 20, 2026
27 checks passed
rubenvdlinde added a commit that referenced this pull request Aug 21, 2026
…esolved lock (#534)

The first real run of this workflow (docudesk PR #710) produced an 84-add /
16-delete package-lock diff for what is a 3-line version bump.

MEASURED, both directions, on that exact pair of lockfiles:

  npm 10 (what setup-node bundles with Node 22):
    packages added 0, removed 0 -- but 81 `@esbuild/*` platform binaries
    newly marked `"dev": true`, while `esbuild` itself stayed unflagged.
  npm 11 (what every sampled app declares, `engines.npm: ^11.0.0`):
    packages added 0, removed 0, dev-flag changes 0, diff exactly +3/-3.

So the tree never changed; npm 10 simply classifies it differently. This is
the same trap release.yml already documented and fixed in #525 -- setup-node
installs the npm BUNDLED WITH NODE, not the one the repo asks for. release.yml
reads ./package.json once because it runs inside one repo; this workflow walks
21, so the range is re-read and re-honoured inside the loop, with the global
install cached per distinct range (one install for the fleet as it stands).

Adds a second, independent check so a wrong toolchain cannot quietly ship
again: a lock-only bump of ONE package must not add packages, remove packages,
or flip an unrelated package's `dev` flag. If it does, the tree was
re-resolved rather than nudged -- the npm side is reverted and annotated
rather than opened as a PR nobody can review. Verified to FAIL on the known-bad
lock (81 flips) and to PASS on the correct one (0 flips), so it is a check that
demonstrably can fail.

Co-authored-by: Conduction Release Bot <release-bot@conduction.nl>
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