fix(#2185): normalize Linuxbrew + any Homebrew Cellar path to the stable symlink

normalizeNodePath had hardcoded branches for macOS Intel (/usr/local/Cellar) and
Apple Silicon (/opt/homebrew/Cellar) but none for Linuxbrew
(/home/linuxbrew/.linuxbrew/Cellar). After `brew upgrade node` on Linux, the
version-pinned Cellar path baked into managed hook commands 404'd, and re-running
the installer did not repair it — normalizeNodePath returned the pinned path
unchanged, so the 'already normalized' skip-rewrite check no-op'd.

Generalize to one branch: match <prefix>/Cellar/node(<@ver>)?/<ver>/bin/node and
rewrite to <prefix>/bin/node, deriving <prefix> from the path itself. This covers
macOS Intel, Apple Silicon, Linuxbrew, and any custom HOMEBREW_PREFIX — the same
failure class as #977 (fnm) and #1619 (mise), now closed for Homebrew on every
platform.

Closes #2185
This commit is contained in:
Tom Boucher
2026-07-12 16:23:54 -04:00
parent dae8e70a5a
commit 7baabad146

View File

@@ -326,11 +326,18 @@ function normalizeNodePath(execPath: string, opts?: NodeNormOpts): string {
return execPath;
}
if (/^\/usr\/local\/Cellar\/node(@\d+)?\/[^/]+\/bin\/node(\.exe)?$/.test(execPath)) {
return '/usr/local/bin/node';
}
if (/^\/opt\/homebrew\/Cellar\/node(@\d+)?\/[^/]+\/bin\/node(\.exe)?$/.test(execPath)) {
return '/opt/homebrew/bin/node';
// Homebrew (macOS Intel /usr/local, Apple Silicon /opt/homebrew, Linuxbrew
// /home/linuxbrew/.linuxbrew, and any custom HOMEBREW_PREFIX) pins node at
// <prefix>/Cellar/node(<@ver>)?/<ver>/bin/node, then deletes prior versions on
// `brew upgrade node`. Rewrite to the stable <prefix>/bin/node symlink, which
// survives the upgrade. Derive <prefix> from the path itself (more reliable
// than HOMEBREW_PREFIX env — the path IS the install location) so every layout
// is covered by one branch instead of one per known prefix (#2185).
const homebrewMatch = normalizedForMatch.match(
/^(.+)\/Cellar\/node(@\d+)?\/[^/]+\/bin\/node(\.exe)?$/i,
);
if (homebrewMatch) {
return `${homebrewMatch[1]}/bin/node${homebrewMatch[3] || ''}`;
}
// mise pins a concrete node version at <data>/installs/node/<ver>/bin/node