fix(#1619): normalize pruned mise node execPath to the stable shim in normalizeNodePath (#1621)

* fix(#1619): normalize pruned mise node execPath to the stable shim

resolveNodeRunner() bakes process.execPath into managed .js hook commands.
Node realpaths execPath, so under mise it resolves to a concrete
<data>/installs/node/<ver>/bin/node that mise prunes on `mise up`, after
which every managed hook 404s — the same ephemeral-path failure #977 fixed
for fnm and #3181 for Homebrew. normalizeNodePath now rewrites a mise
versioned install path to the stable sibling shim <data>/shims/node when it
exists (deriving <data> from execPath so a custom MISE_DATA_DIR works),
falling back to the raw execPath otherwise. Tests folded into
install.test.cjs per the regression test-name lint.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* chore(changeset): set pr number to 1621

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Joe Seymour <joese@iarx.com>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-authored-by: Tom Boucher <trekkie@nomorestars.com>
This commit is contained in:
Joe Seymour
2026-06-23 16:17:03 -07:00
committed by GitHub
parent 207d8f1697
commit 9d12725e4e
3 changed files with 83 additions and 0 deletions

View File

@@ -253,6 +253,23 @@ function normalizeNodePath(execPath: string, opts?: NodeNormOpts): string {
if (/^\/opt\/homebrew\/Cellar\/node(@\d+)?\/[^/]+\/bin\/node(\.exe)?$/.test(execPath)) {
return '/opt/homebrew/bin/node';
}
// mise pins a concrete node version at <data>/installs/node/<ver>/bin/node
// (Windows: <data>/installs/node/<ver>/node.exe). Node realpaths
// process.execPath to that versioned path, and `mise up` prunes old versions,
// so a baked hook command 404s after any node bump — the same ephemeral-path
// failure #977 fixed for fnm. The stable alias is the sibling shim
// (<data>/shims/node), which always resolves to the active version, like the
// Homebrew symlink survives `brew upgrade node`. Derive <data> from execPath
// so a custom MISE_DATA_DIR layout still works, and only rewrite when the shim
// exists — otherwise fall back to the raw execPath unchanged.
const miseMatch = normalizedForMatch.match(
/^(.*)\/installs\/node\/[^/]+\/(?:bin\/)?node(\.exe)?$/,
);
if (miseMatch) {
const shim = `${miseMatch[1]}/shims/node${miseMatch[2] || ''}`;
if (existsSync(shim)) return shim;
}
return execPath;
}