Skip to content

feat(pacquet): honor updateConfig.ignoreDependencies in update#12104

Merged
zkochan merged 8 commits into
mainfrom
pq-update-ignore-deps
Jun 1, 2026
Merged

feat(pacquet): honor updateConfig.ignoreDependencies in update#12104
zkochan merged 8 commits into
mainfrom
pq-update-ignore-deps

Conversation

@zkochan

@zkochan zkochan commented Jun 1, 2026

Copy link
Copy Markdown
Member

Follow-up to #12102, addressing one item from #12101.

When pacquet update runs with no package selectors, the names listed in updateConfig.ignoreDependencies (from pnpm-workspace.yaml) are excluded from the update so they keep their pins. Explicit CLI selectors take precedence over the ignore list — matching pnpm, which only applies ignoreDependencies when params.length === 0. The exclusion is applied over the included direct deps, so --prod/--dev/--no-optional group narrowing still scopes the update.

Ports pnpm's installDeps gate + makeIgnorePatterns.

Also: a resolver fix this surfaced

Writing the compatible-update ignore test revealed that the fresh-resolve path never fed the preferred-versions seed to the version pickerget_preferred_versions_from_lockfile_and_manifests was built but only used for peer hoisting, never written into ResolveOptions.preferred_versions. So a fresh re-resolve bumped every dep to its highest in-range version instead of keeping the lockfile pins that still satisfy their range, diverging from pnpm's update: false behavior (and making update <selector> / ignoreDependencies unable to keep non-targeted pins). Seeding base_opts.preferred_versions fixes it; fresh installs with no lockfile are unaffected, and all 2693 workspace tests still pass.

Tests

