Skip to content
This repository was archived by the owner on May 14, 2026. It is now read-only.

feat: activate global-virtual-store install path (#432)#449

Merged
zkochan merged 6 commits into
mainfrom
feat/432-activate
May 13, 2026
Merged

feat: activate global-virtual-store install path (#432)#449
zkochan merged 6 commits into
mainfrom
feat/432-activate

Conversation

@zkochan

@zkochan zkochan commented May 13, 2026

Copy link
Copy Markdown
Member

Summary

Threads VirtualStoreLayout (from #444) through the frozen-lockfile install pipeline so packages installed with enableGlobalVirtualStore: 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::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 once per importer when enable_global_virtual_store is on, writing <store_dir>/projects/<short-hash> per workspace package so a future pacquet store prune can find every reachable consumer of <store_dir>/links/.... Best-effort: registry write failures degrade to tracing::warn! and the install continues. The walk runs after InstallFrozenLockfile::run succeeds so importer keys have already been validated.
  • Config::current now applies apply_global_virtual_store_derivation after yaml — pacquet's variant of upstream's extendInstallOptions.ts:343-355. An explicit globalVirtualStoreDir: in yaml wins over the derivation; otherwise the field falls back to the user's pinned virtualStoreDir (under GVS-on) or to <store_dir>/links.

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.

Benchmarks

The integrated-benchmark fixture pins enableGlobalVirtualStore: false so 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, while pacquet@main and 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_clean pins the registry write under default GVS-on (single-project case).
  • install::tests::frozen_lockfile_under_gvs_registers_each_workspace_importer pins per-importer registration: workspace root + packages/web each end up with their own symlink in <store_dir>/projects/.
  • install::tests::frozen_lockfile_with_gvs_off_skips_project_registry pins that GVS-off opts out of the registry write.
  • config::tests::yaml_global_virtual_store_dir_wins_over_derivation pins the yaml-override precedence.
  • All 829 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. Partial-install tests from feat: partial install with --frozen-lockfile (#433) #442 pin enable_global_virtual_store = false so their seeded legacy-shape slots match the probe path.

End-to-end port of upstream's 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 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).

Copilot AI review requested due to automatic review settings May 13, 2026 12:18
@coderabbitai

coderabbitai Bot commented May 13, 2026

Copy link
Copy Markdown

Note

Reviews paused

It 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 reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

This PR implements Global Virtual Store (GVS) support for frozen-lockfile installs by abstracting directory selection into VirtualStoreLayout, extending Config with GVS derivation, and threading the layout through all install stages to resolve package locations and symlink targets correctly.

Changes

Global Virtual Store Install Layout Abstraction

Layer / File(s) Summary
Config global_virtual_store_dir derivation
crates/config/src/lib.rs
apply_global_virtual_store_derivation now accepts explicit flags for both virtualStoreDir and globalVirtualStoreDir, honors yaml overrides, derives global_virtual_store_dir based on GVS enablement and whether virtual_store_dir was explicitly pinned, and is invoked after yaml layering in Config::current to ensure derivation uses final resolved values. Tests validate default GVS-on, GVS-off, user-pinned virtualStoreDir mirroring, and explicit yaml globalVirtualStoreDir overrides.
VirtualStoreLayout API and tests
crates/package-manager/src/virtual_store_layout.rs
Introduce VirtualStoreLayout::legacy(root) constructor to force legacy flat layout, change new() engine parameter to Option<&str>, select package_store_dir from global_virtual_store_dir when GVS enabled else virtual_store_dir, and update test helpers to wire both directory fields.
Dev dependency: dunce
crates/package-manager/Cargo.toml
Add dunce to dev-dependencies.
BuildModules layout field and virtual_store_dir_for_key
crates/package-manager/src/build_modules.rs, crates/package-manager/src/build_modules/tests.rs
Replace virtual_store_dir field with layout, thread layout through run() and build-worker paths, rewrite virtual_store_dir_for_key helper to route through layout.slot_dir(key).join("node_modules").join(name), update all test initializers.
create_symlink_layout layout-based resolution
crates/package-manager/src/create_symlink_layout.rs
Change signature to accept layout instead of virtual_root, compute symlink targets via layout.slot_dir(&target)/node_modules/<name>.
CreateVirtualDirBySnapshot layout-driven wiring
crates/package-manager/src/create_virtual_dir_by_snapshot.rs, crates/package-manager/src/create_virtual_dir_by_snapshot/tests.rs
Replace virtual_store_dir with layout field, compute per-package virtual_node_modules_dir via layout.slot_dir(package_key).join("node_modules"), thread layout into create_symlink_layout call, rename and update test fixture.
CreateVirtualStore layout field and warm survivors probe
crates/package-manager/src/create_virtual_store.rs
Add layout field, route warm survivors slot-existence probe through layout.slot_dir(...), thread layout into downstream batching wiring.
SymlinkDirectDependencies layout-based targets
crates/package-manager/src/symlink_direct_dependencies.rs, crates/package-manager/src/symlink_direct_dependencies/tests.rs
Add layout field, resolve direct-dependency targets via layout.slot_dir(dep_key)/node_modules/<name>, broaden importer_root_dir to pub(crate), update helper signature and all test initializers.
LinkVirtualStoreBins layout-driven bin linking
crates/package-manager/src/link_bins.rs, crates/package-manager/src/link_bins/tests.rs
Replace virtual_store_dir with layout, route lockfile and fallback dispatchers through layout, compute per-slot node_modules root via layout.slot_dir(slot_key), update all test initializers.
InstallPackageBySnapshot add layout field
crates/package-manager/src/install_package_by_snapshot.rs
Add public layout field, destructure and pass it into CreateVirtualDirBySnapshot instead of config.virtual_store_dir.
install_without_lockfile legacy layout
crates/package-manager/src/install_without_lockfile.rs
Construct VirtualStoreLayout::legacy(config.virtual_store_dir.clone()) and wire it into LinkVirtualStoreBins for non-lockfile installs.
install_frozen_lockfile single layout wiring
crates/package-manager/src/install_frozen_lockfile.rs
Compute engine_name before CreateVirtualStore, construct a single VirtualStoreLayout instance, pass &layout into CreateVirtualStore, SymlinkDirectDependencies, LinkVirtualStoreBins, and BuildModules so all stages share the precomputed mapping.
Project registry registration on frozen-lockfile
crates/package-manager/src/install.rs
After frozen-lockfile install completes, when enable_global_virtual_store is true, iterate importers and call pacquet_store_dir::register_project for each, logging errors without aborting.
Install tests: warm-reinstall GVS toggles and registry
crates/package-manager/src/install/tests.rs
Disable GVS in three warm-reinstall tests to maintain legacy layout assumptions. Add frozen-lockfile integration tests: GVS-on registers single project symlink with correct target, GVS-off succeeds without projects dir, workspace GVS-on creates per-importer registry entries.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related issues

  • #432: Global Virtual Store support for frozen-lockfile—this PR implements the Stage 1 plan for Config derivation, VirtualStoreLayout abstraction, install pipeline wiring, and project registry registration.

Possibly related PRs

  • pnpm/pacquet#444: Establishes the GVS config foundation that this PR extends by wiring the VirtualStoreLayout through the entire install pipeline and adding project registry support.
  • pnpm/pacquet#333: Both refactor LinkVirtualStoreBins to use VirtualStoreLayout instead of legacy virtual_store_dir field wiring.
  • pnpm/pacquet#439: Both modify the same CreateVirtualStore and BuildModules integration points by adding new struct inputs to support install-stage decision-making.

Suggested reviewers

  • anthonyshew

Poem

🐰 The layout now knows which store to choose—
Global or local, the installer can't lose.
Each snapshot finds its slot with grace,
While projects register their dwelling place!
One layout maps them all, a rabbit's delight,
The virtual store is finally right!

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately summarizes the main change: activating the global-virtual-store install path by threading VirtualStoreLayout through the frozen-lockfile pipeline.
Linked Issues check ✅ Passed The PR successfully implements the Stage 1 objectives from #432: Config changes for enable_global_virtual_store and global_virtual_store_dir derivation [#432-A], path split via VirtualStoreLayout threading [#432-B], project registry writes via register_project calls [#432-C], and comprehensive test coverage for frozen-lockfile under GVS [#432-D].
Out of Scope Changes check ✅ Passed All changes are within scope of #432's Stage 1 objectives. The PR threads VirtualStoreLayout through the install pipeline, adds GVS config derivation, registers projects, updates tests, and adds a dev-dependency (dunce). No unrelated refactoring or scope-creep detected.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Description check ✅ Passed The pull request description is comprehensive and well-structured, covering the summary, specific pipeline changes, field-split deviation from upstream, benchmarks, tests, and test plan. All required sections are complete.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/432-activate

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
crates/package-manager/src/import_indexed_dir/tests.rs (1)

533-534: ⚡ Quick win

Add failure context to these assertions.

These bare assert! checks should include diagnostic context (message and/or eprintln!) 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

📥 Commits

Reviewing files that changed from the base of the PR and between 1fa70a4 and 01f1119.

📒 Files selected for processing (23)
  • crates/config/src/lib.rs
  • crates/package-manager/src/build_modules.rs
  • crates/package-manager/src/build_modules/tests.rs
  • crates/package-manager/src/create_cas_files.rs
  • crates/package-manager/src/create_symlink_layout.rs
  • crates/package-manager/src/create_virtual_dir_by_snapshot.rs
  • crates/package-manager/src/create_virtual_dir_by_snapshot/tests.rs
  • crates/package-manager/src/create_virtual_store.rs
  • crates/package-manager/src/import_indexed_dir.rs
  • crates/package-manager/src/import_indexed_dir/tests.rs
  • crates/package-manager/src/install.rs
  • crates/package-manager/src/install/tests.rs
  • crates/package-manager/src/install_frozen_lockfile.rs
  • crates/package-manager/src/install_package_by_snapshot.rs
  • crates/package-manager/src/install_package_from_registry.rs
  • crates/package-manager/src/install_without_lockfile.rs
  • crates/package-manager/src/lib.rs
  • crates/package-manager/src/link_bins.rs
  • crates/package-manager/src/link_bins/tests.rs
  • crates/package-manager/src/link_file.rs
  • crates/package-manager/src/symlink_direct_dependencies.rs
  • crates/package-manager/src/symlink_direct_dependencies/tests.rs
  • crates/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 and pipe-trait chains; do not break them into intermediate let bindings 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 (&Path over &PathBuf, &str over &String) when it does not force extra copies.
Prefer Arc::clone(&x) and Rc::clone(&x) over x.clone() for reference-counted types to make the cost visible at the call site.
Do not use star imports inside module bodies. Write use super::{Foo, bar} instead of use 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::*; in lib.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 plain String or &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 via TryFrom<String> and/or FromStr; 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 infallible From<String> and From<&str> constructor in the Rust wrapper.
When upstream TypeScript occasionally constructs a branded type without validation (via bare as assertion), add a from_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.rs
  • crates/package-manager/src/create_virtual_dir_by_snapshot/tests.rs
  • crates/package-manager/src/install.rs
  • crates/package-manager/src/create_symlink_layout.rs
  • crates/package-manager/src/symlink_direct_dependencies.rs
  • crates/package-manager/src/install_package_by_snapshot.rs
  • crates/package-manager/src/install_without_lockfile.rs
  • crates/package-manager/src/symlink_direct_dependencies/tests.rs
  • crates/package-manager/src/import_indexed_dir.rs
  • crates/package-manager/src/lib.rs
  • crates/package-manager/src/link_bins.rs
  • crates/package-manager/src/create_virtual_store.rs
  • crates/package-manager/src/create_virtual_dir_by_snapshot.rs
  • crates/package-manager/src/build_modules/tests.rs
  • crates/package-manager/src/install_frozen_lockfile.rs
  • crates/package-manager/src/install/tests.rs
  • crates/package-manager/src/import_indexed_dir/tests.rs
  • crates/package-manager/src/link_bins/tests.rs
  • crates/package-manager/src/install_package_from_registry.rs
  • crates/package-manager/src/virtual_store_layout.rs
  • crates/config/src/lib.rs
  • crates/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.rs
  • crates/package-manager/src/create_virtual_dir_by_snapshot/tests.rs
  • crates/package-manager/src/install.rs
  • crates/package-manager/src/create_symlink_layout.rs
  • crates/package-manager/src/symlink_direct_dependencies.rs
  • crates/package-manager/src/install_package_by_snapshot.rs
  • crates/package-manager/src/install_without_lockfile.rs
  • crates/package-manager/src/symlink_direct_dependencies/tests.rs
  • crates/package-manager/src/import_indexed_dir.rs
  • crates/package-manager/src/lib.rs
  • crates/package-manager/src/link_bins.rs
  • crates/package-manager/src/create_virtual_store.rs
  • crates/package-manager/src/create_virtual_dir_by_snapshot.rs
  • crates/package-manager/src/build_modules/tests.rs
  • crates/package-manager/src/install_frozen_lockfile.rs
  • crates/package-manager/src/install/tests.rs
  • crates/package-manager/src/import_indexed_dir/tests.rs
  • crates/package-manager/src/link_bins/tests.rs
  • crates/package-manager/src/install_package_from_registry.rs
  • crates/package-manager/src/virtual_store_layout.rs
  • crates/config/src/lib.rs
  • crates/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.rs
  • crates/package-manager/src/symlink_direct_dependencies/tests.rs
  • crates/package-manager/src/build_modules/tests.rs
  • crates/package-manager/src/install/tests.rs
  • crates/package-manager/src/import_indexed_dir/tests.rs
  • crates/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!

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 VirtualStoreLayout through frozen-lockfile install steps (virtual dir creation, direct-dep symlinks, bin linking, builds) so slot paths come from layout.slot_dir(...) instead of virtual_store_dir.join(...).
  • Add import_indexed_dir (stage-and-swap support) and migrate call sites off the removed create_cas_files.
  • Apply apply_global_virtual_store_derivation in Config::current and 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 virtualStoreDir is not pinned, it is re-pointed at <store_dir>/links. In pacquet's split-field approach, virtual_store_dir is intentionally not re-pointed; instead global_virtual_store_dir is derived to <store_dir>/links and the install pipeline chooses which one to consume. Updating this comment will prevent future readers from assuming virtual_store_dir mutates 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.

Comment thread crates/package-manager/src/install.rs Outdated
Comment thread crates/package-manager/src/install_frozen_lockfile.rs Outdated
Comment thread crates/package-manager/src/create_virtual_dir_by_snapshot/tests.rs
Comment thread crates/package-manager/src/virtual_store_layout.rs
Comment thread crates/package-manager/src/install/tests.rs
Comment thread crates/config/src/lib.rs Outdated
@zkochan zkochan force-pushed the feat/432-activate branch from 01f1119 to d552942 Compare May 13, 2026 12:29
@github-actions

github-actions Bot commented May 13, 2026

Copy link
Copy Markdown

Micro-Benchmark Results

Linux

group                          main                                   pr
-----                          ----                                   --
tarball/download_dependency    1.01     16.4±0.68ms   265.0 KB/sec    1.00     16.2±0.33ms   267.7 KB/sec

zkochan added a commit that referenced this pull request May 13, 2026
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.
Copilot AI review requested due to automatic review settings May 13, 2026 12:38

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 18 out of 19 changed files in this pull request and generated 2 comments.

Comment thread crates/package-manager/src/build_modules.rs Outdated
Comment thread crates/package-manager/src/create_virtual_store.rs
@codecov

codecov Bot commented May 13, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 92.30769% with 10 lines in your changes missing coverage. Please review.
✅ Project coverage is 87.26%. Comparing base (8b35794) to head (f84f17a).

Files with missing lines Patch % Lines
...kage-manager/src/create_virtual_dir_by_snapshot.rs 40.00% 3 Missing ⚠️
...rates/package-manager/src/create_symlink_layout.rs 0.00% 2 Missing ⚠️
crates/package-manager/src/install.rs 80.00% 2 Missing ⚠️
crates/package-manager/src/create_virtual_store.rs 80.00% 1 Missing ⚠️
...package-manager/src/install_package_by_snapshot.rs 50.00% 1 Missing ⚠️
crates/package-manager/src/link_bins.rs 80.00% 1 Missing ⚠️
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.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

zkochan added a commit that referenced this pull request May 13, 2026
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.
@github-actions

github-actions Bot commented May 13, 2026

Copy link
Copy Markdown

Integrated-Benchmark Report (Linux)

Scenario: Frozen Lockfile

Command Mean [s] Min [s] Max [s] Relative
pacquet@HEAD 2.503 ± 0.110 2.383 2.684 1.02 ± 0.05
pacquet@main 2.455 ± 0.056 2.382 2.573 1.00
pnpm 5.903 ± 0.080 5.781 6.036 2.40 ± 0.06
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)

Command Mean [ms] Min [ms] Max [ms] Relative
pacquet@HEAD 685.7 ± 58.1 644.8 845.0 1.00
pacquet@main 698.7 ± 46.4 659.0 822.7 1.02 ± 0.11
pnpm 2508.1 ± 87.1 2385.4 2643.1 3.66 ± 0.33
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
      ]
    }
  ]
}

