feat: support URL-scoped registry auth via npm_config_// and pnpm_config_// env vars#12338
Conversation
…fig_// env vars Adds a file-free way to configure registry authentication, e.g. npm_config_//registry.npmjs.org/:_authToken=<token> pnpm_config_//registry.npmjs.org/:_authToken=<token> These are host-scoped by construction — the registry the value applies to is encoded in the (trusted) variable name — so they cannot be redirected to another host by repository-controlled config. The env value is trusted: it overrides a project/workspace .npmrc but is still overridden by CLI options. pnpm_config_ wins over npm_config_ for the same key.
Code Review by Qodo
1. Pacquet ignores TLS env vars
|
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
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 (2)
📜 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). (7)
🧰 Additional context used📓 Path-based instructions (2)pacquet/**/*.rs📄 CodeRabbit inference engine (pacquet/AGENTS.md)
Files:
**/*.{ts,tsx,js,jsx}📄 CodeRabbit inference engine (AGENTS.md)
Files:
🧠 Learnings (7)📚 Learning: 2026-05-20T19:40:55.051ZApplied to files:
📚 Learning: 2026-05-22T00:08:44.646ZApplied to files:
📚 Learning: 2026-05-20T23:07:58.444ZApplied to files:
📚 Learning: 2026-06-06T18:58:37.156ZApplied to files:
📚 Learning: 2026-05-14T09:04:00.133ZApplied to files:
📚 Learning: 2026-06-05T13:47:05.929ZApplied to files:
📚 Learning: 2026-06-05T13:47:26.046ZApplied to files:
🪛 Betterleaks (1.3.1)config/reader/test/index.ts[high] 1107-1107: Detected a Generic API Key, potentially exposing access to various services and sensitive operations. (generic-api-key) 🔇 Additional comments (2)
📝 WalkthroughWalkthroughThis PR adds support for configuring URL-scoped registry authentication via environment variables using ChangesURL-scoped environment configuration
Sequence Diagram(s)sequenceDiagram
participant loadNpmrcConfig as loadNpmrcConfig
participant EnvProvider as EnvVar::vars()
participant Parser as readUrlScopedEnvConfig
participant Merger as mergedConfig/trustedConfig/rawConfig
loadNpmrcConfig->>EnvProvider: request env variables
EnvProvider->>Parser: provide (name,value) list
Parser->>Parser: match npm_config_//… / pnpm_config_//…, filter, merge pnpm_ over npm_
Parser-->>loadNpmrcConfig: envScopedConfig
loadNpmrcConfig->>Merger: prepend env-scoped source into merge chain
Merger->>Merger: fold env-scoped before project/auth.ini/user sources
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Possibly related issues
Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Warning There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure. 🔧 ESLint
ESLint install timed out. The project may have too many dependencies for the sandbox. 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 |
PR Summary by QodoSupport URL-scoped registry auth via npm_config_// and pnpm_config_// WalkthroughsDescription• Support URL-scoped registry auth via npm_config_//… and pnpm_config_//… env vars • Merge env-scoped credentials as trusted config above workspace .npmrc but below CLI • Add tests for env parsing, precedence, and workspace override behavior Diagramgraph TD
A["loadNpmrcConfig"] --> M["merge priority"] --> O["mergedConfig + trustedConfig"]
W["workspace .npmrc"] --> M
U["user ~/.npmrc"] --> M
I["pnpm auth.ini"] --> M
E["env (//-scoped)"] --> M
C["CLI options"] --> M
subgraph Legend
direction LR
_proc["Function"] ~~~ _file["Config file"] ~~~ _env["Environment"]
end
High-Level AssessmentThe following are alternative approaches to this PR: 1. Honor all `npm_config_*`/`pnpm_config_*` env vars (not just `//`-scoped)
2. Add a single unscoped token env var (e.g. `PNPM_AUTH_TOKEN`)
3. Delegate parsing to an npm config/env library
Recommendation: The PR’s approach (only File ChangesEnhancement (1)
Tests (1)
Documentation (1)
|
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (1)
config/reader/test/index.ts (1)
983-1045: ⚡ Quick winAdd mixed/upper-case prefix coverage for the new env-key parsing contract.
These tests validate lowercase
npm_config_//andpnpm_config_//, but not the case-insensitive prefix behavior promised by this feature. Please add at least one test usingNPM_CONFIG_//...and/orPnPm_Config_//...(including precedence) to lock that contract.🤖 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 `@config/reader/test/index.ts` around lines 983 - 1045, Add tests mirroring the existing URL-scoped env-var cases but using mixed/upper-case prefixes to assert case-insensitive parsing: create a test that calls getConfig (after prepareEmpty) with env containing 'NPM_CONFIG_//env-test.example/:_authToken' (or 'PnPm_Config_//env-test.example/:_authToken') and assert expect(config.authConfig['//env-test.example/:_authToken']).toBe('...'); also add a precedence test where a mixed-case 'pnpm' style key overrides a lowercase 'npm_config_//' key (use the same pattern as the existing "pnpm_config_// takes precedence..." test), referencing getConfig, prepareEmpty and config.authConfig to locate where to add them.
🤖 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.
Inline comments:
In @.changeset/quick-registries-env-auth.md:
- Around line 8-11: The fenced code block that contains the lines
"npm_config_//registry.npmjs.org/:_authToken=<token>" and
"pnpm_config_//registry.npmjs.org/:_authToken=<token>" is missing a language
hint and triggers MD040; fix it by changing the opening fence to include a
language identifier (e.g., use ```text) so the block becomes a labeled fenced
code block and the lint warning is resolved.
---
Nitpick comments:
In `@config/reader/test/index.ts`:
- Around line 983-1045: Add tests mirroring the existing URL-scoped env-var
cases but using mixed/upper-case prefixes to assert case-insensitive parsing:
create a test that calls getConfig (after prepareEmpty) with env containing
'NPM_CONFIG_//env-test.example/:_authToken' (or
'PnPm_Config_//env-test.example/:_authToken') and assert
expect(config.authConfig['//env-test.example/:_authToken']).toBe('...'); also
add a precedence test where a mixed-case 'pnpm' style key overrides a lowercase
'npm_config_//' key (use the same pattern as the existing "pnpm_config_// takes
precedence..." test), referencing getConfig, prepareEmpty and config.authConfig
to locate where to add them.
🪄 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: Organization UI
Review profile: CHILL
Plan: Pro Plus
Run ID: 290f0230-a56d-42dd-81f4-fd640d4e2982
📒 Files selected for processing (3)
.changeset/quick-registries-env-auth.mdconfig/reader/src/loadNpmrcFiles.tsconfig/reader/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). (1)
- GitHub Check: ubuntu-latest / Node.js 24 / Test
🧰 Additional context used
📓 Path-based instructions (1)
**/*.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (AGENTS.md)
**/*.{ts,tsx,js,jsx}: Use Standard Style with trailing commas, prefer functions over classes, declare functions after they are used (relying on hoisting), limit functions to no more than two or three arguments, and use a single options object for functions needing more parameters
Follow import order: standard libraries first, then external dependencies (sorted alphabetically), then relative imports
Do not write comments that restate what the code already says; rename variables, split helpers, or move checks to more obvious places instead
Do not repeat documentation at call sites that already lives on the callee; update the JSDoc once and let every call site benefit
Use JSDoc for the function's contract (preconditions, postconditions, edge cases, why the function exists), not for re-narrating the function body
Do not record past implementation shape, refactor history, or removed code in comments; use git log and git blame for that information instead
Write comments only when the reason for code is non-obvious, a hidden invariant exists, a workaround for a known bug is needed, or an exception to surrounding pattern is deliberate
Files:
config/reader/test/index.tsconfig/reader/src/loadNpmrcFiles.ts
🧠 Learnings (4)
📚 Learning: 2026-05-26T21:01:06.666Z
Learnt from: zkochan
Repo: pnpm/pnpm PR: 11966
File: .changeset/require-tarball-integrity.md:6-6
Timestamp: 2026-05-26T21:01:06.666Z
Learning: In pnpm lockfile-related release notes/docs (especially changeset markdown), preserve URL hostnames exactly as they appear in pnpm-lock.yaml tarball resolution entries—keep hosts like `codeload.github.com`, `bitbucket.org`, and `gitlab.com` in lowercase. Do not “correct” them to title-case/preserve brand capitalization (e.g., LanguageTool rules like `GITHUB` capitalization) because these are literal URL fragments, not platform brand names.
Applied to files:
.changeset/quick-registries-env-auth.md
📚 Learning: 2026-05-14T09:04:00.133Z
Learnt from: zkochan
Repo: pnpm/pnpm PR: 11622
File: resolving/npm-resolver/test/publishedBy.test.ts:350-354
Timestamp: 2026-05-14T09:04:00.133Z
Learning: In the pnpm/pnpm repository, ESLint is the authoritative style linter. Do not raise review findings for missing trailing commas in multiline function calls (e.g., `fs.writeFileSync(...)`) when this repo’s ESLint configuration does not report them and lint passes. Prefer deferring to the ESLint results for this specific trailing-comma rule rather than enforcing it manually in code review.
Applied to files:
config/reader/test/index.tsconfig/reader/src/loadNpmrcFiles.ts
📚 Learning: 2026-06-05T13:47:05.929Z
Learnt from: vsumner
Repo: pnpm/pnpm PR: 12190
File: installing/deps-installer/test/install/frozenStore.ts:2-17
Timestamp: 2026-06-05T13:47:05.929Z
Learning: In the pnpm/pnpm repository, the shared Jest preset keeps `injectGlobals` at its default (`true`), so `test` and `expect` are available as Jest globals. Therefore, reviewers should not flag (or treat as TypeScript/compilation errors) missing `import { test, expect } from 'jest/globals'` when a test file uses `test`/`expect` without importing them. Importing from `jest/globals` may still be used for consistency with sibling files, but it is not required for execution in this repo unless a Jest preset is explicitly configured with `injectGlobals: false`.
Applied to files:
config/reader/test/index.ts
📚 Learning: 2026-06-05T13:47:26.046Z
Learnt from: vsumner
Repo: pnpm/pnpm PR: 12190
File: installing/deps-installer/src/install/index.ts:2337-2343
Timestamp: 2026-06-05T13:47:26.046Z
Learning: In the pnpm/pnpm codebase, `PnpmError` automatically prefixes `err.code` with `ERR_PNPM_` when you pass a code that does not already start with `ERR_PNPM_` (it normalizes `this.code` via `code.startsWith('ERR_PNPM_') ? code : `ERR_PNPM_${code}``). Therefore, during code review you should NOT flag `new PnpmError(...)` call sites for passing a bare error code (e.g., `new PnpmError('FROZEN_STORE_INCOMPATIBLE_WITH_PNPR', ...)`); the resulting `err.code` will still be `ERR_PNPM_FROZEN_STORE_INCOMPATIBLE_WITH_PNPR`.
Applied to files:
config/reader/test/index.tsconfig/reader/src/loadNpmrcFiles.ts
🪛 markdownlint-cli2 (0.22.1)
.changeset/quick-registries-env-auth.md
[warning] 8-8: Fenced code blocks should have a language specified
(MD040, fenced-code-language)
🪛 OpenGrep (1.22.0)
config/reader/src/loadNpmrcFiles.ts
[ERROR] 177-177: Dynamic command passed to child_process.exec/execSync. Use child_process.execFile or spawn with an argument array instead.
(coderabbit.command-injection.exec-js)
🔇 Additional comments (1)
config/reader/src/loadNpmrcFiles.ts (1)
16-17: LGTM!Also applies to: 88-93, 117-130, 145-145, 160-183
… pnpm_config_// env vars Pacquet parity for the same feature on the JS side: read URL-scoped registry credentials from npm_config_//… and pnpm_config_//… environment variables (e.g. npm_config_//registry.npmjs.org/:_authToken=<token>). These are trusted (sourced from the environment, not the repository) and host-scoped by construction, so they sit at the top of the .npmrc precedence chain — above the project .npmrc. pnpm_config_ wins over npm_config_ for the same key. Adds an EnvVar::vars() enumeration capability (default empty, so existing fakes keep compiling; production providers override it).
|
Code review by qodo was updated up to the latest commit a5454c1 |
Micro-Benchmark ResultsLinux |
There was a problem hiding this comment.
Actionable comments posted: 2
🧹 Nitpick comments (1)
pacquet/crates/config/src/npmrc_auth/tests.rs (1)
1067-1122: ⚡ Quick winAdd a mixed-case prefix regression.
These new tests only exercise lowercase
npm_config_/pnpm_config_, but this feature's contract is case-insensitive prefix matching. OneNPM_CONFIG_//...or mixed-casePnPm_Config_//...case would keep that guarantee locked in. Based on the PR objectives, case-insensitive prefix matching is part of the intended behavior.🤖 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/config/src/npmrc_auth/tests.rs` around lines 1067 - 1122, The tests only cover lowercase prefixes but the prefix matching must be case-insensitive; add new tests that set mixed-case and upper-case env keys and assert NpmrcAuth::from_url_scoped_env::<Env>() treats "NPM_CONFIG_//registry.npmjs.org/:_authToken" and a mixed "PnPm_Config_//registry.npmjs.org/:_authToken" the same as their lowercase equivalents. Add at least: (1) a test that uses "NPM_CONFIG_//registry.npmjs.org/:_authToken" and expects the auth token to be read, and (2) a precedence test that sets both mixed-case pnpm and npm prefixed vars (e.g., "PnPm_Config_..." and "npm_CONFIG_...") and asserts the pnpm-prefixed value wins, mirroring the existing url_scoped_env_reads_* and url_scoped_env_pnpm_prefix_wins_over_npm tests but using mixed-case keys to ensure from_url_scoped_env handles prefixes case-insensitively.
🤖 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.
Inline comments:
In `@pacquet/crates/config/src/npmrc_auth.rs`:
- Around line 835-844: parse_url_scoped_env_name can panic on non-ASCII env var
names due to direct byte-slicing (name[..prefix.len()]) — replace those slices
with boundary-checked reads using name.get(..prefix.len()) and
name.get(prefix.len()..) so you only call eq_ignore_ascii_case and starts_with
on valid &str slices; update the loop in parse_url_scoped_env_name to first
obtain the prefix slice via get(..prefix.len()) and the remainder via
get(prefix.len()..) and only then perform the case-insensitive prefix check and
rest.starts_with("//"), returning Some((is_pnpm, rest)) with the safe rest slice
when matched.
In `@pacquet/crates/env-replace/src/lib.rs`:
- Around line 74-75: Update the env-var collectors to use std::env::vars_os()
and skip non-UTF-8 entries: replace uses of std::env::vars().collect() (e.g.,
the vars() function in SystemEnv in pacquet/crates/env-replace/src/lib.rs and
Host::vars in pacquet/crates/config/src/api.rs) with an iterator over
std::env::vars_os() and collect only pairs where both key and value can be
converted to String (use into_string().ok() or equivalent), preserving iteration
order and returning Vec<(String,String)> as before.
---
Nitpick comments:
In `@pacquet/crates/config/src/npmrc_auth/tests.rs`:
- Around line 1067-1122: The tests only cover lowercase prefixes but the prefix
matching must be case-insensitive; add new tests that set mixed-case and
upper-case env keys and assert NpmrcAuth::from_url_scoped_env::<Env>() treats
"NPM_CONFIG_//registry.npmjs.org/:_authToken" and a mixed
"PnPm_Config_//registry.npmjs.org/:_authToken" the same as their lowercase
equivalents. Add at least: (1) a test that uses
"NPM_CONFIG_//registry.npmjs.org/:_authToken" and expects the auth token to be
read, and (2) a precedence test that sets both mixed-case pnpm and npm prefixed
vars (e.g., "PnPm_Config_..." and "npm_CONFIG_...") and asserts the
pnpm-prefixed value wins, mirroring the existing url_scoped_env_reads_* and
url_scoped_env_pnpm_prefix_wins_over_npm tests but using mixed-case keys to
ensure from_url_scoped_env handles prefixes case-insensitively.
🪄 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: Organization UI
Review profile: CHILL
Plan: Pro Plus
Run ID: 33894fd0-6058-4653-8a09-6bfcd9501570
📒 Files selected for processing (5)
pacquet/crates/config/src/api.rspacquet/crates/config/src/lib.rspacquet/crates/config/src/npmrc_auth.rspacquet/crates/config/src/npmrc_auth/tests.rspacquet/crates/env-replace/src/lib.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: Lint and Test (ubuntu-latest)
- GitHub Check: Dylint
- GitHub Check: Lint and Test (macos-latest)
- GitHub Check: Format
- GitHub Check: Lint and Test (windows-latest)
- GitHub Check: Run benchmark on ubuntu-latest
- GitHub Check: Code Coverage
- GitHub Check: Run benchmark on ubuntu-latest
- GitHub Check: Analyze (javascript)
- 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 firespnpm:<channel>events throughglobalLogger,logger.debug(...), orstreamParser.write(...), mirror the call site, payload, and ordering so@pnpm/cli.default-reporterparses pacquet's NDJSON the same way
Declare a newtype wrapper for branded string types instead of collapsing the brand into a plainStringor&strin Rust
If upstream TypeScript always validates before construction of a branded string, validate in the Rust wrapper too viaTryFrom<String>and/orFromStrand 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 infallibleFrom<String>constructor
If upstream TypeScript occasionally constructs a branded string without validation, exposefrom_str_uncheckedin 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 handwritingimplblocks; use manualimplonly when conversion needs custom logic
Model TypeScript string literal unions (like'auto' | 'always' | 'never') as Rustenums 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 inCODE_STYLE_GUIDE.md— imports, modules, naming, ownership and borrowing, parameter type selection, trait bounds, pattern matching,pipe-trait, error handling, test layout, and cloning ofArcandRc
Choose owned vs. borrowed parameters to minimize copies; widen to t...
Files:
pacquet/crates/env-replace/src/lib.rspacquet/crates/config/src/api.rspacquet/crates/config/src/npmrc_auth/tests.rspacquet/crates/config/src/lib.rspacquet/crates/config/src/npmrc_auth.rs
🧠 Learnings (4)
📚 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/env-replace/src/lib.rspacquet/crates/config/src/api.rspacquet/crates/config/src/npmrc_auth/tests.rspacquet/crates/config/src/lib.rspacquet/crates/config/src/npmrc_auth.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/env-replace/src/lib.rspacquet/crates/config/src/api.rspacquet/crates/config/src/npmrc_auth/tests.rspacquet/crates/config/src/lib.rspacquet/crates/config/src/npmrc_auth.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/env-replace/src/lib.rspacquet/crates/config/src/api.rspacquet/crates/config/src/npmrc_auth/tests.rspacquet/crates/config/src/lib.rspacquet/crates/config/src/npmrc_auth.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/env-replace/src/lib.rspacquet/crates/config/src/api.rspacquet/crates/config/src/npmrc_auth/tests.rspacquet/crates/config/src/lib.rspacquet/crates/config/src/npmrc_auth.rs
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #12338 +/- ##
=======================================
Coverage 87.74% 87.74%
=======================================
Files 291 291
Lines 36061 36107 +46
=======================================
+ Hits 31642 31683 +41
- Misses 4419 4424 +5 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
…ve tests Address review feedback on #12338: - A `//host/:tokenHelper` env var would land in authConfig but trip the TOKEN_HELPER_IN_PROJECT_CONFIG guard (which only trusts the user .npmrc), incorrectly failing. tokenHelper names an executable, so it is now excluded from the env-scoped layer entirely. - Add tests for case-insensitive prefix matching and the tokenHelper exclusion. - Add a 'text' language hint to the changeset's fenced block (MD040).
|
Thanks both — addressed in 9127925: Qodo #1 — env Qodo #2 — empty env value ignored (working as intended). Treating an empty env var as unset matches both npm's own CodeRabbit — case-insensitive coverage (valid). Added a test using CodeRabbit — changeset MD040 (valid). Added a Full |
|
Code review by qodo was updated up to the latest commit 9127925 |
Address CodeRabbit review on the pacquet env-auth code: - EnvVar::vars() used std::env::vars(), which panics if any env var name or value is not valid UTF-8. Iterate vars_os() and skip non-UTF-8 entries, matching var()'s .ok() behavior. (SystemEnv and Host.) - parse_url_scoped_env_name sliced with name[..prefix.len()], which panics when the byte index lands inside a multi-byte char. Use boundary-checked name.get(..) instead. - Add a regression test with non-ASCII env var names.
|
Code review by qodo was updated up to the latest commit 4e97cb1 |
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": 4.130588901179999,
"stddev": 0.1053829406687776,
"median": 4.08981175058,
"user": 3.565938799999999,
"system": 3.44015994,
"min": 4.0219887195799995,
"max": 4.32765551058,
"times": [
4.10408457958,
4.18917116358,
4.0219887195799995,
4.06462956558,
4.13375457658,
4.02819100758,
4.32765551058,
4.07474873658,
4.07553892158,
4.28612623058
]
},
{
"command": "pacquet@main",
"mean": 4.174030940980001,
"stddev": 0.12987779575576672,
"median": 4.13898296608,
"user": 3.6263727,
"system": 3.43482214,
"min": 4.0115576615799995,
"max": 4.32522622958,
"times": [
4.07237080058,
4.07189878758,
4.31790834058,
4.0115576615799995,
4.31135231858,
4.04674393358,
4.32522622958,
4.19531470258,
4.30528540558,
4.08265122958
]
},
{
"command": "pnpr@HEAD",
"mean": 2.3431021888799997,
"stddev": 0.10826497482764484,
"median": 2.39062348758,
"user": 2.5955169,
"system": 2.96846584,
"min": 2.14573106258,
"max": 2.45813758058,
"times": [
2.45813758058,
2.39698379758,
2.45274198758,
2.39197506458,
2.26705321958,
2.3892719105799998,
2.28319552358,
2.21416392058,
2.14573106258,
2.43176782158
]
},
{
"command": "pnpr@main",
"mean": 2.3817968283799997,
"stddev": 0.1459930408671415,
"median": 2.37788541308,
"user": 2.6471484999999997,
"system": 3.01217284,
"min": 2.18478720358,
"max": 2.59800127258,
"times": [
2.18478720358,
2.54044370458,
2.44859528858,
2.30717553758,
2.51044142958,
2.45338275758,
2.59800127258,
2.2192166535799998,
2.3044179425799998,
2.25150649358
]
}
]
}Scenario: Isolated linker: fresh restore, hot cache + hot store
BENCHMARK_REPORT.json{
"results": [
{
"command": "pacquet@HEAD",
"mean": 0.6582940033600002,
"stddev": 0.012890361823739488,
"median": 0.6553054782600001,
"user": 0.37269924,
"system": 1.32237182,
"min": 0.6410253877600001,
"max": 0.68502047476,
"times": [
0.6534539957600001,
0.64602976976,
0.66838583076,
0.64927187876,
0.6410253877600001,
0.6525563737600001,
0.6571569607600001,
0.6657877787600001,
0.6642515827600001,
0.68502047476
]
},
{
"command": "pacquet@main",
"mean": 0.6974134722600001,
"stddev": 0.11996947665554031,
"median": 0.6625083457600001,
"user": 0.37659844000000003,
"system": 1.3334202199999996,
"min": 0.6368415577600001,
"max": 1.03621422476,
"times": [
0.66168131976,
0.66333537176,
0.6368415577600001,
0.65518459776,
0.64300165176,
0.6509351827600001,
0.66898990276,
0.66781543876,
1.03621422476,
0.69013547476
]
},
{
"command": "pnpr@HEAD",
"mean": 0.77987758516,
"stddev": 0.014669094404958902,
"median": 0.78001117076,
"user": 0.38156924,
"system": 1.35235802,
"min": 0.75636594576,
"max": 0.80554290876,
"times": [
0.80554290876,
0.7907356617600001,
0.75636594576,
0.78309162876,
0.7897739437600001,
0.77693071276,
0.7680603237600001,
0.77140693276,
0.76671389276,
0.79015390076
]
},
{
"command": "pnpr@main",
"mean": 0.79107522986,
"stddev": 0.06700543417876474,
"median": 0.76656350626,
"user": 0.39003154000000007,
"system": 1.3350501199999996,
"min": 0.74593229776,
"max": 0.97516134976,
"times": [
0.75039619976,
0.74593229776,
0.7999572677600001,
0.79081119176,
0.76420579176,
0.76505910876,
0.7625437917600001,
0.78861739576,
0.97516134976,
0.7680679037600001
]
}
]
}Scenario: Isolated linker: fresh install, cold cache + cold store
BENCHMARK_REPORT.json{
"results": [
{
"command": "pacquet@HEAD",
"mean": 3.7852603801399995,
"stddev": 0.04525276696228089,
"median": 3.7778403748400002,
"user": 3.4475365599999996,
"system": 3.33075706,
"min": 3.71122334534,
"max": 3.85587891034,
"times": [
3.80822054634,
3.84085414234,
3.73332979834,
3.77788045534,
3.77007080834,
3.85587891034,
3.76334383234,
3.71122334534,
3.77780029434,
3.81400166834
]
},
{
"command": "pacquet@main",
"mean": 3.80552445794,
"stddev": 0.04387349096880973,
"median": 3.82359659634,
"user": 3.5067879599999996,
"system": 3.3468189599999993,
"min": 3.73393808434,
"max": 3.8556830933399997,
"times": [
3.73393808434,
3.76037308634,
3.8537684513399997,
3.83583068634,
3.82603132934,
3.78211940334,
3.75521736434,
3.8556830933399997,
3.82116186334,
3.8311212173399998
]
},
{
"command": "pnpr@HEAD",
"mean": 2.2525341112399997,
"stddev": 0.13585249037144428,
"median": 2.32225879734,
"user": 2.40095796,
"system": 2.8867610599999995,
"min": 2.06080946234,
"max": 2.41142109634,
"times": [
2.38411070034,
2.15713195934,
2.34153007334,
2.33785660534,
2.10709783734,
2.30666098934,
2.0776868453399997,
2.06080946234,
2.34103554334,
2.41142109634
]
},
{
"command": "pnpr@main",
"mean": 2.20219619364,
"stddev": 0.13751761030210283,
"median": 2.16803269034,
"user": 2.472585959999999,
"system": 2.88570766,
"min": 2.01126496934,
"max": 2.39642071334,
"times": [
2.35588851334,
2.15283897134,
2.01126496934,
2.05300799034,
2.39642071334,
2.18322640934,
2.1259243733399997,
2.34128019034,
2.09573835434,
2.30637145134
]
}
]
}Scenario: Isolated linker: fresh install, hot cache + hot store
BENCHMARK_REPORT.json{
"results": [
{
"command": "pacquet@HEAD",
"mean": 1.07083381184,
"stddev": 0.010641203856715315,
"median": 1.0721501682399999,
"user": 1.06593962,
"system": 1.6556435599999997,
"min": 1.04840370124,
"max": 1.08522101924,
"times": [
1.07829328024,
1.0678262122400002,
1.06131854324,
1.07109474824,
1.04840370124,
1.08227740624,
1.07320558824,
1.08522101924,
1.06746211624,
1.07323550324
]
},
{
"command": "pacquet@main",
"mean": 1.1034239839400002,
"stddev": 0.08322217513391185,
"median": 1.0771819362400001,
"user": 1.0608153200000001,
"system": 1.6871061599999997,
"min": 1.05714382224,
"max": 1.33711851624,
"times": [
1.05714382224,
1.07427405924,
1.07457004124,
1.0797938312400002,
1.33711851624,
1.0904741502400002,
1.07053030124,
1.10490068224,
1.08271846024,
1.0627159752400002
]
},
{
"command": "pnpr@HEAD",
"mean": 0.69600797804,
"stddev": 0.09872392906877565,
"median": 0.66176556324,
"user": 0.33490722,
"system": 1.27325966,
"min": 0.65335626624,
"max": 0.97461761824,
"times": [
0.65335626624,
0.66044306124,
0.65809251124,
0.66891959524,
0.65533570124,
0.66089415324,
0.97461761824,
0.69860980424,
0.66263697324,
0.66717409624
]
},
{
"command": "pnpr@main",
"mean": 0.67526041354,
"stddev": 0.010671476988292773,
"median": 0.6742150577399999,
"user": 0.32920282,
"system": 1.2823783599999998,
"min": 0.65879630124,
"max": 0.69831514424,
"times": [
0.67352057624,
0.68348348624,
0.66934676624,
0.66813581424,
0.65879630124,
0.68040893224,
0.67656189024,
0.67490953924,
0.66912568524,
0.69831514424
]
}
]
}Scenario: Isolated linker: fresh install, cold cache + hot store
BENCHMARK_REPORT.json{
"results": [
{
"command": "pacquet@HEAD",
"mean": 2.5600268728600004,
"stddev": 0.02609693109761762,
"median": 2.55996085426,
"user": 1.5167469999999998,
"system": 1.9176853199999997,
"min": 2.5231453567599997,
"max": 2.61272739576,
"times": [
2.58118495476,
2.5632999427599996,
2.55662176576,
2.55121746676,
2.55007802376,
2.5231453567599997,
2.52539352376,
2.56618632676,
2.61272739576,
2.57041397176
]
},
{
"command": "pacquet@main",
"mean": 2.5679410856600002,
"stddev": 0.02832696422156358,
"median": 2.56552279876,
"user": 1.512592,
"system": 1.9402213199999998,
"min": 2.52902538976,
"max": 2.62896743476,
"times": [
2.57830624976,
2.52902538976,
2.5590550687599998,
2.5422947157599998,
2.57008767476,
2.56095792276,
2.54358463376,
2.58213524076,
2.62896743476,
2.58499652576
]
},
{
"command": "pnpr@HEAD",
"mean": 0.6818892742600001,
"stddev": 0.00863581435463962,
"median": 0.68300417726,
"user": 0.3337909,
"system": 1.28869792,
"min": 0.66753215676,
"max": 0.69626820276,
"times": [
0.68538185276,
0.66753215676,
0.69626820276,
0.6806265017600001,
0.6745384827600001,
0.67212678876,
0.68929459676,
0.68632490576,
0.68056182176,
0.68623743276
]
},
{
"command": "pnpr@main",
"mean": 0.6671809394599999,
"stddev": 0.006597235479413103,
"median": 0.6662347877600001,
"user": 0.33625109999999997,
"system": 1.26244432,
"min": 0.65793061476,
"max": 0.67869347576,
"times": [
0.66985794876,
0.66732681276,
0.67869347576,
0.66262586276,
0.67471221376,
0.67147836476,
0.65946436676,
0.65793061476,
0.6651427627600001,
0.66457697176
]
}
]
} |
|
| Branch | pr/12338 |
| 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,785.26 ms(-58.10%)Baseline: 9,033.40 ms | 10,840.08 ms (34.92%) |
| isolated-linker.fresh-install.cold-cache.hot-store | 📈 view plot 🚷 view threshold | 2,560.03 ms(-47.92%)Baseline: 4,915.73 ms | 5,898.87 ms (43.40%) |
| isolated-linker.fresh-install.hot-cache.hot-store | 📈 view plot 🚷 view threshold | 1,070.83 ms(-23.19%)Baseline: 1,394.19 ms | 1,673.03 ms (64.01%) |
| isolated-linker.fresh-restore.cold-cache.cold-store | 📈 view plot 🚷 view threshold | 4,130.59 ms(-58.52%)Baseline: 9,957.83 ms | 11,949.40 ms (34.57%) |
| isolated-linker.fresh-restore.hot-cache.hot-store | 📈 view plot 🚷 view threshold | 658.29 ms(+0.30%)Baseline: 656.30 ms | 787.56 ms (83.59%) |
|
| Branch | pr/12338 |
| 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 | 2,252.53 ms |
| isolated-linker.fresh-install.cold-cache.hot-store | 📈 view plot | 681.89 ms |
| isolated-linker.fresh-install.hot-cache.hot-store | 📈 view plot | 696.01 ms |
| isolated-linker.fresh-restore.cold-cache.cold-store | 📈 view plot | 2,343.10 ms |
| isolated-linker.fresh-restore.hot-cache.hot-store | 📈 view plot | 779.88 ms |
Fill the coverage gaps in the URL-scoped env-auth feature: - JS: assert a CLI-provided //host/:_authToken still beats the same env var (workspace < env < CLI), and that non-token cred fields work while a non-URL-scoped env key is ignored. - pacquet: add end-to-end tests through the full config load — that a npm_config_//… var is honored and outranks a project .npmrc token for the same host, and that the prefix is matched case-insensitively. FakeEnv now enumerates via vars() so the env-scoped reader sees the fixture.
|
Code review by qodo was updated up to the latest commit 0466da0 |
What
Adds a file-free way to configure registry authentication via environment variables:
Both prefixes are supported (matched case-insensitively); the key after the prefix keeps its case. When the same key is set through both,
pnpm_config_wins.Why
Until now pnpm (v11) had no way to provide registry auth purely through the environment — it had to come from a file (
.npmrc/auth.ini) or a CLI flag. npm supportsnpm_config_//host/:_authToken, Yarn/vlt have env-based mechanisms; this closes that gap. It's also the natural answer for users hit by the env-expansion change in repository.npmrcfiles (#12314).Why it's safe
These settings are host-scoped by construction — the registry a value applies to is encoded in the variable name, which comes from the trusted environment, not the repository. So unlike a project
.npmrc, a malicious repo can't redirect the credential to another host. The env layer is treated as trusted config: it sits above project/workspace.npmrc(so it overrides repo config) but below CLI options. It is included in the trusted-config set used for package-manager bootstrap; the untrusted workspace.npmrcis not.This deliberately only covers
//-prefixed (URL-scoped) keys. A bare unscoped "default token" env var is intentionally not added, because the default registry can be repointed by a literal repo config value, which would send the token to the wrong host.Tests
Added coverage for: reading auth from
npm_config_//…, frompnpm_config_//…,pnpm_config_winning overnpm_config_, and the env value overriding a project.npmrcfor the same host. Full@pnpm/config.readersuite shows no new failures (the 5 failing there fail identically onmain).Docs follow-up: I'll update https://pnpm.io/npmrc once this lands.
Summary by CodeRabbit
New Features
Documentation
Tests