Skip to content

[pull] main from pnpm:main#266

Merged
pull[bot] merged 7 commits into
dwongdev:mainfrom
pnpm:main
May 26, 2026
Merged

[pull] main from pnpm:main#266
pull[bot] merged 7 commits into
dwongdev:mainfrom
pnpm:main

Conversation

@pull

@pull pull Bot commented May 26, 2026

Copy link
Copy Markdown

See Commits and Changes for more details.


Created by pull[bot] (v2.0.0-alpha.4)

Can you help keep this open source service alive? 💖 Please sponsor : )

KSXGitHub and others added 7 commits May 26, 2026 18:47
* feat(pacquet/config): port --recursive and --workspace-concurrency settings

Mirror pnpm's `workspaceConcurrency` (a `.npmrc` / `pnpm-workspace.yaml`
/ `PNPM_CONFIG_WORKSPACE_CONCURRENCY` config-file key, default
`getDefaultWorkspaceConcurrency()`, resolved through
`getWorkspaceConcurrency`) and CLI-only `recursive` (`-r`) boolean.

- Add `Config::workspace_concurrency` (resolved via the existing
  `resolve_child_concurrency` port of `getWorkspaceConcurrency`) and
  `Config::recursive`, plus `default_workspace_concurrency`.
- Read `workspaceConcurrency` from workspace yaml, global config.yaml,
  and the `PNPM_CONFIG_*` env overlay; resolve negative offsets the
  same way `childConcurrency` does.
- Add the `--workspace-concurrency` install flag (overrides the
  config-resolved value) and the global `-r` / `--recursive` flag
  (sets `Config::recursive`, matching pnpm's CLI-only nature).

`workspaceConcurrency` is parsed and stored for config-surface parity;
pacquet's frozen install materializes the whole workspace in one shared
pass, so there is no per-project parallel loop for it to throttle yet
(same posture as `preferOffline`). `recursive` is likewise a surface
flag on install today, since install already spans the workspace.

* docs(pacquet/config): drop private intra-doc link to fix Doc CI

`default_workspace_concurrency`'s public doc linked the crate-private
`default_child_concurrency`, which `rustdoc -D rustdoc::private-intra-doc-links`
(implied by `-D warnings`) rejects. Use plain backticks instead.

* style(pacquet/cli): drop redundant comment on workspace_concurrency destructure bind

The sibling `offline: _` / `prefer_offline: _` binds carry no comment;
match them for consistency.

* test(pacquet/cli): cover the --workspace-concurrency override resolution

The inline `if let Some(value) = args.workspace_concurrency` override
body was only reachable when install ran *with* the flag, so the
flag-absent integration runs left it uncovered. Extract the resolution
into `InstallArgs::resolve_workspace_concurrency` and apply it
unconditionally at the dispatch (matching upstream's final
`getWorkspaceConcurrency` pass), so the substantive logic is covered by
fast unit tests (absent / positive / negative) and the call site is an
unconditional assignment the existing install tests already exercise.

* style(pacquet/cli): pipe-trait the workspace-concurrency test parse calls

Per review: rewrite the `InstallArgsHarness::try_parse_from([...])`
calls in the new workspace-concurrency tests as
`[...].pipe(InstallArgsHarness::try_parse_from)` so they read
left-to-right, matching the codebase's pipe-trait convention.

* docs(pacquet/config): drop inaccurate .npmrc source from workspaceConcurrency docs

workspace_concurrency is populated only from pnpm-workspace.yaml, global
config.yaml, and the PNPM_CONFIG_WORKSPACE_CONCURRENCY env overlay.
Config::current reads .npmrc but applies only the auth/network subset, so a
workspace-concurrency= entry in .npmrc never reaches the field. Align the doc
comments and PR summary with actual behavior, matching the sibling
childConcurrency docs.

---------

Co-authored-by: Claude <noreply@anthropic.com>
…1964)

The e2e/integration test harness spawns `pnpm-registry` as a faster
verdaccio replacement. CI used to install Rust and build the crate
from source on every test job — adding several minutes per platform.

`@pnpm/pnpr` now publishes the prebuilt binary to npm, and `pnpm install`
already pulls in the matching `@pnpm/pnpr.<platform>-<arch>` package
via optionalDependencies. The Jest globalSetup resolves that binary
through `@pnpm/pnpr/bin/pnpr`'s own module path (the wrapper carries
the platform packages as siblings in its `node_modules`, not on the
parent chain of this file).

