From edaa9a05001546a6869b18182d2a748272360b45 Mon Sep 17 00:00:00 2001 From: admincsc Date: Tue, 5 May 2026 05:15:42 +0000 Subject: [PATCH] feat: enhance Docker setup and startup script for Hermes WebUI - Added git and xz-utils to Dockerfile for improved build capabilities. - Updated docker-compose.yml to specify build context and Dockerfile location. - Enhanced start.sh to handle environment variable parsing more robustly and set Python executable path. --- Dockerfile | 48 +++++++++++++++++++++++++++++++++------------- docker-compose.yml | 4 +++- start.sh | 47 +++++++++++++++++++++++++++++++++++++++++---- 3 files changed, 81 insertions(+), 18 deletions(-) diff --git a/Dockerfile b/Dockerfile index 26d980228e..6b86138e6b 100644 --- a/Dockerfile +++ b/Dockerfile @@ -25,6 +25,8 @@ RUN apt-get update -y --fix-missing --no-install-recommends \ curl \ rsync \ openssh-client \ + git \ + xz-utils \ && apt-get upgrade -y \ && apt-get clean \ && rm -rf /var/lib/apt/lists/* @@ -45,11 +47,11 @@ WORKDIR /apptoo RUN echo '%sudo ALL=(ALL) NOPASSWD:ALL' >> /etc/sudoers # Create a new group for the hermeswebui and hermeswebuitoo users -RUN groupadd -g 1024 hermeswebui \ +RUN groupadd -g 1024 hermeswebui \ && groupadd -g 1025 hermeswebuitoo -# The hermeswebui (resp. hermeswebuitoo) user will have UID 1024 (resp. 1025), -# be part of the hermeswebui (resp. hermeswebuitoo) and users groups and be sudo capable (passwordless) +# The hermeswebui (resp. hermeswebuitoo) user will have UID 1024 (resp. 1025), +# be part of the hermeswebui (resp. hermeswebuitoo) and users groups and be sudo capable (passwordless) RUN useradd -u 1024 -d /home/hermeswebui -g hermeswebui -s /bin/bash -m hermeswebui \ && usermod -G users hermeswebui \ && adduser hermeswebui sudo @@ -60,24 +62,45 @@ RUN chown -R hermeswebuitoo:hermeswebuitoo /apptoo USER root -COPY --chmod=555 docker_init.bash /hermeswebui_init.bash +# Pre-install uv system-wide so the container doesn't need internet access at runtime. +# Installing as root places uv in /usr/local/bin, available to all users. +# The init script will skip the download when uv is already on PATH. +RUN curl -LsSf https://astral.sh/uv/install.sh | env UV_INSTALL_DIR=/usr/local/bin sh +# Install Node.js 22 LTS (for browser tools) +RUN ARCH=$(uname -m) && \ + if [ "$ARCH" = "x86_64" ]; then NODE_ARCH="x64"; \ + elif [ "$ARCH" = "aarch64" ] || [ "$ARCH" = "arm64" ]; then NODE_ARCH="arm64"; \ + else echo "Unsupported architecture: $ARCH" && exit 1; fi && \ + INDEX_URL="https://nodejs.org/dist/latest-v22.x/" && \ + TARBALL=$(curl -fsSL "$INDEX_URL" | grep -oE "node-v22\.[0-9]+\.[0-9]+-linux-${NODE_ARCH}\.tar\.xz" | head -1) && \ + [ -n "$TARBALL" ] || { echo "Could not find Node.js tarball"; exit 1; } && \ + curl -fsSL "${INDEX_URL}${TARBALL}" -o /tmp/node.tar.xz && \ + tar -C /usr/local --strip-components=1 -xJf /tmp/node.tar.xz && \ + rm /tmp/node.tar.xz && \ + node --version && npm --version + +# Pre-bake the Hermes Agent source into the image to avoid runtime network installs. +# Use /opt/hermes which is not affected by the mounted .hermes volume. +COPY hermes-agent-desktop/hermes-agent /opt/hermes/ + +# Tell the WebUI where to find the agent (used by bootstrap if manually invoked) +ENV HERMES_WEBUI_AGENT_DIR=/opt/hermes + +# Copy the init script (must be before using it as CMD) +COPY --chmod=555 hermes-webui/docker_init.bash /hermeswebui_init.bash + +# Marker that we're inside a container RUN touch /.within_container # Remove APT proxy configuration and clean up APT downloaded files RUN rm -rf /var/lib/apt/lists/* /etc/apt/apt.conf.d/01proxy \ && apt-get clean -USER root - -# Pre-install uv system-wide so the container doesn't need internet access at runtime. -# Installing as root places uv in /usr/local/bin, available to all users. -# The init script will skip the download when uv is already on PATH. -RUN curl -LsSf https://astral.sh/uv/install.sh | env UV_INSTALL_DIR=/usr/local/bin sh - USER hermeswebuitoo -COPY --chown=hermeswebuitoo:hermeswebuitoo . /apptoo +# Copy the WebUI application code +COPY --chown=hermeswebuitoo:hermeswebuitoo hermes-webui/ /apptoo/ # Bake the git version tag into the image so the settings badge works even # when .git is not present (it is excluded by .dockerignore). @@ -96,4 +119,3 @@ HEALTHCHECK --interval=30s --timeout=5s --start-period=10s --retries=3 \ CMD curl -f http://localhost:8787/health || exit 1 CMD ["/hermeswebui_init.bash"] - diff --git a/docker-compose.yml b/docker-compose.yml index 3428ba4a97..4f749d5b35 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -13,7 +13,9 @@ services: hermes-webui: - build: . + build: + context: .. + dockerfile: hermes-webui/Dockerfile ports: # select only one; use 127.0.0.1 version to expose to localhost only - "127.0.0.1:8787:8787" diff --git a/start.sh b/start.sh index 59e0d65a73..9cb6004993 100755 --- a/start.sh +++ b/start.sh @@ -1,13 +1,52 @@ #!/usr/bin/env bash set -euo pipefail +# If invoked as root (e.g., via sudo), re-exec as hermeswebui to avoid +# permission issues with bind-mounted .hermes directory. +if [[ $EUID -eq 0 ]]; then + exec sudo -n -u hermeswebui "$0" "$@" +fi + REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +# When running inside the pre-built Docker container, use the pre-created venv +if [[ -f "/.within_container" && -x "/app/venv/bin/python" ]]; then + export HERMES_WEBUI_PYTHON="/app/venv/bin/python" + exec "/app/venv/bin/python" "${REPO_ROOT}/bootstrap.py" --no-browser "$@" +fi + if [[ -f "${REPO_ROOT}/.env" ]]; then - set -a - # shellcheck source=/dev/null - source "${REPO_ROOT}/.env" - set +a + # Parse .env manually to avoid assigning to readonly vars (UID, GID, etc.) + # This mirrors bootstrap.py's _load_repo_dotenv() logic + while IFS= read -r raw_line || [[ -n "$raw_line" ]]; do + # Strip leading/trailing whitespace + line="${raw_line#"${raw_line%%[![:space:]]*}"}" + line="${line%"${line##*[![:space:]]}"}" + # Skip empty lines and comments + [[ -z "$line" || "$line" == \#* ]] && continue + # Split on first '=' + [[ "$line" == *"="* ]] || continue + key="${line%%=*}" + value="${line#*=}" + key="${key%"${key##*[![:space:]]}"}" + key="${key#"${key%%[![:space:]]*}"}" + # Skip readonly shell variables + case "$key" in + UID|GID|EUID|EGID|PPID|PID|_) continue ;; + esac + # Strip optional 'export' prefix and surrounding quotes + if [[ "$key" == export* ]]; then + key="${key#export}" + key="${key#"${key%%[![:space:]]*}"}" + fi + value="${value%"${value##*[![:space:]]}"}" + value="${value#"${value%%[![:space:]]*}"}" + value="${value%\"}" + value="${value#\"}" + value="${value%\'}" + value="${value#\'}" + export "$key=$value" + done < "${REPO_ROOT}/.env" fi PYTHON="${HERMES_WEBUI_PYTHON:-}"