"""contract test: dockerfile chowns runtime node_modules trees to hermes regression guard for #18800. the container drops privileges to the hermes user (uid 10000) in entrypoint.sh, then the TUI launcher's _tui_need_npm_install() trips on every startup (see the npm_config_install_links=false comment in the Dockerfile) and runs `npm install` in /opt/hermes/ui-tui. that install fails with EACCES unless the runtime node_modules trees are owned by hermes. """ from __future__ import annotations from pathlib import Path REPO_ROOT = Path(__file__).resolve().parents[2] DOCKERFILE = REPO_ROOT / "Dockerfile" def test_dockerfile_chowns_runtime_node_modules_to_hermes_user() -> None: text = DOCKERFILE.read_text() chown_lines = [ line for line in text.splitlines() if "chown" in line and "hermes:hermes" in line ] assert chown_lines, ( "Dockerfile must contain a chown -R hermes:hermes for the runtime " "node_modules trees; see #18800" ) chown_block = "\n".join(chown_lines) # both runtime-mutable trees must be passed to the chown command. # /opt/hermes/web is intentionally excluded: it is build-time only, # because HERMES_WEB_DIST points at hermes_cli/web_dist for runtime. for required_path in ("/opt/hermes/ui-tui", "/opt/hermes/node_modules"): assert required_path in chown_block, ( f"{required_path} must be passed to a chown -R hermes:hermes " f"command in the Dockerfile (see #18800)" )