pnpm 12.4
pnpm 12.4 installs crates and Python packages next to npm packages in the same
workspace, adds pnpm pipeline to run a workspace's tasks the way a CI job
would, and ships binaries for six more platforms. pnpr 0.1.0-alpha.11, released
alongside 12.4.1, serves Cargo, Python, and container registries beside npm,
publishes across all of them in one transaction, and signs users in through
OIDC.
Minor Changes
One workspace, three ecosystems
pnpm can now manage npm, Python, and Cargo dependencies in the same workspace.
Enable cargo.enabled or python.enabled in
pnpm-workspace.yaml, then install them all with one pnpm install.
cargo:
enabled: true
python:
enabled: true
pnpm add crate:serde
pnpm add pypi:httpx
pnpm install
Each ecosystem keeps its own semantics. Cargo dependencies live in
Cargo.toml and Cargo.lock, resolve against crates.io or the sparse registry
named by cargo.indexUrl, and are vendored into a Cargo directory source that
pnpm wires up through .cargo/config.toml. Crates pinned to a git revision,
through a git dependency or [patch.crates-io], are checked out and vendored the
way cargo vendor lays them out. Registry authentication goes through pnpm's own
credentials, plus CARGO_REGISTRY_TOKEN or $CARGO_HOME/credentials.toml for
crates.io.
Python dependencies live in pyproject.toml and a standard
pylock.toml, with a managed .venv per project that pnpm run and pnpm exec
put on PATH. The lockfile format was validated independently with uv.
What is shared is everything below the ecosystem boundary: one HTTP and
authentication budget, one verified-artifact ingestion path, one
content-addressable store. Frozen and offline installs work for all three, and
both new ecosystems can offload resolution to
pnprServer, falling back to local resolution
when the server does not serve it
(#14566).
This is early. The settings and the layout pnpm writes may still change.
pnpm pipeline
pnpm pipeline [name] installs frozen dependencies and runs a
named set of workspace tasks. It selects the affected projects, runs their task
graph, and keeps going after a task fails, so one run reports every failure
rather than the first.
tasks:
build:
dependsOn: ['^build']
outputs: ['dist/**']
test:
dependsOn: ['build']
outputs: []
pipelines:
default: [build, test]
Declaring outputs is what makes a task cacheable, outputs: [] included as the
positive statement that a task writes no files. inputs narrows the key, env
adds environment values to it, and cache: false opts back out. A hit restores
the output files and replays the log.
Cargo tasks can reuse local build state between worktrees through
tasks.<name>.cargoTargetDir, and pnpm pipeline --dry-run prints the graph
without installing anything or running a hook.
Six more platforms
pnpm 12.4 ships binaries for Android on arm64 and x64, FreeBSD on x64, and Linux on ppc64le, s390x, and RISC-V. See supported platforms for the full list (#14431, #14597, #7582).
trustPolicyExcludePrune
trustPolicyExcludePrune
removes the entries of trustPolicyExclude that the freshly written lockfile no
longer resolves. It is off by default. Name patterns such as @scope/* are kept,
and the cleanup is skipped when sharedWorkspaceLockfile is false.
pnpm change check
pnpm change check validates the versions committed in the
workspace against the versioning.epics bands and versioning.fixed groups. It
reads no change intents, so it belongs in CI on every pull request. Every
violation is reported, including ones in packages the current release does not
touch.
Registry metadata is keyed by the full URL
Registry metadata is now kept separate for registries that differ in URL path or scheme. That stops an install using another registry's package versions or tarball URLs, and stops metadata fetched over HTTP being reused for HTTPS (#13558).
The first install after upgrading refetches registry metadata; the package store
is untouched. pnpm cache view now shows full registry URLs, so a script that
parses the directory names out of pnpm cache list-registries or
pnpm cache list needs updating.
Other changes
- A patch that adds a build script or a
binding.gypnow makes the package buildable, subject to build approval. Until it is approved, the package is listed under "Ignored build scripts" (#14648). pnpm add --allow-build=!<pkg>rejects a build before the package is installed, global installs included, andpnpm approve-builds <pkg>saves a decision even when nothing is awaiting approval (#14067).- A registry configured in
.npmrcoutranks a routepnpm loginsaved in the globalconfig.yaml, so logging in cannot redirect a project that names its registry (#14614). fetchTimeoutnow limits how long a request may go without making progress, so a large download over a slow connection is no longer cut off while data is still arriving (#14604).pnpm add --workspace <pkg>works again, saving the dependency with theworkspace:protocol and failing when no workspace project provides it (#14602). Protocol-prefixed selectors such asjsr:@scope/pkg,npm:pkg@^1.0.0, andworkspace:pkg@*are accepted too (#14590).- Boolean flags accept an explicit inline value:
pnpm install --prod=falseinstalls devDependencies (#14553). - Commands run from a project's subdirectory find the nearest ancestor with a
manifest, so
pnpm binstops reporting paths under the wrong directory.pnpm initstill creates its manifest where you stand, andpnpm execstill runs there (#14622). pnpm patch-commitproduces valid patches when files are added or deleted, andpnpm installaccepts patches that delete files without listing their contents, and patch files with CRLF line endings (#14559, #14557).- Version ranges with partial upper bounds behave:
<=16includes every 16.x version (#14419). - Workspace package patterns accept
.and..segments and repeated slashes, so./packages/*and!./packages/foomatch (#14571). packageConfigssettings apply to the projects they name whensharedWorkspaceLockfileisfalse, and a workspace on a shared lockfile is told which entries were ignored (#14556).- Invalid certificates in
caorcafileno longer fail the install withInvalid CA certificate; the valid ones still apply (#14646). pnpm auditsummaries exclude advisories ignored throughaudit.ignoreand report them separately (#14535).- Shell completions cover the
pnalias in bash, fish, pwsh, and zsh (#11955), andpnpm versionaccepts-mfor--message(#14567). - Warm installs in workspaces with many projects are faster
(#14540), and
pnpm deployis faster in large workspaces and no longer fails withERR_PNPM_LOCKFILE_CONFIG_MISMATCHwhen the project has a.pnpmfile.mjs(#14539, #14671).
12.4.1
The follow-up release fixes installs that failed on filesystems refusing hard
links or clones, on Android, and under nodeLinker: hoisted, and makes repeat
installs faster.
pnpm installcopies a file instead of failing withOperation not permittedwhen the filesystem refuses a hard link or a copy-on-write clone, underpackageImportMethod: autoandclone-or-copy(#14722). EdenFS checkouts, which have no hard links, and rootless containers, which refuse the clone syscall, both hit this. pnpm also copies a file whose store entry has reached the filesystem's limit on names for one file, 1024 on NTFS and 65000 on ext4.- Registry requests on Android crashed because pnpm found no system CA certificates, so it uses bundled ones there (#14777).
- Under
nodeLinker: hoisted, a repeat install no longer re-imports packages that are already in place. It used to replace the wholenode_modulestree and reportPackages: +N, lifecycle scripts included. - A build whose whole effect lands outside its own package directory, such as a git hook installer, runs again when its side-effects cache entry has no files to restore (#14717).
ignoredOptionalDependenciesis applied bypnpm install,pnpm add, andpnpm dedupe. pnpm 12 installed those dependencies whenever it resolved from scratch (#14729).- Ctrl+C is passed on to the script pnpm started, and pnpm waits for it to shut down instead of exiting first and letting a still-writing script land on the shell prompt (#14723).
pnpm dedupeprocesses every workspace project by default, including workspaces that keep a lockfile per project (#14732).- The
updateConfigpnpmfile hook receives the resolved configuration, with scoped registries underregistriesByScopeand credentials underconfigByUri(#14676). - Repeat installs are faster: the store's files are checked only for the packages
pnpm links into
node_modules, and creating the command shims makes about 1,500 fewer filesystem calls in a 76 project workspace (#14540). - pnpm warns when the root
package.jsondeclares a non-emptyworkspacesarray and the project has nopnpm-workspace.yaml. Such an install linked no project and said nothing about why (#2255).
pnpr 0.1.0-alpha.11
Cargo, Python, and container registries
pnpr now serves Cargo and Python registries alongside npm
from one instance. Hosted Cargo registries support cargo publish, cargo yank,
cargo search, and crate downloads; hosted Python registries support
pip install --index-url and twine upload. Upstream registries can proxy
crates.io and PyPI with checksum-verified downloads, and one router can combine
sources from all three ecosystems.
A registry that serves more than one ecosystem addresses them through /npm/,
/cargo/, and /pypi/. A registry that serves only one keeps serving packages
at the root, so existing npm URLs still work. Registry names can be reused across
ecosystems by grouping configuration under registries.npm, registries.cargo,
registries.pypi, or registries.oci.
Container images are the fourth ecosystem. Declare a
registry with ecosystem: oci, then push to it with docker, podman, or
skopeo. The distribution API answers at /v2/ on the host root, and an image
keeps its own name with no registry key in the path. Ranged blob downloads,
cross-repository blob mounts, the referrers API, and paginated tag and repository
listings are supported, and pnpr can cache pulls from Docker Hub and GHCR with
per-repository bearer tokens and digest verification
(#14630).
PUT /-/pnpr/v0/publish publishes packages of more than one ecosystem in a
single transaction, so a workspace that ships an npm package, a crate, and a
Python distribution releases them together. A batch that fails a check publishes
none of it, and a server that stops midway finishes the release on the next
startup.
OIDC
pnpr supports OpenID Connect browser sign-in. Administrators map provider subjects to registry users, and GitHub Actions can publish npm packages without a persistent registry token, restricted to the packages you configure.
Builds, pipelines, and discovery
- pnpr can share Cargo compilation caches between CI and developers through sccache, with read and publication access granted separately.
- pnpr can store
pnpm pipelinerun reports, with a listing API, a detail API, and a web viewer. Records live with the hosted packages, so a run submitted through one replica is served by every other. - Browser registry UIs can be served through an origin allowlist, with paginated search, maintainer filters, organization listings, and per-registry upstream discovery. A registry directory endpoint lists the named registries, ecosystem endpoints, and routing order the caller can see.
- Every command-line option can come from an environment variable named after
the flag with a
PNPR_prefix, so--public-urlbecomesPNPR_PUBLIC_URL.
Fixes
- A package name carrying
?,#,%, whitespace, or a control character is rejected, artifact filenames held to the same rule. A percent-encoded delimiter let a request read an upstream package under a name the access rules had not checked. - JSR packages resolve with no configuration:
npm.jsr.iois a built-in public route, like the npm registry. - A staged publish is approved once, whichever replica of a shared registry the
approval reaches; a second approval answers
409(#12199). - A publish that loses a write race reports
document_write_conflicton every registry surface, naming the package document (#14599).
For the complete client changes, see the v12.4.0 and v12.4.1 release notes. The server changes are in the pnpr 0.1.0-alpha.11 release notes.
