fix(install): skip browser download when system chromium exists

This commit is contained in:
helix4u 2026-05-13 18:37:06 -06:00 committed by Teknium
parent 7f08cb5941
commit 52521c937a
4 changed files with 173 additions and 54 deletions

View file

@ -64,6 +64,7 @@ NODE_VERSION="22"
# data still at /root/.hermes (HERMES_HOME). Matches Claude Code / Codex CLI
# and keeps Docker bind-mounted /root/ volumes lean.
ROOT_FHS_LAYOUT=false
DETECTED_BROWSER_EXECUTABLE=""
# Options
USE_VENV=true
@ -1421,6 +1422,7 @@ copy_config_templates() {
else
log_info "~/.hermes/.env already exists, keeping it"
fi
configure_browser_env_from_system_browser
# Create config.yaml at ~/.hermes/config.yaml (top level, easy to find)
if [ ! -f "$HERMES_HOME/config.yaml" ]; then
@ -1469,6 +1471,68 @@ SOUL_EOF
fi
}
find_system_browser() {
# Prefer a user-specified browser path, then common Linux/macOS Chrome and
# Chromium command names. Arch-family distributions commonly ship plain
# `chromium`, while Debian-family systems often use `chromium-browser`.
if [ -n "${AGENT_BROWSER_EXECUTABLE_PATH:-}" ]; then
if [ -x "$AGENT_BROWSER_EXECUTABLE_PATH" ]; then
echo "$AGENT_BROWSER_EXECUTABLE_PATH"
return 0
fi
if command -v "$AGENT_BROWSER_EXECUTABLE_PATH" >/dev/null 2>&1; then
command -v "$AGENT_BROWSER_EXECUTABLE_PATH"
return 0
fi
fi
local candidate
for candidate in google-chrome google-chrome-stable chromium chromium-browser chrome; do
if command -v "$candidate" >/dev/null 2>&1; then
command -v "$candidate"
return 0
fi
done
return 1
}
run_browser_install_with_timeout() {
local timeout_seconds="$1"
shift
if command -v timeout >/dev/null 2>&1; then
timeout "$timeout_seconds" "$@"
else
"$@"
fi
}
configure_browser_env_from_system_browser() {
local env_file="$HERMES_HOME/.env"
local browser_path="${DETECTED_BROWSER_EXECUTABLE:-}"
if [ -z "$browser_path" ]; then
browser_path="$(find_system_browser 2>/dev/null || true)"
fi
if [ -z "$browser_path" ] || [ ! -f "$env_file" ]; then
return 0
fi
if grep -q '^AGENT_BROWSER_EXECUTABLE_PATH=' "$env_file" 2>/dev/null; then
log_info "AGENT_BROWSER_EXECUTABLE_PATH already configured"
return 0
fi
{
echo ""
echo "# Hermes Agent browser tools — use the system Chrome/Chromium binary."
echo "AGENT_BROWSER_EXECUTABLE_PATH=$browser_path"
} >> "$env_file"
log_success "Configured browser tools to use $browser_path"
}
install_node_deps() {
if [ "$HAS_NODE" = false ]; then
log_info "Skipping Node.js dependencies (Node not installed)"
@ -1495,57 +1559,63 @@ install_node_deps() {
# For Arch/Manjaro we install the system libs via pacman first.
# Other systems must install Chromium dependencies manually.
log_info "Installing browser engine (Playwright Chromium)..."
case "$DISTRO" in
ubuntu|debian|raspbian|pop|linuxmint|elementary|zorin|kali|parrot)
log_info "Playwright may request sudo to install browser system dependencies (shared libraries)."
log_info "This is standard Playwright setup — Hermes itself does not require root access."
cd "$INSTALL_DIR" && npx playwright install --with-deps chromium 2>/dev/null || {
log_warn "Playwright browser installation failed — browser tools will not work."
log_warn "Try running manually: cd $INSTALL_DIR && npx playwright install --with-deps chromium"
}
;;
arch|manjaro)
if command -v pacman &> /dev/null; then
log_info "Arch/Manjaro detected — installing Chromium system dependencies via pacman..."
if command -v sudo &> /dev/null && sudo -n true 2>/dev/null; then
sudo NEEDRESTART_MODE=a pacman -S --noconfirm --needed \
nss atk at-spi2-core cups libdrm libxkbcommon mesa pango cairo alsa-lib >/dev/null 2>&1 || true
elif [ "$(id -u)" -eq 0 ]; then
pacman -S --noconfirm --needed \
nss atk at-spi2-core cups libdrm libxkbcommon mesa pango cairo alsa-lib >/dev/null 2>&1 || true
else
log_warn "Cannot install browser deps without sudo. Run manually:"
log_warn " sudo pacman -S nss atk at-spi2-core cups libdrm libxkbcommon mesa pango cairo alsa-lib"
DETECTED_BROWSER_EXECUTABLE="$(find_system_browser 2>/dev/null || true)"
if [ -n "$DETECTED_BROWSER_EXECUTABLE" ]; then
log_success "Found system Chrome/Chromium at $DETECTED_BROWSER_EXECUTABLE"
log_info "Skipping Playwright browser download; Hermes will use the system browser."
else
case "$DISTRO" in
ubuntu|debian|raspbian|pop|linuxmint|elementary|zorin|kali|parrot)
log_info "Playwright may request sudo to install browser system dependencies (shared libraries)."
log_info "This is standard Playwright setup — Hermes itself does not require root access."
cd "$INSTALL_DIR" && run_browser_install_with_timeout 600 npx playwright install --with-deps chromium 2>/dev/null || {
log_warn "Playwright browser installation failed — browser tools will not work."
log_warn "Try running manually: cd $INSTALL_DIR && npx playwright install --with-deps chromium"
}
;;
arch|manjaro|cachyos|endeavouros|garuda)
if command -v pacman &> /dev/null; then
log_info "Arch-family distro detected — installing Chromium system dependencies via pacman..."
if command -v sudo &> /dev/null && sudo -n true 2>/dev/null; then
sudo NEEDRESTART_MODE=a pacman -S --noconfirm --needed \
nss atk at-spi2-core cups libdrm libxkbcommon mesa pango cairo alsa-lib >/dev/null 2>&1 || true
elif [ "$(id -u)" -eq 0 ]; then
pacman -S --noconfirm --needed \
nss atk at-spi2-core cups libdrm libxkbcommon mesa pango cairo alsa-lib >/dev/null 2>&1 || true
else
log_warn "Cannot install browser deps without sudo. Run manually:"
log_warn " sudo pacman -S nss atk at-spi2-core cups libdrm libxkbcommon mesa pango cairo alsa-lib"
fi
fi
fi
cd "$INSTALL_DIR" && npx playwright install chromium 2>/dev/null || {
log_warn "Playwright browser installation failed — browser tools will not work."
}
;;
fedora|rhel|centos|rocky|alma)
log_warn "Playwright does not support automatic dependency installation on RPM-based systems."
log_info "Install Chromium system dependencies manually before using browser tools:"
log_info " sudo dnf install nss atk at-spi2-core cups-libs libdrm libxkbcommon mesa-libgbm pango cairo alsa-lib"
cd "$INSTALL_DIR" && npx playwright install chromium 2>/dev/null || {
log_warn "Playwright browser installation failed — install dependencies above and retry."
}
;;
opensuse*|sles)
log_warn "Playwright does not support automatic dependency installation on zypper-based systems."
log_info "Install Chromium system dependencies manually before using browser tools:"
log_info " sudo zypper install mozilla-nss libatk-1_0-0 at-spi2-core cups-libs libdrm2 libxkbcommon0 Mesa-libgbm1 pango cairo libasound2"
cd "$INSTALL_DIR" && npx playwright install chromium 2>/dev/null || {
log_warn "Playwright browser installation failed — install dependencies above and retry."
}
;;
*)
log_warn "Playwright does not support automatic dependency installation on $DISTRO."
log_info "Install Chromium/browser system dependencies for your distribution, then run:"
log_info " cd $INSTALL_DIR && npx playwright install chromium"
log_info "Browser tools will not work until dependencies are installed."
cd "$INSTALL_DIR" && npx playwright install chromium 2>/dev/null || true
;;
esac
cd "$INSTALL_DIR" && run_browser_install_with_timeout 600 npx playwright install chromium 2>/dev/null || {
log_warn "Playwright browser installation failed — browser tools will not work."
}
;;
fedora|rhel|centos|rocky|alma)
log_warn "Playwright does not support automatic dependency installation on RPM-based systems."
log_info "Install Chromium system dependencies manually before using browser tools:"
log_info " sudo dnf install nss atk at-spi2-core cups-libs libdrm libxkbcommon mesa-libgbm pango cairo alsa-lib"
cd "$INSTALL_DIR" && run_browser_install_with_timeout 600 npx playwright install chromium 2>/dev/null || {
log_warn "Playwright browser installation failed — install dependencies above and retry."
}
;;
opensuse*|sles)
log_warn "Playwright does not support automatic dependency installation on zypper-based systems."
log_info "Install Chromium system dependencies manually before using browser tools:"
log_info " sudo zypper install mozilla-nss libatk-1_0-0 at-spi2-core cups-libs libdrm2 libxkbcommon0 Mesa-libgbm1 pango cairo libasound2"
cd "$INSTALL_DIR" && run_browser_install_with_timeout 600 npx playwright install chromium 2>/dev/null || {
log_warn "Playwright browser installation failed — install dependencies above and retry."
}
;;
*)
log_warn "Playwright does not support automatic dependency installation on $DISTRO."
log_info "Install Chromium/browser system dependencies for your distribution, then run:"
log_info " cd $INSTALL_DIR && npx playwright install chromium"
log_info "Browser tools will not work until dependencies are installed."
cd "$INSTALL_DIR" && run_browser_install_with_timeout 600 npx playwright install chromium 2>/dev/null || true
;;
esac
fi
log_success "Browser engine setup complete"
fi

