fix: preserve user-defined npm_config_* env vars in lifecycle scripts#12400
Conversation
Code Review by Qodo
Context used 1. Mutable dependency reference
|
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (1)
🚧 Files skipped from review as they are similar to previous changes (1)
📜 Recent 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). (11)
📝 WalkthroughWalkthroughThe parent-environment filtering in the Rust executor ( ChangesPreserve user npm_config_* env vars in lifecycle scripts
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Possibly related PRs
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
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. Comment |
There was a problem hiding this comment.
Pull request overview
Note
Copilot was unable to run its full agentic suite in this review.
This PR updates lifecycle script execution to preserve user-defined npm_config_* environment variables, addressing a regression where npm_-prefixed vars were stripped.
Changes:
- Switch
@pnpm/npm-lifecycleto a GitHub PR ref that contains the env filtering fix. - Add a new lifecycle fixture and test to assert preservation of user-defined
npm_config_*vars. - Add a changeset announcing the patch behavior change for
@pnpm/exec.lifecycleandpnpm.
Reviewed changes
Copilot reviewed 5 out of 6 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| pnpm-workspace.yaml | Points @pnpm/npm-lifecycle to a PR ref that presumably contains the env-var preservation fix. |
| exec/lifecycle/test/index.ts | Adds test coverage ensuring user-defined npm_config_* env vars are preserved. |
| exec/lifecycle/test/fixtures/inspect-user-npm-config/postinstall.js | Fixture script that prints the observed npm_config_platform_arch value. |
| exec/lifecycle/test/fixtures/inspect-user-npm-config/package.json | Adds a fixture package with a postinstall that reports env to the IPC server. |
| .changeset/preserve-user-npm-config-vars.md | Documents the behavioral change and bumps relevant packages (patch). |
Files not reviewed (1)
- pnpm-lock.yaml: Generated file
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
PR Summary by QodoPreserve user-defined npm_config_* env vars in lifecycle scripts WalkthroughsDescription• Preserve user-defined npm_config_* variables when spawning lifecycle scripts. • Continue stripping (npm|pnpm)_config_* auth-related keys to avoid credential leaks. • Add/port TS and Rust tests to prevent regressions; bump @pnpm/npm-lifecycle. Diagramgraph TD
A[("Parent process env")] --> B["pacquet make_env filter"] --> C["Lifecycle hook spawn"] --> D["Child lifecycle script"] --> E["Assert visible/stripped env"]
F["TS runLifecycleHook test"] --> C
G["Rust executor e2e + unit tests"] --> B
High-Level AssessmentThe following are alternative approaches to this PR: 1. Allowlist specific npm_config_* keys to preserve
2. Centralize env filtering logic in a shared crate/module
Recommendation: The PR’s approach (bump to the fixed @pnpm/npm-lifecycle and port the upstream parent-env filter into pacquet) is the most compatible and least disruptive. The key review focus should be the auth-key detection rules (prefix File ChangesBug fix (2)
Tests (4)
Documentation (1)
Other (2)
|
71e46de to
4576c79
Compare
Integrated-Benchmark Report (Linux)Each scenario reports direct installs and pnpr installs. Bencher consumes pacquet@HEAD and pnpr@HEAD. Scenario: Isolated linker: fresh restore, cold cache + cold store
BENCHMARK_REPORT.json{
"results": [
{
"command": "pacquet@HEAD",
"mean": 3.5146504515400006,
"stddev": 0.16488256492057274,
"median": 3.43824416774,
"user": 3.8224788000000003,
"system": 2.0611685,
"min": 3.35294744574,
"max": 3.80192083774,
"times": [
3.71413332274,
3.35294744574,
3.66498553674,
3.37135073774,
3.4607970847400003,
3.80192083774,
3.59503763874,
3.41569125074,
3.39502851674,
3.37461214374
]
},
{
"command": "pacquet@main",
"mean": 3.52338909744,
"stddev": 0.17114545773617412,
"median": 3.44126206924,
"user": 3.8219708000000003,
"system": 2.0709218000000003,
"min": 3.35349795274,
"max": 3.76749740774,
"times": [
3.36351684674,
3.40348364374,
3.37817847674,
3.35349795274,
3.76749740774,
3.41409410274,
3.75512947174,
3.46843003574,
3.61332167274,
3.71674136374
]
},
{
"command": "pnpr@HEAD",
"mean": 1.95817075474,
"stddev": 0.16705735420735077,
"median": 1.95860930774,
"user": 2.8751168,
"system": 1.7690194000000001,
"min": 1.74561520274,
"max": 2.17802403174,
"times": [
1.7504606627400001,
2.13538570274,
2.17802403174,
1.87575839374,
2.04190508774,
1.83292988474,
2.04146022174,
1.84067382174,
2.13949453774,
1.74561520274
]
},
{
"command": "pnpr@main",
"mean": 2.0132962024400003,
"stddev": 0.10764879283226089,
"median": 2.0317080587399996,
"user": 2.827972,
"system": 1.7937020000000001,
"min": 1.8421040877400001,
"max": 2.14255639474,
"times": [
1.96813026674,
2.01454627974,
2.04886983774,
1.93954978374,
2.14255639474,
2.12872284374,
2.09418689274,
2.09345314774,
1.8421040877400001,
1.86084248974
]
}
]
}Scenario: Isolated linker: fresh restore, hot cache + hot store
BENCHMARK_REPORT.json{
"results": [
{
"command": "pacquet@HEAD",
"mean": 0.44536769538000004,
"stddev": 0.012078518454689966,
"median": 0.44295699237999997,
"user": 0.36121633999999997,
"system": 0.76003444,
"min": 0.43023078788,
"max": 0.46314507888,
"times": [
0.44585895288,
0.43070292388000003,
0.45140533788000003,
0.43847567088,
0.46314507888,
0.45358158488,
0.43730614788,
0.46291543688000003,
0.43023078788,
0.44005503188
]
},
{
"command": "pacquet@main",
"mean": 0.4400341124800001,
"stddev": 0.009297694584645085,
"median": 0.43562207838,
"user": 0.35752493999999996,
"system": 0.7590570399999998,
"min": 0.43219617188,
"max": 0.46072948088,
"times": [
0.43523303788,
0.45166010088,
0.43305977088000003,
0.43801140588,
0.44337112888,
0.43516195088,
0.46072948088,
0.43490695788,
0.43601111888,
0.43219617188
]
},
{
"command": "pnpr@HEAD",
"mean": 0.57770548288,
"stddev": 0.13780992483084217,
"median": 0.49255546788000004,
"user": 0.3737044399999999,
"system": 0.7870872399999999,
"min": 0.46839422688,
"max": 0.82137510788,
"times": [
0.46957628188,
0.46839422688,
0.47097401888,
0.49137080888,
0.47153658388,
0.62825561688,
0.82137510788,
0.75580439388,
0.7060276628800001,
0.49374012688
]
},
{
"command": "pnpr@main",
"mean": 0.47835870638000005,
"stddev": 0.016237752469651326,
"median": 0.47619870588,
"user": 0.36463983999999994,
"system": 0.78316614,
"min": 0.46178303788,
"max": 0.51338550388,
"times": [
0.48963473788,
0.51338550388,
0.46511808388000003,
0.48522828888,
0.46178303788,
0.46774729088,
0.46859560988,
0.46299595788000003,
0.48380180188,
0.48529675088
]
}
]
}Scenario: Isolated linker: fresh install, cold cache + cold store
BENCHMARK_REPORT.json{
"results": [
{
"command": "pacquet@HEAD",
"mean": 3.92921579888,
"stddev": 0.036512083042880325,
"median": 3.92151285588,
"user": 3.9564478999999992,
"system": 2.0576589399999996,
"min": 3.8792408958799998,
"max": 3.99264298988,
"times": [
3.91339063788,
3.9171099268800003,
3.8872183378800003,
3.99264298988,
3.96851596688,
3.92591578488,
3.8792408958799998,
3.90325424888,
3.95027879588,
3.95459040388
]
},
{
"command": "pacquet@main",
"mean": 3.9438758882799996,
"stddev": 0.04693621480537176,
"median": 3.93990311788,
"user": 3.9621223,
"system": 2.0583756400000004,
"min": 3.88205475888,
"max": 4.0131665418799995,
"times": [
3.94766331388,
3.89972486988,
3.9978587878800003,
3.95365001588,
3.91716918488,
3.88205475888,
4.0131665418799995,
3.93214292188,
3.99938981688,
3.89593867088
]
},
{
"command": "pnpr@HEAD",
"mean": 1.9758315077800002,
"stddev": 0.10696211262168633,
"median": 1.99075332488,
"user": 2.684778,
"system": 1.7493133399999998,
"min": 1.81691144588,
"max": 2.15700814088,
"times": [
2.15700814088,
1.85737390488,
1.8714292218800002,
2.04204666188,
2.04536971588,
1.9638040378800001,
2.01770261188,
2.05746290988,
1.81691144588,
1.92920642688
]
},
{
"command": "pnpr@main",
"mean": 1.87907213528,
"stddev": 0.06378471708266278,
"median": 1.90243369988,
"user": 2.7291799999999995,
"system": 1.76203564,
"min": 1.78998939288,
"max": 1.98370274488,
"times": [
1.9057636598799998,
1.84530739588,
1.98370274488,
1.80680084288,
1.78998939288,
1.90339614688,
1.93882740388,
1.90147125288,
1.90814226988,
1.80732024288
]
}
]
}Scenario: Isolated linker: fresh install, hot cache + hot store
BENCHMARK_REPORT.json{
"results": [
{
"command": "pacquet@HEAD",
"mean": 1.10555246226,
"stddev": 0.015503653320392134,
"median": 1.1005775996599998,
"user": 1.2840084999999999,
"system": 0.9887237800000002,
"min": 1.08608912716,
"max": 1.1294373951599999,
"times": [
1.10074595116,
1.1294373951599999,
1.10000951716,
1.1004092481599999,
1.0998234091599999,
1.10337719316,
1.1294092361599999,
1.08608912716,
1.11915959316,
1.08706395216
]
},
{
"command": "pacquet@main",
"mean": 1.11982142176,
"stddev": 0.043596806123377,
"median": 1.1095106936599999,
"user": 1.2759361,
"system": 1.00922388,
"min": 1.08426954016,
"max": 1.23607349916,
"times": [
1.09128971916,
1.1223057001599999,
1.08426954016,
1.09668054616,
1.11598321516,
1.0966584851599999,
1.10303817216,
1.23607349916,
1.12031480216,
1.13160053816
]
},
{
"command": "pnpr@HEAD",
"mean": 0.46926945166000006,
"stddev": 0.006592125784060579,
"median": 0.46849961766000003,
"user": 0.31558689999999995,
"system": 0.74021518,
"min": 0.46064922816000003,
"max": 0.48263876116000004,
"times": [
0.46213345516000004,
0.46064922816000003,
0.46727173816000006,
0.46453369416000007,
0.46672194116000004,
0.47491650316000006,
0.48263876116000004,
0.47022649216000006,
0.46972749716000006,
0.47387520616000006
]
},
{
"command": "pnpr@main",
"mean": 0.47765111476,
"stddev": 0.005667873690264281,
"median": 0.4768660491600001,
"user": 0.32273089999999993,
"system": 0.7442259799999998,
"min": 0.46768488216000004,
"max": 0.48944362416000003,
"times": [
0.47883302216000007,
0.47508674216,
0.47462111716000005,
0.47625817416000005,
0.48234090616,
0.46768488216000004,
0.47956062916000003,
0.48944362416000003,
0.47520812616,
0.47747392416000006
]
}
]
}Scenario: Isolated linker: fresh install, cold cache + hot store
BENCHMARK_REPORT.json{
"results": [
{
"command": "pacquet@HEAD",
"mean": 2.7891072544,
"stddev": 0.04203004351770844,
"median": 2.7821681446,
"user": 1.7690864199999996,
"system": 1.2016063,
"min": 2.7468767751,
"max": 2.8833377061,
"times": [
2.7973202191,
2.7600682981,
2.7468767751,
2.7578587211,
2.7524254741000003,
2.7675143191,
2.7968219701,
2.8243111721000003,
2.8045378891,
2.8833377061
]
},
{
"command": "pacquet@main",
"mean": 2.813549419,
"stddev": 0.023616895780627725,
"median": 2.8169694706,
"user": 1.7637086199999998,
"system": 1.212416,
"min": 2.7798307261,
"max": 2.8444770001000004,
"times": [
2.8033994831,
2.8233762791,
2.8239828051,
2.7939460311,
2.8418387481000003,
2.7811590411,
2.8444770001000004,
2.7798307261,
2.8329214141000003,
2.8105626621
]
},
{
"command": "pnpr@HEAD",
"mean": 0.48199071260000004,
"stddev": 0.010894500403428183,
"median": 0.48224600410000007,
"user": 0.31441902000000005,
"system": 0.7403345,
"min": 0.4648001051,
"max": 0.4970492441,
"times": [
0.47583464710000006,
0.4923160401,
0.48096601910000003,
0.47646006310000005,
0.49449430210000006,
0.48352598910000005,
0.4970492441,
0.4648001051,
0.4863344371,
0.46812627910000004
]
},
{
"command": "pnpr@main",
"mean": 0.46899170460000006,
"stddev": 0.004257307512507492,
"median": 0.47080495110000004,
"user": 0.31155531999999997,
"system": 0.7369406000000001,
"min": 0.4618823891,
"max": 0.47421026510000003,
"times": [
0.46518751510000006,
0.46509412510000003,
0.47421026510000003,
0.46517972710000005,
0.4618823891,
0.47212115010000005,
0.47342078610000005,
0.47115142810000005,
0.4704584741,
0.4712111861
]
}
]
} |
|
| Branch | pr/12400 |
| Testbed | pacquet |
Click to view all benchmark results
| Benchmark | Latency | Benchmark Result milliseconds (ms) (Result Δ%) | Upper Boundary milliseconds (ms) (Limit %) |
|---|---|---|---|
| isolated-linker.fresh-install.cold-cache.cold-store | 📈 view plot 🚷 view threshold | 3,929.22 ms(-1.66%)Baseline: 3,995.74 ms | 4,794.89 ms (81.95%) |
| isolated-linker.fresh-install.cold-cache.hot-store | 📈 view plot 🚷 view threshold | 2,789.11 ms(+0.24%)Baseline: 2,782.55 ms | 3,339.05 ms (83.53%) |
| isolated-linker.fresh-install.hot-cache.hot-store | 📈 view plot 🚷 view threshold | 1,105.55 ms(-7.77%)Baseline: 1,198.73 ms | 1,438.47 ms (76.86%) |
| isolated-linker.fresh-restore.cold-cache.cold-store | 📈 view plot 🚷 view threshold | 3,514.65 ms(-13.18%)Baseline: 4,048.39 ms | 4,858.07 ms (72.35%) |
| isolated-linker.fresh-restore.hot-cache.hot-store | 📈 view plot 🚷 view threshold | 445.37 ms(-30.63%)Baseline: 641.98 ms | 770.37 ms (57.81%) |
|
| Branch | pr/12400 |
| Testbed | pnpr |
⚠️ WARNING: No Threshold found!Without a Threshold, no Alerts will ever be generated.
Click here to create a new Threshold
For more information, see the Threshold documentation.
To only post results if a Threshold exists, set the--ci-only-thresholdsflag.
Click to view all benchmark results
| Benchmark | Latency | milliseconds (ms) |
|---|---|---|
| isolated-linker.fresh-install.cold-cache.cold-store | 📈 view plot | 1,975.83 ms |
| isolated-linker.fresh-install.cold-cache.hot-store | 📈 view plot | 481.99 ms |
| isolated-linker.fresh-install.hot-cache.hot-store | 📈 view plot | 469.27 ms |
| isolated-linker.fresh-restore.cold-cache.cold-store | 📈 view plot | 1,958.17 ms |
| isolated-linker.fresh-restore.hot-cache.hot-store | 📈 view plot | 577.71 ms |
4576c79 to
0e45b11
Compare
0e45b11 to
03ba3e0
Compare
…to pacquet Pin the catalog to the released `@pnpm/npm-lifecycle` ^1100.0.0 instead of a mutable PR-head ref, regenerating the lockfile to the immutable registry tarball. Port the upstream env filter to pacquet's make_env so user-defined npm_config_* vars (e.g. npm_config_platform_arch) survive lifecycle scripts while (npm|pnpm)_config_* auth keys are still stripped, matching `@pnpm/npm-lifecycle` 9e2ac78148. Harden the new TS test to save/restore npm_config_platform_arch.
|
Code review by qodo was updated up to the latest commit 5b885fc |
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #12400 +/- ##
=======================================
Coverage 88.14% 88.14%
=======================================
Files 300 300
Lines 39716 39730 +14
=======================================
+ Hits 35006 35020 +14
Misses 4710 4710 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
There was a problem hiding this comment.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
pacquet/crates/executor/src/lifecycle/tests.rs (1)
348-357:⚠️ Potential issue | 🟡 MinorRestore prior env values instead of always removing on drop.
EnvGuardcurrently deletes keys unconditionally inDrop. If either variable was already set before this test, the original process env state is lost for subsequent tests. Capturestd::env::var_osbefore setting and restore that value on drop (remove only when it was originally absent).Suggested patch
- struct EnvGuard(&'static str); + struct EnvGuard { + key: &'static str, + prev: Option<std::ffi::OsString>, + } + impl EnvGuard { + fn set(key: &'static str, value: &str) -> Self { + let prev = std::env::var_os(key); + // SAFETY: guarded test-local mutation; cleanup in Drop. + unsafe { std::env::set_var(key, value) } + Self { key, prev } + } + } impl Drop for EnvGuard { fn drop(&mut self) { - // SAFETY: nextest runs each test in its own thread, so the - // only risk is sibling tests calling `env::vars()` - // concurrently — this `Drop` still runs on panic. - unsafe { std::env::remove_var(self.0) } + match &self.prev { + Some(v) => unsafe { std::env::set_var(self.key, v) }, + None => unsafe { std::env::remove_var(self.key) }, + } } } - let _user_guard = EnvGuard("npm_config_platform_arch"); - let _auth_guard = EnvGuard("npm_config__authtoken"); - // SAFETY: nextest runs each test in its own thread, so the only - // risk is sibling tests calling `env::vars()` concurrently — the - // guards' `Drop` removes the vars even on panic. - unsafe { - std::env::set_var("npm_config_platform_arch", "x64"); - std::env::set_var("npm_config__authtoken", "should-not-leak"); - } + let _user_guard = EnvGuard::set("npm_config_platform_arch", "x64"); + let _auth_guard = EnvGuard::set("npm_config__authtoken", "should-not-leak");🤖 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/executor/src/lifecycle/tests.rs` around lines 348 - 357, The EnvGuard struct currently only stores the environment variable name and unconditionally removes it in the Drop implementation, which loses the original value if that variable was already set before the test. Modify the EnvGuard struct to capture and store the original environment variable value using std::env::var_os before any modifications are made. Then update the Drop implementation's drop method to restore the previous value (using std::env::set_var if it existed, or remove it only if it was originally absent) instead of unconditionally calling std::env::remove_var. This ensures that the original process environment state is preserved for subsequent tests.
🤖 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/executor/src/lifecycle/tests.rs`:
- Around line 348-357: The EnvGuard struct currently only stores the environment
variable name and unconditionally removes it in the Drop implementation, which
loses the original value if that variable was already set before the test.
Modify the EnvGuard struct to capture and store the original environment
variable value using std::env::var_os before any modifications are made. Then
update the Drop implementation's drop method to restore the previous value
(using std::env::set_var if it existed, or remove it only if it was originally
absent) instead of unconditionally calling std::env::remove_var. This ensures
that the original process environment state is preserved for subsequent tests.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro Plus
Run ID: fe18acd8-d79f-4cb4-a4bb-b47c00b839f3
⛔ Files ignored due to path filters (1)
pnpm-lock.yamlis excluded by!**/pnpm-lock.yaml
📒 Files selected for processing (9)
.changeset/preserve-user-npm-config-vars.mdexec/lifecycle/test/fixtures/inspect-frozen-lockfile/postinstall.jsexec/lifecycle/test/fixtures/inspect-npm-config-env/package.jsonexec/lifecycle/test/fixtures/inspect-npm-config-env/postinstall.jsexec/lifecycle/test/index.tspacquet/crates/executor/src/lifecycle/tests.rspacquet/crates/executor/src/make_env.rspacquet/crates/executor/src/make_env/tests.rspnpm-workspace.yaml
💤 Files with no reviewable changes (1)
- exec/lifecycle/test/fixtures/inspect-frozen-lockfile/postinstall.js
✅ Files skipped from review due to trivial changes (3)
- exec/lifecycle/test/fixtures/inspect-npm-config-env/postinstall.js
- .changeset/preserve-user-npm-config-vars.md
- exec/lifecycle/test/fixtures/inspect-npm-config-env/package.json
🚧 Files skipped from review as they are similar to previous changes (1)
- exec/lifecycle/test/index.ts
📜 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). (7)
- GitHub Check: ubuntu-latest / Node.js 24 / Test
- GitHub Check: Run benchmark on ubuntu-latest
- GitHub Check: Dylint
- GitHub Check: Lint and Test (macos-latest)
- GitHub Check: Lint and Test (ubuntu-latest)
- GitHub Check: Lint and Test (windows-latest)
- GitHub Check: Code Coverage
🧰 Additional context used
📓 Path-based instructions (1)
pacquet/**/*.rs
📄 CodeRabbit inference engine (pacquet/AGENTS.md)
pacquet/**/*.rs: Match how the same feature is implemented in the TypeScript pnpm CLI in pnpm/, pkg-manager/, resolving/, lockfile/, store/, fetching/, config/, hooks/, and other TypeScript workspaces — behavior, flags, defaults, error codes, file formats, and directory layout must match pnpm exactly
When porting a function that fires pnpm: events through globalLogger, logger.debug(...), or streamParser.write(...), mirror the call site, payload, and ordering so@pnpm/cli.default-reporterparses pacquet's NDJSON the same way it parses pnpm's. Follow the Reporter / log events convention for channel mapping, threading R: Reporter, emit-site placement, and recording-fake tests.
Prefer real fixtures using tempfile::TempDir, the mocked registry, or integration tests over dependency-injection seams for testing. Only use the DI seam (Host capability trait, Sys bounds, etc.) for branches real fixtures cannot cover: filesystem error kinds (PermissionDenied, ENOSPC), deterministic time, shared process-global state (env::set_var, set_current_dir, umask), or external-service happy paths (pnpm login 2FA, pnpm publish OIDC/provenance). Follow the eight principles and naming conventions (Sys, Host, Fs*, Clock, EnvVar) documented in CODE_STYLE_GUIDE.md.
Declare a newtype wrapper when porting code using branded string types from TypeScript pnpm. Do not collapse the brand into plain String or &str. Give the type its own struct so misuse is a type error.
If upstream always validates before construction of a branded string type, validate too. The Rust wrapper must construct only via TryFrom and/or FromStr. Do not provide an infallible public constructor that takes an arbitrary string.
If upstream never validates a branded string type, just brand for type-safety. Expose an infallible From and From<&str> when convenient. The type-safety win is the whole point, and no validator is needed.
If upstream occasionally constructs a branded string type without validatio...
Files:
pacquet/crates/executor/src/make_env/tests.rspacquet/crates/executor/src/lifecycle/tests.rspacquet/crates/executor/src/make_env.rs
🧠 Learnings (5)
📚 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/executor/src/make_env/tests.rspacquet/crates/executor/src/lifecycle/tests.rspacquet/crates/executor/src/make_env.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/executor/src/make_env/tests.rspacquet/crates/executor/src/lifecycle/tests.rspacquet/crates/executor/src/make_env.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/executor/src/make_env/tests.rspacquet/crates/executor/src/lifecycle/tests.rspacquet/crates/executor/src/make_env.rs
📚 Learning: 2026-06-06T18:58:37.156Z
Learnt from: zkochan
Repo: pnpm/pnpm PR: 12243
File: pacquet/crates/package-manager/src/install_package_by_snapshot.rs:319-322
Timestamp: 2026-06-06T18:58:37.156Z
Learning: When reviewing Rust code, do not assume `matches!(expr, Pattern(_))` will move out of `expr` if `Pattern(_)` contains no by-value bindings. `matches!` desugars to a `match` that auto-borrows the scrutinee for discrimination, so even if `expr` is a non-`Copy` value behind a shared reference (e.g., `&T`), the macro should not move-out of the borrowed data purely due to `matches!`. Treat `matches!(&expr, Pattern(_))` as a readability/clarity improvement, not a correctness requirement. Only flag potential move-out-of-borrow risks when the pattern includes by-value bindings that would require moving the matched value.
Applied to files:
pacquet/crates/executor/src/make_env/tests.rspacquet/crates/executor/src/lifecycle/tests.rspacquet/crates/executor/src/make_env.rs
📚 Learning: 2026-06-12T20:41:57.558Z
Learnt from: zkochan
Repo: pnpm/pnpm PR: 12364
File: pacquet/crates/package-manager/src/install/tests.rs:5717-5717
Timestamp: 2026-06-12T20:41:57.558Z
Learning: In the pnpm/pnpm Rust workspace (toolchain Rust 1.95.0), keep using `std::time::Duration::from_mins(...)` and `std::time::Duration::from_hours(...)` as-is. Do not flag these as invalid or suggest replacing them with `Duration::from_secs(...)`, because Clippy’s `clippy::duration_suboptimal_units` lint is denied by `-D warnings` in CI, and changing to seconds may trigger CI failures.
Applied to files:
pacquet/crates/executor/src/make_env/tests.rspacquet/crates/executor/src/lifecycle/tests.rspacquet/crates/executor/src/make_env.rs
🔇 Additional comments (2)
pacquet/crates/executor/src/make_env.rs (1)
11-13: LGTM!Also applies to: 41-44, 57-64, 131-134, 147-159, 164-172, 181-201
pacquet/crates/executor/src/make_env/tests.rs (1)
33-48: LGTM!Also applies to: 51-59, 82-102, 282-317, 340-367
The guard removed the seeded var unconditionally on drop, which would discard any value the process env already had. Capture the original via var_os and restore it (or remove only when originally absent).
|
Code review by qodo was updated up to the latest commit b65f393 |
|
Congrats on merging your first pull request! 🎉🎉🎉 |
|
Thanks a lot for completing this PR as well, @zkochan! It's very nice to learn that pnpm is being rewritten in Rust. |
The actual fix is pnpm/npm-lifecycle#59. This PR only bumps
@pnpm/npm-lifecycleto the version that contains the fix (WIP) and a adds a test to ensure this issue doesn't get reintroduced yet again.Closes #12399
Summary by CodeRabbit
Bug Fixes
npm_config_*values are preserved during execution, preventing accidental removal of settings likenpm_config_platform_arch. Sensitive authentication-relatednpm_config_*/pnpm_config_*credentials continue to be filtered out as before.Tests