Ports four cases from installing/commands/test/update/update.ts (adapted to pacquet's static fixtures, where latest = max(version)):

  • update --latest ignores the listed dep and bumps the rest.
  • update --latest with every dependency ignored is a no-op.
  • A compatible (non---latest) update keeps the ignored dep's resolved version while the rest bump.
  • --prod scoping: an ignored prod dep keeps its range and a devDependency is left untouched.

All update e2e tests + full workspace suite (2693) + clippy -D warnings / dylint / fmt --check / typos pass.

No changeset (pacquet-only change).


Written by an agent (Claude Code, claude-opus-4-8).

Summary by CodeRabbit

  • New Features

    • Update command supports workspace-configured dependency ignores so specified packages skip manifest range rewrites and lockfile pin drops.
  • Bug Fixes

    • update (including --latest and scoped --prod) now correctly honors ignore settings; when all direct deps are ignored, update --latest is a no-op and indirect deps behave appropriately.
  • Tests

    • Expanded CLI tests covering ignore behavior across latest/compatible updates and production scoping; tests now emit virtual-store diagnostics for clearer failure context.
  • Refactor

    • Resolver/installer now share a preferred-versions seed to reduce copying and improve resolution consistency.

When `pacquet update` is run with no package selectors, the names in
`updateConfig.ignoreDependencies` (from pnpm-workspace.yaml) are turned
into negation selectors (`!name`) so the update covers everything except
them. Explicit CLI selectors override the ignore list, matching pnpm.

Ports pnpm's `installDeps` gate + `makeIgnorePatterns`, and the
"ignore packages in updateConfig.ignoreDependencies" and "do not update
anything if all dependencies are ignored" tests from
`installing/commands/test/update/update.ts`.

Follow-up to #12101.

Written by an agent (Claude Code, claude-opus-4-8).
@coderabbitai

coderabbitai Bot commented Jun 1, 2026

Copy link
Copy Markdown

Review Change Stack

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

Apply updateConfig.ignoreDependencies when no package selectors are provided, omit ignored names from update seeds and manifest rewrites, thread a shared Arc into per-importer ResolveOptions, and add test helper plus CLI tests for ignore-dependency scenarios.

Changes

Ignore Dependencies Feature

Layer / File(s) Summary
Selector input derivation and update seed policy
pacquet/crates/package-manager/src/update.rs
When selectors.is_empty(), Update::run reads config.update_config.ignore_dependencies, compiles an ignore matcher (if patterns present), excludes ignored direct deps from drop_names and (under --latest) from rewrites, and uses UpdateSeedPolicy::DropAll only when no ignore patterns; otherwise DropOnly with non-ignored names (lockfile expansion excludes ignored names and respects --latest empty-drop guard).
Fresh-lockfile resolver preferred versions
pacquet/crates/package-manager/src/install_with_fresh_lockfile.rs, pacquet/crates/resolving-resolver-base/src/resolve.rs
Create a shared Arc<PreferredVersions> (preferred_versions_seed) once per install and set ResolveOptions.preferred_versions = Arc::clone(&preferred_versions_seed) for each importer; change ResolveOptions.preferred_versions field type to Arc<PreferredVersions>.
Test helper and CLI tests
pacquet/crates/cli/tests/update.rs
Adds PARENT constant, set_ignore_dependencies helper to append updateConfig.ignoreDependencies into pnpm-workspace.yaml, list_virtual_store diagnostic helper, and tests: update_latest_honors_ignore_dependencies, update_compatible_honors_ignore_dependencies, update_prod_scopes_and_honors_ignore, update_latest_all_direct_ignored_does_not_touch_indirect, update_compatible_all_direct_ignored_still_updates_indirect, and update_latest_all_ignored_is_noop.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related issues

Possibly related PRs

  • pnpm/pnpm#12102: Modifies pacquet/crates/package-manager/src/update.rs update logic in the same area and is closely related to these changes.

Poem

🐇 I nibble YAML, mark names to spare,
I tuck ignoreDependencies safely there.
Some specs leap forward, some quietly stay,
The tests tend the patch and guard the way.
Hooray for small seeds shared across the fray.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the main change: adding support for updateConfig.ignoreDependencies in the update command, which is the core feature across all modified files.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch pq-update-ignore-deps

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@github-actions

github-actions Bot commented Jun 1, 2026

Copy link
Copy Markdown
Contributor

Micro-Benchmark Results

Linux

group                          main                                   pr
-----                          ----                                   --
tarball/download_dependency    1.00      7.7±0.28ms   565.4 KB/sec    1.00      7.7±0.41ms   564.0 KB/sec

@codecov-commenter

codecov-commenter commented Jun 1, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 96.87500% with 1 line in your changes missing coverage. Please review.
✅ Project coverage is 87.08%. Comparing base (0a4d665) to head (b5b5ec3).
⚠️ Report is 6 commits behind head on main.

Files with missing lines Patch % Lines
...package-manager/src/install_with_fresh_lockfile.rs 88.88% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main   #12104      +/-   ##
==========================================
+ Coverage   87.04%   87.08%   +0.04%     
==========================================
  Files         252      252              
  Lines       28146    28236      +90     
==========================================
+ Hits        24499    24590      +91     
+ Misses       3647     3646       -1     

☔ 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 zkochan marked this pull request as ready for review June 1, 2026 13:19
Copilot AI review requested due to automatic review settings June 1, 2026 13:19
@qodo-free-for-open-source-projects

qodo-free-for-open-source-projects Bot commented Jun 1, 2026

Copy link
Copy Markdown

Review Summary by Qodo

(Agentic_describe updated until commit 0970bc9)

Honor updateConfig.ignoreDependencies in update and fix preferred-versions seeding

✨ Enhancement 🐞 Bug fix 🧪 Tests

Grey Divider

Walkthroughs

Description
• Honor updateConfig.ignoreDependencies in pacquet update command
  - Excludes listed packages from no-selector updates to keep lockfile pins
  - Explicit CLI selectors override ignore list, matching pnpm behavior
  - Respects group narrowing (--prod/--dev/--no-optional)
• Fix preferred-versions seed not fed to version picker in fresh-resolve path
  - Prevents unintended bumps to highest in-range when lockfile pins still satisfy ranges
  - Ensures non-targeted pins remain unchanged during selective updates
• Add comprehensive test coverage for ignore dependencies across update scenarios
  - Tests --latest, compatible updates, --prod scoping, and edge cases
Diagram
flowchart LR
  A["Update Command"] --> B["Parse Selectors"]
  B --> C["No Selectors?"]
  C -->|Yes| D["Load ignoreDependencies"]
  D --> E["Filter Direct Deps"]
  E --> F["Apply Update Policy"]
  C -->|No| G["Use CLI Selectors"]
  G --> F
  F --> H["Seed Preferred Versions"]
  H --> I["Resolve & Update"]
  I --> J["Updated Lockfile"]

Loading

Grey Divider

File Changes

1. pacquet/crates/cli/tests/update.rs 🧪 Tests +162/-0

Add comprehensive ignore dependencies test coverage

• Added PARENT constant for testing indirect dependency behavior
• Implemented set_ignore_dependencies() helper to append updateConfig.ignoreDependencies to
 pnpm-workspace.yaml
• Added six new test cases covering ignore dependencies functionality:
 - update_latest_honors_ignore_dependencies: verifies ignored deps keep ranges while others update
 - update_compatible_honors_ignore_dependencies: confirms lockfile pins preserved for ignored deps
 - update_prod_scopes_and_honors_ignore: validates --prod scoping with ignore list
 - update_latest_all_direct_ignored_does_not_touch_indirect: ensures no-op when all direct deps
 ignored
 - update_compatible_all_direct_ignored_still_updates_indirect: confirms indirect deps still update
 in compatible mode
 - update_latest_all_ignored_is_noop: verifies complete no-op when all dependencies ignored

pacquet/crates/cli/tests/update.rs


2. pacquet/crates/package-manager/src/install_with_fresh_lockfile.rs 🐞 Bug fix +6/-0

Seed preferred-versions to version picker via Arc

• Wrapped all_preferred_versions in Arc to enable efficient sharing across per-importer
 ResolveOptions
• Added comment explaining the Arc wrapping rationale for refcount-based sharing
• Passed preferred_versions_seed Arc clone to ResolveOptions.preferred_versions field
• Ensures version picker receives preferred-versions seed for all resolves

pacquet/crates/package-manager/src/install_with_fresh_lockfile.rs


3. pacquet/crates/package-manager/src/update.rs ✨ Enhancement +53/-11

Implement ignoreDependencies filtering in update logic

• Renamed loop variable from p to input for clarity
• Implemented ignoreDependencies filtering logic that applies only when no selectors provided
• Created ignore_matcher from updateConfig.ignoreDependencies patterns
• Modified update loop to skip ignored dependencies and only add non-ignored deps to drop_names
• Added conditional logic to handle --latest no-op case when all direct deps are ignored
• Expanded UpdateSeedPolicy::DropOnly to include all non-ignored lockfile snapshots for
 whole-graph updates
• Added detailed comments explaining ignore list precedence and pnpm compatibility

pacquet/crates/package-manager/src/update.rs


View more (1)
4. pacquet/crates/resolving-resolver-base/src/resolve.rs ✨ Enhancement +7/-1

Wrap preferred_versions in Arc for efficient sharing

• Changed preferred_versions field type from PreferredVersions to Arc
• Added detailed documentation explaining Arc usage for efficient cloning across depth tiers and
 importers
• Clarifies that the seed biases version picker toward lockfile pins that satisfy ranges

pacquet/crates/resolving-resolver-base/src/resolve.rs


Grey Divider

Qodo Logo

@github-actions

github-actions Bot commented Jun 1, 2026

Copy link
Copy Markdown
Contributor

Integrated-Benchmark Report (Linux)

Scenario: Isolated linker: fresh restore, cold cache + cold store

Command Mean [s] Min [s] Max [s] Relative
pacquet@HEAD 2.208 ± 0.141 2.023 2.517 1.01 ± 0.07
pacquet@main 2.177 ± 0.069 2.091 2.255 1.00
BENCHMARK_REPORT.json
{
  "results": [
    {
      "command": "pacquet@HEAD",
      "mean": 2.20793809598,
      "stddev": 0.14056544490355977,
      "median": 2.18168624058,
      "user": 2.61112624,
      "system": 3.40196644,
      "min": 2.02341590158,
      "max": 2.51740541058,
      "times": [
        2.25096181858,
        2.33617247158,
        2.17069235458,
        2.08755155958,
        2.02341590158,
        2.16831078858,
        2.51740541058,
        2.19268012658,
        2.2313995755800002,
        2.10079095258
      ]
    },
    {
      "command": "pacquet@main",
      "mean": 2.17675386238,
      "stddev": 0.06869159761787877,
      "median": 2.18391546258,
      "user": 2.6422152399999996,
      "system": 3.40386184,
      "min": 2.09093817258,
      "max": 2.25490408958,
      "times": [
        2.10606190558,
        2.11584820858,
        2.24736213858,
        2.11083909358,
        2.22876025058,
        2.25490408958,
        2.14211859158,
        2.24499383958,
        2.09093817258,
        2.22571233358
      ]
    }
  ]
}

Scenario: Isolated linker: fresh restore, hot cache + hot store

Command Mean [ms] Min [ms] Max [ms] Relative
pacquet@HEAD 696.3 ± 27.3 670.9 756.7 1.02 ± 0.09
pacquet@main 683.8 ± 56.7 637.9 792.7 1.00
BENCHMARK_REPORT.json
{
  "results": [
    {
      "command": "pacquet@HEAD",
      "mean": 0.6962684671,
      "stddev": 0.02727742336233797,
      "median": 0.6899286740999999,
      "user": 0.35883721999999996,
      "system": 1.3662147599999999,
      "min": 0.6708838906,
      "max": 0.7567368526,
      "times": [
        0.7567368526,
        0.6944895656,
        0.7294802316,
        0.6747878646000001,
        0.6754930126,
        0.6708838906,
        0.7003426506,
        0.6949371536,
        0.6853677826,
        0.6801656666
      ]
    },
    {
      "command": "pacquet@main",
      "mean": 0.6837659945000001,
      "stddev": 0.05673548683449874,
      "median": 0.6570359841,
      "user": 0.35777222,
      "system": 1.33593116,
      "min": 0.6379142926,
      "max": 0.7927341676,
      "times": [
        0.7802201586,
        0.6614127766,
        0.6479694716,
        0.6379142926,
        0.6456833066000001,
        0.6964184816,
        0.6740528186,
        0.6526591916,
        0.6485952796,
        0.7927341676
      ]
    }
  ]
}

Scenario: Isolated linker: fresh install, cold cache + cold store

Command Mean [s] Min [s] Max [s] Relative
pacquet@HEAD 2.455 ± 0.031 2.408 2.517 1.05 ± 0.02
pacquet@main 2.349 ± 0.043 2.286 2.419 1.00
BENCHMARK_REPORT.json
{
  "results": [
    {
      "command": "pacquet@HEAD",
      "mean": 2.4553501924399996,
      "stddev": 0.031204870697229065,
      "median": 2.45900995294,
      "user": 3.81604916,
      "system": 3.18116192,
      "min": 2.40805769394,
      "max": 2.51693597294,
      "times": [
        2.45493101894,
        2.4208308069399997,
        2.47804887494,
        2.43351578894,
        2.46315722094,
        2.4719724139399997,
        2.51693597294,
        2.4429632459399997,
        2.46308888694,
        2.40805769394
      ]
    },
    {
      "command": "pacquet@main",
      "mean": 2.34929880074,
      "stddev": 0.04327887544912064,
      "median": 2.33883450594,
      "user": 3.7691124599999997,
      "system": 3.1710580199999994,
      "min": 2.28594337994,
      "max": 2.4190161729399997,
      "times": [
        2.4059981379399997,
        2.38971025494,
        2.4190161729399997,
        2.32517644394,
        2.3558843179399998,
        2.30738175694,
        2.33945440694,
        2.28594337994,
        2.33821460494,
        2.32620853094
      ]
    }
  ]
}

Scenario: Isolated linker: fresh install, hot cache + hot store

Command Mean [s] Min [s] Max [s] Relative
pacquet@HEAD 1.553 ± 0.036 1.509 1.618 1.00
pacquet@main 1.584 ± 0.048 1.519 1.693 1.02 ± 0.04
BENCHMARK_REPORT.json
{
  "results": [
    {
      "command": "pacquet@HEAD",
      "mean": 1.55327809278,
      "stddev": 0.03648556550745488,
      "median": 1.55128099028,
      "user": 1.7247077799999997,
      "system": 1.8885631399999998,
      "min": 1.5092218327799998,
      "max": 1.61840721978,
      "times": [
        1.55063290478,
        1.5131903007799998,
        1.5201942607799999,
        1.54648356978,
        1.55428457878,
        1.5092218327799998,
        1.61840721978,
        1.55192907578,
        1.6079442717799999,
        1.56049291278
      ]
    },
    {
      "command": "pacquet@main",
      "mean": 1.58399474798,
      "stddev": 0.04830852604083104,
      "median": 1.58620449328,
      "user": 1.7393181799999997,
      "system": 1.90901014,
      "min": 1.51874161778,
      "max": 1.69281808878,
      "times": [
        1.5390088467799998,
        1.51874161778,
        1.69281808878,
        1.5992032647799999,
        1.54309427478,
        1.58228778978,
        1.56905894378,
        1.60143794078,
        1.59012119678,
        1.60417551578
      ]
    }
  ]
}

@github-actions

github-actions Bot commented Jun 1, 2026

Copy link
Copy Markdown
Contributor

🐰 Bencher Report

Branchpr/12104
Testbedpacquet
Click to view all benchmark results
BenchmarkLatencyBenchmark Result
milliseconds (ms)
(Result Δ%)
Upper Boundary
milliseconds (ms)
(Limit %)
isolated-linker.fresh-install.cold-cache.cold-store📈 view plot
🚷 view threshold
2,455.35 ms
(+2.39%)Baseline: 2,397.95 ms
2,877.54 ms
(85.33%)
isolated-linker.fresh-install.hot-cache.hot-store📈 view plot
🚷 view threshold
1,553.28 ms
(+1.95%)Baseline: 1,523.62 ms
1,828.35 ms
(84.96%)
isolated-linker.fresh-restore.cold-cache.cold-store📈 view plot
🚷 view threshold
2,207.94 ms
(+3.81%)Baseline: 2,126.83 ms
2,552.20 ms
(86.51%)
isolated-linker.fresh-restore.hot-cache.hot-store📈 view plot
🚷 view threshold
696.27 ms
(+2.04%)Baseline: 682.35 ms
818.82 ms
(85.03%)
🐰 View full continuous benchmarking report in Bencher

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 updates pacquet’s update command to honor updateConfig.ignoreDependencies (from pnpm-workspace.yaml) when the user runs pacquet update with no explicit package selectors, mirroring pnpm’s “ignore list only applies when params are empty” behavior.

Changes:

  • In Update::run, synthesize negation selectors (!name) from config.update_config.ignore_dependencies when packages.is_empty().
  • Parse selectors and apply the --latest “LATEST_WITH_SPEC” guard against the synthesized selector inputs.
  • Add e2e tests covering update --latest with an ignore list (including the “all ignored = no-op” case).

Reviewed changes

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

File Description
pacquet/crates/package-manager/src/update.rs Applies updateConfig.ignoreDependencies by converting it to negation selector inputs when no CLI selectors are provided.
pacquet/crates/cli/tests/update.rs Adds a helper to append updateConfig.ignoreDependencies to pnpm-workspace.yaml and adds two new --latest regression tests.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread pacquet/crates/package-manager/src/update.rs Outdated
Comment thread pacquet/crates/cli/tests/update.rs
Comment thread pacquet/crates/cli/tests/update.rs
zkochan added 2 commits June 1, 2026 15:56
… picker

The fresh-resolve path built `get_preferred_versions_from_lockfile_and_manifests`
but only ever used it for peer hoisting — it was never written into
`ResolveOptions.preferred_versions`, so the npm picker ignored it and
always took the highest version in range. That meant a fresh re-resolve
(stale lockfile / `preferFrozenLockfile: false`) bumped *every* dependency
to its highest in-range version instead of keeping the lockfile pins for
the ones that still satisfy their range — diverging from pnpm's
`update: false` behavior, and making `pacquet update <selector>` /
`updateConfig.ignoreDependencies` unable to keep non-targeted pins.

Seed `base_opts.preferred_versions` from the same map so the picker biases
toward the locked versions, matching pnpm. Fresh installs with no lockfile
are unaffected (the manifest-only seed carries no concrete version pin);
all 2693 workspace tests still pass.

Written by an agent (Claude Code, claude-opus-4-8).
…ed groups

Apply `ignoreDependencies` as an exclusion filter over the included
direct deps inside the no-selector branch, instead of injecting global
`!name` negation selectors. The injection route took the name-matcher
path and scanned every lockfile snapshot name, which dropped pins for
transitive deps too and ignored `--prod`/`--dev`/`--no-optional` group
narrowing (Copilot review on #12104). Now group narrowing still scopes
the update while ignored names keep their pins.

Adds regression tests: compatible (non-`--latest`) update honoring the
ignore list, and `--prod` scoping with an ignored prod dep + an untouched
devDependency. Guards the test helper against duplicate `updateConfig:`
YAML keys.

Written by an agent (Claude Code, claude-opus-4-8).
Copilot AI review requested due to automatic review settings June 1, 2026 13:58

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 3 out of 3 changed files in this pull request and generated 4 comments.

Comment thread pacquet/crates/package-manager/src/update.rs Outdated
Comment thread pacquet/crates/package-manager/src/update.rs Outdated
Comment thread pacquet/crates/package-manager/src/install_with_fresh_lockfile.rs Outdated
Comment thread pacquet/crates/cli/tests/update.rs
zkochan added 2 commits June 1, 2026 16:13
`ResolveOptions.preferred_versions` was a `PreferredVersions` map cloned
into `direct_opts`/`subdep_opts` per depth tier by the tree walker and
per importer by the install layer. Now that it carries the lockfile +
manifest seed, those deep clones can be sizeable. Hold it behind `Arc`
and build the seed once, so each clone is a refcount bump. The two read
sites (`npm_resolver`, `named_registry_resolver`) reach it through
`Arc` deref unchanged. Addresses a review comment on #12104.

Written by an agent (Claude Code, claude-opus-4-8).
`create_matcher` was built unconditionally in the no-selector update
branch even when `ignoreDependencies` is empty. Wrap it in an `Option`
so the common (no ignore list) path skips the compile. Addresses a
review comment on #12104.

Written by an agent (Claude Code, claude-opus-4-8).

@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.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
pacquet/crates/package-manager/src/update.rs (1)

252-264: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Avoid re-expanding to the full lockfile when every included direct dep was ignored.

If ignoreDependencies filters out all included direct deps, drop_names is empty after the direct scan, but this branch still inserts every non-ignored snapshot name. That turns a “nothing selected” update into a transitive-only re-resolve, so an all-ignored pacquet update is no longer a no-op.

Suggested guard
-            } else if updates_all_groups {
+            } else if updates_all_groups && !drop_names.is_empty() {
                 // Whole-graph update minus the ignored names: drop every
                 // locked name that isn't ignored so the ignored ones keep
                 // their pins.
                 if let Some(snapshots) = lockfile.and_then(|lf| lf.snapshots.as_ref()) {
                     for key in snapshots.keys() {
@@
                 }
                 UpdateSeedPolicy::DropOnly(drop_names)
             } else {
Based on learnings, pacquet should “only match pnpm’s behavior exactly.”
🤖 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 `@pacquet/crates/package-manager/src/update.rs` around lines 252 - 264, The
current updates_all_groups branch repopulates drop_names from lockfile.snapshots
even when the direct-scan produced an empty drop_names (e.g., all included
direct deps were ignored), causing a full transitive re-resolve; change the
logic in the updates_all_groups branch so that you only iterate
lockfile.snapshots and insert snapshot keys into drop_names when drop_names is
already non-empty after the direct dependency scan (i.e., only expand to
non-ignored snapshots if there is at least one direct package selected to drop).
Update the block around updates_all_groups / lockfile.and_then(|lf|
lf.snapshots.as_ref()) to check drop_names.is_empty() and skip the snapshot loop
when empty before returning UpdateSeedPolicy::DropOnly(drop_names), preserving
the no-op behavior when all included direct deps are ignored.
🤖 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.

Outside diff comments:
In `@pacquet/crates/package-manager/src/update.rs`:
- Around line 252-264: The current updates_all_groups branch repopulates
drop_names from lockfile.snapshots even when the direct-scan produced an empty
drop_names (e.g., all included direct deps were ignored), causing a full
transitive re-resolve; change the logic in the updates_all_groups branch so that
you only iterate lockfile.snapshots and insert snapshot keys into drop_names
when drop_names is already non-empty after the direct dependency scan (i.e.,
only expand to non-ignored snapshots if there is at least one direct package
selected to drop). Update the block around updates_all_groups /
lockfile.and_then(|lf| lf.snapshots.as_ref()) to check drop_names.is_empty() and
skip the snapshot loop when empty before returning
UpdateSeedPolicy::DropOnly(drop_names), preserving the no-op behavior when all
included direct deps are ignored.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 75c7058b-2c2a-400a-9669-903d844f360c

📥 Commits

Reviewing files that changed from the base of the PR and between 52b8eb8 and 64a95f9.

📒 Files selected for processing (3)
  • pacquet/crates/package-manager/src/install_with_fresh_lockfile.rs
  • pacquet/crates/package-manager/src/update.rs
  • pacquet/crates/resolving-resolver-base/src/resolve.rs
🚧 Files skipped from review as they are similar to previous changes (1)
  • pacquet/crates/package-manager/src/install_with_fresh_lockfile.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). (10)
  • GitHub Check: Doc
  • GitHub Check: Lint and Test (windows-latest)
  • GitHub Check: Code Coverage
  • GitHub Check: Lint and Test (macos-latest)
  • GitHub Check: Lint and Test (ubuntu-latest)
  • GitHub Check: Dylint
  • GitHub Check: Analyze (javascript)
  • GitHub Check: Run benchmark on ubuntu-latest
  • GitHub Check: Run benchmark on ubuntu-latest
  • GitHub Check: Compile & Lint
🧰 Additional context used
📓 Path-based instructions (1)
pacquet/**/*.rs

📄 CodeRabbit inference engine (pacquet/AGENTS.md)

pacquet/**/*.rs: Log emissions are part of matching pnpm — when porting a function that fires pnpm:<channel> events through globalLogger, logger.debug(...), or streamParser.write(...), mirror the call site, payload, and ordering so @pnpm/cli.default-reporter parses pacquet's NDJSON the same way
Declare a newtype wrapper for branded string types instead of collapsing the brand into a plain String or &str in Rust
If upstream TypeScript always validates before construction of a branded string, validate in the Rust wrapper too via TryFrom<String> and/or FromStr and do not provide an infallible public constructor
If upstream TypeScript never validates a branded string, just brand for type-safety in Rust by exposing an infallible From<String> constructor
If upstream TypeScript occasionally constructs a branded string without validation, expose from_str_unchecked in Rust as an escape hatch alongside the validating constructor
Match upstream serde behavior for branded strings crossing JSON, YAML, or INI boundaries by using #[serde(try_from = "String")] for deserialization and #[serde(into = "String")] for serialization
Derive simple conversions for branded strings using #[derive(derive_more::From)] and #[derive(derive_more::Into)] instead of handwriting impl blocks; use manual impl only when conversion needs custom logic
Model TypeScript string literal unions (like 'auto' | 'always' | 'never') as Rust enums instead of newtype wrappers, since the set of valid values is closed
Treat TypeScript string template literal types (like `${string}@${string}`) the same as branded string types in Rust, using a newtype wrapper with validation
Follow the code style guide in CODE_STYLE_GUIDE.md — imports, modules, naming, ownership and borrowing, parameter type selection, trait bounds, pattern matching, pipe-trait, error handling, test layout, and cloning of Arc and Rc
Choose owned vs. borrowed parameters to minimize copies; widen to t...

Files:

  • pacquet/crates/resolving-resolver-base/src/resolve.rs
  • pacquet/crates/package-manager/src/update.rs
🧠 Learnings (3)
📚 Learning: 2026-05-20T19:40:55.051Z
Learnt from: zkochan
Repo: pnpm/pnpm PR: 11774
File: pacquet/crates/resolving-deps-resolver/src/resolve_peers.rs:0-0
Timestamp: 2026-05-20T19:40:55.051Z
Learning: In the pacquet Rust code, ensure the semver implementation uses the `node-semver` crate (not `nodejs-semver`). `node-semver`’s public API does not include a `satisfies_with_prerelease`-style method; prerelease-tolerant matching should be implemented inline by first calling `Range::satisfies`, and when it rejects a prerelease version, retry matching against a stripped `MAJOR.MINOR.PATCH` base of the prerelease version.

Applied to files:

  • pacquet/crates/resolving-resolver-base/src/resolve.rs
  • pacquet/crates/package-manager/src/update.rs
📚 Learning: 2026-05-22T00:08:44.646Z
Learnt from: zkochan
Repo: pnpm/pnpm PR: 11837
File: pacquet/crates/resolving-npm-resolver/src/pick_package.rs:33-51
Timestamp: 2026-05-22T00:08:44.646Z
Learning: In the pnpm/pnpm repo’s pacquet Rust crates, do not flag Unicode ellipsis characters (U+2026, `…`) in Rust doc comments (`///` / `/** */`) as a lint violation. The pacquet crate’s `dylint.toml` only enables `perfectionist::derive_ordering`, and the Dylint `unicode-ellipsis` rule is not enabled for this project—so `…` in doc comments is an intentional, repo-consistent style.

Applied to files:

  • pacquet/crates/resolving-resolver-base/src/resolve.rs
  • pacquet/crates/package-manager/src/update.rs
📚 Learning: 2026-05-20T23:07:58.444Z
Learnt from: zkochan
Repo: pnpm/pnpm PR: 11784
File: pacquet/crates/resolving-deps-resolver/src/hoist_peers.rs:120-133
Timestamp: 2026-05-20T23:07:58.444Z
Learning: When reviewing code in this pacquet Rust port, follow the upstream pnpm compatibility rule: only match pnpm’s behavior exactly. Do not propose review changes that intentionally deviate from pnpm’s documented/observed behavior, even if pnpm appears buggy. If you identify a real bug in pnpm behavior, the review should prioritize fixing it upstream in pnpm first, and avoid implementing a pnpm-behavior workaround here unless the same fix has already landed upstream.

Applied to files:

  • pacquet/crates/resolving-resolver-base/src/resolve.rs
  • pacquet/crates/package-manager/src/update.rs
🔇 Additional comments (2)
pacquet/crates/package-manager/src/update.rs (1)

230-235: LGTM!

pacquet/crates/resolving-resolver-base/src/resolve.rs (1)

197-203: LGTM!

…ignored

When a no-selector update's included direct deps are all excluded by
`updateConfig.ignoreDependencies`, `--latest` must not re-resolve the
non-ignored indirect deps — pnpm returns early there
(`if (opts.latest) return`). The whole-graph expansion now skips when
`latest && drop_names.is_empty()`.

A non-`--latest` update keeps expanding in that case: pnpm explicitly
"updates indirect dependencies only" when no direct dep matches, so the
indirect deps still re-resolve. Two regression tests pin both halves
(the second guards against narrowing the condition to an unconditional
`drop_names.is_empty()`).

Written by an agent (Claude Code, claude-opus-4-8).
Copilot AI review requested due to automatic review settings June 1, 2026 15:04
@zkochan zkochan marked this pull request as draft June 1, 2026 15:06
@zkochan zkochan marked this pull request as ready for review June 1, 2026 15:06

@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)
pacquet/crates/cli/tests/update.rs (1)

299-306: ⚡ Quick win

Add diagnostic logging before non-assert_eq! assertions in these tests.

These assert! checks currently fail without enough context to debug quickly in CI. Please add a short log (or dbg!) right before each non-assert_eq! assertion group.

Suggested adjustment
+    eprintln!("after update --latest, checking virtual store for indirect dep versions");
     assert!(virtual_store_has(&workspace, "`@pnpm.e2e`+dep-of-pkg-with-1-dep@100.0.0"));
     assert!(!virtual_store_has(&workspace, "`@pnpm.e2e`+dep-of-pkg-with-1-dep@100.1.0"));
...
+    eprintln!("after compatible update, checking indirect dep bumped within range");
     assert!(virtual_store_has(&workspace, "`@pnpm.e2e`+dep-of-pkg-with-1-dep@100.1.0"));

As per coding guidelines: “Follow test-logging guidance — log before non-assert_eq! assertions, dbg! complex structures, skip logging for simple scalar assert_eq!”.

Also applies to: 327-328

🤖 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 `@pacquet/crates/cli/tests/update.rs` around lines 299 - 306, Before each
non-assert_eq! assertion in this test add a short diagnostic log or dbg! of the
relevant state so failing CI runs show context: log the workspace manifest and
the return values from virtual_store_has before each
assert!(virtual_store_has(...)) call (the two checks after pacquet(...
["update","--latest"]) and the other pair referenced at 327-328), and also log
any inputs to write_manifest or pacquet that affect the check; locate the
assertions by the symbol virtual_store_has and the test-local helpers
write_manifest and pacquet and insert concise dbg!/eprintln! statements
immediately above each assert! line.
🤖 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 `@pacquet/crates/cli/tests/update.rs`:
- Around line 299-306: Before each non-assert_eq! assertion in this test add a
short diagnostic log or dbg! of the relevant state so failing CI runs show
context: log the workspace manifest and the return values from virtual_store_has
before each assert!(virtual_store_has(...)) call (the two checks after
pacquet(... ["update","--latest"]) and the other pair referenced at 327-328),
and also log any inputs to write_manifest or pacquet that affect the check;
locate the assertions by the symbol virtual_store_has and the test-local helpers
write_manifest and pacquet and insert concise dbg!/eprintln! statements
immediately above each assert! line.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: b1b5e9cf-ce80-4f79-805c-9041daa9989c

📥 Commits

Reviewing files that changed from the base of the PR and between 64a95f9 and 0970bc9.

📒 Files selected for processing (2)
  • pacquet/crates/cli/tests/update.rs
  • pacquet/crates/package-manager/src/update.rs
🚧 Files skipped from review as they are similar to previous changes (1)
  • pacquet/crates/package-manager/src/update.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). (4)
  • GitHub Check: Code Coverage
  • GitHub Check: Lint and Test (windows-latest)
  • GitHub Check: Lint and Test (ubuntu-latest)
  • GitHub Check: Compile & Lint