View file

@ -0,0 +1,35 @@
"""Regression tests for install.sh browser setup.
Browser automation is optional. The installer should not leave Hermes
half-installed just because Playwright's managed Chromium download hangs on an
unsupported distribution.
"""
from pathlib import Path
REPO_ROOT = Path(__file__).resolve().parent.parent
INSTALL_SH = REPO_ROOT / "scripts" / "install.sh"
def test_install_script_skips_playwright_download_when_system_browser_exists() -> None:
text = INSTALL_SH.read_text()
assert "find_system_browser()" in text
assert "google-chrome google-chrome-stable chromium chromium-browser chrome" in text
assert "Skipping Playwright browser download; Hermes will use the system browser." in text
def test_install_script_persists_system_browser_for_agent_browser() -> None:
text = INSTALL_SH.read_text()
assert "configure_browser_env_from_system_browser()" in text
assert "AGENT_BROWSER_EXECUTABLE_PATH=$browser_path" in text
def test_playwright_installs_are_timeout_guarded() -> None:
text = INSTALL_SH.read_text()
assert "run_browser_install_with_timeout()" in text
assert "run_browser_install_with_timeout 600 npx playwright install chromium" in text
assert "run_browser_install_with_timeout 600 npx playwright install --with-deps chromium" in text

View file

