Repository navigation
Comparing changes
Open a pull request
base repository: npm/cli
base: v12.0.2
head repository: npm/cli
compare: v12.1.0
- 20 commits
- 74 files changed
- 14 contributors
Commits on Jul 30, 2026
-
chore: pass nodedir to node-gyp via npm_package_config env in node in…
…tegration (#9822) ## What / Why The `node-integration` workflow passed `--nodedir` as an npm **CLI flag** to `npm install`. In npm 12 unknown configs are no longer accepted, so this now fails with `EUNKNOWNCONFIG`: ``` npm error code EUNKNOWNCONFIG npm error Unknown cli flag: npm error - --nodedir ``` (seen in the Release Integration citgm jobs, e.g. `citgm - [email protected]`). `nodedir` is a **node-gyp** option, not an npm config — it only ever worked via npm's old "accept arbitrary config and re-export as `npm_config_*`" behavior, which node-gyp itself notes was deprecated in npm v11 ([nodejs/node-gyp#3156](nodejs/node-gyp#3156)). ## Change Export `npm_package_config_node_gyp_nodedir` instead of using the CLI flag. This is node-gyp's preferred prefix since npm v11: node-gyp reads it directly from the environment, and npm does **not** warn on it — unlike `npm_config_nodedir`, which currently warns and is slated to error in npm 13. This also matches how upstream [nodejs/citgm](https://github.com/nodejs/citgm) supplies `nodedir` (via env, not a CLI flag). Applied to both the generated `.github/workflows/node-integration.yml` and its `scripts/template-oss/node-integration-yml.hbs` template; `template-oss-apply --lint` passes. Co-authored-by: Copilot <[email protected]> Copilot-Session: 6e300b82-303b-49ac-ae93-52d984743d5b
Configuration menu - View commit details
-
Copy full SHA for 278df04 - Browse repository at this point
Copy the full SHA 278df04View commit details
Commits on Aug 3, 2026
-
docs: document npm 12 install script blocking (#9838)
## Summary Add the omitted npm 12 breaking-change note explaining that dependency lifecycle scripts are blocked by default unless covered by `allowScripts`, including the approval and rebuild workflow. ## Cause The change was introduced in [`5cd5150`](5cd5150). Although its message described a v12-only default flip, it used `feat:` instead of `feat!:` and did not include a `BREAKING CHANGE:` footer. Release Please therefore classified it as a regular feature and omitted it from the aggregated npm 12 breaking-change notes. ## Release notes The published [`v12.0.0` GitHub release](https://github.com/npm/cli/releases/tag/v12.0.0) was corrected manually with the same breaking-change entry. This PR corrects the source-controlled changelog used by the npm documentation site. ## Manual correction process If a breaking change is omitted from release notes in the future: 1. Do not rewrite the merged commit. Add the missing entry under the released version’s `
⚠️ BREAKING CHANGES` section in the root `CHANGELOG.md` and submit a documentation PR. 2. After the PR merges, the npm documentation repository’s scheduled **Update CLI** workflow copies the root changelog into the corresponding CLI documentation page and publishes it. Dispatch that workflow manually if the docs need to update immediately. 3. Update the existing GitHub release separately because a changelog PR cannot modify an already-published release. Preserve the complete current release body before editing it because `gh release edit --notes-file` replaces the entire body: ```bash gh release view <tag> --repo npm/cli --json body --jq .body > release.md # Add the same breaking-change entry to release.md. gh release edit <tag> --repo npm/cli --notes-file release.md ``` 4. Verify that the source changelog, npm documentation page, and GitHub release contain identical wording. To prevent the omission, breaking commits must use a conventional-commit breaking marker such as `feat!:` and include a `BREAKING CHANGE:` footer describing the user-visible impact. Fixes #9750Configuration menu - View commit details
-
Copy full SHA for bea9066 - Browse repository at this point
Copy the full SHA bea9066View commit details -
chore: update
node-integrationworkflow template to latest actions (#……9836) ## Situation If the GitHub Actions [.github/workflows/node-integration.yml](https://github.com/npm/cli/blob/latest/.github/workflows/node-integration.yml) workflow "nodejs integration" is run, it outputs multiple deprecation warnings, including the text "Node.js 20 is deprecated." and referring to the blog article [Deprecation of Node 20 on GitHub Actions runners](https://github.blog/changelog/2025-09-19-deprecation-of-node-20-on-github-actions-runners/) dated September 19, 2025. ## Change Update action usage in [scripts/template-oss/node-integration-yml.hbs](https://github.com/npm/cli/blob/latest/scripts/template-oss/node-integration-yml.hbs) to latest versions with `runs.using` `node24`: | BEFORE | AFTER | | ------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------ | | [actions/cache@v3](https://github.com/actions/cache/tree/v3) | [actions/cache@v6](https://github.com/actions/cache/tree/v6) | | [actions/download-artifact@v4](https://github.com/actions/download-artifact/tree/v4) | [actions/download-artifact@v8](https://github.com/actions/download-artifact/tree/v8) | | [actions/github-script@v6](https://github.com/actions/github-script/tree/v6) | [actions/github-script@v9](https://github.com/actions/github-script/tree/v9) | | [actions/upload-artifact@v4](https://github.com/actions/upload-artifact/tree/v4) | [actions/upload-artifact@v7](https://github.com/actions/upload-artifact/tree/v7) | ## Verification Run workflow `node-integration` with: nodejs: 26.5.1 npm version: 12.0.2 Confirm that there are no longer deprecation warnings output. ## References - closes #9834
Configuration menu - View commit details
-
Copy full SHA for 4cdccea - Browse repository at this point
Copy the full SHA 4cdcceaView commit details
Commits on Aug 12, 2026
-
fix: don't print the funding message for global installs (#9864)
## What / Why A global install prints "N packages are looking for funding / run `npm fund` for details", but nothing can be done with it. `npm fund -g` fails with `EFUNDGLOBAL`, and a bare `npm fund` loads the tree at the local prefix, so outside a project it prints the directory name and nothing else. `packagesFundingMessage` in `lib/utils/reify-output.js` never looked at `npm.global`, so the hint went out for global installs too. It now returns early in that case. Making `npm fund -g` work is a separate feature request; #3112 already splits it out that way. ## Testing New case in `test/lib/utils/reify-output.js`, mirroring the existing `fund: false` one: with `global: true` the output no longer mentions funding. It fails without the change. ## References Fixes #9863 Fixes #3112
Configuration menu - View commit details
-
Copy full SHA for 51c2bf8 - Browse repository at this point
Copy the full SHA 51c2bf8View commit details
Commits on Aug 20, 2026
-
Configuration menu - View commit details
-
Copy full SHA for dc43591 - Browse repository at this point
Copy the full SHA dc43591View commit details
Commits on Aug 24, 2026
-
Configuration menu - View commit details
-
Copy full SHA for 8723f53 - Browse repository at this point
Copy the full SHA 8723f53View commit details -
fix: exempt explicit pack targets from allow-directory (#9756)
## Summary - allow explicit directory targets through `npm pack` and `npm publish` when `allow-directory` is `none` or `root` - keep dependency fetch policies unchanged and cover the shared library plus both CLI commands ## Why `allow-directory` restricts directory dependencies, but the local package selected for packing or publishing is the command target. The current flow applies the dependency policy to that target during manifest or tarball preparation. Closes #9755. ## Validation - `node . run test` - passed (123 test files, 100% coverage) - `node . run test -w libnpmpack` - passed (100% coverage) - dry-run `pack` and `publish` smokes with `allow-directory=none|root` - passed Co-authored-by: ychampion <[email protected]>
Configuration menu - View commit details
-
Copy full SHA for 71915e8 - Browse repository at this point
Copy the full SHA 71915e8View commit details
Commits on Aug 25, 2026
-
fix(arborist): reject uninstall args that carry a version (#9881)
## What / Why `npm uninstall [email protected]` neither removes the package nor complains about the argument. The spec goes straight through to Arborist as an `rm` entry, nothing in the tree is named `[email protected]`, so the reify finishes with "up to date" and `npm ls` still shows `vite`. `npm update` already validates its own argument list and throws `EUPDATEARGS` for anything that is not a bare package name. This applies the same rule to `rm`, so a version, tag or range now fails with `ERMARGS` and the message names the command to run instead. ## Testing New case in `workspaces/arborist/test/arborist/build-ideal-tree.js`, next to the existing rm test and mirroring the update one. It covers an exact version, a tag, ranges, a scoped name carrying a version, and a filesystem path, where the suggestion falls back to `<pkg>` because there is no name to print. It fails without the change. ## References Fixes #9880
Configuration menu - View commit details
-
Copy full SHA for da50c34 - Browse repository at this point
Copy the full SHA da50c34View commit details -
fix: keep dry-run output valid json (#9823)
## Summary - suppress human-readable reify diff lines when JSON output is requested - preserve detailed diff output for non-JSON `--dry-run` and `--long` commands - cover both `--dry-run --json` and `--long --json` ## Background Detailed dry-run diff output was introduced in npm 10.4.0 by #7133 without accounting for JSON mode. The structured JSON summary already contains the add, remove, and change details, so the extra text is redundant and makes stdout invalid JSON. Related to #8567, with the regression coverage requested during review. ## Testing - `tap --no-coverage --no-check-coverage test/lib/utils/reify-output.js` - `eslint lib/utils/reify-output.js test/lib/utils/reify-output.js` Fixes #8565 Copilot-Session: 48e629f8-eb5a-467c-970b-feb313628c00
Configuration menu - View commit details
-
Copy full SHA for a8c9b2f - Browse repository at this point
Copy the full SHA a8c9b2fView commit details -
deps: [email protected] (#9843)
## Summary Updates the bundled `tar` dependency from `7.5.19` to `7.5.22` on the latest npm 12 branch. This resolves [GHSA-r292-9mhp-454m](GHSA-r292-9mhp-454m), which affects `tar` versions through `7.5.20`. All production dependency paths now resolve to `[email protected]`. ## Testing - `node . run dependencies --ignore-scripts` - `node . ls tar --all --omit=dev` - `node . audit --omit=dev --json` reports no `tar` vulnerability - `node . test --ignore-scripts` reports the environment-specific `EXDEV` failure in `test/bin/windows-shims.js`; all other tests pass Co-authored-by: Copilot <[email protected]>
Configuration menu - View commit details
-
Copy full SHA for 75a943d - Browse repository at this point
Copy the full SHA 75a943dView commit details -
Co-authored-by: Copilot <[email protected]> Copilot-Session: 73e1dfa8-6bec-47aa-b811-4edf404e9883
Configuration menu - View commit details
-
Copy full SHA for b7c490d - Browse repository at this point
Copy the full SHA b7c490dView commit details -
Co-authored-by: Copilot <[email protected]> Copilot-Session: 73e1dfa8-6bec-47aa-b811-4edf404e9883
Configuration menu - View commit details
-
Copy full SHA for 05bd2a4 - Browse repository at this point
Copy the full SHA 05bd2a4View commit details -
Co-authored-by: Copilot <[email protected]> Copilot-Session: 73e1dfa8-6bec-47aa-b811-4edf404e9883
Configuration menu - View commit details
-
Copy full SHA for 6e40f73 - Browse repository at this point
Copy the full SHA 6e40f73View commit details -
fix(arborist): match allowScripts keys for local paths (#9914)
## Summary Fixes `allowScripts` matching for local file and tarball dependencies, particularly on Windows. Arborist stores a resolved Windows file source in this form: ```text file:C:\project\vendor\pkg.tgz ``` However, `npm-package-arg` parses an equivalent policy key into: `saveSpec: file:C:/project/vendor/pkg.tgz` `fetchSpec: C:\project\vendor\pkg.tgz` The existing matcher compared `node.resolved` only with `saveSpec` and `fetchSpec`. Neither comparison could match because one uses different separators and the other lacks the `file:` prefix. ## Changes • Match local file and directory sources against `file:${parsed.fetchSpec}`, which is the same representation produced by Arborist's `consistentResolve()`. • Resolve relative `allowScripts` file keys from the Arborist project root rather than the process working directory. • Reuse the Arborist file/directory matcher in `allow-scripts-writer`, keeping runtime enforcement and policy updates consistent. • Preserve the existing exact matching behavior for remote URLs. • Preserve deny-wins behavior when an existing local-file policy entry is `false`. The implementation intentionally does not globally normalize path separators. On POSIX systems, a backslash can be a literal filename character, so replacing every `\` with `/` could cause two different sources to share an approval. The change also does not modify dependency resolution, lockfile generation, fetching, linking, or `consistentResolve()`. ## Security considerations Local file and tarball dependencies continue to require an exact source identity. This change does not: • Match local packages by their self-reported name or version. • Case-fold paths. • Match by basename. • Resolve paths through `realpath()`. • Treat remote URLs as local file sources. • Change registry or Git identity matching. This preserves the manifest-confusion protections in `allowScripts` while recognizing the exact Windows representation npm already generates. ## Testing Added regression coverage for: • Backslash and forward-slash absolute Windows keys matching the same local tarball. • Relative keys resolving from the project root. • `versionedKeyFor()` producing a key accepted by `isScriptAllowed()`. • Different local paths remaining unmatched. • Existing `false` file entries continuing to block approval. • Windows UNC paths matching through the same `file:${fetchSpec}` representation. • POSIX filenames containing literal backslashes remaining distinct. • Remote URL policies not matching `file: sources`. • `npm install-scripts prune` retaining valid local-file entries. The focused Arborist matcher, policy-writer, and prune tests pass locally. Native Windows path and UNC behavior is covered by platform-specific tests and will run in the repository's Windows CI matrix. Fixes #9900 --------- Co-authored-by: Copilot <[email protected]>Configuration menu - View commit details
-
Copy full SHA for d6c6122 - Browse repository at this point
Copy the full SHA d6c6122View commit details
Commits on Aug 26, 2026
-
chore: recognize prefixed Node.js PR titles (#9915)
## Summary - recognize existing Node.js npm update PRs with branch prefixes such as `[v22.x]` - preserve an existing PR title when updating it ## Background The create-node-pr workflow successfully regenerated and force-pushed nodejs/node#64884, but then failed because the existing-PR parser treated `[v22.x] 10.9.9` as the npm version. It attempted to create a duplicate PR instead of editing the existing one. This extracts the version following the canonical `deps: upgrade npm to` text regardless of any title prefix. Existing base-branch filtering continues to distinguish PRs targeting different Node.js release lines. Reproduction: https://github.com/npm/cli/actions/runs/32768286326 Copilot-Session: 14a9626a-4d0e-4be3-b06c-b6676ecb3895
Configuration menu - View commit details
-
Copy full SHA for 81a901c - Browse repository at this point
Copy the full SHA 81a901cView commit details
Commits on Aug 31, 2026
-
fix(config): avoid exporting persistent allow-scripts (#9913)
## What / Why A user or global `.npmrc` can define `allow-scripts` as persistent policy. `setEnvs()` currently carries that non-default value into lifecycle child processes as `npm_config_allow_scripts`. If a lifecycle script runs a nested project-scoped `npm install`, the inner process treats the inherited value as an environment override and rejects it with `EALLOWSCRIPTS` instead of reloading the policy from its persistent config source. Mark `allow-scripts` as non-exportable. This only prevents `setEnvs()` from synthesizing the lifecycle environment variable; it does not remove an explicitly supplied environment value or change how the outer command reads its config. Pacote's git-preparation environment filtering and #9783 are outside this change. The regression test models a user-level value in the inherited config chain and verifies that lifecycle scripts do not receive `npm_config_allow_scripts`. ## AI assistance OpenAI Codex assisted with analysis, implementation, and test design. The patch was verified with the focused regression, the complete `@npmcli/config` suite, lint, and template checks. ## References Fixes #9912
Configuration menu - View commit details
-
Copy full SHA for b016aa2 - Browse repository at this point
Copy the full SHA b016aa2View commit details
Commits on Sep 3, 2026
-
fix: provenance-file takes precedence over OIDC auto-generated proven…
…ance (#9882) ## Why Needed When publishing with an externally generated provenance bundle under OIDC trusted publishing (`npm publish <tarball> --provenance-file=<bundle>`), npm silently discarded the supplied bundle and published its own auto-generated provenance instead. Three layers interacted: 1. `lib/utils/oidc.js` auto-enabled provenance (`opts.provenance = true`) whenever the `provenance` config was at its default, without checking whether a `provenance-file` was supplied. 2. In `libnpmpublish`'s `buildMetadata()`, the inner `provenance === true` branch then ran `generateProvenance()`, so the `verifyProvenance(subject, provenanceFile)` branch, the only code path that reads the supplied file, never executed. 3. Every documented way to disable automatic provenance was unusable in combination with `--provenance-file` (config-layer mutual exclusivity error, env carve-out, or publishConfig flatten timing). ## What Changes - **`lib/utils/oidc.js`**: skip auto-enabling provenance when a provenance file is configured (`opts.provenanceFile`). `opts` already carries `provenanceFile` from every config source (CLI/env/npmrc/publishConfig) by the time the OIDC flow runs, so this covers all entry paths. The supplied bundle is now verified via `verifyProvenance()` and published, as documented. Automatic provenance is also no longer written to the shared config; it is set only on the current publish's `opts` (which reaches libnpmpublish via `otplease`). Previously the `user`-scoped config write leaked `provenance: true` into later workspace publishes during `npm publish --workspaces`, where a workspace with `publishConfig["provenance-file"]` would then hit the conflict check below. - **`libnpmpublish`**: `buildMetadata()` now throws ~~`EPROVENANCECONFLICT`~~ `EUSAGE` (updated per review) when both `provenance: true` and `provenanceFile` are provided. - **Docs**: config descriptions for `provenance` / `provenance-file` and the libnpmpublish README now state the precedence rule. ###
⚠️ ~~New error code (feedback requested)~~ Resolved: reuses `EUSAGE` ~~This PR introduces `EPROVENANCECONFLICT` in libnpmpublish, thrown when both `provenance: true` and `provenanceFile` are provided programmatically.~~ ~~**Rationale:** the README already documents the two as mutually exclusive, and silently preferring either direction discards a cryptographically meaningful artifact. Note the CLI's config layer reports the same conflict as a `TypeError` without an error code (pre-existing). Happy to align on `EUSAGE` or another convention per review.~~ **Update:** per review, the conflict reuses the existing `EUSAGE` code instead of introducing a new one (`Object.assign(new Error('provenance and provenanceFile cannot be used together'), { code: 'EUSAGE' })`), and the README documents `EUSAGE` accordingly. ## Testing - New CLI regression test: OIDC trusted publishing + `provenance-file` ~~config~~ asserts the published packument's sigstore attachment deep-equals the supplied bundle (and that sigstore generation is never invoked). **Update:** parameterized per review to cover both `provenance-file` sources, CLI config and `publishConfig`; the latter proves `publishConfig["provenance-file"]` is flattened into `opts.provenanceFile` via `Publish.#getManifest()` before `oidc()` decides whether to enable automatic provenance. - New libnpmpublish test: both options set → rejects with ~~`EPROVENANCECONFLICT`~~ `EUSAGE`, no registry PUT, generation not invoked. - New workspace regression test (per review): publishes two public workspaces in order (one where OIDC auto-enables provenance, then one with `publishConfig["provenance-file"]`), and asserts each publish receives exactly its own options (`{ provenance: true, provenanceFile: null }` then `{ provenance: false, provenanceFile }`) and that the shared config stays at its default (`npm.config.isDefault('provenance') === true`). Verified to fail on the pre-fix code for exactly the leak reason, and to pass after the fix. `mock-oidc` gained a `times` option on the GitHub id-token mock to serve one token request per workspace. - Full root suite green with 100% coverage; libnpmpublish workspace suite green; lint clean. ## References Fixes #9879 ## Out of scope (noted for follow-up) - `publishConfig.provenance: false` does not block the OIDC auto-enable (publishConfig flattens into `opts` only, so `config.isDefault('provenance')` stays true). A separate behavioral question about `isDefault` semantics. - Hardening `config.set` itself against bypassing load-time exclusivity is an `@npmcli/config` semver-major conversation; this flow no longer writes `provenance` to the config at all. --------- Signed-off-by: Yunseo Kim <[email protected]>Configuration menu - View commit details
-
Copy full SHA for c9876d7 - Browse repository at this point
Copy the full SHA c9876d7View commit details
Commits on Sep 16, 2026
-
feat(token): support read-write-stage-only granular access tokens (#9948
) ## Summary Adds a `read-write-stage-only` value to the `packages-and-scopes-permission` option for `npm token create`, letting the CLI create granular access tokens with a stage-only publish policy (parity with the web UI). Also adds a deprecation warning when creating a direct-publish-capable token, steering users toward the stage-only value. ## Changes - Add `read-write-stage-only` to the `packages-and-scopes-permission` config enum and update its description. `orgs-permission` is intentionally left unchanged (the registry only accepts `read-write-stage-only` for packages/scopes). - `token create` already forwards `packages_and_scopes_permission` verbatim in the POST body, so the value flows straight to the registry create-token route, which normalizes it to `publish_policy: stage_only` — identical to the web UI. - Add a `log.warn` when creating a token with `read-write` package/scope permission (direct-publish-capable), pointing users to `--packages-and-scopes-permission=read-write-stage-only`. Stage-only, read-only, and no-access tokens do not warn; `--bypass-2fa` is orthogonal and does not by itself trigger the warning. - Tests: creating a stage-only token asserts the POST body carries the value and emits no warning; read-write (with and without `--bypass-2fa`) emits the warning; `--bypass-2fa` alone does not. - Regenerate affected tap snapshots (`docs.js`, `workspaces/config` `type-description.js`). --------- Co-authored-by: Copilot App <[email protected]> Copilot-Session: 0f2e357f-6da8-4978-a637-f14539820093
Configuration menu - View commit details
-
Copy full SHA for 6400533 - Browse repository at this point
Copy the full SHA 6400533View commit details
Commits on Sep 21, 2026
-
chore(pack): select workspace through config (#9961)
The one-workspace test passed `workspace-a` as an explicit pack target, which npm interprets as a registry package spec. Select the workspace through config instead and pack with no target so the test exercises workspace behavior without making an unintended registry request. ## Testing - `node_modules/.bin/tap test/lib/commands/pack.js --no-coverage` Copilot-Session: def7f84f-7065-4820-ad6d-ca0a6436655c
Configuration menu - View commit details
-
Copy full SHA for 7b50811 - Browse repository at this point
Copy the full SHA 7b50811View commit details
Commits on Sep 22, 2026
-
Configuration menu - View commit details
-
Copy full SHA for c039090 - Browse repository at this point
Copy the full SHA c039090View commit details
This comparison is taking too long to generate.
Unfortunately it looks like we can’t render this comparison for you right now. It might be too big, or there might be something weird with your repository.
You can try running this command locally to see the comparison on your machine:
git diff v12.0.2...v12.1.0