🧰 Additional context used
📓 Path-based instructions (2)
pacquet/**/*.rs

📄 CodeRabbit inference engine (pacquet/AGENTS.md)

pacquet/**/*.rs: Log emissions are part of matching pnpm — when porting a function that fires pnpm:<channel> events through globalLogger, logger.debug(...), or streamParser.write(...), mirror the call site, payload, and ordering so @pnpm/cli.default-reporter parses pacquet's NDJSON the same way
Declare a newtype wrapper for branded string types instead of collapsing the brand into a plain String or &str in Rust
If upstream TypeScript always validates before construction of a branded string, validate in the Rust wrapper too via TryFrom<String> and/or FromStr and do not provide an infallible public constructor
If upstream TypeScript never validates a branded string, just brand for type-safety in Rust by exposing an infallible From<String> constructor
If upstream TypeScript occasionally constructs a branded string without validation, expose from_str_unchecked in Rust as an escape hatch alongside the validating constructor
Match upstream serde behavior for branded strings crossing JSON, YAML, or INI boundaries by using #[serde(try_from = "String")] for deserialization and #[serde(into = "String")] for serialization
Derive simple conversions for branded strings using #[derive(derive_more::From)] and #[derive(derive_more::Into)] instead of handwriting impl blocks; use manual impl only when conversion needs custom logic
Model TypeScript string literal unions (like 'auto' | 'always' | 'never') as Rust enums instead of newtype wrappers, since the set of valid values is closed
Treat TypeScript string template literal types (like `${string}@${string}`) the same as branded string types in Rust, using a newtype wrapper with validation
Follow the code style guide in CODE_STYLE_GUIDE.md — imports, modules, naming, ownership and borrowing, parameter type selection, trait bounds, pattern matching, pipe-trait, error handling, test layout, and cloning of Arc and Rc
Choose owned vs. borrowed parameters to minimize copies; widen to t...

