fix(cli): accept native npm paths under mnt

This commit is contained in:
KoNit-K
2026-09-15 23:56:02 +08:00
committed by Teknium
parent bd63866253
commit addcf7d898
2 changed files with 59 additions and 4 deletions

View File

@@ -1206,12 +1206,17 @@ def _is_termux_env(env: dict[str, str] | None = None) -> bool:
def _is_windows_npm_path(npm_path: str) -> bool:
"""True if ``npm_path`` points at a Windows npm shim (WSL ``/mnt/c`` interop, ``.cmd``/``.exe``, UNC).
"""True if ``npm_path`` points at a Windows npm shim (WSL drive interop, ``.cmd``/``.exe``, UNC).
Callers use this only on a POSIX host — on native Windows ``npm.cmd`` is correct.
"""
low = npm_path.lower()
return low.endswith((".exe", ".cmd", ".bat")) or low.startswith("/mnt/") or "\\" in npm_path
mount = low.split("/", 3)[2] if low.startswith("/mnt/") else ""
return (
low.endswith((".exe", ".cmd", ".bat"))
or (len(mount) == 1 and mount.isalpha())
or "\\" in npm_path
)
def _resolve_node_runtime_npm() -> str | None:
@@ -1219,7 +1224,7 @@ def _resolve_node_runtime_npm() -> str | None:
On WSL, PATH interop can hand back a Windows npm that fails with EISDIR / symlink errors over
``\\\\wsl.localhost\\...`` UNC paths. Refuse it on a POSIX host and re-scan PATH minus the
``/mnt/*`` drive mounts. ``None`` when no suitable npm is reachable.
Windows drive mounts. ``None`` when no suitable npm is reachable.
On WSL/Linux ``shutil.which("npm")`` may resolve a Windows npm exposed through PATH interop. See #30271.
"""
@@ -1232,7 +1237,7 @@ def _resolve_node_runtime_npm() -> str | None:
if not _is_windows_npm_path(npm):
return npm
for directory in os.environ.get("PATH", "").split(os.pathsep):
if not directory or directory.lower().startswith("/mnt/"):
if not directory or _is_windows_npm_path(directory):
continue
candidate = shutil.which("npm", path=directory)
if candidate and not _is_windows_npm_path(candidate):

View File

@@ -0,0 +1,50 @@
"""Regression coverage for POSIX npm path classification."""
from __future__ import annotations
from unittest.mock import patch
from hermes_cli.main_install_repair import (
_is_windows_npm_path,
_resolve_node_runtime_npm,
)
def test_windows_npm_path_classifies_only_windows_drive_mounts():
assert _is_windows_npm_path("/mnt/c/nodejs/npm")
assert not _is_windows_npm_path("/mnt/data/node/bin/npm")
def test_resolve_node_runtime_npm_accepts_native_mnt_data_path():
native_npm = "/mnt/data/node/bin/npm"
with (
patch("hermes_cli.main._is_windows", return_value=False),
patch("hermes_constants.find_node_executable", return_value=native_npm),
):
assert _resolve_node_runtime_npm() == native_npm
def test_resolve_node_runtime_npm_rejects_windows_drive_mount(monkeypatch):
monkeypatch.setenv("PATH", "/mnt/c/Program Files/nodejs")
with (
patch("hermes_cli.main._is_windows", return_value=False),
patch(
"hermes_constants.find_node_executable",
return_value="/mnt/c/nodejs/npm",
),
):
assert _resolve_node_runtime_npm() is None
def test_resolve_node_runtime_npm_rescans_native_mnt_data_path(monkeypatch):
native_npm = "/mnt/data/node/bin/npm"
monkeypatch.setenv("PATH", "/mnt/data/node/bin")
with (
patch("hermes_cli.main._is_windows", return_value=False),
patch(
"hermes_constants.find_node_executable",
return_value="/mnt/c/nodejs/npm",
),
patch("hermes_cli.main_install_repair.shutil.which", return_value=native_npm),
):
assert _resolve_node_runtime_npm() == native_npm