[pull] latest from npm:latest - #14
Open
pull[bot] wants to merge 1021 commits into
Open
Conversation
There was a problem hiding this comment.
The pull request #14 has too many files changed.
We can only review pull requests with up to 300 changed files, and this pull request has 587.
owlstronaut
force-pushed
the
latest
branch
2 times, most recently
from
March 27, 2025 18:03
85ec0c9 to
26b6454
Compare
BREAKING CHANGE: npm no longer registers man pages with the system when installed globally. `man npm-install` will no longer work, but `npm help install` is unaffected.
BREAKING CHANGE: `npm sbom --sbom-format=cyclonedx` now reports the `name` field from each package's `package.json` instead of the on-disk directory name. The `name`, `bom-ref`, and `purl` of the root component and of aliased dependencies may change. fixes: #9178 --------- Co-authored-by: cyphercodes <cyphercodes@users.noreply.github.com>
These complement allow-git.
BREAKING CHANGE: npm pack and npm publish now error when a package's overrides apply to one or more of its bundled packages (bundledDependencies / bundleDependencies). Defining both fields is still allowed as long as no override actually targets a bundled package. To resolve the error, remove the affected entries from either overrides or the bundle.
BREAKING CHANGE: npm view --json now always returns an array.
When a peerOptional edge conflicts, search descendants for a satisfying node before fetching from the registry. This prevents extraneous packages from blocking hoisting of required deps. This fixes 1/2 or 1/3 of #9249. Before this change a clean install would resolve `nm/jest-util@30` when resolving the conflict at nm/jest-util between ts-jest's jest-util@^29||^30, and expect's ^28, which had been placed at root. `#nodeFromEdge` would create a brand new node, matching greatest ^30. A subequent install would mark nm/jest-util@30 as extraneous and prune it. This tree is valid, but ts-jest's peerOptional jest-util is unsatisfied, while compatible jest-util are installed and duplicated. This change reduces duplication and can prevent peerOptionals from actively installing. 1. Now during initial installs npm will prefer hoisting a dependency to de-dupe a peerOptional conflict over creating a new extraneous edge. 2. It doesn't solve the problem if there's no compatible version in the sub-tree. npm will still use `#nodeFromEdge` and install an extraneous edge. 3. It doesn't fix installs from lockfiles generated before this fix. I think this is okay, because the trees are techincally valid, just not optimal. I think a better solution to all three issues would be: * During problemEdge conflict resolution, npm would hoist nm/jest-util@28 under expect, without replacing it with anything. ts-jest's peerOptional jest-util would be unsatisfied. This creates the same tree as npm's second installs that prune extraneous. * Check for any dependencies that can be hosited. This can run during the initial install on problemEdge conflict resoultion, and in pruneIdealTree on any nodes that are removed. I think this solves all three issues. I didn't implement it because I couldn't find a way to resolve the conflict by leaving a hole in the tree..
## Description Fixes #9298. `buildIdealTree` handles global updates by reading the global `node_modules` folder directly and adding each entry as a synthetic top-level dependency. That path did not ignore hidden entries, so a directory like `.hidden-non-package` was converted to `.hidden-non-package@*` and rejected by `npm-package-arg`. This applies the same hidden-entry filter already used by `loadActual`, so hidden directories and retired scoped package folders are not treated as installed global packages during `npm up -g`. ## Testing ```sh node node_modules/tap/bin/run.js workspaces/arborist/test/arborist/build-ideal-tree.js -g "update global ignores hidden" ``` ```sh npm_config_prefix=/tmp/npm-hidden-global-patched.azXmRd node . up -g ``` ```sh node . run eslint -- workspaces/arborist/lib/arborist/build-ideal-tree.js workspaces/arborist/test/arborist/build-ideal-tree.js ```
Exposes a public method on the Arborist class that builds (or reuses) an ideal tree, commits the shrinkwrap metadata, and returns the lockfile contents as a string without writing to disk. This makes a previously-undocumented sequence (`buildIdealTree()` -> `tree.meta.commit()` -> `String(tree.meta)`) a discoverable, supported API, enabling callers to inspect, diff, or store generated lockfiles without mutating the project's package-lock.json.
…9309) In continuation of our exploration of using `install-strategy=linked` in the [Gutenberg monorepo](WordPress/gutenberg#75814), which powers the WordPress Block Editor. When using `install-strategy=linked`, removing a dependency leaves a dangling symlink at `node_modules/<pkg>` (and `<workspace>/node_modules/<pkg>` for workspace deps). The store entry under `node_modules/.store/<pkg>@…` is correctly cleaned up by `#cleanOrphanedStoreEntries`, but the top-level link pointing into it is left behind, so `require('<pkg>')` fails with `Cannot find module` even though the entry still appears in `node_modules/`. The root cause is the same family of issue as #9106. `#buildLinkedActualForDiff` builds the synthetic actual tree from the ideal tree, so any dependency that exists on disk but is no longer in the ideal tree is never compared, and the diff produces no REMOVE action for its top-level symlink. Fixed by extending `#cleanOrphanedStoreEntries` to also collect, per `node_modules` directory (root and each workspace), the set of valid top-level link names from the ideal tree, then sweeping each directory and removing any symlink whose name is not in that set. The root `node_modules` and every workspace's `node_modules` (via `idealTree.fsChildren`) are always seeded into the sweep, so the case of removing the last dependency from the project root or from a workspace still triggers cleanup, including when the workspace itself is declared as a root dependency and therefore has its self-link at the root rather than under its own `node_modules`. The sweep is restricted to symlinks whose target resolves inside the project root, so it covers both store links (e.g. `node_modules/eslint -> .store/...`) and workspace self-links that no longer belong (e.g. `node_modules/a -> ../packages/a` after `a` is undeclared) without touching symlinks that point outside the project, such as those created by `npm link <global-pkg>` without `--save`. Real directories and npm-managed entries (`.bin`, `.store`, `.package-lock.json`) are left alone. The workspace self-link inside its own `node_modules` (e.g. `packages/a/node_modules/a -> ..`) is in the ideal tree as a non-store link, so it's preserved. The sweep also respects the install mode: - It is skipped entirely for `dryRun` and `packageLockOnly` installs, both of which short-circuit `#reifyPackages` and must not mutate `node_modules`. - For workspace-filtered installs (`npm install -w <ws> --install-strategy=linked`), the set of `node_modules` directories to sweep is restricted to the workspaces named in `--workspace`, so dropped dependencies from the in-scope workspace get cleaned up while out-of-scope workspaces and the project root are left untouched. `IsolatedNode`/`IsolatedLink` locations are built with `path.join`, which uses backslashes on Windows; locations are normalized to forward slashes inside the sweep so the parser works on both POSIX and Windows. ## Trade-off This aligns the linked strategy with npm's normal `node_modules`-is-managed model: any in-project symlink that isn't in the ideal tree is treated as orphaned, matching what happens today under the default install strategy. A consequence is that hand-made or unsaved `npm link` symlinks pointing to other paths inside the project root (e.g. `node_modules/foo -> ../examples/foo`) are also swept, since npm doesn't currently record which links it owns and they are indistinguishable from workspace self-links by target alone. A more discriminating ownership check (recording managed link names in the hidden lockfile and only sweeping those) is a worthwhile follow-up but materially larger than this fix. ## References Fixes #9308 Related to #9106
## Summary Closes the per-node duplication gap left by #7992. A node can have multiple outgoing edges resolving to the same `name@version` — typically when a package declares both a direct dependency and an npm alias to the same package, e.g.: ```json { "dependencies": { "lodash": "^4.17.21", "lodash-aliased": "npm:lodash@^4.17.21" } } ``` `toCyclonedxDependency` and the SPDX relationship loop both map each edge through `name@version` ID generation without deduplicating, so the per-node `dependsOn` array (CycloneDX) and `DEPENDENCY_OF` relationships (SPDX) end up with duplicate entries. CycloneDX 1.5 requires `dependsOn` items to be unique, so downstream validators (e.g. Dependency Track) reject the SBOM with: ``` $.dependencies[N].dependsOn: must have only unique items in the array ``` ## Changes - `lib/utils/sbom-cyclonedx.js`: wrap the `dependsOn` array in `[...new Set(...)]` after mapping edges to refs. - `lib/utils/sbom-spdx.js`: dedupe per source-node relationships by the `(spdxElementId, relatedSpdxElement, relationshipType)` triple. - Test cases added to both `test/lib/utils/sbom-cyclonedx.js` and `test/lib/utils/sbom-spdx.js` covering the duplicate-edges-to-same-target scenario, with explicit assertions plus snapshot updates. ## Test plan - [x] `node . run test -- test/lib/utils/sbom-cyclonedx.js test/lib/utils/sbom-spdx.js` — passes - [x] 100% coverage on both touched files - [x] Snapshot diff is purely additive (no existing snapshots changed) - [x] Schema-validation tests in both files still pass for all snapshots - [x] Reproduced original issue locally with the alias example, ran patched npm against it, confirmed both CycloneDX `dependsOn` and SPDX relationships are now deduped Fixes #9310
<!-- What / Why --> The --json flag for npm view now always returns an array, even when only a single version matches. Previously, a single item result would be unwrapped and returned as a plain object/string, which made programmatic parsing fragile - consumers had to handle both array and non-array shapes depending on how many versions matched. This doc update adds a note to npm-view.md clarifying that the output is always an array when --json is used, reflecting the behavioral fix. ## References Related to npm/statusboard#1074 (comment)
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This PR fixes #9722. `mock-registry` pinned `@npmcli/arborist@^9.1.2`, which meant the local workspace version wasn't linked and the dependency was pulled from the registry instead. `sigstore@^4` got installed at the root node_modules and `^5` was put into `workspaces/libnpmpublish/node_modules`. The dev-only `^4` was then excluded when npm was packed. Fix: - `mock-registry`: arborist `^9.1.2` -> `^10.0.0` - `workspaces/arborist`: validate-npm-package-name `^7.0.2` -> `^8.0.0` (need to release a patch for arborist) - Lockfile regenerated via install + dedupe; `node . run dependencies` The packed tarball now contains `package/node_modules/sigstore` and `publish --dry-run` from the extracted tarball succeeds.
npm 12.0.0 breaks downstream updaters by returning nested arrays for
`npm view <pkg> versions --json`.
## The bug
On npm 12.0.0, a single array-valued field is wrapped in the outer
results array:
```
$ npm view abbrev versions --json
[["1.0.3","1.0.4", ...]] # should be ["1.0.3","1.0.4", ...]
```
This happens in `lib/commands/view.js` `#packageOutput`: for a
single-field query it maps to `res.map(m => m[first[0]])`, and when that
field's value is itself an array (e.g. `versions`), it gets
double-wrapped.
## The fix
Return a sole array-valued JSON result directly instead of adding a
second result wrapper. Existing output shapes are preserved:
- scalar and object results still return in an array (`["1.0.0"]`,
`[{...}]`)
- multiple matching versions keep the result boundary (`[[...],[...]]`)
- a single array-valued result is returned directly
(`["1.0.0","1.0.1"]`)
Docs and tests updated to cover flat array, nested array,
object-wrapper, workspace, and multi-match cases.
Co-authored-by: Martin Ruiz <martin.ruiz.mares@gmail.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…9746) The `bin links adding and removing` test in `workspaces/arborist/test/arborist/reify.js` reifies `rimraf@2.7.1` without setting up a mock registry. This adds `createRegistry(t, true)` so the test uses the mock registry instead of depending on the real one. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
## Problem The `build nodejs` jobs in `Release Integration / publish` fail at `.github/workflows/node-integration.yml`: ``` node . pack --loglevel=silent --json | jq -r .[0].filename → jq: error (at <stdin>:9859): Cannot index object with number → Process completed with exit code 5 ``` As of #9247 (sync json output of pack and publish), `npm pack --json` no longer outputs an array. `logTar` now buffers `{ [tar.name]: tarball }`, so the output is an object keyed by package name: ```json { "npm": { "filename": "npm-12.0.1.tgz", ... } } ``` The workflow still parsed it with `.[0].filename`, which errors on an object. npm 12.0.x is the first release carrying this change, so the release integration only started breaking now. ## Fix Parse the filename from the object instead of an array index: ```diff -npmtarball="$(node . pack --loglevel=silent --json | jq -r .[0].filename)" +npmtarball="$(node . pack --loglevel=silent --json | jq -r 'to_entries[0].value.filename')" ``` Verified locally: returns `npm-12.0.1.tgz`. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
## What / Why `npm audit` can report that a fix is available through `npm audit fix` when the highest safe version inside the declared dependency range is older than the installed version. Arborist already selects that safe candidate using the advisory range, but `CanPlaceDep` rejects it because replacement candidates normally must be newer than the installed version. This causes `npm audit fix` to complete without applying the advertised remediation. ## How - Pass the existing audit report from `PlaceDep` into `CanPlaceDep` and recursive peer placement checks. - Permit an older candidate only when: - the installed node is vulnerable; - the candidate is not vulnerable; and - the candidate passes the existing replacement and peer dependency checks. - Preserve existing no-downgrade behavior for ordinary installs and updates. - Add synthetic, strictly mocked regressions covering: - compatible safe downgrades; - non-audit placement; - still-vulnerable candidates; - peer conflicts; - actual tree replacement; and - metavulnerability removal by pruning a vulnerable transitive dependency. This does not change audit reporting or `--force` behavior. Fixes outside declared dependency ranges still require `npm audit fix --force`. ## Testing - Focused Arborist placement and audit tests - npm command-level audit tests ## References Fixes #9557 Fixes #9718 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Installing a package whose peers form a cycle with an already-installed optional peer could crash with `TypeError: Cannot read properties of null (reading 'explain')` instead of resolving or reporting a real conflict. A minimal trigger: `vite@8.1.4` declares an optional peer on `@vitejs/devtools`, `@vitejs/devtools` peers back on `vite`, so installing vite and then adding devtools crashes. The root cause is in `#loadPeerSet`. While resolving a package's peer edge through the parent's edge, the recursive `#nodeFromEdge` call can place a compatible peer that replaces and detaches the current node from the tree mid-iteration. The now-invalid edge then reached `#failPeerConflict`, whose `#explainPeerConflict` calls `node.resolve(edge.name).explain()` on the detached node. `resolve()` returns `null` for a node no longer in the tree, so `.explain()` threw. A detached node has been superseded by a compatible peer, so there is no real conflict to report. The fix adds a guard that stops processing when the node has been detached, right before `#failPeerConflict`, mirroring the existing top-of-loop detachment check. This lets the install complete by keeping the compatible peer that replaced the node (for the reproduction, `@vitejs/devtools` backs off to a version that satisfies vite's optional peer range) rather than crashing or raising a spurious `ERESOLVE`. ## References Fixes #5222 Closes #4787
) ## Summary Fixes #9802. Before the npm 12 stable cut, #9729 restored warn-by-default for unknown `.npmrc` keys (with `strict-npmrc` to opt into errors). #9733 updated the `12.0.0-pre.1` changelog bullet to match, but the aggregated stable `12.0.0` / `@npmcli/config@11.0.0` breaking-change notes (and the config package's `pre.1` note) still said unknown `.npmrc` configs throw. That stale line also shipped in the published [v12.0.0 GitHub release notes](https://github.com/npm/cli/releases/tag/v12.0.0). This aligns those changelog bullets with the corrected wording: > unknown CLI flags, abbreviated flags, and single-hyphen multi-char shorthands now throw instead of warning. (Unknown `.npmrc` configs still warn by default; opt into erroring with the new `strict-npmrc` config.) Maintainers may also want to refresh the published `v12.0.0` release body to match; that cannot be updated via this PR alone. ## References - #9729 (warn instead of error on unknown `.npmrc` configs) - #9733 (clarified the `pre.1` changelog note)
## Summary Ensure `npm owner add` and `npm owner rm` resolve users from the same registry used for the target package. The user lookup now receives the package `spec`, allowing `npm-registry-fetch` to honor scoped registry configuration instead of falling back to the global registry. ## Testing Added regression coverage for split global/scoped registry configurations, including: - Preventing substitution of the user added as an owner. - Preventing removal of an unintended existing owner. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 1e1231bc-474e-4a27-8b78-242366f48e19
Make `npm pack` honor `min-release-age-exclude` when resolving packages from a registry. Given: ```ini min-release-age=7 min-release-age-exclude=@myscope/* ``` `npm pack @myscope/some-package@1.2.3` incorrectly failed with ETARGET when the package was newer than seven days, despite matching the exclusion. Root cause `min-release-age` is flattened into the `before` option consumed by `pacote` . However, `pacote` does not interpret `min-release-age-exclude` ; callers must remove before for matching packages. `npm pack` performs two manifest resolutions: 1. Directly through `pacote.manifest` 2. Internally through `libnpmpack` Both resolutions received the unmodified `before` option, so the exclusion was never applied. Fix Derive effective options for each package spec using the existing Arborist release-age helpers: • Clear `before` when the package matches `min-release-age-exclude` • Preserve the cutoff for nonmatching packages • Pass the same effective options to both manifest resolutions Using the alias target prevents an excluded alias name from disabling the release-age policy for an unrelated package. Test coverage Added regression coverage confirming that: • A recently published scoped package matching an exclusion glob can be packed • An excluded alias name does not exempt its non-excluded registry target The original scenario was also reproduced against a local registry: it failed with `ETARGET` before this change and successfully produced the tarball afterward. References Fixes #9759 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: dec204b6-ad66-45a5-8228-831e306f6ba6
…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 - bcrypt@6.0.0`). `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 <223556219+Copilot@users.noreply.github.com> Copilot-Session: 6e300b82-303b-49ac-ae93-52d984743d5b
## 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 #9750
…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
## 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
## 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 <ychampion@users.noreply.github.com>
## What / Why `npm uninstall vite@8.2.1` 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 `vite@8.2.1`, 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
## 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
## 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 `tar@7.5.22`. ## 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 <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 73e1dfa8-6bec-47aa-b811-4edf404e9883
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 73e1dfa8-6bec-47aa-b811-4edf404e9883
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 73e1dfa8-6bec-47aa-b811-4edf404e9883
## 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 <223556219+Copilot@users.noreply.github.com>
## 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
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
See Commits and Changes for more details.
Created by
pull[bot]
Can you help keep this open source service alive? 💖 Please sponsor : )