diff --git a/hermes_cli/main_desktop.py b/hermes_cli/main_desktop.py index 9547f5aabb..dcfceb79a7 100644 --- a/hermes_cli/main_desktop.py +++ b/hermes_cli/main_desktop.py @@ -1228,6 +1228,22 @@ def _promote_staged_desktop_app( return packaged_executable +def _diagnose_esbuild_ignore_scripts(output: Optional[str]) -> None: + """Print an actionable hint when a desktop build failed because esbuild's platform + binary was never staged (`ignore-scripts=true` skips esbuild's postinstall, so the + ``@esbuild/`` optional dependency is absent) — #53082. Best-effort: only + adds context, never masks the original error.""" + text = output or "" + if not ("@esbuild/" in text and "could not be found" in text) and "ignore-scripts" not in text: + return + print(" ⚠ This looks like esbuild's native binary is missing — commonly caused by") + print(" `ignore-scripts=true` in your npm config, which skips esbuild's postinstall") + print(" that stages the @esbuild/ package.") + print(" Fix: run `npm config get ignore-scripts` — if true, either set it to false") + print(" (`npm config set ignore-scripts false`), then reinstall: `npm ci` in the repo root,") + print(" or stage the binary directly: `node node_modules/esbuild/install.js` in apps/desktop.") + + def build_prepared_desktop(desktop_dir: Path, *, source_mode: bool, npm: str, env: dict, icons: Path | None = None) -> Optional[Path]: """Build prepared desktop sources, then publish the verified staged app.""" @@ -1264,6 +1280,9 @@ def build_prepared_desktop(desktop_dir: Path, *, source_mode: bool, npm: str, en _promote_staged_desktop_app(desktop_dir, staging_dir) if staging_dir is not None else None ) return packaged_executable + except subprocess.CalledProcessError as exc: + _diagnose_esbuild_ignore_scripts(exc.output) + raise finally: if staging_dir is not None: _discard_desktop_staging(staging_dir) diff --git a/tests/hermes_cli/test_desktop_esbuild_ignore_scripts.py b/tests/hermes_cli/test_desktop_esbuild_ignore_scripts.py new file mode 100644 index 0000000000..4051edf79f --- /dev/null +++ b/tests/hermes_cli/test_desktop_esbuild_ignore_scripts.py @@ -0,0 +1,33 @@ +"""Regression tests for the esbuild / ignore-scripts build-failure diagnosis (#53082).""" + +import io +import contextlib + +from hermes_cli.main_desktop import _diagnose_esbuild_ignore_scripts + + +def _hintprinted(output: str | None) -> bool: + buf = io.StringIO() + with contextlib.redirect_stdout(buf): + _diagnose_esbuild_ignore_scripts(output) + return "esbuild" in buf.getvalue() + + +def test_diagnose_triggers_on_missing_esbuild_binary() -> None: + output = ( + "Error: The package \"@esbuild/darwin-arm64\" could not be found, " + "and is needed by esbuild." + ) + assert _hintprinted(output) + + +def test_diagnose_triggers_on_ignore_scripts_mention() -> None: + assert _hintprinted("npm error code ERESOLVE\nnpm warn config ignore-scripts=true") + + +def test_diagnose_silent_on_unrelated_failure() -> None: + assert not _hintprinted("vite build failed: unexpected token in index.html") + + +def test_diagnose_silent_on_none_output() -> None: + assert not _hintprinted(None)