Compare commits

..
2 Commits
Author SHA1 Message Date
jknapp b21a568bf5 Merge pull request 'Install from the lockfile, so CI cannot be broken by someone else's release' (#53) from fix/ci-npm-lockfile into main
Build App / compute-version (push) Successful in 4s
Secret Scan / scan (push) Successful in 3s
Build App / build-macos (push) Successful in 2m42s
Build App / build-windows (push) Successful in 4m53s
Build App / build-linux (push) Successful in 5m0s
Build App / create-tag (push) Successful in 3s
Build App / sync-to-github (push) Successful in 13s
2026-09-03 16:41:15 +00:00
shadowdaoandClaude Opus 5 f41b1d9054 Install from the lockfile, so CI cannot be broken by someone else's release
Secret Scan / scan (push) Successful in 4s
Build App (Preview) / compute-version (pull_request) Successful in 3s
Secret Scan / scan (pull_request) Successful in 3s
Build App (Preview) / create-release (pull_request) Successful in 1s
Build App (Preview) / build-macos (pull_request) Successful in 2m40s
Build App (Preview) / build-windows (pull_request) Successful in 4m52s
Build App (Preview) / build-linux (pull_request) Successful in 5m0s
Build App (Preview) / prune-previews (pull_request) Successful in 2s
`build-linux` fails before `tauri build` runs, on every workflow, at "Install
frontend dependencies":

    npm error Cannot read properties of null (reading 'edgesOut')

Reproduced exactly on the first attempt by running the step's own commands
locally on the same Node 22.23.2 the runner installs. The debug log gives the
frame the CI output omits:

    at #loadPeerSet (.../@npmcli/arborist/lib/arborist/build-ideal-tree.js:1289:38)

It is a null dereference in npm 10.9.8's peer-set resolver, reached through
vite → @vitejs/devtools → @vitejs/devtools-vitest → vitest@* →
@vitest/browser-playwright → vitest@4.1.11 → jsdom@* → canvas.

**Nothing in this repo changed to cause it.** The step deleted
`package-lock.json` before installing, so every build re-resolved the entire
tree against the registry against ranges like `vitest@*`. A dependency
published a version that produces a peer graph npm cannot resolve, and our CI
broke — the same command succeeded fifteen hours earlier for 0.4.21. That is
the real defect: the build was never reproducible, and the crash is only how we
found out.

So Linux installs with `npm ci`, from the committed lockfile, like Windows
already did. macOS moves too — it kept the lockfile but still ran `npm
install`, which is free to re-resolve; all three platforms now install
identically and none can re-resolve mid-release.

**The reason the lockfile was being deleted is obsolete, not ignored.** 2d4fce9
removed it "to ensure correct platform-specific bindings", which was a real
problem once. The committed lockfile now records 25 rollup platform variants,
and `npm ci` on Linux installs precisely rollup-linux-x64-{gnu,musl} and
@esbuild/linux-x64 — checked directly. A comment on the step says so, and says
not to reach for deleting the lockfile again: if `npm ci` refuses, package.json
and the lockfile have genuinely diverged and the fix is to commit an updated
lockfile.

Verified from the resulting tree: `tsc --noEmit` clean, `npm run build`
successful, 752 tests across 62 files passing. The `npx tauri --version ||
npm install @tauri-apps/cli` fallback in the next step cannot reintroduce a
fresh resolution — the CLI is a pinned devDependency that `npm ci` installs, so
the fallback is unreachable.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011YPqHpjV4EL6RNEwrRKqQm
2026-09-03 09:28:09 -07:00
2 changed files with 64 additions and 8 deletions
+32 -4
View File
@@ -299,8 +299,34 @@ jobs:
- name: Install frontend dependencies
working-directory: ./app
run: |
rm -rf node_modules package-lock.json
npm install
# `npm ci` — from the lockfile, never resolving afresh.
#
# This used to be `rm -rf node_modules package-lock.json && npm
# install`, which deleted the lockfile "to ensure correct
# platform-specific bindings" (2d4fce9). That made every build
# re-resolve the whole tree against the registry, so a dependency
# publishing a new version could break CI with no change to this
# repo — and one did. Deleting the lockfile then hit a null
# dereference in npm 10.9.8's arborist peer-set resolver:
#
# npm error Cannot read properties of null (reading 'edgesOut')
# at #loadPeerSet (.../build-ideal-tree.js:1289:38)
#
# reached through vite → @vitejs/devtools → @vitejs/devtools-vitest
# → vitest@* → @vitest/browser-playwright → jsdom@* → canvas.
# Reproduced exactly by removing the lockfile locally on the same
# Node 22.23.2 the runner installs.
#
# The binding worry is obsolete: the committed lockfile records 25
# rollup platform variants, and `npm ci` on Linux installs precisely
# rollup-linux-x64-{gnu,musl} and @esbuild/linux-x64. Verified, along
# with a clean tsc, a successful build and 752 passing tests from the
# resulting tree.
#
# Do not "fix" a future dependency error by deleting the lockfile
# again. If `npm ci` refuses, package.json and the lockfile have
# genuinely diverged, and the fix is to commit an updated lockfile.
npm ci
- name: Install Tauri CLI
working-directory: ./app
@@ -427,8 +453,10 @@ jobs:
- name: Install frontend dependencies
working-directory: ./app
run: |
rm -rf node_modules
npm install
# `npm ci` here too, so all three platforms install identically and
# none of them can re-resolve the tree mid-release. Windows already
# did. See the Linux job for what a fresh resolution cost us.
npm ci
- name: Install Tauri CLI
working-directory: ./app
+32 -4
View File
@@ -172,8 +172,34 @@ jobs:
- name: Install frontend dependencies
working-directory: ./app
run: |
rm -rf node_modules package-lock.json
npm install
# `npm ci` — from the lockfile, never resolving afresh.
#
# This used to be `rm -rf node_modules package-lock.json && npm
# install`, which deleted the lockfile "to ensure correct
# platform-specific bindings" (2d4fce9). That made every build
# re-resolve the whole tree against the registry, so a dependency
# publishing a new version could break CI with no change to this
# repo — and one did. Deleting the lockfile then hit a null
# dereference in npm 10.9.8's arborist peer-set resolver:
#
# npm error Cannot read properties of null (reading 'edgesOut')
# at #loadPeerSet (.../build-ideal-tree.js:1289:38)
#
# reached through vite → @vitejs/devtools → @vitejs/devtools-vitest
# → vitest@* → @vitest/browser-playwright → jsdom@* → canvas.
# Reproduced exactly by removing the lockfile locally on the same
# Node 22.23.2 the runner installs.
#
# The binding worry is obsolete: the committed lockfile records 25
# rollup platform variants, and `npm ci` on Linux installs precisely
# rollup-linux-x64-{gnu,musl} and @esbuild/linux-x64. Verified, along
# with a clean tsc, a successful build and 752 passing tests from the
# resulting tree.
#
# Do not "fix" a future dependency error by deleting the lockfile
# again. If `npm ci` refuses, package.json and the lockfile have
# genuinely diverged, and the fix is to commit an updated lockfile.
npm ci
- name: Install Tauri CLI
working-directory: ./app
@@ -343,8 +369,10 @@ jobs:
- name: Install frontend dependencies
working-directory: ./app
run: |
rm -rf node_modules
npm install
# `npm ci` here too, so all three platforms install identically and
# none of them can re-resolve the tree mid-release. Windows already
# did. See the Linux job for what a fresh resolution cost us.
npm ci
- name: Install Tauri CLI
working-directory: ./app