mirror of
https://github.com/pnpm/pnpm.git
synced 2026-10-11 18:14:18 -04:00
pnpm@12.5.1
27
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
e1b573d31d |
chore: format Rust on commit, and diff against the branch as it is (#15045)
Three papercuts that together make a lint cost a full pre-push sweep. `test-affected` took its diff against the local `main`, which is only as current as the last time someone checked it out — and `main` usually lives in another worktree here, so it lags. Every commit it lagged by read as a change of the current branch: crates someone else touched got tested, and a file every crate compiles against, landing upstream, made the whole run refuse to scope and send you to `just ready`. Six commits of lag was enough to attribute an unrelated `Cargo.lock` to a branch that had not touched it. A `--base` that names a branch now diffs against that branch's remote-tracking ref. A tag, a revision expression, a qualified ref and `HEAD` are taken as given, since each already names one commit. The pre-commit hook formats the Rust files being committed, with the pinned rustfmt. Formatting is the one Rust check that needs no compile, so it is the one that can run per commit; clippy and dylint stay in pre-push where their cost is paid once. A file with unstaged changes as well is left alone and named, since formatting the working tree and staging the result would commit the part of it the author held back. The pathnames are handled in Node, one argument per file, so a path holding a space, a glob character, or a leading `-` reaches rustfmt intact, and only a regular file is formatted: rustfmt writes through a symlink. Both lint failures in pre-push named the command that reports the findings rather than the one that applies them. `just fix` and the new `just dylint-fix` fix most of what either finds, which is the difference between one more sweep and several. |
||
|
|
6d73648565 |
chore: run the tests a change affects, with smoke tests for its dependents (#14985)
`pnpm/CONTRIBUTING.md` required `just ready` before every commit, explicitly including documentation and comment edits: a full `cargo nextest run` over ~11,500 tests for changes that cannot break them. The root guide's "never run all tests" rule was scoped to the TypeScript sections, so it read as not applying to Rust. The checks now match what a change can break. Formatter, typos, and workspace-wide check and lint before every commit, since those are cheap and catch the cross-crate breakage a scoped selection hides, plus the tests for what the diff affects. The full local run is reserved for changes whose blast radius cannot be named, because CI already runs the suite on three platforms. `just test-affected` resolves changed files to packages through `cargo metadata` and runs them through `run-rust-tests.mjs`, expanding any `pnpr-*` selection so feature unification does not silently skip backend tests, and refusing to guess when a change reaches files every crate compiles against. Selection is crate-level rather than `rdeps()`-based: `pnpm-cli` holds a third of the workspace's tests and sits downstream of nearly everything, so `rdeps()` selects 84% or more of the suite for any core crate. Restructuring that target is tracked in pnpm/pnpm#14984. Crate-level selection leaves the dependents unrun, so the new `smoke` profile runs in their place: one end-to-end test per area of CLI behavior, triggered by the dependent set rather than added to every run. Membership is by behavior area rather than code coverage, since nearly every end-to-end test walks the same install path. An exact-name filterset fails open, so a test checks every entry against the suite sources. The `testing-changes` skill carries the selection cookbook and the gotchas that make a scoped run lie. Related to pnpm/pnpm#14984. |
||
|
|
5471ef3bd2 |
test(cargo): compare pnpm's Cargo resolution against cargo's own (#14979)
pnpm v12 resolves Cargo dependencies itself rather than shelling out to cargo, so it is a second implementation of cargo's resolver, and being a drop-in replacement means producing the versions cargo produces rather than merely producing a lockfile. Unit tests pin that against synthetic indexes, which is where the rules are easiest to state and easiest to get subtly wrong: the resolver fixes in pnpm/pnpm#14952, pnpm/pnpm#14960, pnpm/pnpm#14961 and pnpm/pnpm#14962 were each confirmed by hand-checking a real workspace against cargo after the unit tests were already green. This is that hand-check, written down. For each workspace in the corpus it resolves twice, compares the locked crates, and asks cargo whether it accepts pnpm's lockfile with `--locked` in a copy pnpm never touched, so the lockfile is measured apart from the source replacement written beside it. Requirements are open rather than pinned: both resolvers read the same index on the same day, so an upstream release changes what they agree on, not whether they agree. A workspace declares whether the two are expected to agree yet. A known gap reports as such and does not fail the run, but does fail if the two start agreeing, so a fixed issue cannot leave a stale expectation behind. `feature-activated-deps` is the one gap today, tracking pnpm/pnpm#14978. It runs on a daily cron rather than per-PR because it reads the live index, where a yank or a release can change a result with nothing in this repo changing. |
||
|
|
4cfcc237bc |
fix(registry-mock): clean up interrupted startup (#14877)
Own the registry child before awaiting readiness so failed or cancelled startup runs the existing kill-and-wait destructor. Preserve ownership on successful startup and leave already-running registries unowned. Add real-process regression coverage. Disarm each test cleanup guard once the child is reaped, so its destructor cannot signal a recycled PID. Move `pnpm-registry-mock` from the `test-pacquet` selection to `test-pnpr`. Its tests spawn the real `pnpr` binary, which `test-pacquet` never builds because it excludes every pnpr crate; the pnpr selection builds it because pnpr's own integration tests force `CARGO_BIN_EXE_pnpr`. Clear every `PNPR_*` variable from the mock's child process. pnpr binds every option to the variable named after it, so an ambient `PNPR_CONFIG` or `PNPR_DISABLE_REGISTRY` silently reconfigured the mock. Also flatten the macOS file-limit fallback, which the nesting lint rejects in its original shape. Closes pnpm/pnpm#14876. --------- Co-authored-by: Zoltan Kochan <z@kochan.io> |
||
|
|
2ea04e6e52 |
style(rust): adopt complexity-aware rustfmt (#14875)
Pin pnpm's rustfmt fork and use a cached wrapper for local formatting, CI, pre-push checks, and editor integration. The formatter has its own dated nightly runtime while the workspace keeps its existing compiler. Choose chain layout by call count and argument complexity, with a 40-column allowance for short expressions. Attach the first method when the complete first line would otherwise end within the continuation indentation. Preserve simple receivers, cap intermediate leading accesses at column 80 and final accesses at 100, and separate fields and await after wrapped methods. Apply the same rules in conditions. Keep Max heuristics and compact struct literals, with an independent 35-column destructuring limit. Reformat pnpm and pnpr, extract only the helpers and test modules needed for existing size limits, and fix macro commas without relaxing lint rules. Related to pnpm/pnpm#14562 and pnpm/pnpm#14862. |
||
|
|
3610b85e30 |
feat(pnpr): serve container images (#14629)
Adds OCI as a fourth ecosystem beside npm, Cargo, and Python: a hosted image registry that docker, podman, and skopeo push to and pull from. The distribution API cannot be moved under a path prefix. A client derives the API root from the image reference's host, so `pnpr.example.com/acme/app:1.0` always requests `/v2/acme/app/manifests/1.0`. `/v2/` therefore mounts at the host root whatever else is served, and the repository name alone selects the registry through the declared-provenance rules the other surfaces use. Artifactory has to burn the first path segment as a repository key for want of that invariant; pnpr does not, so the image name stays the image name. `/oci/v2/` and `/oci/~<name>/v2/` are served too, for podman and containerd, whose registry configuration does accept a path. Repository names are `/`-joined, which the pattern language had no shape for. `PackagePattern::parse` now takes the ecosystem and offers each one only the wildcard its names can carry: npm keeps `@scope/*` and `@*/*`, images get `<namespace>/*` over a single leading component, and Cargo and PyPI get neither, since a flat name could never match one. A `packages:` key is normalized through that language rather than through the name rules, so a wildcard key parses as itself. Blob uploads stream to a local file across requests instead of buffering in memory, and are verified against the promised digest before anything is stored. The manifest write is the commit point and rides the existing publish journal; blobs land outside it, content-addressed and invisible until a manifest names them, which is what leaves unreferenced blobs for a collector rather than half-publishing a release. A tag carries the time it last moved, so a transaction recovered after a crash cannot drag one back to an older manifest. `Basic` credentials now carry a token as the password under any username, which is how `docker login` sends them, and `GET /v2/` challenges an anonymous caller even where reads are open: a client settles its authentication scheme on that one response, so a 200 would leave it no way to authenticate a push. Verified end to end against docker 29.7.2 (login, push, pull, run), podman 5.8.4, and skopeo 1.22.2, plus an anonymous pull and a push refused after logout. The two client stacks take different upload paths and between them cover both: moby sends a blob monolithically, while containers/image chunks it. Not yet served: proxying an upstream image registry, blob collection, the referrers API, cross-repository blob mounts, and ranged blob downloads. |
||
|
|
39dcf560af |
feat: add pipelines, run records, and Cargo build caching (#14233)
Compose frozen installation, affected-project selection, workspace task scheduling, output caching, and run reporting in pnpm pipeline. Add a polling watch agent and an authenticated pnpr run-record service. Keep completed task results separate from Cargo incremental state. Opted-in Cargo tasks share immutable snapshots across linked worktrees, materialize private writable targets, and always execute. Keep snapshot storage separate from installed-package storage so eviction cannot break existing builds or installations. Key Cargo snapshots by repository inputs and compilation environment. Invalidate local-unit and build-script freshness after relocation, and invalidate freshness after content changes even when mtimes are retained. Use staged, integrity-checked restoration and coordinate pipeline writers through locks outside the disposable cache. Document the prototype's input, storage, and remote-service limitations. Add regression coverage for worktree reuse, relocation, deletion, concurrency, copy fallback, and invalid snapshot handling. Related to pnpm/rfcs#22, pnpm/rfcs#23, and pnpm/rfcs#25. |
||
|
|
6ba99fb386 |
feat(pnpr): share cargo compilation caches (#14620)
Expose named compiler caches through the WebDAV subset used by sccache. Apply pnpr account access and publication policies before buffering uploads, including existing token restrictions. Entries are immutable and verified against a digest bound to their cache scope, key, and bytes before serving. Reuse the shared artifact store's filesystem/S3 selection, quota accounting, publication lifecycle, and orphan reclamation. Bound compiler uploads to two concurrent bodies per server and reject excess requests before buffering. Store digest and payload as separate buffers in one conditional object write. Use metadata-only HEAD and duplicate checks; GET still verifies content before serving it. Stock sccache trusts the server and allowed publishers; it does not verify pnpm signed envelopes. Document trusted CI publication and HTTPS requirements. Also document sccache 0.17's absolute Rust build-path requirement and its read-only multilevel behavior: remote hits backfill disk, but new compilation misses are not cached locally when a tier is read-only. Add HTTP, policy, integrity, quota, and real Cargo integration coverage. Install sccache for Rust CI and local test setup. Extract the existing miette diagnostic normalization into a shared test helper to fix a shim assertion exposed by the full suite's longer temporary paths. |
||
|
|
43dc110bcf |
fix(pacquet): regenerate npm Windows shims (#14403)
npm creates global Windows shims before lifecycle scripts run, so the pacquet wrapper's preinstall rewrite to pnpm.exe leaves npm's PowerShell shim targeting the extensionless placeholder. Run the wrapper installer again in postinstall and, only for npm global Windows installs, invoke npm rebuild with lifecycle scripts disabled. npm then re-reads the rewritten bin map and regenerates its shims against the native executable without recursing into postinstall. Keep the published source manifest's bin target extensionless for older pnpm and Unix compatibility. Add a fixture test for npm global installs and isolate the Rust test runner from contributor-level registry configuration. Fixes pnpm/pnpm#14362. |
||
|
|
13d32215d4 |
refactor(pnpr): extract pnpr-osv, pnpr-search, and pnpr-shared-artifacts (#14212)
## Summary The last structural step of the crate split from pnpm/pnpm#14191. Three modules now depend on nothing else inside `pnpr` — everything they touch is already its own crate — so each becomes one: | crate | contents | tests | | --- | --- | --- | | `pnpr-osv` | the OSV advisory index pnpr consults to refuse vulnerable versions | 14 | | `pnpr-search` | the local `/-/v1/search` scan over hosted packages | 7 | | `pnpr-shared-artifacts` | the shared build-artifact store: reservations, blobs, usage accounting | 23 | That leaves **`server` and `resolver`** in `pnpr` itself, which is where they belong — the HTTP surface and the install accelerator behind it, and the only pair left that genuinely references each other. Twelve items the boundaries caught as reaching across become `pub`; seven gained `#[must_use]` as newly public API. ### The whole split, end to end `pnpr` went from one 42k-line crate to **fourteen**, each named for what it holds: ``` pnpr-error → pnpr-package-name → pnpr-registry → pnpr-policy → pnpr-config → { pnpr-auth, pnpr-route, pnpr-upstream, pnpr-osv } → pnpr-storage → { pnpr-search, pnpr-shared-artifacts } → pnpr (server + resolver) ``` Layering is now checked by cargo rather than by convention. |
||
|
|
82e9ae5eb0 |
refactor(pnpr): extract pnpr-storage (#14210)
## Summary Continues the crate split from pnpm/pnpm#14191, after pnpm/pnpm#14202, pnpm/pnpm#14204 and pnpm/pnpm#14207. `storage`, `s3`, `streaming`, `journal` and `publish` move together as **one** crate. That is not a shortcut — the graph says they are one subsystem: ``` storage → journal → publish → streaming → storage (a 4-cycle) s3 ↔ storage (another) ``` Splitting them further would mean inventing seams the code does not have. The crate they form is coherent on its own terms: where packages live, how bytes get into that store, and the journal that makes a publish survive a crash. The two thin edges are `Storage::publish_journal()` (a convenience constructor) and `streaming`'s use of `TarballWrite` — either could be cut, but doing so to manufacture crate boundaries would make the code worse, not better. ### What the boundary caught Fifteen items become `pub`, plus the `HostedPackumentForUpdate` fields the server reads — I had initially guessed that type was crate-internal and the compiler corrected me. `HostedBackend` deliberately stays `pub(crate)`: the trait is implemented and dispatched only inside this crate, so it is the one part of the backend contract that is nobody else's business. The re-export is split by visibility to say so. One over-publish caught on review of my own diff: `revision_ref_owner` exists twice — a method and a free function — and the compiler only needed the method. The free function is back to private. Four doc links pointed at `crate::serve`, `crate::s3` or private items that no longer resolve from the new crate root; those are plain code spans now. `just test-pnpr` covers eleven crates at **733 tests**, unchanged. All feature combinations build and `cargo deny check` passes. |
||
|
|
54957d73c5 |
refactor(pnpr): extract pnpr-auth, pnpr-route, and pnpr-upstream (#14207)
## Summary Continues the crate split from pnpm/pnpm#14191, after pnpm/pnpm#14202 (the bottom layer) and pnpm/pnpm#14204 (`pnpr-config`). The three modules that depended only on `pnpr-config` become crates. They are independent of each other, so this is one *tier* rather than a chain: | crate | contents | tests | | --- | --- | --- | | `pnpr-auth` | the user and token stores: htpasswd, SQLite, networked SQL backends | 43 | | `pnpr-route` | classifies a fetch route public or private, so a shared resolution never leaks private data | 25 | | `pnpr-upstream` | the upstream registry proxy client: packument/tarball fetches, cache validators, circuit breaker | 31 | Nine items the boundaries caught as reaching across become `pub`, and seven gained `#[must_use]` as newly public API. Three doc links pointed at items that are private or at a crate no longer in scope; they are now plain code spans rather than links. ## Two things the boundaries exposed, both silent **`pnpr-auth`'s backend features did not forward.** They enable its own `libsql`/`sqlx`, but the matching `RegistryError` variants are gated in `pnpr-error`, so `cargo check -p pnpr-auth --features backend-libsql` failed to compile with 57 errors. It only ever worked *through* `pnpr`, which forwards to both. Now `pnpr-auth`'s features forward to `pnpr-error` as well. **The justfile's two test recipes had drifted.** `test-pacquet` runs the workspace minus `pnpr` and `pnpr-fixtures`; `test-pnpr` runs the rest. The intent is clear — pacquet crates in one job, pnpr crates in the other — but the excludes were written when `pnpr` was one crate. Every new `pnpr-*` crate has been landing in the *pacquet* job, and building bare there: `pnpr-auth` would have reported 28 tests instead of 43, **dropping its 14 libsql backend tests from CI without failing anything.** Both recipes now name every `pnpr-*` crate: excluded from `test-pacquet`, selected together in `test-pnpr` so cargo's feature unification gives them the backends `pnpr` itself defaults to. `just test-pnpr` covers all ten crates at **732 tests**, and `just test-pacquet` is unchanged at 8606. That drift is worth knowing for the remaining extractions — it is the kind of coverage loss that shows up as a green build. |
||
|
|
11f703c7f3 |
chore(rust): use cargo fixit for the fix target and pin cargo-fixit (#13983)
Switch the fix target from `cargo clippy --fix` to `cargo fixit --clippy` and pin `cargo-fixit@0.1.13` as a dev prerequisite. `cargo fixit` is faster than `cargo clippy --fix` on repeated runs because it skips the full re-check compile between fix rounds, so iterating on a lint cleanup doesn't rebuild the workspace each pass. No fallback to `cargo clippy --fix`. - justfile: add a `fix` recipe running `cargo fixit --clippy --workspace --all-targets --allow-dirty --allow-staged` - justfile: install `cargo-fixit@0.1.13` from source (`cargo install ... --locked`) in `just init`; cargo-fixit has no prebuilt binstall binaries - package.json: `fix:rust` now delegates to `just fix` (single source of truth, drops the `cargo clippy --fix` invocation) - CONTRIBUTING: document cargo-fixit in the `just init` tool list and the `just fix` workflow |
||
|
|
fe8ec8fbec |
refactor(pacquet): rename the Rust crates from pacquet- to pnpm- (#13948)
The Rust CLI ships as `pnpm`; `pacquet` survives only as the in-repo package identifier that keeps the npm wrapper from colliding with the TypeScript CLI's name. Carrying that identifier into all 75 Cargo crate names made every `use` statement and every `cargo nextest run -p ...` spell a name users never see. This renames the crates to the product they belong to. Mechanical, name-only: package/lib/bin names, the root `[workspace.dependencies]` keys, every dependency entry, and every Rust path reference. All the crates are `publish = false`, so nothing about the released artifacts changes and no changeset is needed. Also updated the tooling and docs that spell crate names: the `codecov` cargo alias, the `registry-mock` just recipe, the release workflow's `-p` targets and `libpnpm_napi.*` artifact paths, the `libpacquet` -> `libpnpm` cspell dictionary entry, and the crate names quoted across `AGENTS.md`, `CONTRIBUTING.md`, `CODE_STYLE_GUIDE.md`, and the plans. Two spots needed more than a token swap. The five insta snapshot files carry the crate name in their filename, since insta derives it from the module path, so they are renamed too — otherwise the tests fail with "snapshot not found" rather than a diff. And the private module in `pnpm-lockfile-verification` that mirrors the npm verifier's violation codes is named after the crate it shadows, so it moved with it. Two knock-on fixes: the shorter names let rustfmt fit three macro invocations onto one line, where `perfectionist::macro_trailing_comma` rejects the trailing comma that was legal while they were wrapped; and the `libpacquet` cspell entry became `libpnpm` for the NAPI addon's shared library. Left alone deliberately, none of which are crate names: the product name in prose and in the changeset package identifier, the `--pacquet` benchmark flag and testbed IDs, test helpers and temp-dir prefixes (`pacquet-test-*`, `../pacquet-store`), the released changesets that quote error codes shipped versions actually emitted, the `.github/workflows/pacquet-*.yml` filenames, and the `@pnpm-private/pacquet-registry-mock-launcher` npm package. |
||
|
|
7e5783681a |
fix(cli): restore CI frozen lockfile default (#13782)
## Summary - Restore the Rust CLI default that freezes an existing non-empty `pnpm-lock.yaml` in CI, so an outdated lockfile fails without being rewritten. - Preserve explicit `frozenLockfile` and `preferFrozenLockfile` choices from CLI flags, trusted configuration, environment variables, and `.pnpmfile.cjs` hooks. - Recognize `CI=1`, `CI=true`, and GitHub Actions; let explicit `CI=false` opt out, and prevent repository-controlled `pnpm-workspace.yaml` from overriding CI detection. - Keep missing, byte-empty, and semantically empty lockfiles writable, and isolate CI-sensitive spawned commands in the Rust test workflow. The TypeScript CLI already has this behavior; this brings the Rust `pnpm/` port back into parity. Fixes pnpm/pnpm#13760. |
||
|
|
baf11eefd6 |
test: stop leaking temp dirs from three test fixtures (#13342)
`TempDir::keep()` disables cleanup, and three of the four sites using it did not need to: the `'static` requirement was on the value being leaked (`StoreDir`, `Config`), never on the directory. A full run left ~45 directories in the system temp dir; it now leaves ~10. - `overrides::tests::manifest_from_value` wrote a `package.json` only to read it straight back, for a path its own doc calls a stub. It builds the manifest with `PackageManifest::from_value` instead, which touches no filesystem at all — one leak per call across ~25 callers, gone, and the helper drops to one line. - `env_installer::tests::harness` leaked the store directory alongside the `&'static StoreDir` pointing at it. The `TempDir` moves into `Harness`, which already exists to hold per-test handles. - `install_package_from_registry::tests::create_config` leaked a cache dir while every caller was separately creating one of its own and passing it to the resolver. It takes the caller's, so the config and the resolver now share a cache the way they do in practice. `cli/tests/pnpr_install.rs` keeps its `keep()`: the detached server thread outlives both the function and the test. Add `just sweep-test-temp` for the rest. A killed test process cannot run `Drop`, so a fail-fast or interrupted run always abandons whole fixture trees, including a per-test store for the mocked-registry tests. The sweep skips anything under an hour old so it cannot disturb a concurrent run. Redirecting `TMPDIR` at the whole suite does not work: tests assert on stderr containing paths under the temp dir, and a `TMPDIR` under `target/` makes the walk for `pnpm-workspace.yaml` find this repo's own. Related to pnpm/pnpm#12485. |
||
|
|
0609982b29 |
refactor(pacquet): ship the CLI under its final pnpm name (#12923)
* **New Features** * The CLI build and distributed archives are now `pnpm`, with updated dev commands and release artifacts. * Shell completions are consistently branded as `pnpm` and generate `pnpm completion-server` wiring. * **Bug Fixes** * Version output/user-agent reporting now aligns with `pnpm`. * Diagnostics and retry-related error codes are updated to `ERR_PNPM_*`. * **Documentation** * Updated E2E and integrated-benchmark guides to reference `pnpm` build outputs. * SBOM metadata now identifies the tool as `pnpm`. * **Chores** * Updated CI E2E/benchmark workflows and test suites to run the `pnpm` binary. |
||
|
|
8e17c3d366 |
refactor: rename the pacquet/ directory to pnpm/ (#12913)
Pure directory move plus path fixups: the Rust port ships as pnpm v12, so the source tree now lives at pnpm/ (alongside pnpm11/, the frozen TypeScript line). No identifiers change in this pass — crate names (pacquet-*), the pacquet bin, PACQUET_VERSION, the @pacquet/* npm package names v11's runPacquet spawns, the .pacquet virtual-store dir, the benchmark harness's clone dir, and the pacquet-*.yml workflow filenames (npm trusted publishing is bound to them) all stay for a follow-up. Also removes the root /pnpm/ .gitignore entry (build detritus in the pre-pnpm11 package location): pnpm/ is real source now and must not be ignored. Developers with a stale generated pnpm/ dir should delete it before checking out this change. |
||
|
|
601ab8499f |
fix(pacquet): put workspace root node_modules/.bin on script PATH (#12891)
pnpm adds the workspace root's node_modules/.bin to PATH for scripts and exec in every workspace project (extraBinPaths in the config reader), so root-level dev tools like tsgo are callable from member packages. pacquet had the extra_bin_paths plumbing through run/exec/dlx/pack/publish, but nothing ever populated it, so member scripts failed with 'command not found' for workspace-root binaries. Populate extra_bin_paths in Config::current from the discovered workspace dir, mirroring pnpm's config reader. The gap survived because the behavior-defining upstream tests were never ported: pnpm/test/recursive/run.ts 'pnpm recursive run finds bins from the root of the workspace' and the config reader's 'extraBinPaths' test. Port them — regression tests for root-bin resolution in plain and filtered-recursive run, the bin-priority rule (a project's own node_modules/.bin outranks the workspace root's), and the config-level population — and record them in plans/TEST_PORTING.md. Also scope the typos invocation in the just ready recipe to pacquet and pnpr, matching CI's spell-check job; the bare invocation failed on generated files under pnpm11 that cspell covers instead. |
||
|
|
8ecbbd356c |
docs(contributing): document Rust toolchain and git-hook tooling (#12875)
* docs(contributing): document Rust toolchain and git-hook tooling Rust is now the primary language in this repository, but the root CONTRIBUTING.md only covered the TypeScript setup. Add a "Rust toolchain and git hooks" section under "Setting Up the Environment" that covers rustup and the pinned toolchain, just, the just init tools, and the dylint tools. Two things that are easy to get wrong and cost real debugging time: - cargo-dylint and dylint-link must be installed from source, not with cargo binstall. The prebuilt binaries reference the dylint_driver crate at the path where they were built, so building the per-toolchain driver fails locally with an error pointing at a nonexistent .../dylint/driver directory. - ~/.cargo/bin must be on PATH (ahead of any system Rust in /usr/bin), because the pre-push hook locates its tools through PATH and silently skips a Rust check when the tool is missing rather than failing, so a push that looks clean locally can still fail format, doc, or dylint in CI. Point pacquet/CONTRIBUTING.md at the new root section instead of repeating the tool list, and correct its cargo binstall advice for the dylint tools. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * docs: correct dylint install hint in justfile and pre-push hook The `just dylint` recipe comment and the pre-push hook's skip message both told contributors to install cargo-dylint via `cargo binstall`, which produces a prebuilt binary that fails to build the per-toolchain driver locally. Point both at `cargo install` from source, matching the CONTRIBUTING.md guidance. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
9ddc86b635 |
ci: track test suite durations in bencher (#12404)
## Summary Adds CI duration tracking for the `pnpm-ci-performance` Bencher project. Tracked Rust testbeds and benchmarks: - `pacquet.ubuntu`, `pacquet.windows`, `pacquet.macos` -> `tests.all` - `pnpr.ubuntu`, `pnpr.windows`, `pnpr.macos` -> `tests.all` Tracked pnpm testbeds and benchmarks for full test runs: - `pnpm.ubuntu.node22`, `pnpm.ubuntu.node24`, `pnpm.ubuntu.node26` -> `tests.all`, `tests.cli` - `pnpm.windows.node22`, `pnpm.windows.node24`, `pnpm.windows.node26` -> `tests.all`, `tests.cli` The test workflows produce Bencher-compatible JSON artifacts without receiving `BENCHER_API_TOKEN`. A separate `workflow_run` workflow downloads those artifacts only for same-repository runs, validates their metadata, and uploads from trusted workflow code using the existing `BENCHER_API_TOKEN` secret. The pnpm CLI e2e duration is extracted from `pnpm run --report-summary` output during the same full-test execution, so the CLI e2e suite is not run a second time. |
||
|
|
c5d9d3a8f3 |
refactor(pnpr): rename pnpm-registry to pnpr (#12069)
* refactor(pnpr): rename pnpm-registry to pnpr Rename the registry server across the board to match the npm wrapper package name, which was already `@pnpm/pnpr`. - crate `pnpm-registry` -> `pnpr`, `pnpm-registry-fixtures` -> `pnpr-fixtures` - binaries `pnpm-registry` -> `pnpr`, `pnpm-registry-prepare` -> `pnpr-prepare` - module paths and log targets `pnpm_registry::*` -> `pnpr::*` - binary-locating env vars `PNPM_REGISTRY_BIN` -> `PNPR_BIN`, `PNPM_REGISTRY_PREPARE_BIN` -> `PNPR_PREPARE_BIN` - top-level directory `registry/` -> `pnpr/` (crates, npm wrapper, fixtures) The registry-mock storage concept is intentionally left as-is: `PNPM_REGISTRY_MOCK_PORT`/`PNPM_REGISTRY_MOCK_STORAGE`/`PNPM_REGISTRY_STORAGE`, the `~/.cache/pnpm-registry/storage` path + benchmark cache keys, and the external `pnpm-registry-mock` npm package referenced in test fixtures. * style(pnpr): rustfmt import grouping after rename * ci(pnpr): point typos at pnpr instead of removed registry dir * chore(pnpr): update pre-push path filter from registry to pnpr |
||
|
|
e375a58261 |
chore: run cargo fmt, doc, and dylint checks in husky pre-push (#12035)
The Rust workspace's pre-push checks were sitting in `pacquet/.githooks/pre-push` and only fired if a developer ran `just install-hooks`, which would also disable every husky-managed TypeScript hook by replacing `core.hooksPath`. Move the bash logic to `pacquet/scripts/pre-push-rust.sh`, invoke it from `.husky/pre-push` alongside the existing TS compile and lint checks, and drop the `install-hooks` recipe so nobody re-points `core.hooksPath` by mistake. The script now also runs `cargo doc --no-deps --workspace --all-features` (with `RUSTDOCFLAGS=-D warnings`) and `cargo dylint --all -- --all-targets --workspace` (with `RUSTFLAGS=-D warnings`), matching CI. `--workspace` covers both `pacquet/crates/*` and `registry/crates/*` since they share the root Cargo workspace. |
||
|
|
add6c794f1 |
feat(registry): implement pnpm-registry server and adopt it in pacquet's test mock (#11898)
Creates a working pnpm-compatible npm registry server (verdaccio analogue, in Rust) — and replaces `@pnpm/registry-mock`'s Node + Verdaccio launcher in pacquet's test setup with the new binary, against `@pnpm/registry-mock`'s shipped storage.
### What `pnpm-registry` does
- **HTTP server** (axum + tower-http) with the three endpoints pnpm/npm clients need:
- `GET /<pkg>` — packument (`/{name}` and `/{scope}/{name}`)
- `GET /<pkg>/<version-or-tag>` — single-version manifest, resolves `dist-tags` and rewrites `dist.tarball` to point at this server
- `GET /<pkg>/-/<tarball>` — tarball, streamed
- **Two modes:**
- **Proxy** — fetches missing packuments/tarballs from a configurable upstream (defaults to `https://registry.npmjs.org`), caches to disk
- **Static** (`--static`) — serves the storage directory verbatim, 404s on cache miss
- **Verdaccio-shaped on-disk storage** (`<root>/<pkg>/package.json` + flat tarballs) — drop-in compatible with the storage `@pnpm/registry-mock` publishes
- **Tarball streaming** — cache hits stream off disk; cache misses tee upstream chunks into a temp file via an mpsc channel and forward them to the client at the same time, atomically renaming on success and abandoning on upstream error or client disconnect
- **Tuned HTTP client** — wraps `pacquet_network::ThrottledClient::new_for_installs()`, inheriting pnpm's tuned defaults (`User-Agent: pnpm`, HTTP/1.1, hickory DNS, connection-pool tuning, concurrency semaphore)
- **Gateway-style status mapping** — `is_timeout()` → 504, `is_connect()` → 503, everything else (incl. upstream 5xx) → 502. No proxy-side retry (the pnpm client already has `fetch-retries`; stacking retries would only multiply latency on real failures).
### What changed in pacquet
- `pacquet/tasks/registry-mock` now spawns `pnpm-registry` against `node_modules/@pnpm/registry-mock/registry/storage-cache` (proxy mode with `npmjs.org` upstream and a 1-year packument TTL — matching `@pnpm/registry-mock`'s `'**': proxy: npmjs` verdaccio config). No more Node, no more Verdaccio, no more `launch.mjs`, no more process-tree walk to kill child verdaccios.
- `@pnpm/registry-mock` stays as a devDep — only for the storage data it ships, not the launcher.
### Tests
- **36 pnpm-registry tests** (12 unit + 7 against `@pnpm/registry-mock` storage in static mode + 17 mockito-based proxy/cache/streaming): packument rewrite, version-manifest resolution, tarball streaming (large body, cache finalize, mid-stream upstream error, client disconnect mid-stream, concurrent fetches → one cache file), gateway status mapping (504/503/502), stale-cache fallback on upstream failure, TTL refresh, invalid-package-name 400, scoped vs unscoped routing.
- **Full pacquet test suite** (2043 tests) runs green against `pnpm-registry`-backed mock.
### CI
- `pacquet-ci.yml` and `pacquet-codecov.yml` path filters now include `registry/**` (so registry-only PRs trigger the workspace CI); typos checker covers `registry` too. The workflow name stays "Pacquet CI" but a header comment explains the intentional cross-stack scope.
- `just registry-mock launch` pre-builds with `cargo nextest run --no-run` (workspace-wide) so its fingerprint matches what `just test` will later need — without this, Windows MSVC fails with `os error 5` trying to re-link the running `pnpm-registry.exe`.
### Crates.io name reservations (from the original scaffold commit)
- [`pnpm-registry`](https://crates.io/crates/pnpm-registry) — published from this repo
- [`pnpm-registry-cli`](https://crates.io/crates/pnpm-registry-cli) / [`pnpm-registry-server`](https://crates.io/crates/pnpm-registry-server) — placeholder stubs, name reservation only
|
||
|
|
61e724cef3 |
ci(pacquet): pass --workspace --all-targets to clippy and check (#11735)
Co-authored-by: Claude <noreply@anthropic.com> |
||
|
|
1575076d08 |
chore(pacquet): fold registry-mock into root workspace, fix npm metadata (#11643)
* chore(pacquet): fold registry-mock into root workspace, fix npm metadata
Two unrelated cleanups bundled because they touch the same publishing/
workspace plumbing:
1. **`pacquet/npm/pacquet/package.json` metadata** — the imported file
still pointed at the standalone repo: `repository.url` was
`pnpm/pacquet`, `repository.directory` was `npm/pacquet`, `homepage`
and `bugs` likewise. Repoint at `pnpm/pnpm`. Update
`generate-packages.mjs` so the per-platform packages it emits at
release time also point at `pnpm/pnpm`.
2. **Fold `pacquet/tasks/registry-mock` into the root pnpm workspace**.
Pacquet's standalone-repo nested-workspace setup pinned
`nodeLinker: hoisted` "for verdaccio CJS resolution," but pnpm's
own jest globalSetup (`__utils__/jest-config/with-registry/`) calls
the same `@pnpm/registry-mock.start()` API under the default
isolated linker without issue, and verified locally that
`node launch.mjs prepare` works after consolidation. The hoisted
constraint was scoped to standalone-pacquet's install pattern; in
the monorepo it's unnecessary.
Changes for (2):
- Add `pacquet/tasks/registry-mock` to `pnpm-workspace.yaml`.
- Rename the package `@pnpm-private/pacquet-registry-mock-launcher`
(private, matches the `@pnpm-private/*` convention used by other
internal workspace members) and switch `@pnpm/registry-mock` to
`catalog:` (the root catalog already pins it at 6.0.0).
- Delete `pacquet/tasks/registry-mock/pnpm-lock.yaml` and
`pnpm-workspace.yaml` — root install handles both now.
- Delete `pacquet/package.json` and `pacquet/pnpm-lock.yaml` — the
file only had a `cargo build` script + `devEngines: pnpm`, both
already covered by root, and nothing referenced it.
- `justfile install` is now just `pnpm install` (was
`cd pacquet/tasks/registry-mock && pnpm install --frozen-lockfile`).
- `pacquet-integrated-benchmark.yml` path filter and cache key
swap the deleted nested lockfile for the root `pnpm-lock.yaml` /
`pnpm-workspace.yaml`.
Verified: `pnpm install` resolves the workspace member, the lockfile
gains a `pacquet/tasks/registry-mock` importer entry, and
`pacquet/tasks/registry-mock/node_modules/@pnpm/registry-mock` is
linked correctly under the isolated layout.
* fix(pacquet): match meta-updater conventions in registry-mock launcher
The previous version of `pacquet/tasks/registry-mock/package.json`
omitted `version`, which crashed `pn lint:meta` (meta-updater hits
`manifest.version!.split('.')[0]` for every workspace package).
Backfill all the fields meta-updater would emit for an internal
`@pnpm-private/*` private package, matching `__typecheck__/package.json`
and friends:
- `version: 1100.0.0` (the pnpm 11.x convention for non-experimental
internal packages)
- self-devDep entry (`workspace:*`) that meta-updater would otherwise
inject
- `keywords: [pnpm, pnpm11]`
- `repository` pointing at this directory inside pnpm/pnpm
This is the same shape every other `@pnpm-private/*` private workspace
member uses; it lets `pn lint:meta --test` pass without modifying the
file.
* fix: update lockfile
* chore(pacquet): add build:pacquet script at root
Restore the `cargo build --release --bin pacquet` shortcut that lived in
the deleted `pacquet/package.json`. Naming it `build:pacquet` (rather
than `build`) matches the existing namespacing convention in this
file (`lint:ts`, `lint:meta`) and leaves room for a general `build`
script later. Invoke with `pnpm build:pacquet` or `pn build:pacquet`
from the repo root.
|
||
|
|
763ddf1c99 |
chore(pacquet): wire pacquet workflows into monorepo (#11635)
* chore(pacquet): wire pacquet workflows into monorepo
Move Cargo workspace, Rust toolchain configs, justfile, composite actions,
and 7 workflow files out of `pacquet/` and up to the repo root so:
- cargo / just / taplo run from repo root, the way the rest of the
monorepo's tooling does
- GitHub Actions actually discovers the workflows (it only reads
`.github/workflows/` at the repo root)
Workflows are prefixed with `pacquet-` and renamed to "Pacquet ..." so
they don't collide with the existing pnpm CI. Path filters are scoped
to `pacquet/**` so they don't trigger on every commit. The cargo entry
from pacquet's standalone `dependabot.yml` is folded into the root one;
pacquet's `CODEOWNERS` and `pull_request_template.md` are dropped because
the root copies supersede them.
Path rewrites:
- `Cargo.toml` workspace members → `pacquet/crates/*`, `pacquet/tasks/*`
- all path-deps in `[workspace.dependencies]` → `pacquet/...`
- `justfile` recipes (`install`, `install-hooks`) point at `pacquet/...`
- `.taplo.toml` include globs → `pacquet/crates/*/*.toml`, `pacquet/tasks/*/*.toml`
- `pacquet/npm/pacquet/scripts/generate-packages.mjs` REPO_ROOT walks one
more level up
- workflow `paths:` filters, `hashFiles(...)`, and shell paths all updated
Verified: `cargo metadata` resolves the workspace, `cargo fmt --check`
clean, `taplo format --check` picks up all 26 Cargo.tomls, `actionlint`
reports no new issues (the `type:`-on-input warnings on the rustup action
predate this move).
* chore(pacquet): drop pnpm version pin from pacquet CI workflows
The monorepo's root `package.json` declares `pnpm@11.1.1` under
`packageManager`, which conflicts with the workflows' explicit
`version: 11.0.0-rc.5` and trips `pnpm/action-setup` ERR_PNPM_BAD_PM_VERSION.
The pin was a pacquet-era workaround for the v9 lockfile while pnpm 11
was still pre-release. Stable 11.x writes v9 too, so let action-setup
read the version from `packageManager` like every other workflow in
this repo does.
* chore(pacquet): use pnpm/setup matching the rest of the monorepo
Replaces `pnpm/action-setup@v6` with the same `pnpm/setup@b1cac3...`
SHA the rest of pnpm/pnpm uses (release.yml, test.yml, ci.yml,
benchmark.yml, audit.yml). Reads pnpm version from `packageManager`
in root package.json, and skips the implicit `pnpm install` since
pacquet does its own scoped install via `just install` (which only
touches `pacquet/tasks/registry-mock/`).
The release workflow now also installs Node via the same action
(`runtime: node@22`) instead of via `pnpm runtime -g set node 22`,
since pnpm/setup handles runtimes in one step.
* chore(pacquet): tighten permissions and Dependabot cooldown
Address zizmor warnings on the pacquet CI changes:
- `dependabot.yml`: the cargo entry I added in the previous commit
inherited from pacquet's standalone repo and is missing the
`cooldown: default-days: 7` the github-actions entry uses. Add it
so cargo and github-actions debounce updates consistently.
- `pacquet-ci.yml`, `pacquet-codecov.yml`, `pacquet-cargo-unused.yml`
lacked a top-level `permissions:` block, so GITHUB_TOKEN inherited
the repo default. Declare `contents: read` — every job in these
workflows only reads the repo and runs local checks.
The other four pacquet workflows already declare permissions
explicitly (integrated-benchmark/comment, micro-benchmark, release).
* chore(pacquet): add "reimagining" to cspell dictionary
cspell at the repo root scans all `**/README.md` and was rejecting
`pacquet/README.md` and `pacquet/npm/pacquet/README.md`, which describe
pacquet as "not a reimagining of pnpm." Add the word to the existing
allow-list rather than rewording two READMEs imported from a separate
repo.
* fix(pacquet): prefix workspace-relative paths with pacquet/
Two Rust source files looked up paths off the cargo workspace root
(\`cargo locate-project --workspace\`), which now resolves to the
monorepo root rather than the pacquet directory. Add the \`pacquet/\`
prefix:
- \`tasks/registry-mock/src/dirs.rs\` — \`registry_mock()\` was
pointing the node launcher at \`<repo>/tasks/registry-mock/launch.mjs\`
instead of \`<repo>/pacquet/tasks/registry-mock/launch.mjs\`, which
failed every Pacquet CI test job ("Cannot find module ...launch.mjs").
- \`tasks/micro-benchmark/src/main.rs\` — same idea for the
fixtures folder.
|