Files
simplex-chat/apps/simplex-support-bot-light/Dockerfile
T
shandGitHub 68916aa90a feat: add simplex-support-bot-light (#7378)
* feat: add simplex-support-bot-light

* feat: package simplex-support-bot-light for docker

* docs: document simplex-support-bot-light

* fix: make a missing config actionable and bounded

* docs: note the attached compose Ctrl+C behaviour

* refactor: use the library for startup and custom data

* refactor: drop the display-name workaround

* style: shorten the startup comment

* build: build the core and the library into the image

* docs: correct where the image type rule comes from

* fix: log what the core said a command got wrong

* build: run the container as the operator's uid

* docs: keep the container uid in .env

* docs: tighten the README
2026-08-17 09:12:47 +01:00

131 lines
5.7 KiB
Docker

# syntax=docker/dockerfile:1
#
# Built from the repository root, not this directory: the image is made from the
# Haskell core and the Python library in this tree, neither of them released.
#
# docker compose build # from apps/simplex-support-bot-light
# docker build -f apps/simplex-support-bot-light/Dockerfile .
#
# The first stage compiles libsimplex from src/. That is a full GHC build of
# simplexmq and simplex-chat: hours on a cold cache, and it needs ~15 GB.
ARG UBUNTU=24.04
# The released libs are built on 22.04; a lib built here has to load on a runtime
# with the same glibc or newer, not the other way round.
ARG UBUNTU_LIBS=22.04
ARG GHC=9.6.3
ARG CABAL=3.10.2.0
# --------------------------------------------------------------------------- #
# libsimplex — the cabal invocation of scripts/desktop/build-lib-linux.sh, which
# is what produces the .so the published libs archive is repackaged from.
# --------------------------------------------------------------------------- #
FROM ubuntu:${UBUNTU_LIBS} AS libsimplex
ARG GHC
ARG CABAL
ENV DEBIAN_FRONTEND=noninteractive
RUN apt-get update && apt-get install -y --no-install-recommends \
build-essential ca-certificates curl git libgmp3-dev libnuma-dev \
libsqlite3-dev libssl-dev llvm pkg-config zlib1g-dev && \
rm -rf /var/lib/apt/lists/*
ENV BOOTSTRAP_HASKELL_NONINTERACTIVE=1 \
BOOTSTRAP_HASKELL_GHC_VERSION=${GHC} \
BOOTSTRAP_HASKELL_CABAL_VERSION=${CABAL} \
BOOTSTRAP_HASKELL_INSTALL_NO_STACK=true \
BOOTSTRAP_HASKELL_INSTALL_NO_STACK_HOOK=true
RUN curl --proto '=https' --tlsv1.2 -sSf https://get-ghcup.haskell.org | sh
ENV PATH="/root/.ghcup/bin:/root/.cabal/bin:$PATH"
# Explicit, so the cache mount below is where cabal actually keeps its store.
ENV CABAL_DIR=/root/.cabal
WORKDIR /src
COPY cabal.project simplex-chat.cabal README.md PRIVACY.md ./
COPY scripts/cabal.project.local.linux ./cabal.project.local
COPY src ./src
# Cache mounts, not layers: the Haskell store and the build tree survive a
# source change, which is the difference between minutes and hours. The RTS and
# package libraries are copied next to libsimplex.so because its rpath is $ORIGIN.
RUN --mount=type=cache,target=/root/.cabal \
--mount=type=cache,target=/src/dist-newstyle \
set -eu; \
cabal update; \
cabal build lib:simplex-chat \
--ghc-options='-optl-Wl,-rpath,$ORIGIN -optl-Wl,-soname,libsimplex.so -flink-rts -threaded' \
--constraint 'simplexmq +client_library' \
--constraint 'simplex-chat +client_library'; \
lib=$(ls -t /src/dist-newstyle/build/*/ghc-${GHC}/simplex-chat-*/build/libHSsimplex-chat-*-inplace-ghc${GHC}.so | head -1); \
build_dir=$(dirname "$lib"); \
mv "$lib" "$build_dir/libsimplex.so"; \
mkdir -p /libs; \
ldd "$build_dir/libsimplex.so" | grep ghc | cut -d' ' -f 3 | xargs -I {} cp {} /libs/; \
cp "$build_dir/libsimplex.so" /libs/
# --------------------------------------------------------------------------- #
# the bot
# --------------------------------------------------------------------------- #
# libsimplex is a glibc build and will not load on musl, and it is compiled
# against this image's libraries in the stage above.
FROM ubuntu:${UBUNTU}
ENV DEBIAN_FRONTEND=noninteractive
RUN apt-get update && apt-get install -y --no-install-recommends \
ca-certificates curl dumb-init libffi8 libgmp10 libnuma1 && \
rm -rf /var/lib/apt/lists/*
RUN curl -LsSf https://astral.sh/uv/install.sh | sh && \
mv /root/.local/bin/uv /usr/local/bin/uv
# The ids that must own ./state on the host; a bind mount keeps host ownership.
# Build with your own to avoid needing root to read the bot's state:
# USER_UID=$(id -u) USER_GID=$(id -g) docker compose build
ARG USER_UID=1000
ARG USER_GID=1000
# ubuntu:24.04 ships a default user at 1000, so drop whoever holds the ids.
RUN if existing_user=$(getent passwd ${USER_UID} | cut -d: -f1) && [ -n "${existing_user}" ]; then \
userdel -r "${existing_user}" 2>/dev/null || userdel "${existing_user}"; \
fi && \
if existing_group=$(getent group ${USER_GID} | cut -d: -f1) && [ -n "${existing_group}" ]; then \
groupdel "${existing_group}" 2>/dev/null || true; \
fi && \
groupadd -g ${USER_GID} supportbot && \
useradd -u ${USER_UID} -g ${USER_GID} -m -d /home/supportbot supportbot
# Applies only when /data is not bind-mounted; a bind mount keeps the host
# directory's ownership and mode.
RUN mkdir -p /data && chown supportbot:supportbot /data && chmod 0700 /data
# Read by simplex_chat._native instead of downloading a release, which is what
# makes the bot run against the core built above rather than the published one.
COPY --from=libsimplex /libs /opt/simplex/libs
ENV SIMPLEX_LIBS_DIR=/opt/simplex/libs
USER supportbot
WORKDIR /home/supportbot
ENV VIRTUAL_ENV=/home/supportbot/.venv
ENV PATH="$VIRTUAL_ENV/bin:$PATH"
# The library is installed from this tree: the APIs the bot uses
# (install_signal_handlers, sync_profile, api_merge_*_custom_data) are unreleased.
COPY --chown=supportbot:supportbot packages/simplex-chat-python /home/supportbot/simplex-chat-python
RUN uv venv --python 3.12 "$VIRTUAL_ENV" && \
uv pip install /home/supportbot/simplex-chat-python
COPY --chown=supportbot:supportbot apps/simplex-support-bot-light /home/supportbot/app
RUN uv pip install -e /home/supportbot/app
ENV PYTHONUNBUFFERED=1
# No HEALTHCHECK: /health is published for an external monitor, and a container
# check would restart a bot whose chat controller is merely slow.
# Exec form: shell form would run under `sh -c`, which does not forward SIGTERM
# to the bot, so the graceful-stop path in __main__.py would never fire.
ENTRYPOINT ["dumb-init", "--", "support-bot-light", "--config", "/etc/support-bot-light/config.toml"]