Skip to content

ci: use npm ci for reproducible installs and test the declared Node range#19

Open
dmchaledev wants to merge 1 commit into
mainfrom
claude/magical-ptolemy-pu4w5s
Open

ci: use npm ci for reproducible installs and test the declared Node range#19
dmchaledev wants to merge 1 commit into
mainfrom
claude/magical-ptolemy-pu4w5s

Conversation

@dmchaledev

Copy link
Copy Markdown
Contributor

Problem

All three GitHub Actions workflows install dependencies with npm install --include=dev, which ignores the committed package-lock.json and is free to resolve newer transitive versions than the locked, reviewed tree.

For a tool whose keywords are supply-chain-security, this is most consequential in publish.yml: the package is published to npm with --provenance, but it is built from an unpinned dependency tree. Provenance attests how/where an artifact was built — building it from non-reproducible inputs weakens exactly the guarantee the flag is there to provide. npm ci installs the exact locked tree and fails fast if package.json and the lockfile have drifted.

Two secondary gaps:

  • CI only exercises Node 20, but package.json declares engines.node: ">=18". Nothing verifies the package actually works on its supported minimum (18) or current LTS (22) — a regression that breaks Node 18 would ship undetected.
  • The PR gate (ci.yml) has no dedicated type-check step (the auto-tag workflow already runs one), so a type error only surfaces inside the Build step.

Changes (workflow-only)

  • ci.ymlnpm install --include=devnpm ci; run the job across a Node [18, 20, 22] matrix (fail-fast: false); add an explicit Type check (npm run typecheck) step.
  • publish.ymlnpm install --include=devnpm ci so the published artifact is built from the locked tree.
  • auto-tag.ymlnpm install --include=devnpm ci; switch npx tsc --noEmitnpm run typecheck for parity with the other workflows.

npm ci already installs devDependencies by default, so --include=dev is not needed.

Verification

Locally on the committed lockfile:

  • npm ci — installs cleanly (lockfile is in sync; no drift)
  • npm run typecheck — clean
  • npm run lint — clean
  • npm test — 29 passed
  • npm run build — clean

No source or test changes, so this does not touch — and won't conflict with — the open src/ PRs (#5, #8, #10, #12, #18) or the README docs PRs.

🤖 Generated with Claude Code

https://claude.ai/code/session_018iftJL9x2Bjx7T9q2o5Bd6


Generated by Claude Code

…ange

The workflows installed dependencies with `npm install --include=dev`, which
ignores the committed package-lock.json and can resolve newer transitive
versions than the locked tree. For a supply-chain-security tool this is
especially significant in publish.yml: the package is published with
`--provenance` but was built from an unpinned dependency tree, weakening the
reproducibility that provenance is meant to attest. Switch all three workflows
to `npm ci`, which installs the exact locked tree and fails on lockfile drift.

Also run CI across Node 18, 20 and 22 to actually cover the
`engines.node: ">=18"` range declared in package.json (previously only Node 20
was exercised, so nothing verified the supported minimum or current LTS), and
add an explicit type-check step to the PR gate for a clearer failure signal.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018iftJL9x2Bjx7T9q2o5Bd6
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.

2 participants