ci: enforce npm ci on Studio install paths (follow-up to #5604)#5616
ci: enforce npm ci on Studio install paths (follow-up to #5604)#5616danielhanchen wants to merge 2 commits into
Conversation
PR #5604 committed three npm lockfiles (studio/, studio/frontend/, studio/backend/core/data_recipe/oxc-validator/) but the install paths still ran `npm install` -- mutates the lockfile, ignores integrity hashes. Flip them to `npm ci`, which is non-mutating and refuses to run unless the committed package-lock.json matches the dependency tree byte-for-byte. This closes the gap between "the lockfile passes the audit" and "the install respected that lockfile". For bun: gate every `bun install` on `[ -f bun.lock ]` (Test-Path on Windows) plus `command -v bun` (Get-Command), and add `--frozen-lockfile --no-progress` to the bun invocation. No bun.lock is committed in this PR, so the gate fails closed today and execution falls through to `npm ci`. When the eventual bun.lock PR lands, the gate auto-activates with no further script edits. Existing bun infrastructure (cache-corruption retry + critical-binary verification + npm fallback) is kept intact so the auto-upgrade is clean. Deliberately NOT introducing _BUN_PIN_VERSION or the 3-attempt cache-corruption recovery ladder; both require committed bun.lock to be useful. Touches three files only: - build.sh - studio/setup.sh - studio/setup.ps1
Point the existing audit + install + content-sanity workflows at the two lockfiles PR #5604 introduced (studio/package-lock.json and studio/backend/core/data_recipe/oxc-validator/package-lock.json) so the Studio Tauri CLI holder and the oxc-validator runtime benefit from the same per-PR coverage the frontend already has. release-desktop.yml: - Tauri CLI install: `npm install --save-dev --prefix studio` -> `npm ci --prefix studio --no-fund --no-audit`. - Frontend install: `npm install --no-fund --no-audit` -> `npm ci --no-fund --no-audit`. - New "Lockfile supply-chain audit" step inserted BEFORE the Tauri CLI install so the structural audit fires before any tarball-side lifecycle script. studio-tauri-smoke.yml: - Tauri CLI install: `npm install --save-dev --prefix studio` -> `npm ci --prefix studio --no-fund --no-audit`. - Existing audit step relocated to run BEFORE the Tauri CLI install (same ordering rationale). wheel-smoke.yml: - One-line content-sanity assertion ensuring the oxc-validator package-lock.json is shipped in the wheel. security-audit.yml: - `paths` trigger gains the 4 new entries (oxc + Tauri CLI holder package.json + package-lock.json). - `Scanned:` summary string updated to list the two new lockfiles. - OSV-scanner picks up `--lockfile=` for both. - Two new `npm audit` steps: oxc-validator runtime + Studio Tauri CLI holder. - `npm audit signatures` block split into three (frontend / oxc / Tauri CLI), each preceded by `npm ci --ignore-scripts` in its own directory. - `check_new_install_scripts.py` invocation extended to diff both new lockfiles base->head, with `git show ... 2>/dev/null || echo '{}'` fallback for PRs whose base predates #5604. - `upload-artifact` paths extended for the new log files. Deliberately NOT included: removing `continue-on-error` from the existing `scan_npm_packages` job. That is a policy flip from advisory to blocking and is orthogonal to install-path hardening.
There was a problem hiding this comment.
Code Review
This pull request updates the build and setup scripts (build.sh, setup.ps1, and setup.sh) to enforce lockfile-pinned dependency installations. The scripts now only attempt to use bun if a bun.lock file is present, utilizing the --frozen-lockfile flag, and have transitioned from npm install to npm ci for fallbacks to ensure environment consistency. Reviewer feedback recommends redirecting warning messages to stderr in the shell scripts to align with standard error reporting practices.
| _install_ok=true | ||
| else | ||
| echo "⚠ bun install failed, falling back to npm" | ||
| echo "⚠ bun install --frozen-lockfile failed, falling back to npm ci" |
There was a problem hiding this comment.
It's good practice to direct warning messages to stderr (standard error) rather than stdout (standard output). This helps in distinguishing informational output from potential issues, especially when scripts are used in automated environments or piped to other commands. The error message on line 54 already correctly uses >&2.
| echo "⚠ bun install --frozen-lockfile failed, falling back to npm ci" | |
| echo "⚠ bun install --frozen-lockfile failed, falling back to npm ci" >&2 |
| # Either bun install failed or it exited 0 but left packages missing | ||
| if [ "$_exit_code" -ne 0 ]; then | ||
| echo " bun install failed (exit code $_exit_code):" | ||
| echo " bun install --frozen-lockfile failed (exit code $_exit_code):" |
There was a problem hiding this comment.
| echo " bun install failed (exit code $_exit_code):" | ||
| echo " bun install --frozen-lockfile failed (exit code $_exit_code):" | ||
| else | ||
| echo " bun install exited 0 but critical binaries are missing:" |
Summary
Follow-up to #5604. Now that the three Studio npm lockfiles are committed and audited, flip every install path that depends on them from
npm install(mutates the lockfile, ignores integrity hashes) tonpm ci(refuses to run unless the lockfile is exact). This closes the supply-chain gap between "the lockfile passes the audit" and "the install respected that lockfile".What changes
Install scripts (
build.sh,studio/setup.sh,studio/setup.ps1)npm install->npm ci --no-fund --no-audit.[ -f bun.lock ] && command -v bun(Test-Path/Get-Commandon Windows) plus--frozen-lockfile --no-progress. Forward-compatible for the eventual bun.lock PR; inert today (no bun.lock checked in -> the gate fails closed -> execution falls through tonpm ci).Workflows
release-desktop.yml: 2npm install->npm ciswaps (Tauri CLI + frontend); new pre-install lockfile audit step.studio-tauri-smoke.yml: 1npm install->npm ciswap; audit step relocated to run before install.wheel-smoke.yml: single-line content-sanity assertion thatoxc-validator/package-lock.jsonis shipped in the wheel.security-audit.yml:pathstrigger gains 4 entries; OSV-scanner--lockfile=gains 2; two newnpm auditsteps (oxc + Tauri CLI holder);npm audit signaturesblock split into three (frontend / oxc / Tauri CLI), each preceded bynpm ci --ignore-scripts;check_new_install_scripts.pyextended to diff both new lockfiles withgit show … || echo '{}'fallback for PRs whose base predates ci: advisory lockfile supply-chain audit (no install-script changes) #5604; upload-artifact paths extended.Explicitly NOT in this PR
BUN_PIN_VERSION/ 3-attempt cache-corruption recovery ladders (would need committed bun.lock to be useful).unsloth.exerename-and-restore block changes in setup.ps1 (unrelated Windows pip-replace workaround).continue-on-errorpolicy flips on advisory audit jobs.Supersedes #5479
#5479 was a 41-file draft whose sequential
git merge mainoperations had collateral reverts of files that exist on main (studio/backend/core/tool_healing.py,tests/test_finetune_last_n_layers.py,tests/test_import_fixes_drift.py, etc.). This PR is a clean re-derivation off latestorigin/maincontaining only the install-path hardening.Test plan
bash -n build.sh && bash -n studio/setup.sh— clean[System.Management.Automation.Language.Parser]::ParseFile('studio/setup.ps1', ...)— cleannpm ciinstudio/,studio/frontend/,studio/backend/core/data_recipe/oxc-validator/— all 3 succeed, expected binaries present, no lockfile mutationnpm run buildinstudio/frontend/— producesdist/index.html+dist/assets/*.css+dist/assets/index-*.jsunsloth.exerename-and-restore block in setup.ps1 still present (grep -c 'unsloth.exe.deleteme' studio/setup.ps1= 1)python3 scripts/lockfile_supply_chain_audit.py— 0 findings on edited tree