mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-05-23 05:31:23 +00:00
refactor(docker): drop manual @hermes/ink build, rely on esbuild bundle
the esbuild pipeline (scripts/build.mjs) already bundles ink into a single self-contained dist/entry.js. remove the Dockerfile steps that manually copied packages/hermes-ink into node_modules/@hermes/ink and ran a nested npm install there. - Dockerfile: simplify TUI build step to just 'npm run build' - hermes_cli/main.py: _tui_build_needed now checks dist/entry.js staleness against source files before falling back to the old ink-bundle.js logic - tests: update TUI npm install tests and drop the Dockerfile contract test for the removed ink materialization step
This commit is contained in:
parent
279504d5b8
commit
42e166c7ea
4 changed files with 47 additions and 24 deletions
|
|
@ -45,13 +45,7 @@ COPY --chown=hermes:hermes . .
|
||||||
|
|
||||||
# Build browser dashboard and terminal UI assets.
|
# Build browser dashboard and terminal UI assets.
|
||||||
RUN cd web && npm run build && \
|
RUN cd web && npm run build && \
|
||||||
cd ../ui-tui && npm run build && \
|
cd ../ui-tui && npm run build
|
||||||
rm -rf node_modules/@hermes/ink && \
|
|
||||||
rm -rf packages/hermes-ink/node_modules && \
|
|
||||||
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')"
|
|
||||||
|
|
||||||
# ---------- Permissions ----------
|
# ---------- Permissions ----------
|
||||||
# Make install dir world-readable so any HERMES_UID can read it at runtime.
|
# Make install dir world-readable so any HERMES_UID can read it at runtime.
|
||||||
|
|
|
||||||
|
|
@ -922,9 +922,39 @@ def _find_bundled_tui(tui_dir: Path) -> Optional[Path]:
|
||||||
|
|
||||||
|
|
||||||
def _tui_build_needed(tui_dir: Path) -> bool:
|
def _tui_build_needed(tui_dir: Path) -> bool:
|
||||||
|
entry = tui_dir / "dist" / "entry.js"
|
||||||
|
# In the esbuild pipeline, ink is bundled into dist/entry.js directly.
|
||||||
|
# If the main bundle exists and is up to date with all source files,
|
||||||
|
# no separate ink rebuild is needed.
|
||||||
|
if entry.exists():
|
||||||
|
dist_m = entry.stat().st_mtime
|
||||||
|
skip = frozenset({"node_modules", "dist"})
|
||||||
|
stale = False
|
||||||
|
for dirpath, dirnames, filenames in os.walk(tui_dir, topdown=True):
|
||||||
|
dirnames[:] = [d for d in dirnames if d not in skip]
|
||||||
|
for fn in filenames:
|
||||||
|
if fn.endswith((".ts", ".tsx")):
|
||||||
|
if os.path.getmtime(os.path.join(dirpath, fn)) > dist_m:
|
||||||
|
stale = True
|
||||||
|
break
|
||||||
|
if stale:
|
||||||
|
break
|
||||||
|
if not stale:
|
||||||
|
for meta in (
|
||||||
|
"package.json",
|
||||||
|
"package-lock.json",
|
||||||
|
"tsconfig.json",
|
||||||
|
"tsconfig.build.json",
|
||||||
|
):
|
||||||
|
mp = tui_dir / meta
|
||||||
|
if mp.exists() and mp.stat().st_mtime > dist_m:
|
||||||
|
stale = True
|
||||||
|
break
|
||||||
|
if not stale:
|
||||||
|
return False
|
||||||
|
|
||||||
if _hermes_ink_bundle_stale(tui_dir):
|
if _hermes_ink_bundle_stale(tui_dir):
|
||||||
return True
|
return True
|
||||||
entry = tui_dir / "dist" / "entry.js"
|
|
||||||
if not entry.exists():
|
if not entry.exists():
|
||||||
return True
|
return True
|
||||||
dist_m = entry.stat().st_mtime
|
dist_m = entry.stat().st_mtime
|
||||||
|
|
|
||||||
|
|
@ -95,12 +95,25 @@ def test_no_install_prebuilt_bundle_mode(tmp_path: Path, main_mod) -> None:
|
||||||
assert main_mod._tui_need_npm_install(tmp_path) is False
|
assert main_mod._tui_need_npm_install(tmp_path) is False
|
||||||
|
|
||||||
|
|
||||||
def test_build_needed_when_local_ink_bundle_missing(tmp_path: Path, main_mod) -> None:
|
def test_build_needed_when_source_newer_than_entry(tmp_path: Path, main_mod) -> None:
|
||||||
|
_touch_tui_entry(tmp_path)
|
||||||
|
_touch_ink(tmp_path)
|
||||||
|
src = tmp_path / "src" / "entry.tsx"
|
||||||
|
src.parent.mkdir(parents=True, exist_ok=True)
|
||||||
|
src.write_text("console.log('newer')")
|
||||||
|
os.utime(src, (200, 200))
|
||||||
|
os.utime(tmp_path / "dist" / "entry.js", (100, 100))
|
||||||
|
|
||||||
|
assert main_mod._tui_need_npm_install(tmp_path) is False
|
||||||
|
assert main_mod._tui_build_needed(tmp_path) is True
|
||||||
|
|
||||||
|
|
||||||
|
def test_build_not_needed_when_entry_exists_and_sources_unchanged(tmp_path: Path, main_mod) -> None:
|
||||||
_touch_tui_entry(tmp_path)
|
_touch_tui_entry(tmp_path)
|
||||||
_touch_ink(tmp_path)
|
_touch_ink(tmp_path)
|
||||||
|
|
||||||
assert main_mod._tui_need_npm_install(tmp_path) is False
|
assert main_mod._tui_need_npm_install(tmp_path) is False
|
||||||
assert main_mod._tui_build_needed(tmp_path) is True
|
assert main_mod._tui_build_needed(tmp_path) is False
|
||||||
|
|
||||||
|
|
||||||
def test_build_not_needed_when_entry_and_ink_bundle_present(tmp_path: Path, main_mod) -> None:
|
def test_build_not_needed_when_entry_and_ink_bundle_present(tmp_path: Path, main_mod) -> None:
|
||||||
|
|
|
||||||
|
|
@ -121,20 +121,6 @@ def test_dockerfile_builds_tui_assets(dockerfile_text):
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def test_dockerfile_materializes_local_tui_ink_package(dockerfile_text):
|
|
||||||
assert any(
|
|
||||||
"ui-tui" in step
|
|
||||||
and "node_modules/@hermes/ink" in step
|
|
||||||
and "packages/hermes-ink" in step
|
|
||||||
and "rm -rf packages/hermes-ink/node_modules" in step
|
|
||||||
and "npm install --omit=dev" in step
|
|
||||||
and "--prefix node_modules/@hermes/ink" in step
|
|
||||||
and "rm -rf node_modules/@hermes/ink/node_modules/react" in step
|
|
||||||
and "await import('@hermes/ink')" in step
|
|
||||||
for step in _run_steps(dockerfile_text)
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def test_dockerignore_excludes_nested_dependency_dirs():
|
def test_dockerignore_excludes_nested_dependency_dirs():
|
||||||
if not DOCKERIGNORE.exists():
|
if not DOCKERIGNORE.exists():
|
||||||
pytest.skip(".dockerignore not present in this checkout")
|
pytest.skip(".dockerignore not present in this checkout")
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue