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

feat(reporter): emit pnpm:package-manifest, pnpm:root, pnpm:stats, and pnpm:request-retry#375

Merged
zkochan merged 4 commits into
mainfrom
feat/reporter-remaining-channels
May 7, 2026
Merged

feat(reporter): emit pnpm:package-manifest, pnpm:root, pnpm:stats, and pnpm:request-retry#375
zkochan merged 4 commits into
mainfrom
feat/reporter-remaining-channels

Conversation

@zkochan

@zkochan zkochan commented May 2, 2026

Copy link
Copy Markdown
Member

Summary

Wires the four remaining portable channels from #347 into pacquet's reporter pipeline. Each follows the convention documented in CODE_STYLE_GUIDE.md § "Reporter / log events":

  • a LogEvent variant + payload struct in pacquet-reporter
  • a wire-shape unit test in crates/reporter/src/tests.rs
  • the emit at the matching upstream pnpm site, with a pinned permalink at SHA 086c5e91e8
  • a recording-fake test where the surrounding code makes one reachable

Channels

  • pnpm:package-manifest — presence-tagged on initial / updated. Initial fires from Install::run next to the existing pnpm:context emit so consumers have the on-disk manifest before the install header renders. Updated fires from Add::run after manifest.save() so the audit pipeline can diff initial vs updated.
  • pnpm:rootAdded per direct dependency from SymlinkDirectDependencies::run. Iterates per DependencyGroup so each emit can label its dependencyType (prod / dev / optional); peer deps aren't materialised here so they're skipped, matching pnpm.
  • pnpm:statsAdded (snapshot count) and Removed (placeholder 0 until pruning lands) from CreateVirtualStore::run. The code comment notes the upstream-divergence: pnpm reports the current/wanted diff (newDepPathsSet.size), pacquet has no current-lockfile tracking yet so every install looks like a fresh install and added ends up as the total snapshot count.
  • pnpm:request-retry — emitted from fetch_and_extract_with_retry before each backoff sleep, with attempt one-indexed (the failed attempt) and timeout mirroring RetryOpts::delay_for ms. A new tarball_error_to_request_retry helper maps TarballError onto pnpm's JS-shaped error object: httpStatusCode for HTTP failures, a placeholder code derived from the variant name for everything else, so the JS reporter's ?? -chain dispatch (httpStatusCode ?? status ?? errno ?? code) picks up the right field.

Tests

  • crates/reporter/src/tests.rs — wire-shape tests for all four new channels, asserting the JSON shape @pnpm/cli.default-reporter consumes (camelCase field names, presence-tagged dispatch, optional fields skipping rather than rendering as null).
  • crates/package-manager/src/install/tests.rs — extends install_emits_pnpm_event_sequence to pin the new [PackageManifest, Context, ImportingStarted, Stats(Added), Stats(Removed), ImportingDone, Summary] order.
  • crates/package-manager/src/symlink_direct_dependencies/tests.rs — new unit tests proving pnpm:root added fires once per direct dep with the right dependencyType per group, and pinning the existing MissingRootImporter error path.
  • crates/tarball/src/tests.rsrequest_retry_event_fires_per_retried_attempt proves the retry-loop emits exactly once per failed-and-being-retried attempt with httpStatusCode: "503" (no placeholder code when the JS-side dispatch field is populated).

Pure additive — no existing wire-shape changed.

Test plan

  • just ready (typos / fmt / check / test / lint, including the new wire-shape and recording-fake tests)
  • cargo nextest run -p pacquet-reporter — 15/15 passing
  • cargo nextest run -p pacquet-package-manager symlink_direct_dependencies install_emits_pnpm_event_sequence — passes
  • cargo nextest run -p pacquet-tarball request_retry — passes

References

Summary by CodeRabbit

  • New Features

    • Enhanced runtime reporting: package-manifest initial/updated events, per-dependency "root added" events with dependency type, add/remove stats, and network request-retry diagnostics.
  • Tests

    • New unit/integration tests validating log event shapes, emit ordering, retry reporting, and direct-dependency symlink behavior.

…d pnpm:request-retry

Wires the four remaining portable channels from #347 into pacquet's
reporter pipeline. Each follows the convention documented in
CODE_STYLE_GUIDE.md: a `LogEvent` variant + payload struct, a
wire-shape unit test in `pacquet-reporter`, the emit at the matching
upstream pnpm site (with a pinned permalink at SHA `086c5e91e8`), and
a recording-fake test where the surrounding code makes one reachable.

* `pnpm:package-manifest`: `Initial` from `Install::run` next to the
  existing `pnpm:context` emit; `Updated` from `Add::run` after the
  manifest is rewritten. Presence-tagged on `initial` / `updated`
  per pnpm's wire shape.

* `pnpm:root`: `Added` per direct dependency in
  `SymlinkDirectDependencies::run`. Iterates per dependency group so
  each emit can label its `dependencyType`. Reuses the install root
  `requester` for the `prefix` field.

* `pnpm:stats`: `Added` (snapshot count) and `Removed` (placeholder
  `0` until pruning lands) from `CreateVirtualStore::run`. Comment
  notes the upstream-divergence: pnpm reports the current/wanted
  diff, pacquet has no current-lockfile tracking yet so every
  install looks like a fresh install.

* `pnpm:request-retry`: emitted from
  `fetch_and_extract_with_retry` before each backoff sleep. Maps
  `TarballError` onto pnpm's JS-shaped error object —
  `httpStatusCode` for HTTP failures, a placeholder `code` derived
  from the variant name for everything else, so the JS reporter's
  `?? `-chain dispatch picks up the right field.
Copilot AI review requested due to automatic review settings May 2, 2026 22:16
@coderabbitai

coderabbitai Bot commented May 2, 2026

Copy link
Copy Markdown

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 68bcc5f6-b20c-4047-ab0a-d0235c51e43f

📥 Commits

Reviewing files that changed from the base of the PR and between 3195049 and 29a02d7.

📒 Files selected for processing (3)
  • crates/package-manager/src/symlink_direct_dependencies.rs
  • crates/package-manager/src/symlink_direct_dependencies/tests.rs
  • crates/tarball/src/lib.rs
🚧 Files skipped from review as they are similar to previous changes (3)
  • crates/tarball/src/lib.rs
  • crates/package-manager/src/symlink_direct_dependencies.rs
  • crates/package-manager/src/symlink_direct_dependencies/tests.rs

📝 Walkthrough

Walkthrough

Adds new pnpm-shaped reporter channels and payloads (package-manifest, root, stats, request-retry); emits those events from package-manager operations (add, install, create_virtual_store, symlink_direct_dependencies) and from tarball retry logic; includes serialization and behavioral tests.

Changes

Reporter core & tests

Layer / File(s) Summary
Type Definitions
crates/reporter/src/lib.rs
Adds LogEvent variants pnpm:package-manifest/pnpm:root/pnpm:stats/pnpm:request-retry and public payload types (PackageManifestLog, PackageManifestMessage, RootLog, RootMessage, AddedRoot, RemovedRoot, DependencyType, StatsLog, StatsMessage, RequestRetryLog, RequestRetryError).
Serialization Tests
crates/reporter/src/tests.rs
Adds tests asserting pnpm wire-shape serialization for package-manifest, root (added/removed), stats (added/removed), and request-retry payloads.
Imports / Formatting
crates/reporter/src/tests.rs
Reformats grouped use imports (non-functional).

Package-manager instrumentation & tests

Layer / File(s) Summary
Interface / Signature
crates/package-manager/src/symlink_direct_dependencies.rs, crates/package-manager/src/install_frozen_lockfile.rs
SymlinkDirectDependencies now includes pub requester: &'a str and run is generic over R: Reporter; callers (e.g., InstallFrozenLockfile::run) construct with requester and invoke .run::<R>().
Core Implementation
crates/package-manager/src/symlink_direct_dependencies.rs
Reworks candidate selection to iterate dependency groups, filter peers, deduplicate first-wins, create symlinks in parallel, and emit a pnpm:root added RootLog per linked dependency with prefix = requester and dependency_type mapped from originating group.
Higher-level emits
crates/package-manager/src/install.rs, crates/package-manager/src/add.rs, crates/package-manager/src/create_virtual_store.rs
Install::run emits pnpm:package-manifest initial early; Add::run emits pnpm:package-manifest updated after saving manifest; CreateVirtualStore::run emits pnpm:stats (added = snapshots.len(), removed = 0) at start.
Tests / Assertions
crates/package-manager/src/symlink_direct_dependencies/tests.rs, crates/package-manager/src/install/tests.rs
Adds tests that assert filesystem symlinks and that one pnpm:root added event is emitted per direct dependency (with correct dependency_type and prefix), duplicate-across-groups collapses to first-wins, missing-importer surfaces error, and install test updated to expect initial manifest, context, importing stages, stats emits, and consistent prefix in summary.

Tarball retry observability & tests

Layer / File(s) Summary
Error Mapping Helper
crates/tarball/src/lib.rs
Adds private tarball_error_to_request_retry mapping TarballError variants into RequestRetryError fields (http_status_code for HTTP status, variant-specific code otherwise).
Retry Loop Emission
crates/tarball/src/lib.rs
On transient fetch failures, emits LogEvent::RequestRetry(RequestRetryLog { attempt, max_retries, timeout, method, url, error }) before sleeping/backoff and retrying.
Tests
crates/tarball/src/tests.rs
Adds a Tokio test asserting a pnpm:request-retry event is emitted for a transient 503 followed by success, validating attempt, max_retries, timeout, and that the error contains httpStatusCode while omitting the placeholder code.

Sequence Diagram(s)

sequenceDiagram
    participant Installer
    participant SymlinkWorker
    participant Reporter
    participant FS as FileSystem
    participant Fetcher

    Installer->>Reporter: emit pnpm:package-manifest (initial)
    Installer->>SymlinkWorker: SymlinkDirectDependencies::run::<R>(requester, dependency_groups)
    SymlinkWorker->>FS: create symlink for dep A
    SymlinkWorker->>Reporter: emit pnpm:root added (prefix=requester, dependency_type)
    SymlinkWorker->>FS: create symlink for dep B
    SymlinkWorker->>Reporter: emit pnpm:root added (prefix=requester, dependency_type)
    Installer->>Reporter: emit pnpm:stats (added/removed) [create_virtual_store]
    Fetcher->>Fetcher: encounter transient error (e.g., 503)
    Fetcher->>Reporter: emit pnpm:request-retry (attempt, error with httpStatusCode)
    Fetcher->>Fetcher: retry and succeed
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related issues

Possibly related PRs

Suggested reviewers

  • KSXGitHub

Poem

🐰 Soft paws on logs tonight,

Manifests glow in gentle light,
Roots and stats and retries sing,
Tiny hops track every thing,
A rabbit cheers each emitted byte.

🚥 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 emitting four new pnpm reporter channels (pnpm:package-manifest, pnpm:root, pnpm:stats, pnpm:request-retry) throughout the codebase.
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 docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/reporter-remaining-channels

Review rate limit: 9/10 reviews remaining, refill in 6 minutes.

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

@codecov

codecov Bot commented May 2, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 81.88976% with 23 lines in your changes missing coverage. Please review.
✅ Project coverage is 81.50%. Comparing base (78afb18) to head (29a02d7).
⚠️ Report is 1 commits behind head on main.

Files with missing lines Patch % Lines
crates/tarball/src/lib.rs 55.31% 21 Missing ⚠️
...package-manager/src/symlink_direct_dependencies.rs 96.00% 2 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main     #375      +/-   ##
==========================================
+ Coverage   81.03%   81.50%   +0.46%     
==========================================
  Files          64       64              
  Lines        3517     3622     +105     
==========================================
+ Hits         2850     2952     +102     
- Misses        667      670       +3     

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

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

Actionable comments posted: 3

🧹 Nitpick comments (2)
crates/reporter/src/tests.rs (1)

266-465: ⚡ Quick win

Add context logging before new non-assert_eq! checks.

The new tests add several assert! key-presence assertions without logging the serialized payload/context first. Add eprintln!/dbg! immediately before those checks to keep failures diagnosable in CI.

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

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@crates/reporter/src/tests.rs` around lines 266 - 465, Several tests add
assert! presence checks without prior diagnostic logging; insert logging calls
before those non-assert_eq! assertions to aid CI debugging. For each test
function (package_manifest_event_matches_pnpm_wire_shape,
root_event_matches_pnpm_wire_shape, stats_event_matches_pnpm_wire_shape,
request_retry_event_matches_pnpm_wire_shape) add a dbg! or eprintln! of the
serialized JSON (the local variable json produced from envelope.pipe_ref(...) ->
serde_json::from_str) immediately before the assert! loops that check
.get(...).is_none() (and before the for k in [...] {
assert!(json["..."].get(k)... ) } blocks); prefer dbg!(json) for complex objects
and eprintln! for short scalar checks, and do not add logging before existing
assert_eq! scalar comparisons. Ensure you reference the json variable and place
the logging right before the offending assert! so failures emit the payload.
crates/package-manager/src/symlink_direct_dependencies/tests.rs (1)

80-85: ⚡ Quick win

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

Please log relevant path/result context right before the assert! checks (symlink existence and matches! result) to align with the test diagnostics convention.

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: 139-139

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@crates/package-manager/src/symlink_direct_dependencies/tests.rs` around lines
80 - 85, Before the non-assert_eq! assertions, add context logging of the
checked path and the call result: evaluate and store the result of
is_symlink_or_junction(&modules_dir.join("fastify")) and log the path and the
bool/Result (e.g., using dbg! or tracing::info!) immediately before the assert!;
do the same for is_symlink_or_junction(&modules_dir.join("@pnpm.e2e/dev-dep"))
and for the matches! expression referenced around the second location (line
~139), logging the value being matched and the subject path/structure; keep
assert_eq! uses unchanged and only add logs immediately prior to the non-assert
assertions so test diagnostics follow the project guideline.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@crates/package-manager/src/add.rs`:
- Around line 100-106: The current prefix derivation uses unwraps that can
panic: the expression around manifest.path().parent().map(...).unwrap() and
to_str().unwrap() should be made robust. Replace the chain that sets prefix (the
variable named prefix in add.rs derived from manifest.path()) with safe
handling: check manifest.path().parent() and provide a sensible fallback if None
(e.g., use the manifest path itself or "."), and convert the parent path to a
UTF-8-safe string using to_string_lossy() or OsString handling instead of
to_str().unwrap(), ensuring no panics occur on non-UTF-8 paths. Ensure the new
logic preserves the existing semantics of prefix when parent exists and
documents the fallback choice.

In `@crates/package-manager/src/symlink_direct_dependencies.rs`:
- Around line 74-90: The current parallel loop panics on symlink errors via
expect("symlink pkg"); change the loop to propagate errors by using
par_iter().try_for_each and make the closure return Result<(),
SymlinkDirectDependenciesError>, replacing expect(...) with
symlink_package(...)? and mapping the symlink error into
SymlinkDirectDependenciesError; ensure the surrounding function's
signature/return type supports returning Result so the propagated error from the
closure bubbles up instead of aborting a rayon worker (refer to symlink_package,
PkgNameVerPeer::new, and SymlinkDirectDependenciesError to locate and convert
the error).
- Around line 100-105: The match currently maps DependencyGroup::Peer to
dependency_type = None but the later emit/symlink path still processes it;
change the logic to skip peers entirely by adding an early guard: if group ==
DependencyGroup::Peer { continue; } (or equivalent) before computing
dependency_type so the code that emits "pnpm:root added" and creates the symlink
is not executed for peers; reference the DependencyGroup enum, the
dependency_type variable, and the code path that emits "pnpm:root added"/creates
the symlink to locate where to insert the guard.

---

Nitpick comments:
In `@crates/package-manager/src/symlink_direct_dependencies/tests.rs`:
- Around line 80-85: Before the non-assert_eq! assertions, add context logging
of the checked path and the call result: evaluate and store the result of
is_symlink_or_junction(&modules_dir.join("fastify")) and log the path and the
bool/Result (e.g., using dbg! or tracing::info!) immediately before the assert!;
do the same for is_symlink_or_junction(&modules_dir.join("@pnpm.e2e/dev-dep"))
and for the matches! expression referenced around the second location (line
~139), logging the value being matched and the subject path/structure; keep
assert_eq! uses unchanged and only add logs immediately prior to the non-assert
assertions so test diagnostics follow the project guideline.

In `@crates/reporter/src/tests.rs`:
- Around line 266-465: Several tests add assert! presence checks without prior
diagnostic logging; insert logging calls before those non-assert_eq! assertions
to aid CI debugging. For each test function
(package_manifest_event_matches_pnpm_wire_shape,
root_event_matches_pnpm_wire_shape, stats_event_matches_pnpm_wire_shape,
request_retry_event_matches_pnpm_wire_shape) add a dbg! or eprintln! of the
serialized JSON (the local variable json produced from envelope.pipe_ref(...) ->
serde_json::from_str) immediately before the assert! loops that check
.get(...).is_none() (and before the for k in [...] {
assert!(json["..."].get(k)... ) } blocks); prefer dbg!(json) for complex objects
and eprintln! for short scalar checks, and do not add logging before existing
assert_eq! scalar comparisons. Ensure you reference the json variable and place
the logging right before the offending assert! so failures emit the payload.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 6777d7a5-26a8-4a07-a4a0-9d6e5df748f3

📥 Commits

Reviewing files that changed from the base of the PR and between 2c80ba6 and 60b1af7.

📒 Files selected for processing (11)
  • crates/package-manager/src/add.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/symlink_direct_dependencies.rs
  • crates/package-manager/src/symlink_direct_dependencies/tests.rs
  • crates/reporter/src/lib.rs
  • crates/reporter/src/tests.rs
  • crates/tarball/src/lib.rs
  • crates/tarball/src/tests.rs

Comment thread crates/package-manager/src/add.rs Outdated
Comment on lines +74 to +90
entries.par_iter().for_each(|(name, spec, group)| {
// TODO: the code below is not optimal
let virtual_store_name =
PkgNameVerPeer::new(PkgName::clone(name), spec.version.clone())
.to_virtual_store_name();

let name_str = name.to_string();
symlink_package(
&config
.virtual_store_dir
.join(virtual_store_name)
.join("node_modules")
.join(&name_str),
&config.modules_dir.join(&name_str),
)
.expect("symlink pkg"); // TODO: properly propagate this error

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | 🏗️ Heavy lift

Do not panic on symlink failures inside the parallel install path.

expect("symlink pkg") turns filesystem errors into panics, which can abort the install from a rayon worker. This should be propagated as SymlinkDirectDependenciesError instead.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@crates/package-manager/src/symlink_direct_dependencies.rs` around lines 74 -
90, The current parallel loop panics on symlink errors via expect("symlink
pkg"); change the loop to propagate errors by using par_iter().try_for_each and
make the closure return Result<(), SymlinkDirectDependenciesError>, replacing
expect(...) with symlink_package(...)? and mapping the symlink error into
SymlinkDirectDependenciesError; ensure the surrounding function's
signature/return type supports returning Result so the propagated error from the
closure bubbles up instead of aborting a rayon worker (refer to symlink_package,
PkgNameVerPeer::new, and SymlinkDirectDependenciesError to locate and convert
the error).

Comment thread crates/package-manager/src/symlink_direct_dependencies.rs
@github-actions

github-actions Bot commented May 2, 2026

Copy link
Copy Markdown

Micro-Benchmark Results

Linux

group                          main                                   pr
-----                          ----                                   --
tarball/download_dependency    1.00     16.4±0.55ms   264.7 KB/sec    1.00     16.3±0.54ms   265.6 KB/sec

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 finishes wiring four remaining pnpm-compatible reporter channels into pacquet’s reporter pipeline, adding the corresponding LogEvent variants/payloads, emit sites, and wire-shape + recording-fake tests.

Changes:

  • Add pnpm:package-manifest, pnpm:root, pnpm:stats, and pnpm:request-retry to pacquet-reporter (types + wire-shape tests).
  • Emit the new events from the matching pacquet call sites (install, add, create_virtual_store, symlink_direct_dependencies, tarball retry loop).
  • Extend/install new tests to pin event ordering and validate emitted payloads.

Reviewed changes

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

Show a summary per file
File Description
crates/tarball/src/lib.rs Adds pnpm:request-retry emission and tarball-error → request-retry error mapping.
crates/tarball/src/tests.rs Adds a recording-fake test asserting retry-event emission behavior.
crates/reporter/src/lib.rs Introduces new LogEvent variants and payload structs/enums for the four channels.
crates/reporter/src/tests.rs Adds wire-shape serialization tests for the new channels.
crates/package-manager/src/symlink_direct_dependencies.rs Emits pnpm:root added per direct dependency and threads reporter/prefix through.
crates/package-manager/src/symlink_direct_dependencies/tests.rs Adds unit tests for pnpm:root added emissions and error path.
crates/package-manager/src/install_frozen_lockfile.rs Updates call to SymlinkDirectDependencies::run::<R>() and threads requester.
crates/package-manager/src/install.rs Emits pnpm:package-manifest initial before pnpm:context.
crates/package-manager/src/install/tests.rs Updates event-sequence test to include manifest + stats events.
crates/package-manager/src/create_virtual_store.rs Emits pnpm:stats added and placeholder removed: 0.
crates/package-manager/src/add.rs Emits pnpm:package-manifest updated after saving the manifest.

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

Comment thread crates/package-manager/src/symlink_direct_dependencies/tests.rs
Comment thread crates/package-manager/src/symlink_direct_dependencies/tests.rs Outdated
Comment on lines +80 to +90
let name_str = name.to_string();
symlink_package(
&config
.virtual_store_dir
.join(virtual_store_name)
.join("node_modules")
.join(&name_str),
&config.modules_dir.join(&name_str),
)
.expect("symlink pkg"); // TODO: properly propagate this error

Comment thread crates/reporter/src/lib.rs Outdated
@github-actions

github-actions Bot commented May 2, 2026

Copy link
Copy Markdown

Integrated-Benchmark Report (Linux)

Scenario: Frozen Lockfile

Command Mean [s] Min [s] Max [s] Relative
pacquet@HEAD 2.716 ± 0.109 2.562 2.959 1.02 ± 0.04
pacquet@main 2.676 ± 0.041 2.623 2.756 1.00
pnpm 6.655 ± 0.066 6.519 6.740 2.49 ± 0.05
BENCHMARK_REPORT.json
{
  "results": [
    {
      "command": "pacquet@HEAD",
      "mean": 2.71636527726,
      "stddev": 0.10864183298024227,
      "median": 2.69978219906,
      "user": 2.3965814,
      "system": 3.4038292999999995,
      "min": 2.5616842810600002,
      "max": 2.95949639506,
      "times": [
        2.95949639506,
        2.70394325606,
        2.69562114206,
        2.83152112906,
        2.70499957006,
        2.71428283606,
        2.68460344006,
        2.66715555406,
        2.64034516906,
        2.5616842810600002
      ]
    },
    {
      "command": "pacquet@main",
      "mean": 2.67588145026,
      "stddev": 0.04110673052971748,
      "median": 2.6675257645599997,
      "user": 2.4029591,
      "system": 3.4002719,
      "min": 2.62304205606,
      "max": 2.75632350706,
      "times": [
        2.72298704006,
        2.70252252206,
        2.6536732610600002,
        2.67206872606,
        2.66298280306,
        2.62304205606,
        2.67372545406,
        2.66188508706,
        2.62960404606,
        2.75632350706
      ]
    },
    {
      "command": "pnpm",
      "mean": 6.6546352542600005,
      "stddev": 0.06626926471255074,
      "median": 6.66331427456,
      "user": 9.267293899999999,
      "system": 4.4768636,
      "min": 6.51945531806,
      "max": 6.73954089606,
      "times": [
        6.63280439506,
        6.73210386406,
        6.665517797060001,
        6.583899601060001,
        6.51945531806,
        6.73954089606,
        6.66645312206,
        6.644401951060001,
        6.66111075206,
        6.70106484606
      ]
    }
  ]
}

Scenario: Frozen Lockfile (Hot Cache)

Command Mean [ms] Min [ms] Max [ms] Relative
pacquet@HEAD 727.3 ± 77.2 689.8 943.3 1.01 ± 0.11
pacquet@main 722.9 ± 28.0 698.5 798.6 1.00
pnpm 2711.1 ± 76.6 2574.6 2857.9 3.75 ± 0.18
BENCHMARK_REPORT.json
{
  "results": [
    {
      "command": "pacquet@HEAD",
      "mean": 0.72729329838,
      "stddev": 0.0772296549321924,
      "median": 0.69927623888,
      "user": 0.25499908,
      "system": 1.3530937999999997,
      "min": 0.68984396788,
      "max": 0.9432701078800001,
      "times": [
        0.9432701078800001,
        0.70627327788,
        0.69362938588,
        0.70053741088,
        0.68984396788,
        0.69440140888,
        0.69801506688,
        0.7215840788800001,
        0.7343929948800001,
        0.69098528388
      ]
    },
    {
      "command": "pacquet@main",
      "mean": 0.7229295583800001,
      "stddev": 0.028020547495824166,
      "median": 0.71536819288,
      "user": 0.2544656799999999,
      "system": 1.3707614000000001,
      "min": 0.69854225088,
      "max": 0.79858005288,
      "times": [
        0.79858005288,
        0.71567225288,
        0.70804259888,
        0.70647301588,
        0.72764463288,
        0.72141694288,
        0.71506413288,
        0.7124055258800001,
        0.69854225088,
        0.72545417788
      ]
    },
    {
      "command": "pnpm",
      "mean": 2.71114355728,
      "stddev": 0.07658663724827394,
      "median": 2.71034498888,
      "user": 3.22762598,
      "system": 2.1951493999999996,
      "min": 2.57455196588,
      "max": 2.85787179588,
      "times": [
        2.57455196588,
        2.65017654588,
        2.85787179588,
        2.67907592188,
        2.75555259588,
        2.75009485788,
        2.70421391688,
        2.66538924088,
        2.75803267088,
        2.71647606088
      ]
    }
  ]
}

* `cargo doc` failed because `pacquet-reporter` cannot link to
  `pacquet-tarball` (the dependency runs the other way). Drop the
  intra-doc link to plain backticks.

* `pacquet-package-manager symlink_direct_dependencies` test failed
  on Windows because junctions need their target directories to
  exist before `junction::create`. Pre-create the virtual-store
  target paths so the test stays cross-platform.

* `Add::run`'s post-save `pnpm:package-manifest updated` emit
  derived its `prefix` via panic-prone `to_str().unwrap()` /
  `parent().unwrap()`. Switch to `to_string_lossy()` with a
  manifest-path fallback so non-UTF-8 paths and root-level
  `package.json`s don't crash the install.

* `SymlinkDirectDependencies::run` filtered `DependencyGroup::Peer`
  to `dependency_type: None` while still proceeding to symlink and
  emit. `get_map_by_group(Peer)` returns `None` already so the
  emit can't actually fire, but the dead `Peer` arm in the match
  was misleading. Filter peers explicitly at the iteration source
  and replace the match arm with `unreachable!`.

* Drop a stray `eprintln!` from the new `pnpm:root` test.

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@crates/package-manager/src/symlink_direct_dependencies.rs`:
- Around line 72-82: The entries vector currently collects package entries from
each dependency group (skipping DependencyGroup::Peer) which can include
duplicates and cause concurrent symlink races; update the construction of
entries (the let entries: Vec<(&PkgName, &_, DependencyGroup)> block that uses
dependency_groups and project_snapshot.get_map_by_group) to first merge/flatten
entries by package name and apply the same precedence rules used elsewhere for
direct-dependency flattening (so each package appears only once with the
highest-precedence group), then collect that deduplicated list before the
parallel loop (par_iter) so no duplicate symlink work or duplicate `pnpm:root
added` events occur.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 0b3b2291-6fe6-4a7b-a011-61a066be7c69

📥 Commits

Reviewing files that changed from the base of the PR and between 60b1af7 and 4bd4571.

📒 Files selected for processing (4)
  • crates/package-manager/src/add.rs
  • crates/package-manager/src/symlink_direct_dependencies.rs
  • crates/package-manager/src/symlink_direct_dependencies/tests.rs
  • crates/reporter/src/lib.rs
✅ Files skipped from review due to trivial changes (1)
  • crates/package-manager/src/symlink_direct_dependencies/tests.rs
🚧 Files skipped from review as they are similar to previous changes (1)
  • crates/reporter/src/lib.rs

Comment thread crates/package-manager/src/symlink_direct_dependencies.rs
A malformed v9 lockfile that lists the same package across multiple
importer sections (e.g. `dependencies` + `optionalDependencies`)
used to race two `symlink_package` calls to the same
`node_modules/<name>` and emit two `pnpm:root added` events.

Pnpm's resolver normalises this upstream of `linkDirectDeps` (a
package marked `optional: true` only lands in
`optionalDependencies`), so by the time the symlink stage runs the
input list is already unique. Pacquet ingests user-supplied
lockfiles so the same guarantee doesn't carry — collapse with a
first-wins `HashSet<PkgName>` pass before `par_iter` so the
behavioural envelope matches pnpm.

First-wins picks the highest-priority group from the caller's
`dependency_groups` order. The CLI passes `[Prod, Dev, Optional]`
so prod beats dev/optional, matching pnpm's
dependencies-over-optional precedence.

Adds a unit test pinning the collapse: same dep in `dependencies`
and `optionalDependencies` must produce exactly one symlink and
one `pnpm:root added` event with `dependencyType: prod`.
Copilot AI review requested due to automatic review settings May 2, 2026 22:41

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@crates/package-manager/src/symlink_direct_dependencies/tests.rs`:
- Around line 95-96: Before the two assert! checks that call
is_symlink_or_junction on modules_dir.join("fastify") and
modules_dir.join("@pnpm.e2e/dev-dep"), add diagnostic logging that prints the
full path and the result of is_symlink_or_junction (or the error) so failures
show context; specifically, evaluate the path into a local variable (e.g.,
fastify_path / dev_dep_path), call is_symlink_or_junction and log the path and
the returned Result/boolean (using eprintln! or println!) immediately before the
assert!, and apply the same pattern to the other occurrence noted around line
239 so test failures include the checked path and result.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 6fe7e66f-e7ce-4f9e-a97b-bd384bd17c2e

📥 Commits

Reviewing files that changed from the base of the PR and between 4bd4571 and 3195049.

📒 Files selected for processing (2)
  • crates/package-manager/src/symlink_direct_dependencies.rs
  • crates/package-manager/src/symlink_direct_dependencies/tests.rs
🚧 Files skipped from review as they are similar to previous changes (1)
  • crates/package-manager/src/symlink_direct_dependencies.rs

Comment thread crates/package-manager/src/symlink_direct_dependencies/tests.rs Outdated

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


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

Comment on lines +107 to +115
symlink_package(
&config
.virtual_store_dir
.join(virtual_store_name)
.join("node_modules")
.join(&name_str),
&config.modules_dir.join(&name_str),
)
.expect("symlink pkg"); // TODO: properly propagate this error
Comment thread crates/package-manager/src/symlink_direct_dependencies.rs Outdated
Comment thread crates/tarball/src/lib.rs Outdated
* `tarball_error_to_request_retry` doc claimed `code` was "derived
  from the variant name" — the implementation actually uses curated
  `ERR_PACQUET_*` constants per match arm. Update the comment so a
  future maintainer doesn't assume a `TarballError` rename
  propagates automatically (Copilot review).

* `pnpm:root added` emit converted `name` to a `String` twice
  (`name_str` and again for `real_name`). Reuse the already-built
  string via clone — the registry name and alias key are the same
  value at this layer (Copilot review).

* Test asserts that aren't `assert_eq!` now log their checked
  values first per CODE_STYLE_GUIDE.md (`eprintln!` for paths,
  `dbg!` for the `Result`) so failures surface diagnostic context
  (CodeRabbit review).
@zkochan zkochan merged commit 33f86d9 into main May 7, 2026
15 checks passed
@zkochan zkochan deleted the feat/reporter-remaining-channels branch May 7, 2026 14:19
KSXGitHub pushed a commit that referenced this pull request May 7, 2026
The merge in `d6e8400` brought in a new `pacquet-tarball` lib test
from #375 that calls `fetch_and_extract_with_retry::<RecordingReporter>`
without the `&AuthHeaders` argument this PR added to the function
signature. Add `&AuthHeaders::default()` to the call site so the
crate compiles.
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.

2 participants