Compare commits

...

1 Commits

Author SHA1 Message Date
Brooklyn Nicholson
d049d88dd7 fix(docker): run bundled TUI without npm install 2026-04-30 12:36:02 -05:00
4 changed files with 52 additions and 1 deletions

View File

@@ -51,7 +51,8 @@ RUN cd web && npm run build && \
cp -R packages/hermes-ink node_modules/@hermes/ink && \
npm install --omit=dev --prefer-offline --no-audit --prefix node_modules/@hermes/ink && \
rm -rf node_modules/@hermes/ink/node_modules/react && \
node --input-type=module -e "await import('@hermes/ink')"
node --input-type=module -e "await import('@hermes/ink')" && \
touch .hermes-prebuilt-tui
# ---------- Permissions ----------
# Make install dir world-readable so any HERMES_UID can read it at runtime.

View File

@@ -836,6 +836,16 @@ def _print_tui_exit_summary(session_id: Optional[str], active_session_file: Opti
_NPM_LOCK_RUNTIME_KEYS = frozenset({"ideallyInert"})
_TUI_PREBUILT_MARKER = ".hermes-prebuilt-tui"
def _tui_prebuilt_ready(root: Path) -> bool:
return (
(root / _TUI_PREBUILT_MARKER).is_file()
and (root / "dist" / "entry.js").is_file()
and (root / "node_modules" / "@hermes" / "ink" / "package.json").is_file()
and (root / "packages" / "hermes-ink" / "dist" / "ink-bundle.js").is_file()
)
def _tui_need_npm_install(root: Path) -> bool:
@@ -858,6 +868,9 @@ def _tui_need_npm_install(root: Path) -> bool:
we'd rather not force a reinstall for them. Falls back to mtime
comparison if either lockfile is unparseable.
"""
if _tui_prebuilt_ready(root):
return False
ink = root / "node_modules" / "@hermes" / "ink" / "package.json"
if not ink.is_file():
return True
@@ -911,6 +924,8 @@ def _find_bundled_tui(tui_dir: Path) -> Optional[Path]:
def _tui_build_needed(tui_dir: Path) -> bool:
if _tui_prebuilt_ready(tui_dir):
return False
if _hermes_ink_bundle_stale(tui_dir):
return True
entry = tui_dir / "dist" / "entry.js"

View File

@@ -31,6 +31,13 @@ def _touch_ink_bundle(root: Path) -> None:
bundle.write_text("export {}")
def _touch_prebuilt_tui(root: Path) -> None:
_touch_tui_entry(root)
_touch_ink(root)
_touch_ink_bundle(root)
(root / ".hermes-prebuilt-tui").write_text("")
def test_need_install_when_ink_missing(tmp_path: Path, main_mod) -> None:
(tmp_path / "package-lock.json").write_text("{}")
assert main_mod._tui_need_npm_install(tmp_path) is True
@@ -103,3 +110,22 @@ def test_build_not_needed_when_entry_and_ink_bundle_present(tmp_path: Path, main
_touch_ink_bundle(tmp_path)
assert main_mod._tui_build_needed(tmp_path) is False
def test_prebuilt_tui_marker_skips_install_without_hidden_lock(tmp_path: Path, main_mod) -> None:
_touch_prebuilt_tui(tmp_path)
(tmp_path / "package-lock.json").write_text(
'{"packages":{"node_modules/foo":{"version":"1.0.0"}}}'
)
assert main_mod._tui_need_npm_install(tmp_path) is False
def test_prebuilt_tui_marker_skips_build(tmp_path: Path, main_mod) -> None:
_touch_prebuilt_tui(tmp_path)
(tmp_path / "src").mkdir()
(tmp_path / "src" / "entry.tsx").write_text("newer")
os.utime(tmp_path / "src" / "entry.tsx", (200, 200))
os.utime(tmp_path / "dist" / "entry.js", (100, 100))
assert main_mod._tui_build_needed(tmp_path) is False

View File

@@ -135,6 +135,15 @@ def test_dockerfile_materializes_local_tui_ink_package(dockerfile_text):
)
def test_dockerfile_marks_tui_as_prebuilt_after_validation(dockerfile_text):
assert any(
"ui-tui" in step
and "await import('@hermes/ink')" in step
and "touch .hermes-prebuilt-tui" in step
for step in _run_steps(dockerfile_text)
)
def test_dockerignore_excludes_nested_dependency_dirs():
if not DOCKERIGNORE.exists():
pytest.skip(".dockerignore not present in this checkout")