diff --git a/Dockerfile b/Dockerfile index 5c57897f572e..5dcf9ab00fae 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,28 +1,39 @@ -FROM debian:13.4 +FROM ghcr.io/astral-sh/uv:0.11.6-python3.13-trixie@sha256:b3c543b6c4f23a5f2df22866bd7857e5d304b67a564f4feab6ac22044dde719b AS uv_source +FROM tianon/gosu:1.19-trixie@sha256:3b176695959c71e123eb390d427efc665eeb561b1540e82679c15e992006b8b9 AS gosu_source +FROM debian:13.4-slim # Disable Python stdout buffering to ensure logs are printed immediately ENV PYTHONUNBUFFERED=1 -# Install system dependencies in one layer, clear APT cache +# Install system dependencies in one layer, including chromium dependencies, clear APT cache RUN apt-get update && \ apt-get install -y --no-install-recommends \ - build-essential nodejs npm python3 python3-pip ripgrep ffmpeg gcc python3-dev libffi-dev procps && \ + build-essential nodejs npm python3 ripgrep ffmpeg gcc python3-dev libffi-dev git \ + libasound2t64 libatk-bridge2.0-0t64 libatk1.0-0t64 libatspi2.0-0t64 libcairo2 libcups2t64 libdbus-1-3 libdrm2 libgbm1 libglib2.0-0t64 libnspr4 libnss3 libpango-1.0-0 libx11-6 libxcb1 libxcomposite1 libxdamage1 libxext6 libxfixes3 libxkbcommon0 libxrandr2 xvfb fonts-noto-color-emoji fonts-unifont libfontconfig1 libfreetype6 xfonts-scalable fonts-liberation fonts-ipafont-gothic fonts-wqy-zenhei fonts-tlwg-loma-otf fonts-freefont-ttf && \ rm -rf /var/lib/apt/lists/* + +# user IDs over 10000 are recommended for security; this can be overridden at runtime for easier permissions handling (running under the same UID as the computer user, for instance) +RUN useradd -u 10000 -m -d /opt/data hermes -COPY . /opt/hermes +COPY --chmod=0755 --from=gosu_source /gosu /usr/local/bin/ +COPY --chmod=0755 --from=uv_source /usr/local/bin/uv /usr/local/bin/uvx /usr/local/bin/ +COPY --chown=hermes:hermes . /opt/hermes WORKDIR /opt/hermes +USER hermes + # Install Python and Node dependencies in one layer, no cache -RUN pip install --no-cache-dir uv --break-system-packages && \ - uv pip install --system --break-system-packages --no-cache -e ".[all]" && \ +RUN uv venv && \ + uv pip install --no-cache-dir -e ".[all]" && \ + uv pip install --no-cache-dir -e ".[matrix]" && \ npm install --prefer-offline --no-audit && \ - npx playwright install --with-deps chromium --only-shell && \ cd /opt/hermes/scripts/whatsapp-bridge && \ npm install --prefer-offline --no-audit && \ npm cache clean --force - -WORKDIR /opt/hermes + RUN chmod +x /opt/hermes/docker/entrypoint.sh + +USER root ENV HERMES_HOME=/opt/data VOLUME [ "/opt/data" ] diff --git a/docker/entrypoint.sh b/docker/entrypoint.sh index 68e3b79c1d16..260c2950637b 100644 --- a/docker/entrypoint.sh +++ b/docker/entrypoint.sh @@ -5,6 +5,30 @@ set -e HERMES_HOME="/opt/data" INSTALL_DIR="/opt/hermes" +if [ "$(id -u)" = "0" ]; then + if [ -n "$HERMES_UID" -a "$HERMES_UID" != "$(id -u hermes)" ]; then + echo "Changing hermes UID to $HERMES_UID" + usermod -u "$HERMES_UID" hermes + fi + + if [ -n "$HERMES_GID" -a "$HERMES_GID" != "$(id -g hermes)" ]; then + echo "Changing hermes GID to $HERMES_GID" + groupmod -g "$HERMES_GID" hermes + fi + + actual_hermes_uid=$(id -u hermes) + if [ "$(stat -c %u $HERMES_HOME)" != "$actual_hermes_uid" ]; then + echo "$HERMES_HOME is not owned by $actual_hermes_uid, fixing" + chown -R hermes:hermes $HERMES_HOME + fi + echo "Dropping root privileges" + exec gosu hermes $0 "$@" +fi + +source ${INSTALL_DIR}/.venv/bin/activate + +npx playwright install chromium --only-shell + # Create essential directory structure. Cache and platform directories # (cache/images, cache/audio, platforms/whatsapp, etc.) are created on # demand by the application — don't pre-create them here so new installs