zkochan added a commit that referenced this pull request May 13, 2026
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.
Copilot AI review requested due to automatic review settings May 13, 2026 13:29
zkochan added a commit that referenced this pull request May 13, 2026
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.
@zkochan zkochan force-pushed the feat/432-activate branch from 51c5ad3 to 6898423 Compare May 13, 2026 13:29

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot encountered an error and was unable to review this pull request. You can try again by re-requesting a review.

zkochan added a commit that referenced this pull request May 13, 2026
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.
zkochan added a commit that referenced this pull request May 13, 2026
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.
@zkochan zkochan force-pushed the feat/432-activate branch from 6898423 to 01e8af6 Compare May 13, 2026 13:47
zkochan added 5 commits May 13, 2026 16:00
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.
@zkochan zkochan force-pushed the feat/432-activate branch from 01e8af6 to e9a1887 Compare May 13, 2026 14:12

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
crates/package-manager/src/install/tests.rs (1)

1279-1283: ⚡ Quick win

Use blob/main upstream 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

📥 Commits

Reviewing files that changed from the base of the PR and between 01e8af6 and e9a1887.

⛔ Files ignored due to path filters (1)
  • Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (18)
  • crates/config/src/lib.rs
  • crates/package-manager/Cargo.toml
  • crates/package-manager/src/build_modules.rs
  • crates/package-manager/src/build_modules/tests.rs
  • crates/package-manager/src/create_symlink_layout.rs
  • crates/package-manager/src/create_virtual_dir_by_snapshot.rs
  • crates/package-manager/src/create_virtual_dir_by_snapshot/tests.rs
  • crates/package-manager/src/create_virtual_store.rs
  • crates/package-manager/src/install.rs
  • crates/package-manager/src/install/tests.rs
  • crates/package-manager/src/install_frozen_lockfile.rs
  • crates/package-manager/src/install_package_by_snapshot.rs
  • crates/package-manager/src/install_without_lockfile.rs
  • crates/package-manager/src/link_bins.rs
  • crates/package-manager/src/link_bins/tests.rs
  • crates/package-manager/src/symlink_direct_dependencies.rs
  • crates/package-manager/src/symlink_direct_dependencies/tests.rs
  • crates/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 and pipe-trait chains; do not break them into intermediate let bindings 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 (&Path over &PathBuf, &str over &String) when it does not force extra copies.