- Add `@pnpm/pnpr` to `pnpm-workspace.yaml` catalog and depend on it
  from `@pnpm/jest-config`.
- Replace `resolvePnpmRegistryBin`'s `$CARGO_TARGET_DIR` lookup with
  `require.resolve` through the npm-installed wrapper. The
  `PNPM_REGISTRY_BIN` env var is still honored as an escape hatch for
  contributors who want to point at a locally-built Rust binary.
- Remove the "Install Rust toolchain" + "Build pnpm-registry" steps
  from `.github/workflows/test.yml`.

---
Written by an agent (Claude Code, claude-opus-4-7).
trailofbits/dylint 6.0.1 (published 2026-05-26 17:51 UTC) ships
prebuilt cargo-binstall artifacts that bake in a path from the
dylint repo's own CI workspace:

    error: failed to get `dylint_driver` as a dependency of package
      `dylint_driver-nightly-2026-04-16-x86_64-unknown-linux-gnu`
    Caused by:
      failed to read `/home/runner/work/dylint/dylint/driver/Cargo.toml`
    Caused by:
      No such file or directory (os error 2)

Downstream runners don't have that workspace, so the driver bootstrap
fails before any lint runs and the Dylint job goes red on every PR.
6.0.0 (the version main was passing with 90 minutes earlier) is
unaffected. Pin both binaries until upstream cuts 6.0.2.

---
Written by an agent (Claude Code, claude-opus-4-7).
…11967)

`fetching/git-fetcher/src/index.ts` passed the lockfile-controlled `resolution.commit` value straight to `git fetch --depth 1 origin <commit>` and `git checkout <commit>` with no `--` separator and no format validation. A malicious `pnpm-lock.yaml` could put a value such as `--upload-pack=touch /tmp/pwned` in `resolution.commit`; `git` parses anything starting with `-` as an option, and on SSH or local-file transports `--upload-pack` runs the supplied command as the user running `pnpm install`. HTTPS ignores `--upload-pack`, but the SSH/file paths are enough to reach code execution.

The fix validates `resolution.commit` against `/^[0-9a-f]{40}$/i` at the entry of the fetcher and throws `INVALID_GIT_COMMIT` otherwise. This is strictly stronger than adding a `--` separator — a validated value cannot start with `-` or contain shell-significant characters at all.

Pacquet's `pacquet-git-fetcher` crate shells out to `git` along the same code path (`pacquet/crates/git-fetcher/src/fetcher.rs`) and had the identical issue. Ported the same check there, with a new `GitFetcherError::InvalidCommit` variant carrying the `INVALID_GIT_COMMIT` diagnostic code.

Reported by [AutoFyn](https://github.com/SignalPilot-Labs/AutoFyn).
* fix(lockfile.utils): require integrity for tarball-shaped lockfile resolutions

A tampered lockfile that strips the `integrity` field from a tarball
resolution let the worker download the URL contents and mint a fresh
integrity from the unverified bytes, so an attacker who could also
serve content at the referenced URL would install a tampered package
without any error — including under `--frozen-lockfile`. pnpm now
rejects such entries at lockfile-read time with
`ERR_PNPM_MISSING_TARBALL_INTEGRITY`, matching pacquet's existing
`pacquet_package_manager::missing_tarball_integrity` guard.

* test(lockfile.utils): drop redundant integrity-less snapshot that fails strict typecheck

* test(pacquet/package-manager): cover MissingTarballIntegrity rejection in snapshot_cache_key

Match the upstream guard landed alongside #11966
(`lockfile/utils/src/pkgSnapshotToResolution.ts`) with a test on
the pacquet side: a `LockfileResolution::Tarball` with `integrity:
None` — what a tampered lockfile looks like — must short-circuit
the warm-batch cache-key derivation by surfacing
`InstallPackageBySnapshotError::MissingTarballIntegrity`. The
structural guard already existed but had no negative test.

* fix(lockfile.utils): exempt git-hosted and file: tarballs from the integrity guard

The strict guard added in the parent commit broke pnpm's own
`with-git-protocol-dep` and `with-non-package-dep` fixtures: the
install pipeline writes git-hosted tarball entries (codeload.github.com
URLs) to the lockfile without an `integrity:` line, because the commit
SHA in the URL is the integrity anchor — git's content-addressed model
binds the bytes to the commit, so a separate hash adds nothing.

Exempt git-hosted tarballs (detected either via the `gitHosted: true`
flag or a URL on the known git hosts, matching the URL fallback in
`toLockfileResolution`) and `file:` tarballs (local paths the user
already controls). The strict check still fires for any other remote
tarball — which is where the AutoFyn-reported vector actually
manifests.

Also export `isGitHostedTarballUrl` from `toLockfileResolution.ts` so
the URL fallback can be shared rather than duplicated.

* test(pacquet/package-manager): trim doc comment to the contract-level intent

Per the repo convention that tests are documentation, the test name
and body already cover what's being asserted; the prior comment
duplicated that. Keep only the non-obvious why: why this guard exists
at the cache-key site at all (warm-batch short-circuit) when the
install-side check also rejects the same input.
Reshape pnpm-registry's Config to match verdaccio's `config.yaml` schema (storage, uplinks, packages) so the same file can drive either server. The previous Config exposed a single `upstream: Option<String>` resolved at startup; this replaces it with named uplinks plus per-package `proxy:` rules walked in declared order — same semantics as verdaccio, minus the surface pnpm-registry does not implement (auth, web, plugins, middlewares, logs routing, secret), which are accepted and ignored.

Highlights:

  * `Config { listen, public_url, storage, uplinks, packages, packument_ttl }` with `UplinkConfig { url }` and `PackageAccess { access, publish, unpublish, proxy }`. `packages` is an `IndexMap` walked in declared order, first-match-wins: the first pattern matching a request is the rule that applies, and if that rule has no `proxy:` the package is storage-only (resolution returns `None` instead of falling through to a later catch-all). That makes the bundled `@private/*` / `@pnpm.e2e/needs-auth` / unscoped-fixture rules behave the way the YAML says they should.
  * `Config::from_yaml(path, ...)` loads via `serde-saphyr` and resolves a relative `storage:` against the config file's parent. The verdaccio-only sections in the YAML are skipped silently so `registry-mock`'s upstream `config.yaml` parses untouched.
  * `DEFAULT_CONFIG_YAML` — the bundled file mirrored from `@pnpm/registry-mock` — is `include_str!`-ed and re-exported from `lib.rs` so other crates (tests, benchmarks, future embedders) can use the same defaults without reading from disk. `Config::from_default_yaml(base_dir, ...)` parses it.
  * CLI: `-c` / `--config <path>` overrides the bundled default. `--storage` survives as a runtime override (handy for tests without a custom YAML); `--upstream` and `--static` are gone because the YAML now drives both. `--packument-ttl-secs` is optional — the loaded config's value wins when the flag is not supplied. The mock orchestrator at `pacquet/tasks/registry-mock` drops its `--upstream` flag — that command spawns the locally-built binary so it tracks this PR's source directly. The jest harness at `__utils__/jest-config/with-registry/globalSetup.js` keeps `--upstream` for now because CI installs `@pnpm/pnpr` from npm; the flag will be dropped in the same PR that bumps `@pnpm/pnpr` to a build that lacks it.
  * `server.rs` pre-builds one `Upstream` per declared uplink at router construction (keyed by name, in an `IndexMap`) and resolves the right client per request via `Config::resolve_uplink`. No per-request `ThrottledClient` allocations.

Existing tests are kept working by retaining the `Config::proxy` / `Config::static_serve` constructors and switching the test helper from `config.upstream = ...` to mutating
`config.uplinks["npmjs"].url`. All 107 tests in `pnpm-registry` pass (55 unit + 26 + 9 + 17 integration).

<!-- This is an auto-generated comment: release notes by coderabbit.ai -->
## Summary by CodeRabbit

* **New Features**
  * YAML-based registry configuration with package-level routing, multiple uplinks, auth and audit middleware
  * New --config option to load custom registry configs; optional storage override and packument TTL setting

* **Chores**
  * Default registry now uses the bundled configuration (web UI disabled by default)
  * Configuration refactor to support Verdaccio-style routing patterns and per-package access/publish rules

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-authored-by: Zoltan Kochan <z@kochan.io>
@pull pull Bot locked and limited conversation to collaborators May 26, 2026
@pull pull Bot added the ⤵️ pull label May 26, 2026
@pull pull Bot merged commit f03dc2d into dwongdev:main May 26, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants