# syntax=docker/dockerfile:1.4 # smartbotic-automation Package Builder # Compiles the C++ binaries + WebUI bundle, then assembles a single .deb. # # Build: # docker buildx build -f packaging/Dockerfile.build \ # --build-arg BASE_IMAGE=smartbotic-automation-build-base:debian13 \ # --build-arg BUILD_VERSION=1.0.0 \ # --target packages \ # --output "type=local,dest=dist/debian13/" . ARG BASE_IMAGE=debian:trixie FROM ${BASE_IMAGE} AS builder ARG BUILD_VERSION=0.0.0 ARG BUILD_DEB_REVISION=1 ARG BUILD_JOBS=8 ARG BUILD_GIT_COMMIT=unknown ENV DEBIAN_FRONTEND=noninteractive ENV BUILD_VERSION=${BUILD_VERSION} ENV BUILD_DEB_REVISION=${BUILD_DEB_REVISION} ENV BUILD_JOBS=${BUILD_JOBS} ENV CCACHE_DIR=/ccache ENV CCACHE_MAXSIZE=5G ENV PATH="/usr/lib/ccache:${PATH}" # Fall back to fresh dep install if the base image isn't our prebuilt one RUN if [ ! -f /etc/smartbotic-automation-build-base ]; then \ apt-get update && apt-get install -y --no-install-recommends \ build-essential cmake ninja-build git pkg-config ccache dpkg-dev \ libssl-dev zlib1g-dev libbrotli-dev uuid-dev \ protobuf-compiler libprotobuf-dev libgrpc++-dev protobuf-compiler-grpc \ nlohmann-json3-dev libspdlog-dev libfmt-dev libcurl4-openssl-dev libwebsockets-dev \ libmariadb-dev libpq-dev \ nodejs npm libsmartbotic-db-client-dev \ && rm -rf /var/lib/apt/lists/*; \ else echo "Using pre-built base: $(cat /etc/smartbotic-automation-build-base)"; fi WORKDIR /build # WebUI build (cached separately from C++) COPY webui/package.json webui/package-lock.json ./webui/ RUN --mount=type=cache,target=/npm-cache,id=smartbotic-automation-npm-cache \ cd webui && npm ci --cache /npm-cache COPY VERSION ./ COPY webui/ ./webui/ RUN cd webui && npm run build # C++ build COPY CMakeLists.txt ./ COPY cmake/ ./cmake/ COPY proto/ ./proto/ COPY lib/ ./lib/ COPY src/ ./src/ COPY config/ ./config/ COPY nodes/ ./nodes/ COPY packaging/ ./packaging/ RUN --mount=type=cache,target=/ccache,id=smartbotic-automation-ccache \ cmake -B build -G Ninja \ -DCMAKE_BUILD_TYPE=Release \ -DCMAKE_C_COMPILER_LAUNCHER=ccache \ -DCMAKE_CXX_COMPILER_LAUNCHER=ccache \ && cmake --build build --parallel ${BUILD_JOBS} \ && (ccache --show-stats || true) # ----- Package assembly stage ----- FROM builder AS packager RUN mkdir -p /packages RUN chmod +x /build/packaging/scripts/create-deb.sh \ && OUTPUT_DIR=/packages /build/packaging/scripts/create-deb.sh # ----- Final export stage ----- FROM scratch AS packages COPY --from=packager /packages/*.deb / # ----- Runtime image ----- # A slim image that runs the services, as opposed to the `packages` stage which # only emits a .deb. Built by hand rather than by installing that .deb, because # the package pulls in a chain intended for a single-machine install; here the # database is a separate container and each service must be free to run on its # own host. # # docker buildx build -f packaging/Dockerfile.build \ # --build-arg BASE_IMAGE=smartbotic-automation-build-base:debian13 \ # --build-arg REPO_PASS= \ # --target runtime -t smartbotic-automation:current . FROM debian:trixie-slim AS runtime ENV DEBIAN_FRONTEND=noninteractive ARG REPO_USER=callerai ARG REPO_PASS COPY packaging/smartbotics-repo.gpg /usr/share/keyrings/smartbotics-repo.gpg # libsmartbotic-db-client comes from the SmartBotics repo; everything else is # Debian. The credential is written and removed inside one layer so it is not # left in the image. RUN apt-get update && apt-get install -y --no-install-recommends ca-certificates \ && echo "deb [signed-by=/usr/share/keyrings/smartbotics-repo.gpg] https://repository.smartbotics.ai trixie main" \ > /etc/apt/sources.list.d/smartbotics.list \ && printf "machine repository.smartbotics.ai\nlogin %s\npassword %s\n" \ "$REPO_USER" "$REPO_PASS" > /etc/apt/auth.conf.d/smartbotics.conf \ && chmod 600 /etc/apt/auth.conf.d/smartbotics.conf \ && apt-get update \ && apt-get install -y --no-install-recommends \ libsmartbotic-db-client \ libssl3t64 libprotobuf32t64 libgrpc++1.51t64 libspdlog1.15 libfmt10 \ libuuid1 libcurl4t64 zlib1g libbrotli1 libwebsockets19t64 \ libmariadb3 libpq5 \ tzdata \ && rm -f /etc/apt/auth.conf.d/smartbotics.conf /etc/apt/sources.list.d/smartbotics.list \ && rm -rf /var/lib/apt/lists/* # Timezone data matters here: cron schedules are evaluated in the workflow's own # zone through std::chrono::locate_zone, which needs the IANA database present. # Without tzdata every zone falls back to UTC and a workflow set for 02:00 # Europe/Budapest fires at the wrong hour, silently. RUN useradd --system --create-home --home-dir /var/lib/smartbotic --shell /usr/sbin/nologin smartbotic COPY --from=builder /build/build/smartbotic-webserver /usr/bin/ COPY --from=builder /build/build/smartbotic-runner /usr/bin/ COPY --from=builder /build/webui/dist/ /usr/share/smartbotic-automation/webui/ COPY --from=builder /build/nodes/ /usr/share/smartbotic-automation/nodes/ COPY --from=builder /build/config/ /usr/share/smartbotic-automation/config/ # The working directory is what config/ and data/ resolve against. WORKDIR /var/lib/smartbotic RUN mkdir -p /var/lib/smartbotic/data /var/lib/smartbotic/config \ && cp -r /usr/share/smartbotic-automation/config/. /var/lib/smartbotic/config/ \ && ln -s /usr/share/smartbotic-automation/nodes /var/lib/smartbotic/nodes \ && chown -R smartbotic:smartbotic /var/lib/smartbotic USER smartbotic ENV WEBUI_PATH=/usr/share/smartbotic-automation/webui \ NODES_PATH=/usr/share/smartbotic-automation/nodes \ LOG_LEVEL=info # No default CMD: compose names the service to run, because one image serves # both and guessing here would start the wrong one.