Files:

  • pacquet/crates/cli/tests/update.rs
pacquet/**/tests/**/*.rs

📄 CodeRabbit inference engine (pacquet/AGENTS.md)

pacquet/**/tests/**/*.rs: Prefer real fixtures over dependency-injection seams — use tempfile::TempDir, the mocked registry, or integration tests spawning the actual binary for happy paths and most error paths; use the DI seam only for filesystem error kinds, deterministic time, shared process-global state, or external-service happy paths
Follow test-logging guidance — log before non-assert_eq! assertions, dbg! complex structures, skip logging for simple scalar assert_eq!
Tests must not be tolerant of missing build/runtime environment by silently returning early when a tool isn't found — let the test panic when a required tool is absent
Prefer #[cfg_attr(target_os = "windows", ignore = "...")] (or matching #[cfg(unix)] gates) over runtime probe-and-skip helpers for platform-locked tools
Port relevant pnpm tests to Rust tests whenever they translate when porting behavior from pnpm
Use snapshot tests with insta and carefully review diffs when intentional changes alter snapshots; accept with cargo insta review only after careful review
Tests that need the mocked registry should start pnpr through pacquet-testing-utils; cargo test / cargo nextest run should not require a separate just registry-mock launch step

Files:

  • pacquet/crates/cli/tests/update.rs
🧠 Learnings (3)
📚 Learning: 2026-05-20T19:40:55.051Z
Learnt from: zkochan
Repo: pnpm/pnpm PR: 11774
File: pacquet/crates/resolving-deps-resolver/src/resolve_peers.rs:0-0
Timestamp: 2026-05-20T19:40:55.051Z
Learning: In the pacquet Rust code, ensure the semver implementation uses the `node-semver` crate (not `nodejs-semver`). `node-semver`’s public API does not include a `satisfies_with_prerelease`-style method; prerelease-tolerant matching should be implemented inline by first calling `Range::satisfies`, and when it rejects a prerelease version, retry matching against a stripped `MAJOR.MINOR.PATCH` base of the prerelease version.

Applied to files:

  • pacquet/crates/cli/tests/update.rs
📚 Learning: 2026-05-22T00:08:44.646Z
Learnt from: zkochan
Repo: pnpm/pnpm PR: 11837
File: pacquet/crates/resolving-npm-resolver/src/pick_package.rs:33-51
Timestamp: 2026-05-22T00:08:44.646Z
Learning: In the pnpm/pnpm repo’s pacquet Rust crates, do not flag Unicode ellipsis characters (U+2026, `…`) in Rust doc comments (`///` / `/** */`) as a lint violation. The pacquet crate’s `dylint.toml` only enables `perfectionist::derive_ordering`, and the Dylint `unicode-ellipsis` rule is not enabled for this project—so `…` in doc comments is an intentional, repo-consistent style.

Applied to files:

  • pacquet/crates/cli/tests/update.rs
📚 Learning: 2026-05-20T23:07:58.444Z
Learnt from: zkochan
Repo: pnpm/pnpm PR: 11784
File: pacquet/crates/resolving-deps-resolver/src/hoist_peers.rs:120-133
Timestamp: 2026-05-20T23:07:58.444Z
Learning: When reviewing code in this pacquet Rust port, follow the upstream pnpm compatibility rule: only match pnpm’s behavior exactly. Do not propose review changes that intentionally deviate from pnpm’s documented/observed behavior, even if pnpm appears buggy. If you identify a real bug in pnpm behavior, the review should prioritize fixing it upstream in pnpm first, and avoid implementing a pnpm-behavior workaround here unless the same fix has already landed upstream.

Applied to files:

  • pacquet/crates/cli/tests/update.rs
🔇 Additional comments (1)
pacquet/crates/cli/tests/update.rs (1)

11-13: 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

Copilot reviewed 4 out of 4 changed files in this pull request and generated 3 comments.

Comment thread pacquet/crates/resolving-resolver-base/src/resolve.rs
Comment thread pacquet/crates/package-manager/src/install_with_fresh_lockfile.rs Outdated
Comment thread pacquet/crates/package-manager/src/update.rs
Per pacquet's test-logging convention (log before non-`assert_eq!`
assertions), add a `list_virtual_store` helper and `eprintln!` the
`node_modules/.pnpm` contents before each `virtual_store_has` assertion
cluster, so a failing CI run shows what was actually materialized.
Addresses a review nit on #12104.

Written by an agent (Claude Code, claude-opus-4-8).
`Arc::new(all_preferred_versions.clone())` deep-cloned the map once just
to build the shared seed. Move it into the `Arc` instead and clone the
map out of the seed for the per-importer hoist copy (which mutates its
own copy). Saves the one-time clone. Addresses a review comment on #12104.

Written by an agent (Claude Code, claude-opus-4-8).
Copilot AI review requested due to automatic review settings June 1, 2026 15:34

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 4 out of 4 changed files in this pull request and generated 12 comments.

Comment thread pacquet/crates/cli/tests/update.rs
Comment thread pacquet/crates/package-manager/src/update.rs
Comment thread pacquet/crates/cli/tests/update.rs
Comment thread pacquet/crates/cli/tests/update.rs
Comment thread pacquet/crates/cli/tests/update.rs
Comment thread pacquet/crates/cli/tests/update.rs
Comment thread pacquet/crates/cli/tests/update.rs
Comment thread pacquet/crates/cli/tests/update.rs
Comment thread pacquet/crates/cli/tests/update.rs
Comment thread pacquet/crates/cli/tests/update.rs
@zkochan zkochan merged commit 0b49084 into main Jun 1, 2026
29 checks passed
@zkochan zkochan deleted the pq-update-ignore-deps branch June 1, 2026 16:05
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants