Commit Graph

10870 Commits

Author SHA1 Message Date
Zoltan Kochan
d98ac7e4bb ci(release): split publish into three steps to force trusted publishing (#11496)
The previous "Publish Packages" step ran `pn release` after writing
NPM_TOKEN into pnpm's config. With a static `_authToken` configured,
`pnpm publish` bails out of OIDC entirely (see #11495 for the longer-
term fix), so every package — including `pnpm` and `@pnpm/exe` — was
silently being published with the legacy token instead of using npm's
trusted publishing. The result: published metadata showed
`_npmUser: pnpmuser` and no provenance attestation.

Until #11495 ships, work around the precedence bug by structuring the
job so the packages we *want* trusted publishing for never see a
static token at all:

1. `@pnpm/exe` — published in a step with no NPM_TOKEN. pnpm has no
   token to short-circuit on, performs OIDC, gets a `trustedPublisher`
   entry on npm.
2. Internal workspace packages — these don't have trusted publishing
   configured on npm, so they still need the static token. The token
   is written, the publish runs, then `pn config delete` removes the
   token before the next step.
3. `pnpm` — published in a step with no NPM_TOKEN, same rationale as
   step 1.

CI-only change; no changeset needed.
2026-05-06 17:19:24 +02:00
Zoltan Kochan
7bcfd970e9 fix(config): align scoped registry resolution between config get and publish (#11494)
Fixes [#11492](https://github.com/pnpm/pnpm/issues/11492).

In pnpm v11 a scoped registry resolved to different URLs depending on which command read it:

- `pnpm config get @<scope>:registry` returned the value from `.npmrc`
- `pnpm publish` used the value from `pnpm-workspace.yaml`'s `registries` block

When the two sources disagreed, `pnpm publish` silently targeted the URL from `pnpm-workspace.yaml`, even though `pnpm config get` reported a different (and seemingly authoritative) URL — so users could publish to the wrong registry without any indication.

This PR makes `pnpm config get @<scope>:registry` read from the merged `Config.registries` map (the same map `publish` and the resolvers use) before falling back to `authConfig`. Both commands now report and use the same URL.
2026-05-06 17:06:18 +02:00
Zoltan Kochan
0477da503b feat: implement native 'pnpm bugs' command (#11493)
Adds a native `pnpm bugs` command (npm-compatible) that opens a package's bug tracker URL in the browser.

- With no arguments, reads `bugs` / `repository` from the current project's `package.json`.
- With one or more package names, fetches each package's metadata from the registry (via `fetchPackageInfo`, so auth, scoped names, and proxies are handled) and opens its bug tracker.
- Falls back to `<repository>/issues` when `bugs` is missing, normalizing `git+`, `.git`, `git://`, and `git@github.com:` forms first.
- Implemented in `@pnpm/deps.inspection.commands` alongside `docs`, reusing the `open` package for cross-platform browser launching.

Picked up from #11279 (kairosci's branch) and reworked per @zkochan's review:
- moved out of `pnpm/src/cmd/` into the inspection commands package
- supports `pnpm bugs [<pkgname> [<pkgname> ...]]` per npm
- proper scoped-name encoding via the npm resolver
- changeset split (no more `bins.linker` bump)
- dropped unrelated test/build noise

Closes #11279.
2026-05-06 16:54:29 +02:00
Zoltan Kochan
832d898683 fix: stop install from recreating node_modules after pnpm fetch (#11490)
Closes #11488.

`pnpm fetch` writes forced-empty `hoistPattern: []` and `publicHoistPattern: []` into `.modules.yaml` (because its `virtualStoreOnly` install path skips hoisting). In v10 the follow-up `pnpm install` ignored these unless the user had explicitly set a hoist-pattern in their config. v11's [#11199](https://github.com/pnpm/pnpm/pull/11199) removed that explicit-config gate, so `validateModules` now always sees the empty patterns as a hoist-pattern change and purges `node_modules` — slow on every CI run, and per the bug report sometimes leaves the modules dir in an `ERR_MODULE_NOT_FOUND` state on subsequent runs.

The fix marks `.modules.yaml` with a new `virtualStoreOnly: true` field after a fetch. `validateModules` recognizes this flag as "incomplete install state" and skips the `PUBLIC_HOIST_PATTERN_DIFF` / `HOIST_PATTERN_DIFF` comparisons. The next install then completes the missing post-import linking in place rather than purging. The flag is dropped from `.modules.yaml` once a normal install runs.

A genuine hoist-pattern change (without a fetch in between) still triggers the purge as before — verified manually with `publicHoistPattern` in `pnpm-workspace.yaml`.
2026-05-06 14:39:40 +02:00
Zoltan Kochan
bcd337fa72 fix(cli): honor --pm-on-fail when combined with --help / --version (#11489)
The CLI argument parser short-circuits `--help` and `--version` and was discarding every other parsed option in the process — including universal flags like `--pm-on-fail`. So `pnpm audit --pm-on-fail=ignore --help` and `pnpm --pm-on-fail=ignore --version` failed with the strict `packageManager` mismatch error instead of doing what was asked. Users had no documented way out: the suggested escape hatch in the error message itself didn't work.

The fix plucks universal options back out of the exploratory `nopt` parse and surfaces them through both short-circuits. They were already typed correctly there; only the regular per-command parse adds command-specific options. Command-specific options (e.g. `--frozen-lockfile`) stay dropped, since the matching command isn't being executed.

Closes [#11487](https://github.com/pnpm/pnpm/issues/11487).
2026-05-06 14:28:06 +02:00
Zoltan Kochan
60fd20536d fix: pin integrity of git-hosted tarballs in lockfile (#11481)
For git-hosted tarballs (`codeload.github.com` / `gitlab.com` / `bitbucket.org`) the fetcher dropped the integrity it computed while downloading, so the lockfile only ever stored the URL. A compromised git host or man-in-the-middle could serve a substituted tarball on subsequent installs and pnpm would install it — the lockfile had no hash to compare against.

This pins the SHA-512 SRI of the raw tarball in the lockfile, in the same `sha512-<base64>` form npm-registry tarballs use. The only difference is the source: for npm we pass through `dist.integrity`, for git we compute it locally from the downloaded buffer. Subsequent installs validate the download against that integrity in the worker (`addTarballToStore` → `parseIntegrity` → hash compare), so a tampered tarball fails with `TarballIntegrityError`.

## Why git-hosted stays on `gitHostedStoreIndexKey`

The lockfile pins integrity for security, but the *store key* for git-hosted resolutions stays on `gitHostedStoreIndexKey(pkgId, { built })` rather than collapsing under the integrity-based key. Reason: git-hosted tarballs are post-processed (`preparePackage` / `packlist`), so the cached file set depends on whether build scripts ran during fetch. The integrity-only key would fold the built and not-built variants into a single slot, letting one overwrite the other and serving the wrong content if `ignoreScripts` was toggled between runs. Keeping git-hosted on the existing key shape preserves that dimension; the integrity is still validated on every fresh download.

## How the routing stays clean

The naive way to express "use gitHostedStoreIndexKey for git-hosted, integrity key for npm" is to call `isGitHostedPkgUrl(resolution.tarball)` everywhere a store key is computed — fragile, scattered, and easy to forget when adding new readers (Copilot caught two of those during review). Instead, a typed annotation: `TarballResolution` gets an optional `gitHosted: boolean` field. The git resolver sets it; the lockfile loader (`convertToLockfileObject`) backfills it for entries written by older pnpm versions; `toLockfileResolution` carries it through on serialize. Every consumer reads `resolution.gitHosted` directly. URL detection lives in exactly two places — the resolver and the loader — instead of seven.

## Changes

### Security fix
- `fetching/tarball-fetcher/src/gitHostedTarballFetcher.ts` — return the `integrity` that the inner remote-tarball fetch already computed (was being silently dropped by the destructure).

### Lockfile schema (additive)
- `@pnpm/lockfile.types` and `@pnpm/resolving.resolver-base` — `TarballResolution` gains optional `gitHosted: boolean`.
- `@pnpm/resolving.git-resolver` — sets `gitHosted: true` on every git-hosted tarball it produces.
- `@pnpm/lockfile.fs` (`convertToLockfileObject`) — backfills the field on load for older lockfiles via inlined URL detection.
- `@pnpm/lockfile.utils` (`toLockfileResolution`, `pkgSnapshotToResolution`) — preserve / read the field.

### Store-key consumers (now one-line typed reads, dropped the URL-sniffing dep)
- `installing/package-requester` (`getFilesIndexFilePath`)
- `store/pkg-finder` (`readPackageFileMap`)
- `modules-mounter/daemon` (`createFuseHandlers`)
- `building/after-install` (side-effects-cache lookup + write)
- `store/commands/storeStatus`
- `installing/deps-installer` (agent-mode store-controller wrapper)

### Fetcher routing
- `fetching/pick-fetcher` — `pickFetcher` prefers `resolution.gitHosted`; URL fallback retained for ad-hoc resolutions.

### Tests
- New integrity-validation test in `tarball-fetcher` (mismatched `integrity` on the resolution must throw `TarballIntegrityError`).
- New git-hosted lookup test in `pkg-finder` asserting routing through `gitHostedStoreIndexKey` even when integrity is present.
- New `toLockfileResolution` test asserting `gitHosted: true` flows through serialization.
- `fromRepo.ts` lockfile snapshot updated for the now-pinned integrity + `gitHosted: true`.
- `git-resolver` tests updated to assert `gitHosted: true` in produced resolutions.
2026-05-06 13:22:25 +02:00
Zoltan Kochan
29c2e7a5d9 fix(exe): restore execute bit on node-gyp shims in @pnpm/exe (#11485)
- Closes #11483.
- `@pnpm/exe@11.x` was packing `dist/node-gyp-bin/node-gyp`, `dist/node-gyp-bin/node-gyp.cmd`, and `dist/node_modules/node-gyp/bin/node-gyp.js` with mode `0644` instead of `0755`. Any install whose lifecycle script invokes `node-gyp rebuild` while pnpm has prepended `dist/node-gyp-bin/` to `PATH` failed with `sh: 1: node-gyp: Permission denied` — most visibly via `pnpm/action-setup@v6`'s standalone path on GitHub Actions runners with system Node < 22.13.
- The regular `pnpm` package's `package.json` already declares `publishConfig.executableFiles` for these three shims, which `pnpm publish` honors by packing matching tar entries with mode `0755` (see `releasing/commands/src/publish/pack.ts:273` and the `bins`-driven mode selection at `pack.ts:360-361`). `@pnpm/exe`'s `package.json` was missing the same field, so the shims it copies from `pnpm/dist/` shipped with `0644`. This PR mirrors the field into `@pnpm/exe`.
- To prevent the two manifests from drifting again, meta-updater is now the single source of truth for the executable-files list and writes it into both `pnpm/package.json` and `pnpm/artifacts/exe/package.json`.
2026-05-06 10:46:14 +02:00
Zoltan Kochan
9018103edc fix: keep workspace-root excluded when --recursive --filter uses only negative filters (#11480)
* fix: include workspace root only when --filter is positive

PR #10465 stopped the implicit workspace-root exclusion for `pnpm -r
run/exec/test/add` whenever any --filter was provided. That gate was
too broad: with a negative-only filter set (e.g. `--filter '!a'`) the
workspace root started showing up in the matched projects, contradicting
the documented default behavior.

Only suppress the implicit root exclusion when the user provided at
least one positive (non-`!`) filter that could have been intended to
select the root. Negative-only filter sets keep the documented default,
and `--include-workspace-root` continues to opt the root in explicitly.

Close #11341.

* test: fix root-exclusion assertion to match --stream prefix

The --stream reporter prefixes output lines with the project's relative
directory, so the workspace root appears as `. which$`, not
`root which$`. The original assertion would have passed even if the
regression were still present. Verified the corrected assertion fails
against the buggy code and passes against the fix.
2026-05-06 01:51:37 +02:00
Zoltan Kochan
fcec623c00 fix(config): allow user-level preferences in global config.yaml (#11477)
Moves 20 user-level preference settings from the workspace-only exclusion list into the global config allowlist (`config/reader/src/configFileKey.ts`):

- Shell / scripts: `scriptShell`, `shellEmulator`
- Notifications & UI: `updateNotifier`, `useStderr`
- Trust policy (already DLX-inherited as user-level posture): `trustPolicy`, `trustPolicyExclude`, `trustPolicyIgnoreAfter`
- Store / virtual store: `globalVirtualStoreDir`, `virtualStoreDir`, `virtualStoreDirMaxLength`, `verifyStoreIntegrity`, `sideEffectsCache`, `sideEffectsCacheReadonly`
- Build / dep verification: `strictDepBuilds`, `verifyDepsBeforeRun`
- Misc personal/system prefs: `stateDir`, `registrySupportsTimeField`, `initPackageManager`, `initType`, `agent`

These are personal/system preferences rather than workspace structure. In v10 they could be set in `~/.npmrc`. v11 silently dropped them from both `~/.npmrc` and the new global `config.yaml`, leaving `pnpm-workspace.yaml` as the only working location — which the issue author rightly points out is impractical for system-level defaults like `scriptShell`.

After this change:
- Settings in `~/.config/pnpm/config.yaml` are applied instead of being filtered out by `isConfigFileKey` (`config/reader/src/index.ts:296`).
- `pnpm config set --location global scriptShell <path>` succeeds instead of throwing `ConfigSetUnsupportedYamlConfigKeyError` (same predicate used in `config/commands/src/configSet.ts:237`).

`pmOnFail` and `runtimeOnFail` are intentionally left workspace-only because they would cause lockfile divergence between contributors when set globally. `~/.npmrc` support for non-auth/non-network keys is also intentionally not restored — the team has moved those settings to YAML config.

Closes #11474.
2026-05-06 01:44:46 +02:00
Zoltan Kochan
76083acd54 chore(release): track consumed changesets per branch to prevent re-application after cherry-pick (#11479)
* chore(release): wrap changeset version with cross-branch consumed-id ledger

When a fix is cherry-picked from main to a release branch (or vice
versa), the changeset file ends up on both branches. The release
branch's release consumes and deletes its copy, but the cherry-picked
copy on main survives the merge back and would be re-applied on the
next main release.

Introduce a small wrapper around `changeset version` that maintains a
per-branch ledger at .changeset/.released/<branch>.txt. Each entry is a
consumed changeset id; the file is written only by the branch it is
named after, so the records merge across branches without conflicts.

Before running `changeset version` the wrapper reads the union of every
ledger file, hides matching .changeset/<id>.md files (rename to
.md.released), then runs `changeset version` against the remaining set.
Newly consumed ids are appended to the current branch's ledger; hidden
files are removed afterward (their consumption is already on record
elsewhere). On failure the hidden files are restored to keep the
working tree clean.

* docs: move release-ledger explanation out of AGENTS.md

AGENTS.md is for instructions to AI agents working on the codebase, but
the cross-branch ledger is release machinery that the maintainer running
`pnpm bump` interacts with — agents authoring changesets do not need to
know about it. Move the explanation to where someone runs into it:

- .changeset/.released/README.md — discovered by anyone exploring the
  directory.
- A short doc-comment header at the top of __utils__/scripts/src/bump.ts
  pointing readers there.

* fix(scripts): harden bump wrapper edge cases from PR review

- Use url.pathToFileURL(realpathSync(...)) to compare against
  import.meta.url so the direct-invocation guard works on Windows
  paths and through symlinks (Copilot review).
- hideReleased() now iterates the changeset directory and filters by
  the released set instead of iterating the (potentially long) ledger
  and probing existsSync per entry (Copilot review).
- hideReleased() restores already-renamed files if a later rename
  throws, so a partial failure leaves the .changeset directory in its
  original state (CodeRabbit review).
- Move deleteHidden() into a finally so the .md.released files are
  cleaned up even if appendReleased() throws after a successful
  changeset version run (CodeRabbit review).
- Add a unit test that forces hideReleased() to fail mid-loop and
  asserts the rollback.
2026-05-06 01:34:52 +02:00
Charlie Croom
87b1c24452 feat(publish): restore npm-CLI-compatible --json stdout for publish (#11478)
pnpm 10 transitively emitted an npm-CLI-shaped per-package JSON object on stdout for
`pnpm publish --json` because the publish command shelled out to npm. pnpm 11 (#10591)
reimplemented publish natively and the new handler returned undefined, so `--json` now
produces 0 bytes on stdout on success — silently breaking downstream tooling that grepped
that contract. The most impactful regression is `nx release publish`, which parses
stdout JSON to confirm success; under `--nx-bail=true` it aborts the rest of a release
run when JSON is missing (see nrwl/nx#35575).

This restores the contract for both single-package and recursive publish, and aligns
`--report-summary` to use the same per-package shape:

- `pnpm publish --json`         → single object `{ id, name, version, size,
                                   unpackedSize, shasum, integrity, filename, files,
                                   entryCount, bundled }`, mirroring `npm publish --json`.
- `pnpm publish -r --json`      → array of those objects, mirroring `pnpm pack --json`'s
                                   shape choice for single vs multi.
- `pnpm publish -r --report-summary` → existing `pnpm-publish-summary.json` envelope
                                       `{ publishedPackages: [...] }` is preserved, but
                                       each entry is upgraded to the same per-package shape
                                       (additive — `name` and `version` are still present).

Implementation:
- `pack.api()` computes `unpackedSize` while it still has the filesMap.
- `publishPackedPkg` returns a `PublishSummary` (SHA-1 `shasum` + SHA-512 `integrity`
  via `node:crypto`, no new deps) instead of `void`.
- `bundled` normalizes `bundledDependencies` / `bundleDependencies` per npm's semantics
  (including `true` → all dep names).
- `recursivePublish` collects per-package `PublishSummary` objects and returns them; the
  existing manifest-pick fallback is kept for paths that don't produce a summary.
- `publish.handler` returns `{ output, exitCode }` with the serialized summary (object
  or array) when `opts.json` is set; main.ts already writes `output` to stdout.

Refs #11476
Refs nrwl/nx#35575


Amp-Thread-ID: https://ampcode.com/threads/T-019df90f-8f75-763f-b528-4602e870a972

Co-authored-by: Charlie Croom <charlie+amp@noreply.com>
Co-authored-by: Amp <amp@ampcode.com>
2026-05-06 00:40:20 +02:00
Zoltan Kochan
f79f0540ac chore: update Node.js to 26.0.0 (#11472)
* chore: update Node.js to 26.0.0

* fix(jest-config): use amaro for type stripping on Node.js 26

Node.js v26 removed the `transform` mode and `sourceMap` option from
`module.stripTypeScriptTypes`. Switch the Jest transform to call
`amaro.transformSync` directly (the same wasm transformer Node.js wraps)
so we keep inline source maps for tests.
2026-05-05 22:18:12 +02:00
Zoltan Kochan
286018ef2f docs: add PR/issue/comment instructions for agents (#11475)
Add a "Working with GitHub PRs, Issues, and Comments" section to AGENTS.md covering three rules:
Keep PR title and description current when pushing new changes.
Reply to resolved review comments with the resolution + commit hash and close the conversation.
Sign all agent-authored comments, issues, and PRs with a footer that names the agent and model.
2026-05-05 21:15:46 +02:00
Zoltan Kochan
e5ec38a861 chore: remove released changesets 2026-05-05 20:24:32 +02:00
Zoltan Kochan
16fd0cbc32 chore: remove released changesets 2026-05-05 20:20:55 +02:00
Zoltan Kochan
280cf7b858 Merge branch 'release/11.0' 2026-05-05 20:12:28 +02:00
Zoltan Kochan
65f9327014 chore(release): 11.0.6 v11.0.6 2026-05-05 19:50:32 +02:00
Dami Oyeniyi
8131d7c53e fix: validate readPackage dependency maps (#11230) 2026-05-05 19:38:19 +02:00
Dami Oyeniyi
5f34a8d028 fix: reject invalid overrides values (#11380)
* fix: reject invalid overrides values

* fix: improve overrides validation error messages
2026-05-05 19:38:06 +02:00
Zoltan Kochan
8fdd9a9a6f feat(config.reader): export getNetworkConfigs and getDefaultCreds (#11471)
Expose the helpers that build the `configByUri` registry-config map
from a flat rawConfig-style auth dict so that downstream consumers
(e.g. third-party clients of `@pnpm/installing.client` /
`@pnpm/store.connection-manager`) can produce the same configByUri
shape pnpm itself uses, without re-implementing the parsing logic.
2026-05-05 19:37:54 +02:00
Zoltan Kochan
2de318b2e9 fix(config): warn on ignored settings in global config.yaml (#11470)
- pnpm v11 silently drops local-only settings (e.g. `nodeLinker`, `hoistPattern`, `linkWorkspacePackages`) when they appear in the global `config.yaml`. Users had no way to tell their global configuration was being ignored.
- The reader now emits a warning that names the ignored keys and the path of the global config file, and directs users to either move the settings to a project-level `pnpm-workspace.yaml` or share them across projects via [config dependencies](https://pnpm.io/11.x/config-dependencies).
- Allowed keys (e.g. `dangerouslyAllowAllBuilds`, proxy settings) continue to be accepted with no warning.

close #11429
2026-05-05 19:37:46 +02:00
Zoltan Kochan
0d791f3107 fix(workspace-manifest-writer): preserve key order in pnpm-workspace.yaml (#11469)
- When updating `pnpm-workspace.yaml`, existing keys keep their positions instead of being globally re-sorted alphabetically.
- New keys are inserted in alphabetical position when the existing layout is sorted; otherwise they are appended at the end. A leading `packages` key is treated as a sorted-first slot, matching the conventional pnpm layout.
- Blank lines between top-level fields are propagated to newly inserted entries so they match the surrounding style — including the case where a new key sorts to the front and demotes the previously-first pair.
2026-05-05 19:37:38 +02:00
Zoltan Kochan
c96939266c fix: accept uppercase PNPM_CONFIG_* env vars (#11468)
* fix(config): accept uppercase PNPM_CONFIG_* env vars

Env vars are case-sensitive on macOS/Linux, so PNPM_CONFIG_USERCONFIG —
the rename suggested by the v11 migration guide — was silently ignored
because parseEnvVars only matched lowercase pnpm_config_*. Also wire the
env var into the early npmrcAuthFile lookup so it actually decides which
user-level .npmrc gets read.

Closes #11465

* chore: add changeset for npmrc auth file env-var load order

* test: cover lowercase pnpm_config_npmrc_auth_file env var

Matches the exact env var name reported in #11465. Without the early
env-var lookup before loadNpmrcConfig, this case is parsed too late
to actually load the custom .npmrc.

* test: lock precedence when both lowercase and uppercase env vars are set
2026-05-05 19:37:30 +02:00
Dami Oyeniyi
3fd440bc7f docs: clarify default catalog protocol behavior (#11381) 2026-05-05 19:37:24 +02:00
Fotis Papadogeorgopoulos
16f5cd302a fix(config): gracefully handle configDependencies errors during config commands (#11362)
Closes #10684. Running `pnpm config set` triggers `resolveAndInstallConfigDeps` before the new config (e.g. auth token) is saved to `.npmrc`, causing a 401 crash when the config dependency is hosted in a private registry.

This patch adds a `tolerateConfigDependenciesErrors` flag to `installConfigDepsAndLoadHooks`. For `cmd === 'config'`, `'set'` and `'get'`, installation failures are now caught and logged at debug level so the command can proceed and actually write / read the auth token.

`pnpm set` and `pnpm get` are separate top-level commands whose handlers delegate to the `config` command internally, so they are explicitly listed alongside `'config'`; users can hit #10684 via any of the three entry points.

Plugin pnpmfile paths from `configDependencies` are resolved by checking each file's existence on disk, so previously-installed hooks survive a transient install failure and `requireHooks` won't throw `PNPMFILE_NOT_FOUND` when nothing has been installed yet.

Covers the fix with:
- Unit tests in `pnpm/test/getConfig.test.ts` exercising the `installConfigDepsAndLoadHooks` contract (success, tolerated error, rethrown error, store creation/close errors propagating) and the on-disk pnpmfile resolution behavior.
- Four e2e tests in `pnpm/test/configurationalDependencies.test.ts` that spawn the pnpm CLI against a bogus `configDependency` and assert each entrypoint (`pnpm config set`, `pnpm config get`, `pnpm set`, `pnpm get`) still works.

---------

Co-authored-by: Zoltan Kochan <z@kochan.io>
2026-05-05 19:37:14 +02:00
Zoltan Kochan
0219ab2484 fix(self-update): refresh legacy v10 bootstrap shim at PNPM_HOME (#11467)
pnpm v10 setup added PNPM_HOME (not PNPM_HOME/bin) to PATH and wrote
a pnpm bootstrap shim there. After upgrading to v11, that shim still
points into the old .tools/<version> install, so PATH continues to
resolve `pnpm` to the pre-update version even though the new version
was installed under global/v11.

Detect that layout during self-update, refresh the shims at PNPM_HOME
so the upgrade actually takes effect, and warn the user to run
`pnpm setup` for a clean migration to the v11 PATH layout.

Closes #11464.
2026-05-05 19:37:03 +02:00
Maikel van Dort
817b1b4c3e fix: dlx catalogs not found (#11308)
Fixes #10594, catalogs not being read from the workspace when using the `catalog:` protocol with the `pnpm dlx` / `pnpx` command, resulting in a catalog entry not found error.

Added e2e tests to check if the workspace config is actually loaded. Also added that pnpm dlx reads the retry options from the workspace (Could potentially put that in a separate PR)

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

* **Bug Fixes**
  * Fixed catalog resolution when using the `catalog:` protocol with `pnpm dlx` / `pnpx` so catalogs are correctly read from the workspace.

* **New Features**
  * `dlx` now inherits workspace catalog and fetch retry/timeout settings so CLI runs respect those local configs.

* **Tests**
  * Added tests validating catalog inheritance and failure cases for `dlx` catalog resolution.

* **Chores**
  * Updated changeset metadata to mark related packages for patch releases.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Co-authored-by: Zoltan Kochan <z@kochan.io>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-05-05 19:36:55 +02:00
palkim
ab6c42d99e fix: refresh ignored builds when allowBuilds changes (#11366)
* fix: refresh ignored builds when allowBuilds changes

* refactor: extract isBuildExplicitlyDisallowed into @pnpm/building.policy

Removes the duplicated ignored-build filter from deps-installer and
deps-restorer and exposes it as `isBuildExplicitlyDisallowed` on
`@pnpm/building.policy`, alongside `createAllowBuildFunction`.

* fix: respect ignoredWorkspaceStateSettings in allowBuilds stale-state check

The fallback that flagged installs when allowBuilds went from unset to
non-empty bypassed the ignoredSettings filter, so callers that explicitly
opted out of allowBuilds tracking (via ignoredWorkspaceStateSettings)
could still be forced into a redundant install.

---------

Co-authored-by: Zoltan Kochan <z@kochan.io>
2026-05-05 19:36:42 +02:00
Zoltan Kochan
159b4c0fb4 ci: update pnpm/action-setup 2026-05-05 19:36:18 +02:00
Zoltan Kochan
6bed8ac25a feat(config.reader): export getNetworkConfigs and getDefaultCreds (#11471)
Expose the helpers that build the `configByUri` registry-config map
from a flat rawConfig-style auth dict so that downstream consumers
(e.g. third-party clients of `@pnpm/installing.client` /
`@pnpm/store.connection-manager`) can produce the same configByUri
shape pnpm itself uses, without re-implementing the parsing logic.
2026-05-05 18:58:23 +02:00
Zoltan Kochan
42378d07fb fix(config): warn on ignored settings in global config.yaml (#11470)
- pnpm v11 silently drops local-only settings (e.g. `nodeLinker`, `hoistPattern`, `linkWorkspacePackages`) when they appear in the global `config.yaml`. Users had no way to tell their global configuration was being ignored.
- The reader now emits a warning that names the ignored keys and the path of the global config file, and directs users to either move the settings to a project-level `pnpm-workspace.yaml` or share them across projects via [config dependencies](https://pnpm.io/11.x/config-dependencies).
- Allowed keys (e.g. `dangerouslyAllowAllBuilds`, proxy settings) continue to be accepted with no warning.

close #11429
2026-05-05 18:58:01 +02:00
Zoltan Kochan
4d7b355d42 fix(workspace-manifest-writer): preserve key order in pnpm-workspace.yaml (#11469)
- When updating `pnpm-workspace.yaml`, existing keys keep their positions instead of being globally re-sorted alphabetically.
- New keys are inserted in alphabetical position when the existing layout is sorted; otherwise they are appended at the end. A leading `packages` key is treated as a sorted-first slot, matching the conventional pnpm layout.
- Blank lines between top-level fields are propagated to newly inserted entries so they match the surrounding style — including the case where a new key sorts to the front and demotes the previously-first pair.
2026-05-05 17:14:00 +02:00
Zoltan Kochan
d74ddaaecd fix: accept uppercase PNPM_CONFIG_* env vars (#11468)
* fix(config): accept uppercase PNPM_CONFIG_* env vars

Env vars are case-sensitive on macOS/Linux, so PNPM_CONFIG_USERCONFIG —
the rename suggested by the v11 migration guide — was silently ignored
because parseEnvVars only matched lowercase pnpm_config_*. Also wire the
env var into the early npmrcAuthFile lookup so it actually decides which
user-level .npmrc gets read.

Closes #11465

* chore: add changeset for npmrc auth file env-var load order

* test: cover lowercase pnpm_config_npmrc_auth_file env var

Matches the exact env var name reported in #11465. Without the early
env-var lookup before loadNpmrcConfig, this case is parsed too late
to actually load the custom .npmrc.

* test: lock precedence when both lowercase and uppercase env vars are set
2026-05-05 16:16:51 +02:00
Dami Oyeniyi
a3fb1b6661 docs: clarify default catalog protocol behavior (#11381) 2026-05-05 15:02:36 +02:00
Fotis Papadogeorgopoulos
0c137bbbe0 fix(config): gracefully handle configDependencies errors during config commands (#11362)
Closes #10684. Running `pnpm config set` triggers `resolveAndInstallConfigDeps` before the new config (e.g. auth token) is saved to `.npmrc`, causing a 401 crash when the config dependency is hosted in a private registry.

This patch adds a `tolerateConfigDependenciesErrors` flag to `installConfigDepsAndLoadHooks`. For `cmd === 'config'`, `'set'` and `'get'`, installation failures are now caught and logged at debug level so the command can proceed and actually write / read the auth token.

`pnpm set` and `pnpm get` are separate top-level commands whose handlers delegate to the `config` command internally, so they are explicitly listed alongside `'config'`; users can hit #10684 via any of the three entry points.

Plugin pnpmfile paths from `configDependencies` are resolved by checking each file's existence on disk, so previously-installed hooks survive a transient install failure and `requireHooks` won't throw `PNPMFILE_NOT_FOUND` when nothing has been installed yet.

Covers the fix with:
- Unit tests in `pnpm/test/getConfig.test.ts` exercising the `installConfigDepsAndLoadHooks` contract (success, tolerated error, rethrown error, store creation/close errors propagating) and the on-disk pnpmfile resolution behavior.
- Four e2e tests in `pnpm/test/configurationalDependencies.test.ts` that spawn the pnpm CLI against a bogus `configDependency` and assert each entrypoint (`pnpm config set`, `pnpm config get`, `pnpm set`, `pnpm get`) still works.

---------

Co-authored-by: Zoltan Kochan <z@kochan.io>
2026-05-05 12:27:12 +00:00
Zoltan Kochan
75f4af9451 chore: update pnpm-lock.yaml (#11192) 2026-05-05 14:21:48 +02:00
Dami Oyeniyi
c317c7700c fix: validate readPackage dependency maps (#11230) 2026-05-05 14:15:21 +02:00
Zoltan Kochan
d351d52adf fix(self-update): refresh legacy v10 bootstrap shim at PNPM_HOME (#11467)
pnpm v10 setup added PNPM_HOME (not PNPM_HOME/bin) to PATH and wrote
a pnpm bootstrap shim there. After upgrading to v11, that shim still
points into the old .tools/<version> install, so PATH continues to
resolve `pnpm` to the pre-update version even though the new version
was installed under global/v11.

Detect that layout during self-update, refresh the shims at PNPM_HOME
so the upgrade actually takes effect, and warn the user to run
`pnpm setup` for a clean migration to the v11 PATH layout.

Closes #11464.
2026-05-05 13:48:55 +02:00
Maikel van Dort
b14496af4e fix: dlx catalogs not found (#11308)
Fixes #10594, catalogs not being read from the workspace when using the `catalog:` protocol with the `pnpm dlx` / `pnpx` command, resulting in a catalog entry not found error.

Added e2e tests to check if the workspace config is actually loaded. Also added that pnpm dlx reads the retry options from the workspace (Could potentially put that in a separate PR)

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

* **Bug Fixes**
  * Fixed catalog resolution when using the `catalog:` protocol with `pnpm dlx` / `pnpx` so catalogs are correctly read from the workspace.

* **New Features**
  * `dlx` now inherits workspace catalog and fetch retry/timeout settings so CLI runs respect those local configs.

* **Tests**
  * Added tests validating catalog inheritance and failure cases for `dlx` catalog resolution.

* **Chores**
  * Updated changeset metadata to mark related packages for patch releases.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Co-authored-by: Zoltan Kochan <z@kochan.io>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-05-05 11:35:05 +00:00
palkim
e51c8e281e fix: refresh ignored builds when allowBuilds changes (#11366)
* fix: refresh ignored builds when allowBuilds changes

* refactor: extract isBuildExplicitlyDisallowed into @pnpm/building.policy

Removes the duplicated ignored-build filter from deps-installer and
deps-restorer and exposes it as `isBuildExplicitlyDisallowed` on
`@pnpm/building.policy`, alongside `createAllowBuildFunction`.

* fix: respect ignoredWorkspaceStateSettings in allowBuilds stale-state check

The fallback that flagged installs when allowBuilds went from unset to
non-empty bypassed the ignoredSettings filter, so callers that explicitly
opted out of allowBuilds tracking (via ignoredWorkspaceStateSettings)
could still be forced into a redundant install.

---------

Co-authored-by: Zoltan Kochan <z@kochan.io>
2026-05-05 01:17:11 +00:00
Sebastian Qvarfordt
87b4bac2bd feat(sbom): added support for specifying --sbom-spec-version (#11389)
Currently only supported for cyclonedx. Allowed values are 1.5, 1.6, and 1.7 (defaulting to 1.7); the lower bound is 1.5 because the generated JSON uses fields (e.g. `metadata.lifecycles`) that are only valid from CycloneDX 1.5+.

---------

Co-authored-by: Zoltan Kochan <z@kochan.io>
2026-05-05 01:12:51 +00:00
Dami Oyeniyi
6d7903a8b7 fix: reject invalid overrides values (#11380)
* fix: reject invalid overrides values

* fix: improve overrides validation error messages
2026-05-05 01:06:33 +02:00
Zoltan Kochan
7e91e4b35f ci: update pnpm/action-setup 2026-05-04 22:24:17 +02:00
Zoltan Kochan
9f58e44220 chore: update pnpm to v11.0.5 2026-05-04 22:23:08 +02:00
Zoltan Kochan
1abf5b4467 Merge branch 'release/11.0' 2026-05-04 22:16:17 +02:00
Zoltan Kochan
cc373c39f1 chore(release): 11.0.5 v11.0.5 2026-05-04 22:14:24 +02:00
Zoltan Kochan
cd87c16dd5 fix(reporter): make WARN and error labels readable without color (#11460)
The `WARN` and `ERR_PNPM_*` labels in pnpm's output relied entirely on a colored background to stand out from the surrounding text. In terminals without color — `NO_COLOR` set, output piped, dumb terminals — the badge collapsed into bare `WARN` / `ERR_PNPM_FOO` and became hard to spot inside the message.

This PR wraps each label in brackets (`[WARN]`, `[ERR_PNPM_FOO]`, `[ERROR]`). The bracket characters are painted in the same color as the badge background, so in a color-capable terminal they appear as plain padding inside the colored badge — the rendering matches what we had before. When ANSI is stripped the brackets reappear as ordinary text, giving the label a clear delimiter.
2026-05-04 22:11:34 +02:00
Zoltan Kochan
47b100ecf7 fix(exe): drop darwin-x64 artifact (upstream Node SEA bug) (#11455)
## Summary

Closes #11423.

`pnpm-darwin-x64.tar.gz` and `@pnpm/macos-x64` are removed because the binaries they contain segfault at startup on Intel Macs and the underlying bug is upstream and unfixed.

## Why this isn't a fix in code

The crash happens in `__cxx_global_var_init` with `EXC_BAD_ACCESS (code=1, address=0x3)` — the unprocessed-chain-entry tag — in dyld's chained-fixup processing. PR #11415's hypothesis was that `ldid`'s page hashes were the cause, but switching to native `codesign` in #11415 didn't fix it: the upstream minimal repro in [nodejs/node#62893](https://github.com/nodejs/node/issues/62893) is `node --build-sea` + `codesign --sign -` + run, with no pnpm and no `ldid`, and it still crashes. The corruption is in LIEF's Mach-O surgery during `--build-sea` for x64 — chained-fixup chain entries get rewritten incorrectly when the SEA segment is inserted, and re-signing produces a valid signature over the broken bytes.

The Node.js team is not going to fix this:

- [nodejs/node#60250](https://github.com/nodejs/node/pull/60250) (merged) — *"It's unlikely that anyone would invest in fixing them on x64 macOS in the near future, now that x64 macOS is being phased out."* They skipped the SEA tests on x64 macOS rather than chase the bug.
- [nodejs/node#59553](https://github.com/nodejs/node/issues/59553) (open) — long-running test failures on macOS x64 with the same root cause (sometimes surfacing as `unsupported thread-local, larger than 4GB`).

`@yao-pkg/pkg` works around it by appending the JS payload to the file tail and using a custom-patched Node binary that reads from the tail at startup; this avoids Mach-O surgery entirely. We can't reuse pack-app for that because vanilla Node from nodejs.org doesn't read tail-appended payloads — only pkg-fetch's patched binaries do — so adopting that path would mean re-implementing pkg-fetch for one target. For now we're dropping the broken artifact rather than introducing a second build mechanism.

## Changes

- **`pnpm/artifacts/exe/package.json`** — remove `@pnpm/macos-x64` from `optionalDependencies`; remove `darwin-x64` from `pnpm.app.targets`.
- **`.meta-updater/src/index.ts`** — remove `@pnpm/macos-x64` from the enforced `optionalDependencies` list (otherwise `meta-updater` would put it back).
- **`pnpm/artifacts/exe/scripts/build-artifacts.ts`** — drop `darwin-x64` from `narrowTargets` so dev-local builds match the published matrix; comment explains why.
- **`__utils__/scripts/src/copy-artifacts.ts`** — stop creating `pnpm-darwin-x64.tar.gz` so the GitHub release page no longer ships it.
- **`pnpm/artifacts/darwin-x64/`** — deleted (was the workspace source for `@pnpm/macos-x64`).
- **`pnpm/artifacts/exe/setup.js`** — wraps the `import.meta.resolve('${pkgName}/package.json')` lookup in `try`/`catch`. On Intel Mac specifically, prints a clear message pointing at this issue, the upstream Node.js issue, and the two workarounds (`npm install -g pnpm` to use the system Node.js, or stay on pnpm 10.x). Other unsupported hosts get a generic message in the same shape. Exits non-zero so the install fails loudly instead of silently leaving a broken `pnpm`.
- **`pnpm-lock.yaml`** — regenerated.
- **`.changeset/drop-darwin-x64-broken-sea.md`** — patch bumps for `@pnpm/exe` and `pnpm` with user-facing explanation and pointers.

Docs side already lists this limitation under `pack-app` Known limitations: pnpm/pnpm.io@36d962f6 / pnpm/pnpm.io@91f45632.

## Compat

- Intel Mac users on existing `@pnpm/exe` (≤ 11.0.4) keep working with the (broken) old binary they already have.
- `pnpm self-update` from an Intel Mac on an older `@pnpm/exe` will hit the new `setup.js` error path with a clear pointer to the workarounds.
- New Intel Mac installs via `npm install -g @pnpm/exe` will fail loudly with the same pointer.
- Install via `npm install -g pnpm` (the JS-only package, uses system Node) is unaffected and remains the recommended path.
- The `install.sh` from `get.pnpm.io` will fail with a 404 on the missing `pnpm-darwin-x64.tar.gz`. That's a separate repo and a follow-up — happy to do that as a second PR.
2026-05-04 22:11:28 +02:00
Zoltan Kochan
f6bc1db683 fix(dlx): prompt to approve ignored builds (#11452)
`pnpm dlx` (and `pnpx`/`pnx`/`pnpm create`) now mirrors the `pnpm add -g` flow when the launched package's transitive deps have install scripts:

- dlx overrides `strictDepBuilds: false` for its install so the v11 default no longer turns ignored builds into an `ERR_PNPM_IGNORED_BUILDS` error. Without this, `pnpx @google/gemini-cli` (and similar — `node-pty`, `@github/keytar`) failed outright and forced users to retry with `--allow-build=<pkg>` for every offending dependency.
- After install, dlx detects skipped builds via `getAutomaticallyIgnoredBuilds` and runs the same interactive `approve-builds` prompt as `pnpm add -g`. In non-interactive mode the install is committed with builds skipped, matching `pnpm add -g` in CI; users who need those scripts can re-invoke with `--allow-build=<pkg>` to force a fresh cache key.
- If the install errors for unrelated reasons (network, etc.) the partially-populated prepare directory is removed so the next dlx run starts clean.

Closes #11444.

### Plumbing

- Exports `getAutomaticallyIgnoredBuilds` from `@pnpm/building.commands` so dlx can detect skipped builds without re-implementing modules-yaml reading.
- Adds `strictDepBuilds` (optional) to `InstallCommandOptions` — already accepted at runtime via the spread, this just makes it explicit at the type level so callers can override it.
2026-05-04 22:11:22 +02:00
Zoltan Kochan
0b2f86ee86 fix: render peer dependency issues on strict error (#11450)
Fixes #11439.

When `strictPeerDependencies: true` causes `ERR_PNPM_PEER_DEP_ISSUES`, the peer dependency issues are again rendered inline — using the **same format as `pnpm peers check`** — so users (and CI tools like Renovate) can see what failed without running another command.

The non-strict warning path is unchanged: it still emits the short "Run `pnpm peers check`" hint.

### Behavior

`strictPeerDependencies: true`:
```
 ERR_PNPM_PEER_DEP_ISSUES  Unmet peer dependencies

✕ unmet peer react
  Installed: 17.0.2
  Wanted:
    ^18.2.0:
      react-dom@18.2.0
hint: To disable failing on peer dependency issues, add the following to pnpm-workspace.yaml in your project root:

  strictPeerDependencies: false
```

`strictPeerDependencies: false` (unchanged):
```
 WARN  Issues with peer dependencies found. Run "pnpm peers check" to list them.
```

### Implementation

- Added a new `@pnpm/deps.inspection.peers-issues-renderer` package at `deps/inspection/peers-issues-renderer/`, alongside its data producer `@pnpm/deps.inspection.peers-checker`. It exposes a single `renderPeerIssues()` that emits the flat issue list previously inlined in `pnpm peers check`.
- Removed the duplicated formatter from `deps/inspection/commands/src/peers.ts` and made the `pnpm peers check` command consume the new renderer.
- `cli/default-reporter/src/reportError.ts`: `reportPeerDependencyIssuesError` now calls the shared `renderPeerIssues()` and prefixes the hint block with the rendered output. Tests strip ANSI escapes before substring assertions so they stay correct under `FORCE_COLOR=1`.

Result: a single renderer is shared between the install error and the `pnpm peers check` command — output is identical between the two paths.
2026-05-04 22:11:13 +02:00