Skip to content

feat: allow global install to override bins owned by the new package#10828

Merged
zkochan merged 7 commits into
mainfrom
improve-global-install
Mar 4, 2026
Merged

feat: allow global install to override bins owned by the new package#10828
zkochan merged 7 commits into
mainfrom
improve-global-install

Conversation

@zkochan

@zkochan zkochan commented Mar 2, 2026

Copy link
Copy Markdown
Member

When a package name matches the bin name (e.g., npm package providing npm bin), the new package gets priority and can override an existing bin from another global package. The npx bin is also treated as owned by the npm package via a hardcoded override map.

zkochan and others added 4 commits March 2, 2026 01:37
When a package name matches the bin name (e.g., `npm` package providing
`npm` bin), the new package gets priority and can override an existing
bin from another global package. The `npx` bin is also treated as owned
by the `npm` package via a hardcoded override map.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…failing

When installing a package globally (e.g. node) whose bins conflict with
an already-installed package that owns those bins (e.g. npm owns npm/npx),
the install now succeeds and skips linking the conflicting bins rather
than aborting with GLOBAL_BIN_CONFLICT.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@zkochan zkochan marked this pull request as ready for review March 3, 2026 22:16
Copilot AI review requested due to automatic review settings March 3, 2026 22:16
@zkochan zkochan added this to the v11.0 milestone Mar 3, 2026

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR updates pnpm’s global install/update flow to allow a newly installed global package to take precedence for binaries it “owns” (when the bin name matches the package name, plus a hardcoded ownership override for npxnpm), and to skip linking bins that are owned by an already-installed global package rather than failing the install.

Changes:

  • Update checkGlobalBinConflicts to support “bin ownership” priority rules and return a Set of bin names to skip during linking.
  • Pass the returned skip set through globalAdd / globalUpdate into linkBinsOfPackages, which now supports excluding specific bin names from linking.
  • Add Jest tests for the new conflict/ownership behavior and enable linting/testing for global/commands tests.

Reviewed changes

Copilot reviewed 9 out of 9 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
pkg-manager/link-bins/src/index.ts Adds excludeBins option to filter out bins before linking.
global/commands/src/checkGlobalBinConflicts.ts Implements bin ownership priority/skip logic and returns Set<string> of bins to skip.
global/commands/src/globalAdd.ts Captures binsToSkip from conflict check and passes it into bin linking.
global/commands/src/globalUpdate.ts Captures binsToSkip from conflict check and passes it into bin linking.
global/commands/test/tsconfig.json Adds a TS config for tests in the global/commands package.
global/commands/test/checkGlobalBinConflicts.test.ts Adds test coverage for ownership/override/skip scenarios.
global/commands/package.json Runs lint on tests and adds Jest test script.
cspell.json Adds fakehash to dictionary for test text.
.changeset/global-bin-ownership-priority.md Declares a minor release for the behavior change.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread global/commands/src/checkGlobalBinConflicts.ts Outdated
Comment thread global/commands/src/checkGlobalBinConflicts.ts
Comment thread global/commands/test/checkGlobalBinConflicts.test.ts Outdated
Comment thread pkg-manager/link-bins/src/index.ts Outdated
…i-provider bins

Track all new bin providers (not just the last one) so ownership is
correctly resolved when multiple new packages provide the same bin.
Use manifest.name instead of the dependency alias when checking
existing package ownership, fixing incorrect decisions for aliased
installs. Use symlink-dir in tests for Windows compatibility and
remove a non-null assertion in link-bins.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 9 out of 9 changed files in this pull request and generated 3 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread global/commands/src/checkGlobalBinConflicts.ts Outdated
Comment on lines +12 to +21
// Bins that are logically owned by a package other than the one matching
// the bin name. For example, `npx` ships inside the `npm` package, so
// when `npm` is being installed globally it should be allowed to claim
// the `npx` bin as well.
const BIN_OWNER_OVERRIDES: Record<string, string> = {
npx: 'npm',
}

function pkgOwnsBin (binName: string, pkgName: string): boolean {
return binName === pkgName || BIN_OWNER_OVERRIDES[binName] === pkgName

Copilot AI Mar 4, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

BIN_OWNER_OVERRIDES / pkgOwnsBin() defines ownership rules (e.g. npx owned by npm), but the actual bin linking conflict resolution in @pnpm/link-bins only treats cmd.name === manifest.name as “owned” (see ownName in pkg-manager/link-bins/src/index.ts). If multiple new packages provide an overridden bin like npx, the linker may choose a non-owner, contradicting the ownership override semantics. Consider aligning the linker’s ownership check with the same override rules or ensuring checkGlobalBinConflicts enforces the intended winner when overrides apply.

Suggested change
// Bins that are logically owned by a package other than the one matching
// the bin name. For example, `npx` ships inside the `npm` package, so
// when `npm` is being installed globally it should be allowed to claim
// the `npx` bin as well.
const BIN_OWNER_OVERRIDES: Record<string, string> = {
npx: 'npm',
}
function pkgOwnsBin (binName: string, pkgName: string): boolean {
return binName === pkgName || BIN_OWNER_OVERRIDES[binName] === pkgName
function pkgOwnsBin (binName: string, pkgName: string): boolean {
// A package is considered to "own" a bin only when the bin name matches
// the package name. This mirrors the ownership rule used by
// `@pnpm/link-bins` (see `ownName` in `pkg-manager/link-bins/src/index.ts`),
// ensuring that conflict detection here is consistent with the actual
// bin linking behavior.
return binName === pkgName

Copilot uses AI. Check for mistakes.
Comment thread global/commands/src/checkGlobalBinConflicts.ts Outdated
Convert the conflicting bins collection from an array to a Set for O(1)
lookups. Use the dependency alias in the `pnpm remove -g` hint since
that is what the user originally installed with, showing both alias and
manifest name when they differ.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@zkochan zkochan merged commit 00e7787 into main Mar 4, 2026
6 checks passed
@zkochan zkochan deleted the improve-global-install branch March 4, 2026 14:42
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants