link: don't chdir into the global dir for `bun link -g` / `bun unlink -g`

Closed
RO
1mo ago
+105-1

Summary

bun link -g (no package name) crashes with an error pointing at Bun's internal global package.json:

error: package.json missing "name" in "/root/.bun/install/global/package.json"

Plain bun link already registers the current package globally and creates the ~/.bun/bin/<name> binary, so a user coming from npm (npm link is implicitly global) naturally types bun link -g and hits the error. bun unlink -g has the identical crash. Fixes #33141. Fixes #21397 (same root cause, reported as circular self-linking of the global dir into its own node_modules).

Reproduction

$ mkdir /tmp/linktest && cd /tmp/linktest
$ echo '{ "name": "my-pkg", "version": "1.0.0", "bin": { "my-pkg": "./cli.js" } }' > package.json
$ echo 'console.log("hi")' > cli.js

$ bun link -g
bun link v1.4.0-canary.1
error: package.json missing "name" in "/root/.bun/install/global/package.json"
# exit 1

Cause

bun link with no positional package name takes the "register the current package" branch, which reads manager.original_package_json_path. That path is derived from the cwd in pm::init. When cli.global is set, pm::init first fchdirs into the global install dir (correct for bun add -g / bun install -g):

if cli.global {
    let global_dir = package_manager_options::open_global_dir(explicit_global_dir)?;
    bun_sys::fchdir(global_dir)?;
}

So the cwd becomes ~/.bun/install/global, original_package_json_path points at Bun's internal global manifest (no "name"), and the missing-name guard crashes. The same chdir would also have made the node_modules/<name> symlink target the global dir instead of the user's package. bun unlink -g (no package name) unregisters the current package through the same structure and crashes identically.

Fix

Skip the fchdir for the bun link / bun unlink-current-package case (Subcommand::Link or Subcommand::Unlink with no package-name positional). The -g flag is redundant there, so bun link -g / bun unlink -g now behave identically to the flagless forms. Every other subcommand and bun link <name> -g are unchanged (the chdir still happens).

Verification

New tests in test/cli/install/bun-link.test.ts run bun link -g and bun unlink -g in an isolated BUN_INSTALL. The link test asserts the global node_modules/<name> entry resolves to the user's package (not the internal global dir) and that the global bin is created; the unlink test registers with plain bun link and asserts bun unlink -g removes the registration.

  • Fail before the fix: the commands crash (Received: "bun link v1.4.0-canary.1\n" / "bun unlink v1.4.0-canary.1\n").
  • Pass after the fix.