fix(cli): accept native npm paths under mnt
This commit is contained in:
@@ -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):
|
||||
|
||||
50
tests/hermes_cli/test_node_runtime_npm_resolution.py
Normal file
50
tests/hermes_cli/test_node_runtime_npm_resolution.py
Normal 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
|
||||
Reference in New Issue
Block a user