feat(pnpr): forward uplink auth token and custom headers to upstreams#12186
Conversation
|
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)
🚧 Files skipped from review as they are similar to previous changes (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). (6)
📝 WalkthroughWalkthroughPer-uplink headers are resolved at config load (including derived Authorization headers) into runtime UplinkConfig entries. These headers are passed to Upstream instances and attached to all outgoing packument and tarball HTTP requests. Tests verify resolution, validation, and forwarding behavior. ChangesUplink Headers and Authentication
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes 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 docstrings
🧪 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 |
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #12186 +/- ##
==========================================
- Coverage 87.88% 87.86% -0.03%
==========================================
Files 278 278
Lines 32075 32150 +75
==========================================
+ Hits 28188 28247 +59
- Misses 3887 3903 +16 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
Integrated-Benchmark Report (Linux)Each scenario has pacquet rows (direct install) and pnpr rows (the same client through the pnpr install accelerator), so pnpr@HEAD vs pacquet@HEAD is the pnpr-vs-direct ratio. Cold-store scenarios wipe the client store between runs (warm server); hot-store scenarios keep it warm. The pacquet@HEAD rows feed the pacquet Bencher testbed; the pnpr@HEAD rows feed the pnpr testbed. Scenario: Isolated linker: fresh restore, cold cache + cold store
BENCHMARK_REPORT.json{
"results": [
{
"command": "pacquet@HEAD",
"mean": 4.819984128139998,
"stddev": 0.0694768617182024,
"median": 4.78245187744,
"user": 2.42573452,
"system": 3.7067731999999998,
"min": 4.7487503239399995,
"max": 4.93963303394,
"times": [
4.7801994389399995,
4.77418342794,
4.892154726939999,
4.93963303394,
4.776576599939999,
4.7487503239399995,
4.9095303569399995,
4.78470431594,
4.83555864194,
4.758550414939999
]
},
{
"command": "pacquet@main",
"mean": 4.83541919354,
"stddev": 0.07666639036083331,
"median": 4.80715011644,
"user": 2.45406852,
"system": 3.735816,
"min": 4.79176911094,
"max": 5.04896937794,
"times": [
4.81013444494,
4.84148081394,
4.79176911094,
4.81621628094,
4.80416578794,
4.80408459394,
4.80215145594,
4.83558344494,
4.79963662394,
5.04896937794
]
},
{
"command": "pnpr@HEAD",
"mean": 2.0714506099400003,
"stddev": 0.039620115749640854,
"median": 2.07645522494,
"user": 2.6401649199999997,
"system": 3.3633406999999997,
"min": 2.0144015039400003,
"max": 2.12919066194,
"times": [
2.08515628394,
2.0710149759400003,
2.0144015039400003,
2.01607208794,
2.06316235494,
2.03869479294,
2.12230001394,
2.09261794994,
2.12919066194,
2.08189547394
]
},
{
"command": "pnpr@main",
"mean": 2.0533197448400005,
"stddev": 0.049216659656360476,
"median": 2.04072079544,
"user": 2.64669272,
"system": 3.3232937999999996,
"min": 1.9918364909400001,
"max": 2.15138237894,
"times": [
2.08287819894,
2.10384694594,
1.99558326294,
1.9918364909400001,
2.0289496059400003,
2.15138237894,
2.0319093499400003,
2.04874789494,
2.06536962394,
2.03269369594
]
}
]
}Scenario: Isolated linker: fresh restore, hot cache + hot store
BENCHMARK_REPORT.json{
"results": [
{
"command": "pacquet@HEAD",
"mean": 0.6707270804200001,
"stddev": 0.025333491619108583,
"median": 0.6632321299200001,
"user": 0.350474,
"system": 1.3277922,
"min": 0.65131128392,
"max": 0.7383255709200001,
"times": [
0.7383255709200001,
0.67204041692,
0.65131128392,
0.6597661329200001,
0.6797279029200001,
0.6666981269200001,
0.6531553149200001,
0.6692352379200001,
0.6590959769200001,
0.6579148399200001
]
},
{
"command": "pacquet@main",
"mean": 0.68052255362,
"stddev": 0.0480617003007627,
"median": 0.6602542879200001,
"user": 0.35824789999999995,
"system": 1.314979,
"min": 0.6422403149200001,
"max": 0.8015059669200001,
"times": [
0.7229996509200001,
0.6549638969200001,
0.6560423649200001,
0.6795337049200001,
0.65536035692,
0.66446621092,
0.65416160692,
0.6422403149200001,
0.8015059669200001,
0.6739514619200001
]
},
{
"command": "pnpr@HEAD",
"mean": 0.6711259326200001,
"stddev": 0.06177598330709484,
"median": 0.6472660409200002,
"user": 0.34711479999999995,
"system": 1.3231514,
"min": 0.6410300049200001,
"max": 0.8398086239200001,
"times": [
0.7003669679200001,
0.6515403659200001,
0.6444717819200001,
0.64480841292,
0.6410300049200001,
0.6490661889200001,
0.6414540419200001,
0.6454658929200001,
0.8398086239200001,
0.6532470449200001
]
},
{
"command": "pnpr@main",
"mean": 0.7265370294200001,
"stddev": 0.11750334867137108,
"median": 0.6970297724200001,
"user": 0.36211599999999994,
"system": 1.3034617999999998,
"min": 0.6461489009200001,
"max": 1.04106185192,
"times": [
1.04106185192,
0.7304169009200001,
0.6461489009200001,
0.6883353739200001,
0.7275053959200001,
0.6494829359200001,
0.7057241709200001,
0.7627501089200001,
0.6614146629200001,
0.65252999192
]
}
]
}Scenario: Isolated linker: fresh install, cold cache + cold store
BENCHMARK_REPORT.json{
"results": [
{
"command": "pacquet@HEAD",
"mean": 2.1792807801199996,
"stddev": 0.02264184090634787,
"median": 2.1799681565199998,
"user": 3.4725507999999996,
"system": 3.03577962,
"min": 2.1262901365199998,
"max": 2.20481859552,
"times": [
2.1713445445199997,
2.20207045752,
2.20481859552,
2.1262901365199998,
2.1822266565199997,
2.17770965652,
2.18473453352,
2.1991545545199997,
2.16798478852,
2.17647387752
]
},
{
"command": "pacquet@main",
"mean": 2.1421200525199997,
"stddev": 0.0337778336490116,
"median": 2.1514044120199998,
"user": 3.4448288999999996,
"system": 3.02675122,
"min": 2.0512948455199997,
"max": 2.17253240452,
"times": [
2.15427905552,
2.1564204825199997,
2.0512948455199997,
2.13153170552,
2.14198646852,
2.17253240452,
2.14852976852,
2.14765140852,
2.16183588752,
2.15513849852
]
},
{
"command": "pnpr@HEAD",
"mean": 2.15542986012,
"stddev": 0.02402393231956696,
"median": 2.1621033915199996,
"user": 3.4290115,
"system": 3.0185916199999996,
"min": 2.10527298052,
"max": 2.18352303452,
"times": [
2.15958173952,
2.1675902745199997,
2.10527298052,
2.18352303452,
2.13706832052,
2.1311990065199997,
2.1618306335199997,
2.17994264152,
2.1659138205199997,
2.16237614952
]
},
{
"command": "pnpr@main",
"mean": 2.14710630952,
"stddev": 0.019273215817974,
"median": 2.1503454925199996,
"user": 3.4501526999999994,
"system": 2.9898255199999997,
"min": 2.11384933552,
"max": 2.17248019752,
"times": [
2.1427931555199997,
2.16509857552,
2.15861479952,
2.13407959852,
2.15789782952,
2.13723747252,
2.16349833052,
2.12551380052,
2.17248019752,
2.11384933552
]
}
]
}Scenario: Isolated linker: fresh install, hot cache + hot store
BENCHMARK_REPORT.json{
"results": [
{
"command": "pacquet@HEAD",
"mean": 1.2585497698800001,
"stddev": 0.025440647183153076,
"median": 1.25446215888,
"user": 1.35511618,
"system": 1.70495302,
"min": 1.2279380848800001,
"max": 1.3071298638800002,
"times": [
1.24843976988,
1.23010055888,
1.2377447678800002,
1.24973075088,
1.28119763688,
1.26128809288,
1.2279380848800001,
1.3071298638800002,
1.28273460588,
1.25919356688
]
},
{
"command": "pacquet@main",
"mean": 1.2870737868800002,
"stddev": 0.06300847152569453,
"median": 1.27665173238,
"user": 1.36295718,
"system": 1.6955909200000001,
"min": 1.23252469888,
"max": 1.4570757098800002,
"times": [
1.23252469888,
1.2498624398800002,
1.2822531298800002,
1.27860773488,
1.29067107188,
1.4570757098800002,
1.2746957298800001,
1.29227506488,
1.26859434988,
1.24417793888
]
},
{
"command": "pnpr@HEAD",
"mean": 1.28871890528,
"stddev": 0.08130952502575242,
"median": 1.2556119688800003,
"user": 1.37922428,
"system": 1.67958392,
"min": 1.2383283468800002,
"max": 1.5021022668800001,
"times": [
1.2588852518800002,
1.25233868588,
1.2489067098800002,
1.2383283468800002,
1.27113966088,
1.5021022668800001,
1.34830064388,
1.2468621188800002,
1.2462166328800002,
1.27410873488
]
},
{
"command": "pnpr@main",
"mean": 1.2954376841800002,
"stddev": 0.07384121328317046,
"median": 1.27474809388,
"user": 1.39985718,
"system": 1.67870462,
"min": 1.25033875588,
"max": 1.50242582888,
"times": [
1.26424896388,
1.27395003588,
1.26298313188,
1.27827207488,
1.25033875588,
1.50242582888,
1.29750634588,
1.26618632388,
1.27554615188,
1.28291922888
]
}
]
} |
|
| Branch | pr/12186 |
| Testbed | pacquet |
🚨 1 Alert
| Benchmark | Measure Units | View | Benchmark Result (Result Δ%) | Upper Boundary (Limit %) |
|---|---|---|---|---|
| isolated-linker.fresh-restore.cold-cache.cold-store | Latency seconds (s) | 📈 plot 🚷 threshold 🚨 alert (🔔) | 4.82 s(+49.67%)Baseline: 3.22 s | 3.86 s (124.72%) |
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 | 2,179.28 ms(-4.07%)Baseline: 2,271.66 ms | 2,725.99 ms (79.94%) |
| isolated-linker.fresh-install.hot-cache.hot-store | 📈 view plot 🚷 view threshold | 1,258.55 ms(-10.46%)Baseline: 1,405.61 ms | 1,686.73 ms (74.61%) |
| isolated-linker.fresh-restore.cold-cache.cold-store | 📈 view plot 🚷 view threshold 🚨 view alert (🔔) | 4,819.98 ms(+49.67%)Baseline: 3,220.45 ms | 3,864.54 ms (124.72%) |
| isolated-linker.fresh-restore.hot-cache.hot-store | 📈 view plot 🚷 view threshold | 670.73 ms(-1.60%)Baseline: 681.61 ms | 817.93 ms (82.00%) |
3693f0e to
f674927
Compare
Review Summary by QodoForward uplink auth tokens and custom headers to upstream registries
WalkthroughsDescription• Forward uplink Authorization header from auth: block to upstream requests • Support bearer/basic auth types with token resolution from config or env vars • Merge custom headers: map with auth-derived headers, with custom overrides • Validate auth tokens and header names/values at config load time, fail fast on errors • Add comprehensive test coverage for auth resolution and header forwarding Diagramflowchart LR
A["YAML Config<br/>auth + headers"] --> B["resolve_uplink<br/>at config load"]
B --> C["HeaderMap<br/>auth-derived +<br/>custom headers"]
C --> D["Upstream<br/>attaches to<br/>every request"]
D --> E["Private upstreams<br/>receive credentials"]
File Changes1. pnpr/crates/pnpr/src/config.rs
|
There was a problem hiding this comment.
Pull request overview
This PR adds verdaccio-parity support for forwarding per-uplink authentication and operator-specified custom headers on all upstream packument and tarball fetches performed by @pnpm/pnpr, enabling proxying of upstream registries that require authorization (e.g. CodeArtifact, GitHub Packages).
Changes:
- Extend uplink configuration to resolve
auth(bearer|basic,token/token_env) andheadersinto a per-uplinkHeaderMapat config-load time, failing fast on invalid/missing values. - Attach resolved per-uplink headers to every upstream packument and tarball request.
- Add unit + integration tests verifying header forwarding and config resolution/error cases.
Reviewed changes
Copilot reviewed 6 out of 6 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| pnpr/crates/pnpr/src/config.rs | Adds uplink auth/headers parsing + resolution into runtime UplinkConfig.headers. |
| pnpr/crates/pnpr/src/config/tests.rs | Adds extensive tests for uplink auth/header resolution, precedence, and invalid configs. |
| pnpr/crates/pnpr/src/upstream.rs | Stores per-uplink headers on Upstream and attaches them to packument/tarball requests. |
| pnpr/crates/pnpr/src/upstream/tests.rs | Verifies Upstream forwards configured headers (and omits auth when empty). |
| pnpr/crates/pnpr/src/server.rs | Plumbs resolved uplink headers into Upstream::new(...) when building app state. |
| pnpr/crates/pnpr/tests/server.rs | Adds an integration-style test proving forwarded headers reach the upstream server. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
pnpr now attaches a per-uplink `Authorization` header (derived from the verdaccio-shaped `auth:` block) plus any operator-supplied custom `headers:` on every packument and tarball request it makes to an upstream registry. This unblocks proxying private upstreams (CodeArtifact, GitHub Packages, authed npm Enterprise, private verdaccio) that previously returned 401/403. `auth` supports `type: bearer | basic`, an inline `token`, and verdaccio's `token_env` (`true` -> `NPM_TOKEN`, or a named var); an inline `token` takes priority. A custom `headers.Authorization` overrides the auth-derived one, matching verdaccio's merge order. Tokens/headers resolve once at config load through the existing `EnvVar` seam, so a missing token or invalid header value fails fast as an `InvalidConfig` error rather than a silent unauthenticated request. Implements the "Forward auth.token / custom headers to uplinks" item under Uplinks & caching in the pnpr-verdaccio-parity tracking issue. Ref: #11973 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Add tests for the previously-uncovered branches in resolve_uplink and from_yaml_str flagged by Codecov on the uplink-auth patch: - token_env false resolves no token (config error) - an auth token that is not a valid header value (config error) - an invalid custom header name (config error) - an invalid custom header value (config error) - an unresolved env-var reference is tolerated, not an error Brings config.rs from 92.05% to 96.69% line coverage; the patch's nine missing lines are now fully covered. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
CI's stricter dylint/perfectionist pass and `cargo doc` (which plain clippy does not run) flagged the uplink-auth changes: - collapse the split `reqwest::` and `crate::` imports to one `use` per crate root (perfectionist::import-granularity = crate) - add the trailing comma to the multi-line `format!` in resolve_uplink (perfectionist::macro-trailing-comma) - drop the intra-doc links from the public `UplinkConfig` docs to the private `UplinkFile`/`resolve_uplink` items, which broke `cargo doc --document-private-items` under -D warnings No behavior change. Verified locally with `cargo dylint --all`, `cargo doc --document-private-items`, clippy, and the config tests. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
UplinkConfig and Upstream both hold a resolved HeaderMap that can carry an Authorization credential (or a secret in a custom header). The derived Debug printed those values verbatim, so a debug log or span could leak them. Replace the derives with hand-written impls that route the map through a RedactedHeaders wrapper, which lists header names with every value rendered as <redacted>.
636dcf7 to
bfd3a6b
Compare
* chore: enable clippy::pedantic lint group for pacquet workspace * style(pacquet): comply with clippy::pedantic Apply clippy's machine-applicable pedantic fixes across the workspace (inlined format args, removed needless borrows/closures, added must_use, etc.), fix a few doc-comment backtick nits, and drop pointless #[inline(always)] on trivial accessors. Opt specific pedantic lints back out in [workspace.lints.clippy] with documented justifications, grouped into false positives, library-API hygiene that doesn't fit an internal CLI, suggestions that conflict with the cardinal rule of porting pnpm 1:1, and opinionated style. * style: taplo-format Cargo.toml lint table * style(pnpr): comply with clippy::pedantic in merged auth backend code Re-apply pedantic compliance to the networked-SQLite auth backend that landed on main (#12186, #12199/#12206): doc-comment backticks, #[must_use] on constructors and status_code, i64::from over `as`, map_or, and a method-reference closure. * docs(clippy): trim and inline the pedantic allow-list comments * docs(clippy): note perfectionist supersedes many_single_char_names * docs(clippy): note pnpm-mirroring rationale on structure/naming lints * docs(clippy): mark unused_async as deferred pending audit * style: enable clippy::match_wildcard_for_single_variants * refactor: enable clippy::unused_self Convert two self-less private methods (overrides pick_most_specific, tarball head_only_result) to associated functions. * refactor: enable clippy::ref_option Widen engine_json to Option<&str>; #[expect] the two serde serialize_with helpers, which serde must call as f(&field, ser). * perf: enable clippy::trivially_copy_pass_by_ref Pass the 1-byte Copy types NodeLinker and FilterWorkspaceProjectsOptions by value; #[expect] the serde skip_serializing_if helper is_false. * perf: enable clippy::assigning_clones Use clone_from for seven field assignments to reuse allocations. * style: enable clippy::manual_let_else Convert 27 match/if-let guards to let-else; preserve the non-UTF-8 skip rationale comment in the directory walker. * style: enable clippy::default_trait_access Name the concrete type on Default::default() call sites; #[expect] two struct-literal test fixtures where naming each field type would force ~20 imports. * refactor: enable clippy::format_push_string Replace push_str(&format!(...)) with write!/writeln! into the target String (local 'use std::fmt::Write as _'); writeln! preserves the exact LF/CRLF shell-shim output. * refactor: enable clippy::needless_pass_by_value Take by reference where the argument is only read (incl. dropping some redundant clones in resolve_peers' recursion). Where converting would cascade badly, #[expect] with a reason: functions that destructure/consume the arg (build_resolve_result, PrefetchingResolver, S3Store::new), the by-value `impl IntoIterator + Clone` in build_direct_deps_by_importer, and the serde/test helpers whose owned fixtures keep call sites clean. * fix(perfectionist): satisfy dylint after format_push_string changes Add trailing commas to the multi-line writeln! shell-shim templates (macro_trailing_comma) and merge the new `fmt::Write as _` imports into each file's existing `use std::{...}` block (import_granularity). * docs(clippy): explain missing_errors_doc suppression; mark missing_panics_doc deferred * fix(perfectionist): collapse fmt::{self, Write as _} in work_env imports The format_push_string Write import landed as a sibling fmt:: path next to the existing fmt import; merge them so import_granularity passes. * style: enable clippy::return_self_not_must_use Add #[must_use] to the WorkspaceTreeCtx builder methods, matching the #[must_use] already on the parallel TreeCtx builders. * perf: enable clippy::large_stack_arrays Heap-allocate the 64 KiB read buffer in verify_file_integrity with a Vec instead of placing it on the stack. * chore(clippy): enable clippy::nursery group Enable the nursery lint group on the pacquet/pnpr workspace and bring the code into compliance. Fixed in code: - iter_on_single_items: [x].into_iter()/.iter() -> std::iter::once - equatable_if_let: pattern match -> equality check (the install_accelerator rewrite wraps in a multi-line matches!, which gets a trailing comma for perfectionist::macro_trailing_comma) - needless_pass_by_ref_mut: load_pending_row/apply_write_msg take &StoreIndex Opted back out in Cargo.toml, each with a documented justification: use_self, too_long_first_doc_paragraph, missing_const_for_fn, option_if_let_else, significant_drop_tightening, redundant_pub_crate, derive_partial_eq_without_eq, branches_sharing_code, useless_let_if_seq, single_option_map, iter_with_drain, literal_string_with_formatting_args, collection_is_never_read. Dropped the now-redundant individual nursery warns (needless_collect, or_fun_call, redundant_clone) the group now covers, plus the default-on unnecessary_lazy_evaluations. Kept clone_on_ref_ptr and if_then_some_else_none (restriction lints not enabled by any group). * style: bring merged main code into clippy pedantic compliance The 17 commits merged from main predate this branch's pedantic/nursery lint config, so their new code tripped pedantic lints. Apply the machine-applicable fixes (uninlined_format_args, if_not_else, elidable_lifetime_names, must_use_candidate, single_match_else, map_unwrap_or, default_trait_access, assigning_clones, doc_markdown, ...) and re-add the documented #[expect(needless_pass_by_value)] on S3Store::new that this branch had carried on the now-replaced file. * style: bring merged main code into clippy pedantic compliance The 28 commits merged from main predate this branch's lint config, so their new code tripped pedantic lints. Apply the machine-applicable fixes (uninlined_format_args, manual_let_else, needless_raw_string_hashes, redundant_closure_for_method_calls, map_unwrap_or, elidable_lifetime_names, doc_markdown, ...) plus a few by hand: - derive Copy on LinkSlotsParallel (all fields are Copy/refs) to clear needless_pass_by_value without a signature change - deduplicate_all takes &[Vec<DepPath>] (it only borrows the duplicates) - pick_most_specific becomes an associated fn (it never used self) - default_trait_access -> concrete types; assigning_clones -> clone_from; format_push_string -> write! - #[expect] with reasons where a fix would churn main's feature code: needless_pass_by_value on the recursive resolve_node and a test helper, and float_cmp on two deterministic-fixture assertions * style: enable clippy::allow_attributes and allow_attributes_without_reason Both are restriction lints (not implied by any group), enabled alongside the existing clone_on_ref_ptr / if_then_some_else_none. Convert every #[allow(...)] (including one nested in cfg_attr) to #[expect(...)]; all already carried a reason, so allow_attributes_without_reason is satisfied. Drop two now-redundant suppressions surfaced by the conversion: a duplicated #[expect(too_many_arguments)] on fetch_and_extract_zip_once (a prior merge left both an allow and an expect), and the #[expect(dead_code)] on MissingPeerInfo's fields (the #[derive(Debug, Clone)] already reads them, so dead_code never fired). clone_on_ref_ptr was already enabled. mod_module_files is intentionally NOT enabled: it mandates mod.rs, the opposite of the flat module.rs pattern this project requires (CODE_STYLE_GUIDE.md, enforced by perfectionist::flat_module_pattern). * style: enable clippy::mod_module_files to enforce the flat module layout mod_module_files bans mod.rs files, enforcing the flat module.rs pattern this project already uses (0 mod.rs in the tree, so no violations). Update CODE_STYLE_GUIDE.md to cite it as the enforcer; perfectionist's flat_module_pattern is being retired in favor of this Clippy rule. * fix(perfectionist): trailing comma on wrapped assert_eq! in workspace_yaml tests The default_trait_access fix lengthened the assert_eq! so fmt wrapped it to multi-line, which perfectionist::macro_trailing_comma requires to end with a trailing comma. * fix(fs): use cfg_attr expect instead of allow for Windows-unused mode args With clippy::allow_attributes enabled, the #[cfg_attr(windows, allow(unused))] on make_file_executable and the ensure_file/write_atomic mode params fails Windows CI. Switch to #[cfg_attr(windows, expect(unused, reason = ...))]; on Windows the lint fires (Unix mode unused there) so the expectation is fulfilled, and the attribute stays inert on Unix. * fix(fs): drop the Windows unused suppression on ensure_file's mode arg ensure_file forwards mode to verify_or_rewrite unconditionally, so it is used on Windows too; the #[cfg_attr(windows, expect(unused))] was therefore unfulfilled and failed Windows CI under -D warnings. write_atomic and make_file_executable keep their expect — they use mode/file only under #[cfg(unix)], so the lint fires (and the expectation holds) on Windows. * chore(git): revert "fix(fs): drop the Windows unused suppression on ensure_file's mode arg" This reverts commit 1d617c3. * chore(git): revert "fix(fs): use cfg_attr expect instead of allow for Windows-unused mode args" This reverts commit 155e4a3. * chore(git): revert "style: enable clippy::allow_attributes and allow_attributes_without_reason" This reverts commit a47d792. * style: bring merged main code into clippy compliance + fix merge mismatch - Add & at the two run_postinstall_hooks / run_project_lifecycle_scripts call sites: this branch widened lifecycle.rs to take &RunPostinstallHooks, but main's by-value call sites came in via the conflict resolution. - pedantic fixes on main's new code: must_use_candidate, unnested_or_patterns, manual_let_else, default_trait_access, iter_on_single_items, and trivially_copy_pass_by_ref (map_node_linker takes NodeLinker by value). --------- Co-authored-by: Claude <noreply@anthropic.com>
pnpr now attaches a per-uplink
Authorizationheader (derived from the verdaccio-shapedauth:block) plus any operator-supplied customheaders:on every packument and tarball request it makes to an upstream registry. This unblocks proxying private upstreams (CodeArtifact, GitHub Packages, authed npm Enterprise, private verdaccio) that previously returned 401/403.authsupportstype: bearer | basic, an inlinetoken, and verdaccio'stoken_env(true->NPM_TOKEN, or a named var); an inlinetokentakes priority. A customheaders.Authorizationoverrides the auth-derived one, matching verdaccio's merge order. Tokens/headers resolve once at config load through the existingEnvVarseam, so a missing token or invalid header value fails fast as anInvalidConfigerror rather than a silent unauthenticated request.Implements the "Forward auth.token / custom headers to uplinks" item under Uplinks & caching in the pnpr-verdaccio-parity tracking issue.
Ref: #11973
Summary by CodeRabbit
New Features
Bug Fixes
Tests