@ -41,6 +41,16 @@ class TestChromiumSearchRoots:
class TestChromiumInstalled:
def test_true_when_plain_chromium_on_path(self, monkeypatch):
monkeypatch.delenv("AGENT_BROWSER_EXECUTABLE_PATH", raising=False)
monkeypatch.setattr(
bt.shutil,
"which",
lambda name: "/usr/bin/chromium" if name == "chromium" else None,
)
assert bt._chromium_installed() is True
def test_true_when_chromium_dir_present(self, monkeypatch, tmp_path):
monkeypatch.setenv("PLAYWRIGHT_BROWSERS_PATH", str(tmp_path))
(tmp_path / "chromium-1208").mkdir()
@ -108,4 +118,3 @@ class TestRunBrowserCommandChromiumGuard:
"""

View file

@ -3381,8 +3381,8 @@ def _chromium_installed() -> bool:
1. ``AGENT_BROWSER_EXECUTABLE_PATH`` env var the official way to point
agent-browser at a pre-installed Chrome/Chromium.
2. System Chrome/Chromium in PATH (``google-chrome``, ``chromium-browser``,
``chrome``).
2. System Chrome/Chromium in PATH (``google-chrome``, ``chromium``,
``chromium-browser``, ``chrome``).
3. Playwright's browser cache (current logic) — directories containing
``chromium-*`` or ``chromium_headless_shell-*``.
@ -3405,7 +3405,12 @@ def _chromium_installed() -> bool:
return True
# 2. System Chrome/Chromium in PATH (common names)
system_chrome = shutil.which("google-chrome") or shutil.which("chromium-browser") or shutil.which("chrome")
system_chrome = (
shutil.which("google-chrome")
or shutil.which("chromium")
or shutil.which("chromium-browser")
or shutil.which("chrome")
)
if system_chrome:
_cached_chromium_installed = True
return True