fix(ui): ignore inherited esbuild binary overrides

This commit is contained in:
fangliquanflq
2026-08-16 11:37:07 +08:00
committed by Teknium
parent be8b58dcbc
commit f33ec68478
3 changed files with 26 additions and 8 deletions
+11 -2
View File
@@ -2189,6 +2189,15 @@ def _ensure_tui_workspace(tui_dir: Path) -> None:
sys.exit(1)
def _npm_install_env(env: dict[str, str] | None = None) -> dict[str, str]:
"""Build a clean environment for installing the pinned UI toolchain."""
run_env = {**os.environ, **(env or {}), "CI": "1"}
# esbuild treats this as an executable override. If a shell points it at a
# different release, the pinned package's postinstall rejects that binary.
run_env.pop("ESBUILD_BINARY_PATH", None)
return run_env
def _make_tui_argv(tui_dir: Path, tui_dev: bool) -> tuple[list[str], Path]:
"""TUI: --dev → tsx src; else node dist (HERMES_TUI_DIR prebuilt or esbuild)."""
_ensure_tui_node()
@@ -2319,7 +2328,7 @@ def _make_tui_argv(tui_dir: Path, tui_dev: bool) -> tuple[list[str], Path]:
text=True,
encoding="utf-8",
errors="replace",
env={**with_hermes_node_path(), "CI": "1"},
env=_npm_install_env(with_hermes_node_path()),
)
result = _run_tui_install()
@@ -5942,7 +5951,7 @@ def _run_npm_install_deterministic(
# unicode-animations' postinstall animates to /dev/tty (bypasses
# --silent/capture_output). It no-ops when CI is set — same as the TUI
# install path and nix/lib.nix npm ci hooks.
run_env = {**os.environ, **(env or {}), "CI": "1"}
run_env = _npm_install_env(env)
def _run(cmd: list[str]) -> subprocess.CompletedProcess:
return _run_npm_watching_for_engine_failure(
+7 -3
View File
@@ -137,14 +137,15 @@ def test_no_stray_lockfiles_in_workspace_subdirs(main_mod) -> None:
)
def test_make_tui_argv_omits_workspace_when_tui_has_own_lockfile(
def test_make_tui_argv_omits_workspace_and_scrubs_esbuild_override(
tmp_path: Path, main_mod, monkeypatch
) -> None:
"""When ui-tui/ has its own package-lock.json, _workspace_root returns
tui_dir itself. npm install --workspace ui-tui would fail in that case
because npm cannot find a workspace named "ui-tui" inside ui-tui/.
The fix omits --workspace and runs plain npm install from tui_dir.
See #42973.
See #42973. The npm child must also ignore an inherited esbuild binary
override: a version mismatch makes esbuild's postinstall abort (#87405).
"""
tui_dir = tmp_path / "ui-tui"
tui_dir.mkdir()
@@ -156,6 +157,7 @@ def test_make_tui_argv_omits_workspace_when_tui_has_own_lockfile(
monkeypatch.delenv("TERMUX_VERSION", raising=False)
monkeypatch.setenv("PREFIX", "/usr")
monkeypatch.setenv("ESBUILD_BINARY_PATH", "/opt/esbuild-0.28.2")
monkeypatch.setattr(main_mod, "_tui_need_npm_install", lambda _root: True)
monkeypatch.setattr(main_mod.shutil, "which", lambda name: f"/bin/{name}")
calls = []
@@ -173,6 +175,8 @@ def test_make_tui_argv_omits_workspace_when_tui_has_own_lockfile(
assert "--workspace" not in install_cmd, (
f"npm install should omit --workspace when tui_dir has its own lockfile, got: {install_cmd}"
)
assert install_cmd[:2] == ["/bin/npm", "install"]
assert Path(install_cmd[0]).name in {"npm", "npm.cmd"}
assert install_cmd[1] == "install"
# cwd must be tui_dir (standalone), not parent
assert calls[0][1]["cwd"] == str(tui_dir)
assert "ESBUILD_BINARY_PATH" not in calls[0][1]["env"]
+8 -3
View File
@@ -123,7 +123,7 @@ class TestBuildWebUISkipsWhenFresh:
def test_web_install_omits_workspace_when_web_has_own_lockfile(
def test_web_install_omits_workspace_and_scrubs_esbuild_override(
self, tmp_path, monkeypatch
):
"""web/ with its own lockfile => _workspace_root returns web_dir, so
@@ -132,13 +132,16 @@ class TestBuildWebUISkipsWhenFresh:
Symmetric to the TUI fix in test_tui_npm_install.py. See #42973.
With web's own lockfile present at cwd, _run_npm_install_deterministic
uses ``npm ci`` (not ``npm install``).
uses ``npm ci`` (not ``npm install``). The shared installer must also
remove an inherited esbuild binary override so package/binary versions
cannot diverge (#87405).
"""
web_dir, _ = _make_web_dir(tmp_path)
(web_dir / "package-lock.json").write_text("{}", encoding="utf-8")
(tmp_path / "package-lock.json").write_text("{}", encoding="utf-8")
monkeypatch.delenv("TERMUX_VERSION", raising=False)
monkeypatch.setenv("PREFIX", "/usr")
monkeypatch.setenv("ESBUILD_BINARY_PATH", "/opt/esbuild-0.28.2")
install_cp = __import__("subprocess").CompletedProcess([], 0, stdout="", stderr="")
build_cp = __import__("subprocess").CompletedProcess([], 0, stdout="", stderr="")
@@ -150,8 +153,10 @@ class TestBuildWebUISkipsWhenFresh:
assert result is True
args, kwargs = mock_run.call_args
assert "--workspace" not in args[0]
assert args[0] == ["/usr/bin/npm", "ci", "--include=dev", "--silent", "--prefer-offline"]
assert Path(args[0][0]).name in {"npm", "npm.cmd"}
assert args[0][1:] == ["ci", "--include=dev", "--silent", "--prefer-offline"]
assert kwargs["cwd"] == web_dir
assert "ESBUILD_BINARY_PATH" not in kwargs["env"]
def test_workspace_root_install_names_update_closure(self, tmp_path, monkeypatch):
"""From the workspace root, _build_web_ui must install the SAME