Prefer Arc::clone(&x) and Rc::clone(&x) over x.clone() for reference-counted types to make the cost visible at the call site.
Do not use star imports inside module bodies. Write use super::{Foo, bar} instead of use 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::*; in lib.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 plain String or &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 via TryFrom<String> and/or FromStr; 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 infallible From<String> and From<&str> constructor in the Rust wrapper.
When upstream TypeScript occasionally constructs a branded type without validation (via bare as assertion), add a from_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.rs
  • crates/package-manager/src/symlink_direct_dependencies/tests.rs
  • crates/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.rs
  • crates/package-manager/src/symlink_direct_dependencies/tests.rs
  • crates/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.rs
  • crates/package-manager/src/symlink_direct_dependencies/tests.rs
  • crates/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.
Copilot AI review requested due to automatic review settings May 13, 2026 14:24

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot encountered an error and was unable to review this pull request. You can try again by re-requesting a review.

@zkochan zkochan merged commit 3bdaf2d into main May 13, 2026
19 of 20 checks passed
@zkochan zkochan deleted the feat/432-activate branch May 13, 2026 14:56
zkochan added a commit that referenced this pull request May 13, 2026
… 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.
zkochan added a commit that referenced this pull request May 13, 2026
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.
zkochan added a commit that referenced this pull request May 13, 2026
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.
KSXGitHub pushed a commit that referenced this pull request May 13, 2026
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.
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add global virtual store support to pacquet install --frozen-lockfile

2 participants