feat: activate global-virtual-store install path (#432)#449
Conversation
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
📝 WalkthroughWalkthroughThis PR implements Global Virtual Store (GVS) support for frozen-lockfile installs by abstracting directory selection into ChangesGlobal Virtual Store Install Layout Abstraction
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Possibly related issues
Possibly related PRs
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
🧹 Nitpick comments (1)
crates/package-manager/src/import_indexed_dir/tests.rs (1)
533-534: ⚡ Quick winAdd failure context to these assertions.
These bare
assert!checks should include diagnostic context (message and/oreprintln!) to make failures actionable without reruns.Based on learnings: In Rust test code, follow the repo’s CODE_STYLE_GUIDE test-logging rule by adding diagnostic logging/context for assertions like
assert!when failures otherwise hide useful values.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@crates/package-manager/src/import_indexed_dir/tests.rs` around lines 533 - 534, The two bare assertions verifying that stale.txt was removed lack failure context; update the checks around target_a and target_b to include diagnostic messages (or use eprintln!) that print the full path and/or directory listing when the file exists so failures show actionable info; specifically modify the assertions that reference target_a.join("stale.txt").exists() and target_b.join("stale.txt").exists() to include a message like which target failed and the path (or call eprintln! with target_a/display()/fs::read_dir(...) before asserting) so test output contains useful debug details.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Nitpick comments:
In `@crates/package-manager/src/import_indexed_dir/tests.rs`:
- Around line 533-534: The two bare assertions verifying that stale.txt was
removed lack failure context; update the checks around target_a and target_b to
include diagnostic messages (or use eprintln!) that print the full path and/or
directory listing when the file exists so failures show actionable info;
specifically modify the assertions that reference
target_a.join("stale.txt").exists() and target_b.join("stale.txt").exists() to
include a message like which target failed and the path (or call eprintln! with
target_a/display()/fs::read_dir(...) before asserting) so test output contains
useful debug details.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro Plus
Run ID: 781a938d-736c-4a10-b8ac-4a4de19160a2
📒 Files selected for processing (23)
crates/config/src/lib.rscrates/package-manager/src/build_modules.rscrates/package-manager/src/build_modules/tests.rscrates/package-manager/src/create_cas_files.rscrates/package-manager/src/create_symlink_layout.rscrates/package-manager/src/create_virtual_dir_by_snapshot.rscrates/package-manager/src/create_virtual_dir_by_snapshot/tests.rscrates/package-manager/src/create_virtual_store.rscrates/package-manager/src/import_indexed_dir.rscrates/package-manager/src/import_indexed_dir/tests.rscrates/package-manager/src/install.rscrates/package-manager/src/install/tests.rscrates/package-manager/src/install_frozen_lockfile.rscrates/package-manager/src/install_package_by_snapshot.rscrates/package-manager/src/install_package_from_registry.rscrates/package-manager/src/install_without_lockfile.rscrates/package-manager/src/lib.rscrates/package-manager/src/link_bins.rscrates/package-manager/src/link_bins/tests.rscrates/package-manager/src/link_file.rscrates/package-manager/src/symlink_direct_dependencies.rscrates/package-manager/src/symlink_direct_dependencies/tests.rscrates/package-manager/src/virtual_store_layout.rs
💤 Files with no reviewable changes (1)
- crates/package-manager/src/create_cas_files.rs
📜 Review details
🧰 Additional context used
📓 Path-based instructions (1)
**/*.rs
📄 CodeRabbit inference engine (AGENTS.md)
**/*.rs: Preserve existing method chains andpipe-traitchains; do not break them into intermediateletbindings unless there is a concrete justification such as a compilation failure, borrow checker rejection, meaningful performance improvement, or other technical necessity. Refactoring for style alone is not sufficient justification.
Choose owned vs. borrowed parameters to minimize copies; prefer borrowed types (&Pathover&PathBuf,&strover&String) when it does not force extra copies.
PreferArc::clone(&x)andRc::clone(&x)overx.clone()for reference-counted types to make the cost visible at the call site.
Do not use star imports inside module bodies. Writeuse super::{Foo, bar}instead ofuse super::*;for any glob whose target is a module you control. External-crate preludes (e.g.,use rayon::prelude::*;) and root-of-module re-exports (e.g.,pub use submodule::*;inlib.rs) are exceptions.
Follow Rust API Guidelines for naming, as documented in https://rust-lang.github.io/api-guidelines/naming.html.
Declare a newtype wrapper for any branded string type being ported from TypeScript pnpm. Do not collapse the brand into a plainStringor&str; give the type its own struct so misuse is a type error.
When porting branded string types where upstream TypeScript always validates before construction, validate in the Rust port too. Construct the wrapper only viaTryFrom<String>and/orFromStr; do not provide an infallible public constructor that takes an arbitrary string.
For branded string types where upstream TypeScript never validates (used purely for type-safety to prevent confusion between string slots), expose an infallibleFrom<String>andFrom<&str>constructor in the Rust wrapper.
When upstream TypeScript occasionally constructs a branded type without validation (via bareasassertion), add afrom_str_unchecked(or similarly named) constructor on the Rust side. Keep the validating constructor as well; `from_str_u...
Files:
crates/package-manager/src/link_file.rscrates/package-manager/src/create_virtual_dir_by_snapshot/tests.rscrates/package-manager/src/install.rscrates/package-manager/src/create_symlink_layout.rscrates/package-manager/src/symlink_direct_dependencies.rscrates/package-manager/src/install_package_by_snapshot.rscrates/package-manager/src/install_without_lockfile.rscrates/package-manager/src/symlink_direct_dependencies/tests.rscrates/package-manager/src/import_indexed_dir.rscrates/package-manager/src/lib.rscrates/package-manager/src/link_bins.rscrates/package-manager/src/create_virtual_store.rscrates/package-manager/src/create_virtual_dir_by_snapshot.rscrates/package-manager/src/build_modules/tests.rscrates/package-manager/src/install_frozen_lockfile.rscrates/package-manager/src/install/tests.rscrates/package-manager/src/import_indexed_dir/tests.rscrates/package-manager/src/link_bins/tests.rscrates/package-manager/src/install_package_from_registry.rscrates/package-manager/src/virtual_store_layout.rscrates/config/src/lib.rscrates/package-manager/src/build_modules.rs
🧠 Learnings (2)
📚 Learning: 2026-05-07T23:19:08.272Z
Learnt from: KSXGitHub
Repo: pnpm/pacquet PR: 401
File: tasks/integrated-benchmark/src/work_env.rs:343-344
Timestamp: 2026-05-07T23:19:08.272Z
Learning: When reviewing Rust code in pnpm/pacquet for deprecated API usage, do not automatically treat `serde_saphyr::to_string` as deprecated. In `serde-saphyr` v0.0.25, `serde_saphyr::to_string` has no `#[deprecated]` attribute (the `#[deprecated]` later in `serde-saphyr-0.0.25/src/lib.rs` applies to a different function). Only flag `serde_saphyr::to_string` as deprecated if the resolved dependency version’s source shows `#[deprecated]` on that specific function.
Applied to files:
crates/package-manager/src/link_file.rscrates/package-manager/src/create_virtual_dir_by_snapshot/tests.rscrates/package-manager/src/install.rscrates/package-manager/src/create_symlink_layout.rscrates/package-manager/src/symlink_direct_dependencies.rscrates/package-manager/src/install_package_by_snapshot.rscrates/package-manager/src/install_without_lockfile.rscrates/package-manager/src/symlink_direct_dependencies/tests.rscrates/package-manager/src/import_indexed_dir.rscrates/package-manager/src/lib.rscrates/package-manager/src/link_bins.rscrates/package-manager/src/create_virtual_store.rscrates/package-manager/src/create_virtual_dir_by_snapshot.rscrates/package-manager/src/build_modules/tests.rscrates/package-manager/src/install_frozen_lockfile.rscrates/package-manager/src/install/tests.rscrates/package-manager/src/import_indexed_dir/tests.rscrates/package-manager/src/link_bins/tests.rscrates/package-manager/src/install_package_from_registry.rscrates/package-manager/src/virtual_store_layout.rscrates/config/src/lib.rscrates/package-manager/src/build_modules.rs
📚 Learning: 2026-05-01T10:01:33.766Z
Learnt from: zkochan
Repo: pnpm/pacquet PR: 349
File: crates/reporter/src/tests.rs:121-121
Timestamp: 2026-05-01T10:01:33.766Z
Learning: In Rust test code, follow the repo’s CODE_STYLE_GUIDE test-logging rule: add logging (e.g., `eprintln!`/`eprintln!(...)`) so that useful diagnostic values are printed when a test fails, unless the assertion is `assert_eq!` (where the differing values are already included). Concretely, if you use assertions like `assert!`, `assert_ne!`, etc., ensure the test logs the relevant actual/expected values (or context) before/around the assertion so failures can be diagnosed without rerunning.
Applied to files:
crates/package-manager/src/create_virtual_dir_by_snapshot/tests.rscrates/package-manager/src/symlink_direct_dependencies/tests.rscrates/package-manager/src/build_modules/tests.rscrates/package-manager/src/install/tests.rscrates/package-manager/src/import_indexed_dir/tests.rscrates/package-manager/src/link_bins/tests.rs
🔇 Additional comments (22)
crates/config/src/lib.rs (1)
507-514: LGTM!crates/package-manager/src/install.rs (1)
167-177: LGTM!crates/package-manager/src/virtual_store_layout.rs (1)
104-115: LGTM!crates/package-manager/src/symlink_direct_dependencies.rs (1)
26-30: LGTM!Also applies to: 115-117
crates/package-manager/src/symlink_direct_dependencies/tests.rs (1)
85-85: LGTM!Also applies to: 237-240
crates/package-manager/src/create_symlink_layout.rs (1)
22-42: LGTM!crates/package-manager/src/build_modules.rs (1)
177-182: LGTM!Also applies to: 723-731
crates/package-manager/src/link_bins.rs (1)
118-123: LGTM!Also applies to: 170-181, 287-287
crates/package-manager/src/install_without_lockfile.rs (1)
208-218: LGTM!crates/package-manager/src/link_bins/tests.rs (1)
2-2: LGTM!Also applies to: 44-45, 111-112, 135-136, 172-173, 222-223, 271-272, 295-296, 321-322, 468-469
crates/package-manager/src/build_modules/tests.rs (1)
2-2: LGTM!Also applies to: 290-291, 356-357, 410-411, 488-489, 614-615, 675-676, 729-730, 960-961, 1066-1067, 1181-1182, 1406-1407, 1509-1510, 1583-1584
crates/package-manager/src/link_file.rs (1)
113-114: LGTM!crates/package-manager/src/create_virtual_dir_by_snapshot/tests.rs (1)
63-65: LGTM!crates/package-manager/src/install_package_by_snapshot.rs (1)
2-3: LGTM!Also applies to: 27-31, 81-81, 149-149
crates/package-manager/src/lib.rs (1)
10-10: LGTM!Also applies to: 34-34
crates/package-manager/src/install/tests.rs (1)
729-860: LGTM!crates/package-manager/src/create_virtual_store.rs (1)
72-76: LGTM!Also applies to: 121-121, 391-391, 434-434
crates/package-manager/src/install_frozen_lockfile.rs (1)
5-5: LGTM!Also applies to: 101-140, 160-160, 169-177, 190-190, 275-275
crates/package-manager/src/install_package_from_registry.rs (1)
2-2: LGTM!Also applies to: 61-61, 179-186
crates/package-manager/src/create_virtual_dir_by_snapshot.rs (1)
1-4: LGTM!Also applies to: 13-13, 25-32, 64-64, 74-74, 84-84, 95-97, 103-115
crates/package-manager/src/import_indexed_dir/tests.rs (1)
1-532: LGTM!Also applies to: 535-536
crates/package-manager/src/import_indexed_dir.rs (1)
1-418: LGTM!
There was a problem hiding this comment.
Pull request overview
This PR activates the global-virtual-store (GVS) slot path for the --frozen-lockfile install pipeline by threading an install-scoped VirtualStoreLayout throughout the package-manager flow. It also replaces the old create_cas_files primitive with the more pnpm-faithful import_indexed_dir (including force/keep-modules options), and wires GVS derivation into Config::current.
Changes:
- Thread
VirtualStoreLayoutthrough frozen-lockfile install steps (virtual dir creation, direct-dep symlinks, bin linking, builds) so slot paths come fromlayout.slot_dir(...)instead ofvirtual_store_dir.join(...). - Add
import_indexed_dir(stage-and-swap support) and migrate call sites off the removedcreate_cas_files. - Apply
apply_global_virtual_store_derivationinConfig::currentand register projects in the store registry when GVS is enabled (plus tests around registry creation).
Reviewed changes
Copilot reviewed 23 out of 23 changed files in this pull request and generated 6 comments.
Show a summary per file
| File | Description |
|---|---|
| crates/package-manager/src/virtual_store_layout.rs | Adds legacy() constructor and switches frozen layout root to global_virtual_store_dir when GVS is enabled. |
| crates/package-manager/src/symlink_direct_dependencies.rs | Uses VirtualStoreLayout to compute per-dep symlink targets (GVS-aware). |
| crates/package-manager/src/symlink_direct_dependencies/tests.rs | Updates tests to provide a legacy VirtualStoreLayout. |
| crates/package-manager/src/link_bins.rs | Replaces virtual_store_dir parameter with layout and uses layout.slot_dir(...) in lockfile-driven bin linking. |
| crates/package-manager/src/link_bins/tests.rs | Updates tests to pass a legacy VirtualStoreLayout. |
| crates/package-manager/src/build_modules.rs | Routes per-snapshot package-dir lookup through VirtualStoreLayout. |
| crates/package-manager/src/build_modules/tests.rs | Updates tests to pass a legacy VirtualStoreLayout. |
| crates/package-manager/src/create_symlink_layout.rs | Uses layout.slot_dir(...) for child symlink targets rather than virtual_root.join(...). |
| crates/package-manager/src/create_virtual_dir_by_snapshot.rs | Uses layout.slot_dir(...) and switches CAS materialization to import_indexed_dir. |
| crates/package-manager/src/create_virtual_dir_by_snapshot/tests.rs | Updates test to pass a legacy VirtualStoreLayout (docs now need follow-up). |
| crates/package-manager/src/create_virtual_store.rs | Threads layout to warm/cold batch install logic and downstream helpers. |
| crates/package-manager/src/install_package_by_snapshot.rs | Threads layout into per-snapshot virtual-dir creation. |
| crates/package-manager/src/install_frozen_lockfile.rs | Constructs an install-scoped VirtualStoreLayout and threads it through the frozen pipeline. |
| crates/package-manager/src/install_without_lockfile.rs | Constructs a legacy VirtualStoreLayout for the non-frozen path. |
| crates/package-manager/src/install.rs | Registers projects in the store registry when GVS is enabled. |
| crates/package-manager/src/install/tests.rs | Adds tests asserting registry creation/absence under GVS on/off for frozen installs. |
| crates/package-manager/src/install_package_from_registry.rs | Migrates from create_cas_files to import_indexed_dir. |
| crates/package-manager/src/import_indexed_dir.rs | New implementation of pnpm-like indexed-dir import (force + keep-modules support). |
| crates/package-manager/src/import_indexed_dir/tests.rs | Comprehensive tests for import_indexed_dir behavior (force/keep, rescue paths, concurrency). |
| crates/package-manager/src/link_file.rs | Updates docs to reference import_indexed_dir as the production caller. |
| crates/package-manager/src/lib.rs | Swaps module export from create_cas_files to import_indexed_dir. |
| crates/package-manager/src/create_cas_files.rs | Removes the old CAS import primitive in favor of import_indexed_dir. |
| crates/config/src/lib.rs | Applies GVS derivation in Config::current and updates derivation semantics/docs. |
Comments suppressed due to low confidence (1)
crates/config/src/lib.rs:610
- The comment in this block says that when GVS is on and
virtualStoreDiris not pinned, it is re-pointed at<store_dir>/links. In pacquet's split-field approach,virtual_store_diris intentionally not re-pointed; insteadglobal_virtual_store_diris derived to<store_dir>/linksand the install pipeline chooses which one to consume. Updating this comment will prevent future readers from assumingvirtual_store_dirmutates under GVS.
// Derive the GVS-related paths last so they see the final
// `store_dir` / `virtual_store_dir` after yaml has been
// applied. Mirrors upstream's
// [`extendInstallOptions.ts:343-355`](https://github.com/pnpm/pnpm/blob/94240bc046/installing/deps-installer/src/install/extendInstallOptions.ts#L343-L355):
// when GVS is on and the user hasn't pinned `virtualStoreDir`,
// it's re-pointed at `<store_dir>/links`. The install layer
// then reads the resolved value through `VirtualStoreLayout`.
config.apply_global_virtual_store_derivation(virtual_store_dir_explicit);
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
01f1119 to
d552942
Compare
Micro-Benchmark ResultsLinux |
Six items from the Copilot review on #449: 1. **Gate `register_project` on frozen-lockfile** — the call previously fired for any GVS-on install, including the legacy `InstallWithoutLockfile` path that uses `VirtualStoreLayout::legacy` and never references `<store_dir>/links/...`. Tightened to `frozen_lockfile && config.enable_global_virtual_store` so a registry entry only appears for installs that actually consume the shared store. 2. **`VirtualStoreLayout::new` engine: `Option<&str>`** — the param was `&str` and the call site passed `engine_name.as_deref() .unwrap_or("")`, which turned a missing `node` into `Some("")` and hashed to a different value than `None` (the latter omits the `engine` contribution from `calc_graph_node_hash`). Switched the signature to `Option<&str>` and the call site to `engine_name.as_deref()` so the missing-engine case round-trips correctly. Test call sites updated. 3. **`run_emits_imported_event_after_create_cas_files`** — renamed to `run_emits_imported_event_after_import_indexed_dir` (and updated the doc comment) since `create_cas_files` was renamed to `import_indexed_dir` in #441. 4. **`VirtualStoreLayout::package_store_dir` doc** — the field doc still claimed the root was `Config::virtual_store_dir` in both modes. Updated to reflect the post-split design: it's `Config::global_virtual_store_dir` under GVS, `virtual_store_dir` otherwise. 5. **Test manifest placement** — the new `frozen_lockfile_under_gvs_registers_project_and_runs_clean` and `frozen_lockfile_with_gvs_off_skips_project_registry` tests created the manifest at `<tmp>/package.json` while `project_root` was `<tmp>/project/`. `Install::run` derives the registry target from `manifest.path().parent()`, so the registry entry actually pointed at `<tmp>`, not the intended project root. Moved the manifest into `project_root` and replaced the entry-count-only check with a `read_link` + canonicalize assertion that the symlink resolves back to `project_root`. 6. **Preserve yaml-set `globalVirtualStoreDir`** — `apply_global_virtual_store_derivation` unconditionally overwrote the field, so a user-pinned `globalVirtualStoreDir:` in `pnpm-workspace.yaml` was silently lost. Added a `global_virtual_store_dir_explicit` parameter that short-circuits the derivation when yaml provided the value, mirroring the pre-existing `virtual_store_dir_explicit` plumbing. New `yaml_global_virtual_store_dir_wins_over_derivation` test pins the behaviour. All 704 workspace tests pass; `taplo --check`, `just dylint`, `cargo doc -D warnings` clean.
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## main #449 +/- ##
==========================================
+ Coverage 87.15% 87.26% +0.10%
==========================================
Files 113 113
Lines 9187 9224 +37
==========================================
+ Hits 8007 8049 +42
+ Misses 1180 1175 -5 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
Two issues flagged by Copilot on #449: 1. `build_modules::virtual_store_dir_for_key` was calling `layout.slot_dir(&bare_key)` after peer-stripping the snapshot key. `VirtualStoreLayout` keys its precomputed GVS suffixes by the *full* snapshot key (with the peer suffix), so the lookup missed for peer-resolved snapshots and fell through to the legacy flat-name path — pointing at a directory `CreateVirtualDirBySnapshot` never created. Effect: build scripts silently skipped for peer-resolved packages. Switched the lookup to the full `key`; the package-name segment still comes from the peer-stripped key. 2. `CreateVirtualStore::run`'s partial-install skip probe (added in #442) still hard-coded `config.virtual_store_dir.join(to_virtual_store_name)`, which is the legacy layout. Under GVS-on the slot lives at `layout.slot_dir(snapshot_key)`, so warm slots were being classified as missing and `BrokenModules` emitted for the wrong path. Routed through the layout to match what the actual install / link steps use. #442's three partial-install tests (`warm_reinstall_skips_snapshot_when_current_lockfile_matches`, `warm_reinstall_emits_broken_modules_when_dir_is_missing`, `warm_reinstall_reports_added_zero_and_emits_no_imported_events`) seeded `<virtual_store_dir>/<flat-name>` slots manually — the legacy shape. With the probe now routed through the layout under default GVS-on, those legacy seeds no longer match. Each test sets `enable_global_virtual_store = false` so the seeded layout matches the probe; the partial-install behaviour under test is independent of the GVS layout, and the GVS-on dispatch is exercised by the `frozen_lockfile_*_gvs_*` tests. All 704 workspace tests pass; taplo / dylint / cargo doc clean.
Integrated-Benchmark Report (Linux)Scenario: Frozen Lockfile
BENCHMARK_REPORT.json{
"results": [
{
"command": "pacquet@HEAD",
"mean": 2.5028742708400005,
"stddev": 0.11025687436167891,
"median": 2.4940636226399997,
"user": 2.6245841199999997,
"system": 3.4296656999999997,
"min": 2.3825472996399997,
"max": 2.68397729264,
"times": [
2.48660725964,
2.58281197464,
2.68397729264,
2.41548588464,
2.61330822364,
2.5920617336399996,
2.3825472996399997,
2.50151998564,
2.38519815164,
2.3852249026399996
]
},
{
"command": "pacquet@main",
"mean": 2.45520339184,
"stddev": 0.05590506332766363,
"median": 2.4502786736399997,
"user": 2.61573632,
"system": 3.4171340999999997,
"min": 2.38236401464,
"max": 2.57315757564,
"times": [
2.45649624464,
2.45400572864,
2.44655161864,
2.57315757564,
2.43952066964,
2.40935714264,
2.5168526606399997,
2.4051580916399997,
2.38236401464,
2.4685701716399997
]
},
{
"command": "pnpm",
"mean": 5.903290806439999,
"stddev": 0.08000379770327536,
"median": 5.88068777664,
"user": 8.47642892,
"system": 4.339711499999999,
"min": 5.78090960664,
"max": 6.03589731564,
"times": [
5.83845056064,
5.8455565496399995,
5.78090960664,
5.99459190164,
5.93574279764,
5.86508466264,
5.97529911664,
5.8956977866399995,
5.86567776664,
6.03589731564
]
}
]
}Scenario: Frozen Lockfile (Hot Cache)
BENCHMARK_REPORT.json{
"results": [
{
"command": "pacquet@HEAD",
"mean": 0.6856617612000001,
"stddev": 0.05805622261703342,
"median": 0.6673388360000001,
"user": 0.36081995999999994,
"system": 1.4860888399999999,
"min": 0.6447540175,
"max": 0.8450294265,
"times": [
0.8450294265,
0.6733807245000001,
0.6917535475000001,
0.6564063425000001,
0.6558363115,
0.6447540175,
0.6613008585000001,
0.6687245595000001,
0.6934787115000001,
0.6659531125
]
},
{
"command": "pacquet@main",
"mean": 0.6986839980000001,
"stddev": 0.046405720107266624,
"median": 0.6832600265000001,
"user": 0.35525665999999995,
"system": 1.5051315400000003,
"min": 0.6590219875000001,
"max": 0.8227421735,
"times": [
0.8227421735,
0.6876285855000001,
0.6788914675000001,
0.7094946475,
0.6776299605,
0.7031247725,
0.6590219875000001,
0.6749427795,
0.6713141195000001,
0.7020494865000001
]
},
{
"command": "pnpm",
"mean": 2.5081078036,
"stddev": 0.0871426666431042,
"median": 2.513186266,
"user": 2.9024748600000003,
"system": 2.2256554399999997,
"min": 2.3853504635,
"max": 2.6430609374999996,
"times": [
2.4897487614999996,
2.3853504635,
2.6430609374999996,
2.5458658525,
2.5366237705,
2.4137081124999997,
2.5628419074999997,
2.4199347865,
2.6144695374999998,
2.4694739064999998
]
}
]
} |
Six items from the Copilot review on #449: 1. **Gate `register_project` on frozen-lockfile** — the call previously fired for any GVS-on install, including the legacy `InstallWithoutLockfile` path that uses `VirtualStoreLayout::legacy` and never references `<store_dir>/links/...`. Tightened to `frozen_lockfile && config.enable_global_virtual_store` so a registry entry only appears for installs that actually consume the shared store. 2. **`VirtualStoreLayout::new` engine: `Option<&str>`** — the param was `&str` and the call site passed `engine_name.as_deref() .unwrap_or("")`, which turned a missing `node` into `Some("")` and hashed to a different value than `None` (the latter omits the `engine` contribution from `calc_graph_node_hash`). Switched the signature to `Option<&str>` and the call site to `engine_name.as_deref()` so the missing-engine case round-trips correctly. Test call sites updated. 3. **`run_emits_imported_event_after_create_cas_files`** — renamed to `run_emits_imported_event_after_import_indexed_dir` (and updated the doc comment) since `create_cas_files` was renamed to `import_indexed_dir` in #441. 4. **`VirtualStoreLayout::package_store_dir` doc** — the field doc still claimed the root was `Config::virtual_store_dir` in both modes. Updated to reflect the post-split design: it's `Config::global_virtual_store_dir` under GVS, `virtual_store_dir` otherwise. 5. **Test manifest placement** — the new `frozen_lockfile_under_gvs_registers_project_and_runs_clean` and `frozen_lockfile_with_gvs_off_skips_project_registry` tests created the manifest at `<tmp>/package.json` while `project_root` was `<tmp>/project/`. `Install::run` derives the registry target from `manifest.path().parent()`, so the registry entry actually pointed at `<tmp>`, not the intended project root. Moved the manifest into `project_root` and replaced the entry-count-only check with a `read_link` + canonicalize assertion that the symlink resolves back to `project_root`. 6. **Preserve yaml-set `globalVirtualStoreDir`** — `apply_global_virtual_store_derivation` unconditionally overwrote the field, so a user-pinned `globalVirtualStoreDir:` in `pnpm-workspace.yaml` was silently lost. Added a `global_virtual_store_dir_explicit` parameter that short-circuits the derivation when yaml provided the value, mirroring the pre-existing `virtual_store_dir_explicit` plumbing. New `yaml_global_virtual_store_dir_wins_over_derivation` test pins the behaviour. All 704 workspace tests pass; `taplo --check`, `just dylint`, `cargo doc -D warnings` clean.
Two issues flagged by Copilot on #449: 1. `build_modules::virtual_store_dir_for_key` was calling `layout.slot_dir(&bare_key)` after peer-stripping the snapshot key. `VirtualStoreLayout` keys its precomputed GVS suffixes by the *full* snapshot key (with the peer suffix), so the lookup missed for peer-resolved snapshots and fell through to the legacy flat-name path — pointing at a directory `CreateVirtualDirBySnapshot` never created. Effect: build scripts silently skipped for peer-resolved packages. Switched the lookup to the full `key`; the package-name segment still comes from the peer-stripped key. 2. `CreateVirtualStore::run`'s partial-install skip probe (added in #442) still hard-coded `config.virtual_store_dir.join(to_virtual_store_name)`, which is the legacy layout. Under GVS-on the slot lives at `layout.slot_dir(snapshot_key)`, so warm slots were being classified as missing and `BrokenModules` emitted for the wrong path. Routed through the layout to match what the actual install / link steps use. (`warm_reinstall_skips_snapshot_when_current_lockfile_matches`, `warm_reinstall_emits_broken_modules_when_dir_is_missing`, `warm_reinstall_reports_added_zero_and_emits_no_imported_events`) seeded `<virtual_store_dir>/<flat-name>` slots manually — the legacy shape. With the probe now routed through the layout under default GVS-on, those legacy seeds no longer match. Each test sets `enable_global_virtual_store = false` so the seeded layout matches the probe; the partial-install behaviour under test is independent of the GVS layout, and the GVS-on dispatch is exercised by the `frozen_lockfile_*_gvs_*` tests. All 704 workspace tests pass; taplo / dylint / cargo doc clean.
51c5ad3 to
6898423
Compare
Six items from the Copilot review on #449: 1. **Gate `register_project` on frozen-lockfile** — the call previously fired for any GVS-on install, including the legacy `InstallWithoutLockfile` path that uses `VirtualStoreLayout::legacy` and never references `<store_dir>/links/...`. Tightened to `frozen_lockfile && config.enable_global_virtual_store` so a registry entry only appears for installs that actually consume the shared store. 2. **`VirtualStoreLayout::new` engine: `Option<&str>`** — the param was `&str` and the call site passed `engine_name.as_deref() .unwrap_or("")`, which turned a missing `node` into `Some("")` and hashed to a different value than `None` (the latter omits the `engine` contribution from `calc_graph_node_hash`). Switched the signature to `Option<&str>` and the call site to `engine_name.as_deref()` so the missing-engine case round-trips correctly. Test call sites updated. 3. **`run_emits_imported_event_after_create_cas_files`** — renamed to `run_emits_imported_event_after_import_indexed_dir` (and updated the doc comment) since `create_cas_files` was renamed to `import_indexed_dir` in #441. 4. **`VirtualStoreLayout::package_store_dir` doc** — the field doc still claimed the root was `Config::virtual_store_dir` in both modes. Updated to reflect the post-split design: it's `Config::global_virtual_store_dir` under GVS, `virtual_store_dir` otherwise. 5. **Test manifest placement** — the new `frozen_lockfile_under_gvs_registers_project_and_runs_clean` and `frozen_lockfile_with_gvs_off_skips_project_registry` tests created the manifest at `<tmp>/package.json` while `project_root` was `<tmp>/project/`. `Install::run` derives the registry target from `manifest.path().parent()`, so the registry entry actually pointed at `<tmp>`, not the intended project root. Moved the manifest into `project_root` and replaced the entry-count-only check with a `read_link` + canonicalize assertion that the symlink resolves back to `project_root`. 6. **Preserve yaml-set `globalVirtualStoreDir`** — `apply_global_virtual_store_derivation` unconditionally overwrote the field, so a user-pinned `globalVirtualStoreDir:` in `pnpm-workspace.yaml` was silently lost. Added a `global_virtual_store_dir_explicit` parameter that short-circuits the derivation when yaml provided the value, mirroring the pre-existing `virtual_store_dir_explicit` plumbing. New `yaml_global_virtual_store_dir_wins_over_derivation` test pins the behaviour. All 704 workspace tests pass; `taplo --check`, `just dylint`, `cargo doc -D warnings` clean.
Two issues flagged by Copilot on #449: 1. `build_modules::virtual_store_dir_for_key` was calling `layout.slot_dir(&bare_key)` after peer-stripping the snapshot key. `VirtualStoreLayout` keys its precomputed GVS suffixes by the *full* snapshot key (with the peer suffix), so the lookup missed for peer-resolved snapshots and fell through to the legacy flat-name path — pointing at a directory `CreateVirtualDirBySnapshot` never created. Effect: build scripts silently skipped for peer-resolved packages. Switched the lookup to the full `key`; the package-name segment still comes from the peer-stripped key. 2. `CreateVirtualStore::run`'s partial-install skip probe (added in #442) still hard-coded `config.virtual_store_dir.join(to_virtual_store_name)`, which is the legacy layout. Under GVS-on the slot lives at `layout.slot_dir(snapshot_key)`, so warm slots were being classified as missing and `BrokenModules` emitted for the wrong path. Routed through the layout to match what the actual install / link steps use. (`warm_reinstall_skips_snapshot_when_current_lockfile_matches`, `warm_reinstall_emits_broken_modules_when_dir_is_missing`, `warm_reinstall_reports_added_zero_and_emits_no_imported_events`) seeded `<virtual_store_dir>/<flat-name>` slots manually — the legacy shape. With the probe now routed through the layout under default GVS-on, those legacy seeds no longer match. Each test sets `enable_global_virtual_store = false` so the seeded layout matches the probe; the partial-install behaviour under test is independent of the GVS layout, and the GVS-on dispatch is exercised by the `frozen_lockfile_*_gvs_*` tests. All 704 workspace tests pass; taplo / dylint / cargo doc clean.
6898423 to
01e8af6
Compare
Threads `VirtualStoreLayout` through the frozen-lockfile install pipeline so packages installed under `enableGlobalVirtualStore: true` (pacquet's default since #444) land at the upstream-shaped `<store_dir>/links/<scope>/<name>/<version>/<hash>/node_modules/<pkg>` path instead of the legacy per-project flat layout. Closes the rest of #432 (Section B + C activation + D entry tests). Pipeline changes (frozen-lockfile path): - `InstallFrozenLockfile::run` constructs the install-scoped `VirtualStoreLayout` from the lockfile + engine_name + config, then threads `&VirtualStoreLayout` through `CreateVirtualStore`, `CreateVirtualDirBySnapshot`, `create_symlink_layout`, `SymlinkDirectDependencies`, `InstallPackageBySnapshot`, `BuildModules`, and `LinkVirtualStoreBins`. Every site that used to compute `virtual_store_dir.join(key.to_virtual_store_name())` now goes through one `layout.slot_dir(key)` lookup. - `Install::run` calls `pacquet_store_dir::register_project` when `enable_global_virtual_store` is on, writing `<store_dir>/projects/<short-hash>` so a future `pacquet store prune` can find the projects that still reference `<store_dir>/links/...` slots. Best-effort: registry write failures degrade to `tracing::warn!` and the install continues. - `Config::current` now applies `apply_global_virtual_store_derivation` after yaml — pacquet's variant of upstream's [`extendInstallOptions.ts:343-355`](https://github.com/pnpm/pnpm/blob/94240bc046/installing/deps-installer/src/install/extendInstallOptions.ts#L343-L355). Field-split deviation from upstream: Upstream pnpm *mutates* `virtualStoreDir` in place under GVS, so every consumer that reads `virtualStoreDir` sees `<storeDir>/links`. Pacquet keeps `virtual_store_dir` at its project-local value and writes the GVS path into the separate `global_virtual_store_dir` field. The frozen-lockfile path consumes `global_virtual_store_dir` through `VirtualStoreLayout`; the legacy non-frozen `InstallWithoutLockfile` keeps reading `virtual_store_dir` (now via `VirtualStoreLayout::legacy`). The split is a pacquet-only concern: upstream has no without-lockfile install path and so doesn't need the second field. See the doc on `Config::apply_global_virtual_store_derivation` for the reasoning. Tests: - `install::tests::frozen_lockfile_under_gvs_registers_project_and_runs_clean` pins the registry write under default GVS-on. - `install::tests::frozen_lockfile_with_gvs_off_skips_project_registry` pins that GVS-off opts out of the registry write. - All 653 workspace tests pass under the new layout-threaded path. - Existing per-snapshot legacy tests construct `VirtualStoreLayout::legacy(...)` explicitly so they keep asserting the flat-name layout regardless of any global default. End-to-end port of upstream's [`installing/deps-installer/test/install/globalVirtualStore.ts`](https://github.com/pnpm/pnpm/blob/94240bc046/installing/deps-installer/test/install/globalVirtualStore.ts) is tracked as a follow-up — those tests need a small frozen- lockfile fixture against the registry-mock, which is out of scope here. Based on #441 (`feat/438`); the `import_indexed_dir` rename from that PR is already merged in. The two PRs only co-touch `create_virtual_dir_by_snapshot.rs` — both pieces land cleanly on top of each other.
Six items from the Copilot review on #449: 1. **Gate `register_project` on frozen-lockfile** — the call previously fired for any GVS-on install, including the legacy `InstallWithoutLockfile` path that uses `VirtualStoreLayout::legacy` and never references `<store_dir>/links/...`. Tightened to `frozen_lockfile && config.enable_global_virtual_store` so a registry entry only appears for installs that actually consume the shared store. 2. **`VirtualStoreLayout::new` engine: `Option<&str>`** — the param was `&str` and the call site passed `engine_name.as_deref() .unwrap_or("")`, which turned a missing `node` into `Some("")` and hashed to a different value than `None` (the latter omits the `engine` contribution from `calc_graph_node_hash`). Switched the signature to `Option<&str>` and the call site to `engine_name.as_deref()` so the missing-engine case round-trips correctly. Test call sites updated. 3. **`run_emits_imported_event_after_create_cas_files`** — renamed to `run_emits_imported_event_after_import_indexed_dir` (and updated the doc comment) since `create_cas_files` was renamed to `import_indexed_dir` in #441. 4. **`VirtualStoreLayout::package_store_dir` doc** — the field doc still claimed the root was `Config::virtual_store_dir` in both modes. Updated to reflect the post-split design: it's `Config::global_virtual_store_dir` under GVS, `virtual_store_dir` otherwise. 5. **Test manifest placement** — the new `frozen_lockfile_under_gvs_registers_project_and_runs_clean` and `frozen_lockfile_with_gvs_off_skips_project_registry` tests created the manifest at `<tmp>/package.json` while `project_root` was `<tmp>/project/`. `Install::run` derives the registry target from `manifest.path().parent()`, so the registry entry actually pointed at `<tmp>`, not the intended project root. Moved the manifest into `project_root` and replaced the entry-count-only check with a `read_link` + canonicalize assertion that the symlink resolves back to `project_root`. 6. **Preserve yaml-set `globalVirtualStoreDir`** — `apply_global_virtual_store_derivation` unconditionally overwrote the field, so a user-pinned `globalVirtualStoreDir:` in `pnpm-workspace.yaml` was silently lost. Added a `global_virtual_store_dir_explicit` parameter that short-circuits the derivation when yaml provided the value, mirroring the pre-existing `virtual_store_dir_explicit` plumbing. New `yaml_global_virtual_store_dir_wins_over_derivation` test pins the behaviour. All 704 workspace tests pass; `taplo --check`, `just dylint`, `cargo doc -D warnings` clean.
`cargo fmt` wants the `VirtualStoreLayout::new(...)` call in `slot_dir_prefixes_unscoped_with_at_slash_under_gvs` broken onto multiple lines once the `&str` → `Some(&str)` rewrite made it too wide for the one-line form. Applies `cargo fmt` verbatim. Format CI green locally afterwards.
Two issues flagged by Copilot on #449: 1. `build_modules::virtual_store_dir_for_key` was calling `layout.slot_dir(&bare_key)` after peer-stripping the snapshot key. `VirtualStoreLayout` keys its precomputed GVS suffixes by the *full* snapshot key (with the peer suffix), so the lookup missed for peer-resolved snapshots and fell through to the legacy flat-name path — pointing at a directory `CreateVirtualDirBySnapshot` never created. Effect: build scripts silently skipped for peer-resolved packages. Switched the lookup to the full `key`; the package-name segment still comes from the peer-stripped key. 2. `CreateVirtualStore::run`'s partial-install skip probe (added in #442) still hard-coded `config.virtual_store_dir.join(to_virtual_store_name)`, which is the legacy layout. Under GVS-on the slot lives at `layout.slot_dir(snapshot_key)`, so warm slots were being classified as missing and `BrokenModules` emitted for the wrong path. Routed through the layout to match what the actual install / link steps use. (`warm_reinstall_skips_snapshot_when_current_lockfile_matches`, `warm_reinstall_emits_broken_modules_when_dir_is_missing`, `warm_reinstall_reports_added_zero_and_emits_no_imported_events`) seeded `<virtual_store_dir>/<flat-name>` slots manually — the legacy shape. With the probe now routed through the layout under default GVS-on, those legacy seeds no longer match. Each test sets `enable_global_virtual_store = false` so the seeded layout matches the probe; the partial-install behaviour under test is independent of the GVS layout, and the GVS-on dispatch is exercised by the `frozen_lockfile_*_gvs_*` tests. All 704 workspace tests pass; taplo / dylint / cargo doc clean.
…ort) #443 (workspace support for `--frozen-lockfile`, the #431 issue pacquet/pacquet#432 explicitly flagged as a follow-up) just landed. With it, the lockfile's `importers` map can carry multiple entries, and the install pipeline materialises a `node_modules/` for each sibling project. The project-registry write needs to follow suit: upstream pnpm registers every importer separately under `<store_dir>/projects/<short-hash>` so the prune sweep can keep every reachable consumer of `<store_dir>/links/...` alive. Changes: - `Install::run` now iterates `lockfile.importers.keys()` and registers each importer's project directory (computed via the newly-exposed `pub(crate) importer_root_dir(...)` from `symlink_direct_dependencies`). Replaces the previous one-shot registration of the workspace root, which would have left sub-importer entries missing from the registry. - The registry write moves to *after* `InstallFrozenLockfile::run` succeeds so the importer keys have already been validated by `SymlinkDirectDependencies::run`'s `validate_importer_id` — no more pre-validation entries for malformed lockfiles. - Gating unchanged: still only fires under `frozen_lockfile && config.enable_global_virtual_store`. - New test `frozen_lockfile_under_gvs_registers_each_workspace_importer` walks the multi-importer case: workspace root + `packages/web` each end up with their own symlink in `<store_dir>/projects/`, resolving back to the right directory. All 829 workspace tests pass; taplo / dylint / cargo doc clean.
01e8af6 to
e9a1887
Compare
There was a problem hiding this comment.
🧹 Nitpick comments (1)
crates/package-manager/src/install/tests.rs (1)
1279-1283: ⚡ Quick winUse
blob/mainupstream links in test docs.Line 1279 and Line 1425 use commit-pinned pnpm URLs. Prefer
https://github.com/pnpm/pnpm/blob/main/...so references track upstream and stay aligned with porting guidance.As per coding guidelines: "When porting features... open files from
https://github.com/pnpm/pnpm/blob/main/...rather than from a permalinked SHA."Also applies to: 1425-1426
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@crates/package-manager/src/install/tests.rs` around lines 1279 - 1283, The test docs use commit-pinned pnpm URLs; update the upstream links to use blob/main instead of the SHA so they track the main branch—replace the two permalinked URLs referenced in the comment around the Install::run documentation (the links to config/reader and to storeController/projectRegistry/registerProject) with their corresponding https://github.com/pnpm/pnpm/blob/main/... URLs; ensure both occurrences (the Install::run doc comment and the later reference to registerProject/projectRegistry) are changed.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Nitpick comments:
In `@crates/package-manager/src/install/tests.rs`:
- Around line 1279-1283: The test docs use commit-pinned pnpm URLs; update the
upstream links to use blob/main instead of the SHA so they track the main
branch—replace the two permalinked URLs referenced in the comment around the
Install::run documentation (the links to config/reader and to
storeController/projectRegistry/registerProject) with their corresponding
https://github.com/pnpm/pnpm/blob/main/... URLs; ensure both occurrences (the
Install::run doc comment and the later reference to
registerProject/projectRegistry) are changed.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro Plus
Run ID: 55128053-2b49-4476-a7cf-d2df6a476a52
⛔ Files ignored due to path filters (1)
Cargo.lockis excluded by!**/*.lock
📒 Files selected for processing (18)
crates/config/src/lib.rscrates/package-manager/Cargo.tomlcrates/package-manager/src/build_modules.rscrates/package-manager/src/build_modules/tests.rscrates/package-manager/src/create_symlink_layout.rscrates/package-manager/src/create_virtual_dir_by_snapshot.rscrates/package-manager/src/create_virtual_dir_by_snapshot/tests.rscrates/package-manager/src/create_virtual_store.rscrates/package-manager/src/install.rscrates/package-manager/src/install/tests.rscrates/package-manager/src/install_frozen_lockfile.rscrates/package-manager/src/install_package_by_snapshot.rscrates/package-manager/src/install_without_lockfile.rscrates/package-manager/src/link_bins.rscrates/package-manager/src/link_bins/tests.rscrates/package-manager/src/symlink_direct_dependencies.rscrates/package-manager/src/symlink_direct_dependencies/tests.rscrates/package-manager/src/virtual_store_layout.rs
🚧 Files skipped from review as they are similar to previous changes (15)
- crates/package-manager/src/install.rs
- crates/package-manager/src/install_without_lockfile.rs
- crates/package-manager/Cargo.toml
- crates/package-manager/src/symlink_direct_dependencies.rs
- crates/package-manager/src/link_bins/tests.rs
- crates/package-manager/src/build_modules/tests.rs
- crates/package-manager/src/build_modules.rs
- crates/package-manager/src/create_symlink_layout.rs
- crates/package-manager/src/install_package_by_snapshot.rs
- crates/package-manager/src/link_bins.rs
- crates/config/src/lib.rs
- crates/package-manager/src/virtual_store_layout.rs
- crates/package-manager/src/create_virtual_store.rs
- crates/package-manager/src/install_frozen_lockfile.rs
- crates/package-manager/src/create_virtual_dir_by_snapshot.rs
📜 Review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (6)
- GitHub Check: Run benchmark on ubuntu-latest
- GitHub Check: Lint and Test (windows-latest)
- GitHub Check: Lint and Test (macos-latest)
- GitHub Check: Dylint
- GitHub Check: Run benchmark on ubuntu-latest
- GitHub Check: Code Coverage
🧰 Additional context used
📓 Path-based instructions (1)
**/*.rs
📄 CodeRabbit inference engine (AGENTS.md)
**/*.rs: Preserve existing method chains andpipe-traitchains; do not break them into intermediateletbindings unless there is a concrete justification such as a compilation failure, borrow checker rejection, meaningful performance improvement, or other technical necessity. Refactoring for style alone is not sufficient justification.
Choose owned vs. borrowed parameters to minimize copies; prefer borrowed types (&Pathover&PathBuf,&strover&String) when it does not force extra copies.
PreferArc::clone(&x)andRc::clone(&x)overx.clone()for reference-counted types to make the cost visible at the call site.
Do not use star imports inside module bodies. Writeuse super::{Foo, bar}instead ofuse super::*;for any glob whose target is a module you control. External-crate preludes (e.g.,use rayon::prelude::*;) and root-of-module re-exports (e.g.,pub use submodule::*;inlib.rs) are exceptions.
Follow Rust API Guidelines for naming, as documented in https://rust-lang.github.io/api-guidelines/naming.html.
Declare a newtype wrapper for any branded string type being ported from TypeScript pnpm. Do not collapse the brand into a plainStringor&str; give the type its own struct so misuse is a type error.
When porting branded string types where upstream TypeScript always validates before construction, validate in the Rust port too. Construct the wrapper only viaTryFrom<String>and/orFromStr; do not provide an infallible public constructor that takes an arbitrary string.
For branded string types where upstream TypeScript never validates (used purely for type-safety to prevent confusion between string slots), expose an infallibleFrom<String>andFrom<&str>constructor in the Rust wrapper.
When upstream TypeScript occasionally constructs a branded type without validation (via bareasassertion), add afrom_str_unchecked(or similarly named) constructor on the Rust side. Keep the validating constructor as well; `from_str_u...
Files:
crates/package-manager/src/create_virtual_dir_by_snapshot/tests.rscrates/package-manager/src/symlink_direct_dependencies/tests.rscrates/package-manager/src/install/tests.rs
🧠 Learnings (2)
📚 Learning: 2026-05-01T10:01:33.766Z
Learnt from: zkochan
Repo: pnpm/pacquet PR: 349
File: crates/reporter/src/tests.rs:121-121
Timestamp: 2026-05-01T10:01:33.766Z
Learning: In Rust test code, follow the repo’s CODE_STYLE_GUIDE test-logging rule: add logging (e.g., `eprintln!`/`eprintln!(...)`) so that useful diagnostic values are printed when a test fails, unless the assertion is `assert_eq!` (where the differing values are already included). Concretely, if you use assertions like `assert!`, `assert_ne!`, etc., ensure the test logs the relevant actual/expected values (or context) before/around the assertion so failures can be diagnosed without rerunning.
Applied to files:
crates/package-manager/src/create_virtual_dir_by_snapshot/tests.rscrates/package-manager/src/symlink_direct_dependencies/tests.rscrates/package-manager/src/install/tests.rs
📚 Learning: 2026-05-07T23:19:08.272Z
Learnt from: KSXGitHub
Repo: pnpm/pacquet PR: 401
File: tasks/integrated-benchmark/src/work_env.rs:343-344
Timestamp: 2026-05-07T23:19:08.272Z
Learning: When reviewing Rust code in pnpm/pacquet for deprecated API usage, do not automatically treat `serde_saphyr::to_string` as deprecated. In `serde-saphyr` v0.0.25, `serde_saphyr::to_string` has no `#[deprecated]` attribute (the `#[deprecated]` later in `serde-saphyr-0.0.25/src/lib.rs` applies to a different function). Only flag `serde_saphyr::to_string` as deprecated if the resolved dependency version’s source shows `#[deprecated]` on that specific function.
Applied to files:
crates/package-manager/src/create_virtual_dir_by_snapshot/tests.rscrates/package-manager/src/symlink_direct_dependencies/tests.rscrates/package-manager/src/install/tests.rs
🔇 Additional comments (4)
crates/package-manager/src/symlink_direct_dependencies/tests.rs (1)
85-85: LGTM!Also applies to: 204-204, 284-284, 342-342, 345-345, 420-420, 484-484, 558-558
crates/package-manager/src/create_virtual_dir_by_snapshot/tests.rs (1)
30-33: LGTM!Also applies to: 37-37, 63-65
crates/package-manager/src/install/tests.rs (2)
17-17: LGTM!Also applies to: 786-794, 868-873, 1102-1106
1291-1364: LGTM!Also applies to: 1372-1421, 1432-1510
…r override The integrated benchmark's default workspace manifest now writes `enableGlobalVirtualStore: false` so existing scenarios continue to measure the same project-local `<modules>/.pnpm/<flat-name>` shape they did before #432 flipped pacquet's default to GVS-on. Without this pin, every revision newer than #432 would silently switch to the GVS layout (`<store_dir>/links/<scope>/<name>/<ver>/<hash>`), making `pacquet@HEAD` vs `pacquet@main` vs `pnpm` comparisons measure two different disk shapes — the benchmark's job is to track isolated-linker perf across revisions, so the layout has to stay constant. Two changes: - `MinimalWorkspaceManifest` gains an `enable_global_virtual_store: Option<bool>` field so the fixture's `enableGlobalVirtualStore: false` round-trips through the typed parse/emit instead of being silently dropped on serialization. - `tasks/integrated-benchmark/src/fixtures/pnpm-workspace.yaml` adds the `enableGlobalVirtualStore: false` line with a comment explaining how to flip it for GVS-specific benchmarking (`--fixture-dir` override). GVS-specific benchmark variants — once we want them — can land as a separate fixture-dir or a new `BenchmarkScenario` variant; this PR keeps the default scenario pointed at the layout the historical numbers were taken against.
… sites added by GVS PR
`Install { ... }` literals at lines 1349, 1418, 1493 were added by
the global-virtual-store activation PR (#449) that landed on `main`
while this branch was open. They were missing the
`supported_architectures` field this PR introduced.
`None` for all three since none exercise platform-tagged optional
dependencies.
Workspace install (#431 / #443) and GVS install activation (#432 / #449) both landed since the hoist work in #435 began. The rebase needed two adjustments to keep the symlink target paths correct under GVS: - `symlink_hoisted_dependencies` now takes `&VirtualStoreLayout` instead of `virtual_store_dir: &Path`. The symlink target (the source of each hoist symlink) goes through `layout.slot_dir(key)`, which under GVS resolves to `<store_dir>/links/<scope>/<name>/<version>/<hash>/` and falls back to the legacy `<virtual_store_dir>/<key.virtual_store_name>/` flat name when GVS is off. The hoist code never has to branch on `enable_global_virtual_store` itself. - The private hoist *target dir* (where hoist symlinks live) stays `config.virtual_store_dir.join("node_modules")`. Pacquet keeps `virtual_store_dir` project-local even with GVS enabled — only `global_virtual_store_dir` carries the shared `<store_dir>/links` path (see `Config::apply_global_virtual_store_derivation`). So `<root>/node_modules/.pnpm/node_modules` is still the right placement under both modes; the GVS-rewire concern from the issue description doesn't apply to pacquet's split-field design. Updated the call site comment to record this and dropped the stale "GVS not implemented yet — tracked at #432" note. Tests: `just ready` 894/894 pass; `cargo doc --workspace --no-deps` with `RUSTDOCFLAGS="-D warnings"` clean; `just dylint` clean. The full hoist CLI suite (36/36) and unit suite (12/12) still pass with the GVS layout pulled into the symlink path.
Workspace install (#431 / #443) and GVS install activation (#432 / #449) both landed since the hoist work in #435 began. The rebase needed two adjustments to keep the symlink target paths correct under GVS: - `symlink_hoisted_dependencies` now takes `&VirtualStoreLayout` instead of `virtual_store_dir: &Path`. The symlink target (the source of each hoist symlink) goes through `layout.slot_dir(key)`, which under GVS resolves to `<store_dir>/links/<scope>/<name>/<version>/<hash>/` and falls back to the legacy `<virtual_store_dir>/<key.virtual_store_name>/` flat name when GVS is off. The hoist code never has to branch on `enable_global_virtual_store` itself. - The private hoist *target dir* (where hoist symlinks live) stays `config.virtual_store_dir.join("node_modules")`. Pacquet keeps `virtual_store_dir` project-local even with GVS enabled — only `global_virtual_store_dir` carries the shared `<store_dir>/links` path (see `Config::apply_global_virtual_store_derivation`). So `<root>/node_modules/.pnpm/node_modules` is still the right placement under both modes; the GVS-rewire concern from the issue description doesn't apply to pacquet's split-field design. Updated the call site comment to record this and dropped the stale "GVS not implemented yet — tracked at #432" note. Tests: `just ready` 894/894 pass; `cargo doc --workspace --no-deps` with `RUSTDOCFLAGS="-D warnings"` clean; `just dylint` clean. The full hoist CLI suite (36/36) and unit suite (12/12) still pass with the GVS layout pulled into the symlink path.
Pull in 28 commits from upstream main, including the `pacquet-npmrc` → `pacquet-config` rename (#420) plus features: - supportedArchitectures + --cpu/--os/--libc (#456) - frozen-lockfile (#442, #443, #447, #450) - git-fetcher (#436 / #446, #451, #454) - side-effects cache (#421 / #422, #423, #424) - real-hoist + global-virtual-store (#432 / #438 / #444, #449, #452) - patchedDependencies + allow-builds (#425, #427, #428) - engine/platform installability (#434 / #439) Conflicts resolved: - `crates/npmrc/` files migrated under the renamed `crates/config/` directory; `Npmrc` → `Config` everywhere except `NpmrcAuth` (which keeps the `.npmrc`-domain name). - `Config::current` reads the env-var DI generic `Api: EnvVar` for ${VAR}-substitution in `.npmrc`. Production turbofish in `cli_args.rs` is `Config::current::<RealApi, _, _, _, _>(...)`. - Two-phase `NpmrcAuth::apply_*` retained so default-registry creds key at the yaml-resolved registry URL. - New `Config::auth_headers` field plumbed through `install_package_by_snapshot`'s `DownloadTarballToStore`. - Tests under `crates/config/src/workspace_yaml/tests.rs` pick up the new ParseYaml unit test added on this branch.
Summary
Threads
VirtualStoreLayout(from #444) through the frozen-lockfile install pipeline so packages installed withenableGlobalVirtualStore: true(pacquet's default since #444) land at the upstream-shaped<store_dir>/links/<scope>/<name>/<version>/<hash>/node_modules/<pkg>path. Closes the rest of #432 (Section B + C activation + D entry tests).Pipeline changes (frozen-lockfile path)
InstallFrozenLockfile::runconstructs the install-scopedVirtualStoreLayoutfrom the lockfile + engine name + config, then threads&VirtualStoreLayoutthroughCreateVirtualStore,CreateVirtualDirBySnapshot,create_symlink_layout,SymlinkDirectDependencies,InstallPackageBySnapshot,BuildModules, andLinkVirtualStoreBins. Every site that used to computevirtual_store_dir.join(key.to_virtual_store_name())now goes through onelayout.slot_dir(key)lookup.Install::runcallspacquet_store_dir::register_projectonce per importer whenenable_global_virtual_storeis on, writing<store_dir>/projects/<short-hash>per workspace package so a futurepacquet store prunecan find every reachable consumer of<store_dir>/links/.... Best-effort: registry write failures degrade totracing::warn!and the install continues. The walk runs afterInstallFrozenLockfile::runsucceeds so importer keys have already been validated.Config::currentnow appliesapply_global_virtual_store_derivationafter yaml — pacquet's variant of upstream'sextendInstallOptions.ts:343-355. An explicitglobalVirtualStoreDir:in yaml wins over the derivation; otherwise the field falls back to the user's pinnedvirtualStoreDir(under GVS-on) or to<store_dir>/links.Field-split deviation from upstream
Upstream pnpm mutates
virtualStoreDirin place under GVS, so every consumer that readsvirtualStoreDirsees<storeDir>/links. Pacquet keepsvirtual_store_dirat its project-local value and writes the GVS path into the separateglobal_virtual_store_dirfield. The frozen-lockfile path consumesglobal_virtual_store_dirthroughVirtualStoreLayout; the legacy non-frozenInstallWithoutLockfilekeeps readingvirtual_store_dir(now viaVirtualStoreLayout::legacy). The split is a pacquet-only concern: upstream has no without-lockfile install path and so doesn't need the second field. See the doc onConfig::apply_global_virtual_store_derivationfor the reasoning.Benchmarks
The integrated-benchmark fixture pins
enableGlobalVirtualStore: falseso existing scenarios continue to compare apples-to-apples against the project-local<modules>/.pnpm/<flat-name>shape. Without the pin, every revision newer than this PR would silently switch to the GVS layout, whilepacquet@mainand the pnpm side of the comparison would still use the isolated layout — different shape, unfair comparison. GVS-specific benchmark runs are available by passing--fixture-dir <dir>at a copy of the fixture with the flag flipped.Tests
install::tests::frozen_lockfile_under_gvs_registers_project_and_runs_cleanpins the registry write under default GVS-on (single-project case).install::tests::frozen_lockfile_under_gvs_registers_each_workspace_importerpins per-importer registration: workspace root +packages/webeach end up with their own symlink in<store_dir>/projects/.install::tests::frozen_lockfile_with_gvs_off_skips_project_registrypins that GVS-off opts out of the registry write.config::tests::yaml_global_virtual_store_dir_wins_over_derivationpins the yaml-override precedence.VirtualStoreLayout::legacy(...)explicitly so they keep asserting the flat-name layout regardless of any global default. Partial-install tests from feat: partial install with --frozen-lockfile (#433) #442 pinenable_global_virtual_store = falseso their seeded legacy-shape slots match the probe path.End-to-end port of upstream's
installing/deps-installer/test/install/globalVirtualStore.tsis tracked as a follow-up — those tests need a small frozen-lockfile fixture against the registry-mock that doesn't exist yet.Test plan
just ready(829 tests green)taplo format --check(clean)just dylint(clean)RUSTDOCFLAGS="-D warnings" cargo doc --no-deps --workspace --document-private-items(clean)cargo fmt --check(clean)Closes #432.
Written by an agent (Claude Code, claude-opus-4-7).