Repository navigation
Fix pnpm member settings written to an ignored nested pnpm-workspace.yaml (#880, #881) - #888
Mikola Lysenko (mikolalysenko) wants to merge 7 commits into
Conversation
Assisted-by: Claude Code:claude-opus-5-5
pnpm reads settings (overrides, trustLockfile) only from the nearest pnpm-workspace.yaml above a project. A workspace member with its own lock but no settings file of its own is governed by the root's file, and any file socket-patch creates inside the member is ignored. Add utils::pnpm_workspace::governing_workspace_file so hosted and vendored modes can tell that layout apart from a standalone project. Refs #880, #881 Assisted-by: Claude Code:claude-opus-5-5
In a pnpm workspace with sharedWorkspaceLockfile: false, vendored mode run from a member wrote the override into the member's package.json and a new nested pnpm-workspace.yaml. pnpm reads overrides only from the workspace root's file, so on pnpm 11/12 frozen installs failed with ERR_PNPM_LOCKFILE_CONFIG_MISMATCH and a plain pnpm install silently reinstalled the unpatched package, while the scan reported success. The run now refuses before any write (dry runs and the pre-download preflight included) with vendor_pnpm_settings_elsewhere, naming the governing root file and pointing at hosted mode. Fixes #881 Assisted-by: Claude Code:claude-opus-5-5
Hosted mode run from a pnpm workspace member with its own lock created a nested pnpm-workspace.yaml holding trustLockfile: true. pnpm ignores a member's settings file, so every root install on pnpm 11/12 failed with ERR_PNPM_TARBALL_URL_MISMATCH while the scan reported success and told users to commit the file. The hosted pre-check now refuses such a member before any takeover or write with redirect_pnpm_settings_elsewhere, naming the root file to add trustLockfile: true to. Once the root file trusts the lock, or explicitly opts out, the member is pinned, no nested file is created, and the trust warning names the root file. --no-trust-lockfile-config still pins without the key. Both new refusal codes are documented in CLI_CONTRACT.md. Fixes #880 Assisted-by: Claude Code:claude-opus-5-5
Assisted-by: Claude Code:claude-opus-5-5
|
BugBot review Generated by Claude Code |
|
[agent] The Generated by Claude Code |
test (windows-latest) failed two ways: - governing_workspace_file returned the canonicalized path, so the refusal named \\?\C:\Users\runneradmin\...\pnpm-workspace.yaml. That is a spelling users never type, and it didn't match the test's raw tempdir path (C:\Users\RUNNER~1\..., an 8.3 short name). - The CLI tests matched paths against a JSON dump of the warnings, which doubles every Windows backslash. governing_workspace_file now strips the verbatim prefix (without_verbatim_prefix, string-level and unit-tested on every host). The tests compare against that canonical spelling and read warning text unescaped. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018d82ZH8U4XqGShmX3UgoEx
|
BugBot review Generated by Claude Code |
There was a problem hiding this comment.
✅ Bugbot reviewed your changes and found no new issues!
Comment @cursor review or bugbot run to trigger another review on this PR
Reviewed by Cursor Bugbot for commit 83c5105. Configure here.
|
[agent]
Generated by Claude Code |
|
Burn-down agent: labeled Ready for review at
Generated by Claude Code |
LLM Description written by Claude Code:claude-opus-5-5
Fixes #880
Fixes #881
Root cause
In a pnpm workspace with per-project locks (
sharedWorkspaceLockfile: false), a member has its ownpnpm-lock.yaml, so both hosted and vendored runs from the member go ahead. Both then looked for pnpm settings only at<cwd>/pnpm-workspace.yaml:engine.rs::read_workspace→pnpm_trustplanned aCreateof a nestedpackages: ['.']+trustLockfile: truefile (Hosted scan from a pnpm 11/12 workspace member with its own lock writes trustLockfile into a nested member pnpm-workspace.yaml that pnpm ignores, so the root install fails with ERR_PNPM_TARBALL_URL_MISMATCH #880);pnpm_lock.rs::read_project→ the override was mirrored into a newly created nestedpackages: ['.']+overrides:file (Vendored scan from a pnpm 11/12 workspace member with its own lock writes the override into a nested member pnpm-workspace.yaml that pnpm ignores, so the root frozen install fails and a plainpnpm installsilently reinstalls the unpatched package #881).pnpm reads settings only from the nearest
pnpm-workspace.yamlabove the member, which is the workspace root's, so both writes are ignored. On pnpm 11/12, root installs then fail (ERR_PNPM_TARBALL_URL_MISMATCH/ERR_PNPM_LOCKFILE_CONFIG_MISMATCH), or for vendored mode, a plain install silently drops the patch.Fix
utils::pnpm_workspace::governing_workspace_file: for a project with nopnpm-workspace.yamlof its own, it returns the nearest ancestorpnpm-workspace.yaml. Both modes use it.governing_root::refusal, which runs before any takeover or write, dry runs included): a member with its own v9 lock whose root file neither trusts the lock nor explicitly opts out is refused withredirect_pnpm_settings_elsewhere. The message names the root file and tells the user to addtrustLockfile: truethere, or to pass--no-trust-lockfile-config. Once the root file trusts the lock or opts out, the member is pinned, no nested file is created, and theredirect_pnpm_trust_lockfilewarning names the root file (pnpm_trust).read_project, shared by the vendor step and the pre-download preflight): refused withvendor_pnpm_settings_elsewhere, naming the governing file and pointing at--mode hosted.CLI_CONTRACT.md.Why refuse instead of writing the root file: hosted and vendored writes, and their ledgers and reverts, are all scoped to the project directory, and the vendored
file:override path would have to be re-rooted for the root file. Refusing before any write follows the existing #590 / #598 governing-root pattern. Behaviour change: pnpm 9/10 users of this layout previously got a run that "worked" but left a nestedpackages: ['.']file. That file turns the member into its own workspace whenever pnpm runs inside it. These users now get the refusal. Hosted mode's remedy is one line in the root file that pnpm ≤ 10 ignores. Vendored mode's remedy is to use hosted mode.Ported #878 (commit 5d94800):
main'sproduction_digests_go_through_the_helpersguard is red, and this carries #878's 3-file Gradle digest fix so CI can go green. It becomes a no-op once #878 lands.Tests (red → green)
in_process_redirect_pnpm::hosted_scan_from_pnpm_member_with_own_lock_never_nests_trust_configSome(0), rightSome(1))in_process_redirect_pnpm::hosted_scan_from_pnpm_member_respects_root_trust_opt_outpnpm-workspace.yamlcreatedgoverning_root::tests::pnpm_member_with_own_lock_needs_the_root_to_trust_it(unit)vendor::pnpm_lock::tests::workspace_member_with_its_own_lock_is_refused_before_any_write(wet, dry run, preflight)utils::pnpm_workspace::tests::*pnpm_member_with_own_lock_or_lockless_root_is_left_alonenow gives its root filetrustLockfile: true, so it keeps testing what its name says (no lock-elsewhere refusal).Local runs
cargo clippy --workspace --all-features -- -D warnings: clean.cargo test -p socket-patch-cli --all-features --test in_process_redirect_pnpm: 18/18 pass.e2e_redirect_pnpm_build -- --ignored --skip pnpm_pinned_matrix(real pnpm): 8/8 pass.e2e_vendor_pnpm_build: 24/24 pass, plus the ignored pinned matrix withSOCKET_PATCH_PNPM_E2E_VERSION=10.28.0: 1/1 pass.cargo test --workspace --all-features --no-fail-fast: everything passes except 12 tests that inject write failures withchmod 0o555/ read-only parents. The sandbox runs as root, so those writes succeed:covgap_commands_vendor×3,in_process_redirect×3,repair×2, and corecopy_tree/vlt_heal/pypi_poetry/pypi_requirements. None touch pnpm settings, and CI runs them as non-root. The 13th, the digest guard, is fixed by the Route Gradle digests through utils::digest #878 port.cargo fmt:mainitself isn't rustfmt-clean with the pinned 1.93.1 toolchain (466 diffs), so only this PR's own hunks are formatted.No wrapper changes are needed (
npm/,pypi/andgem/only dispatch the binary).🤖 Generated with Claude Code
https://claude.ai/code/session_018d82ZH8U4XqGShmX3UgoEx
Note
Medium Risk
Changes hosted/vendored preflight and refusal paths for pnpm workspace members with own locks; behavior shifts from silent nested files to fail-closed errors until root
trustLockfileis set.Overview
Fixes pnpm workspace members that use per-package locks (
sharedWorkspaceLockfile: false): hosted and vendored runs no longer create a nestedpnpm-workspace.yamlin the member (pnpm ignores it and only reads settings from the ancestor root file).Hosted adds a pre-write refusal (
redirect_pnpm_settings_elsewhere) when the member has a v9 lock but the rootpnpm-workspace.yamldoes not yet settrustLockfile(unless the user passes--no-trust-lockfile-config). After the root trusts the lock—or explicitly opts out—pins proceed and trust warnings reference the root file, not a nested scaffold.Vendored refuses the same layout with
vendor_pnpm_settings_elsewherebecauseoverrides:cannot be applied in the member; remedy is--mode hosted.Shared helper
governing_workspace_fileresolves which workspace YAML governs a project;CLI_CONTRACT.mddocuments both codes.Also routes Gradle/JVM SHA1/SHA256 hashing through shared
utils::digesthelpers (CI guard / #878 port).Reviewed by Cursor Bugbot for commit 83c5105. Configure here.
Generated by Claude Code