Dockerfile.build 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151
  1. # syntax=docker/dockerfile:1.4
  2. # smartbotic-automation Package Builder
  3. # Compiles the C++ binaries + WebUI bundle, then assembles a single .deb.
  4. #
  5. # Build:
  6. # docker buildx build -f packaging/Dockerfile.build \
  7. # --build-arg BASE_IMAGE=smartbotic-automation-build-base:debian13 \
  8. # --build-arg BUILD_VERSION=1.0.0 \
  9. # --target packages \
  10. # --output "type=local,dest=dist/debian13/" .
  11. ARG BASE_IMAGE=debian:trixie
  12. FROM ${BASE_IMAGE} AS builder
  13. ARG BUILD_VERSION=0.0.0
  14. ARG BUILD_DEB_REVISION=1
  15. ARG BUILD_JOBS=8
  16. ARG BUILD_GIT_COMMIT=unknown
  17. ENV DEBIAN_FRONTEND=noninteractive
  18. ENV BUILD_VERSION=${BUILD_VERSION}
  19. ENV BUILD_DEB_REVISION=${BUILD_DEB_REVISION}
  20. ENV BUILD_JOBS=${BUILD_JOBS}
  21. ENV CCACHE_DIR=/ccache
  22. ENV CCACHE_MAXSIZE=5G
  23. ENV PATH="/usr/lib/ccache:${PATH}"
  24. # Fall back to fresh dep install if the base image isn't our prebuilt one
  25. RUN if [ ! -f /etc/smartbotic-automation-build-base ]; then \
  26. apt-get update && apt-get install -y --no-install-recommends \
  27. build-essential cmake ninja-build git pkg-config ccache dpkg-dev \
  28. libssl-dev zlib1g-dev libbrotli-dev uuid-dev \
  29. protobuf-compiler libprotobuf-dev libgrpc++-dev protobuf-compiler-grpc \
  30. nlohmann-json3-dev libspdlog-dev libfmt-dev libcurl4-openssl-dev libwebsockets-dev \
  31. libmariadb-dev libpq-dev \
  32. # Image processing: Magick++ is the image node's engine, libexif reads EXIF.
  33. libmagick++-dev libexif-dev \
  34. nodejs npm libsmartbotic-db-client-dev \
  35. && rm -rf /var/lib/apt/lists/*; \
  36. else echo "Using pre-built base: $(cat /etc/smartbotic-automation-build-base)"; fi
  37. WORKDIR /build
  38. # WebUI build (cached separately from C++)
  39. COPY webui/package.json webui/package-lock.json ./webui/
  40. RUN --mount=type=cache,target=/npm-cache,id=smartbotic-automation-npm-cache \
  41. cd webui && npm ci --cache /npm-cache
  42. COPY VERSION ./
  43. COPY webui/ ./webui/
  44. RUN cd webui && npm run build
  45. # C++ build
  46. COPY CMakeLists.txt ./
  47. COPY cmake/ ./cmake/
  48. COPY proto/ ./proto/
  49. COPY lib/ ./lib/
  50. COPY src/ ./src/
  51. COPY config/ ./config/
  52. COPY nodes/ ./nodes/
  53. COPY packaging/ ./packaging/
  54. RUN --mount=type=cache,target=/ccache,id=smartbotic-automation-ccache \
  55. cmake -B build -G Ninja \
  56. -DCMAKE_BUILD_TYPE=Release \
  57. -DCMAKE_C_COMPILER_LAUNCHER=ccache \
  58. -DCMAKE_CXX_COMPILER_LAUNCHER=ccache \
  59. && cmake --build build --parallel ${BUILD_JOBS} \
  60. && (ccache --show-stats || true)
  61. # ----- Package assembly stage -----
  62. FROM builder AS packager
  63. RUN mkdir -p /packages
  64. RUN chmod +x /build/packaging/scripts/create-deb.sh \
  65. && OUTPUT_DIR=/packages /build/packaging/scripts/create-deb.sh
  66. # ----- Final export stage -----
  67. FROM scratch AS packages
  68. COPY --from=packager /packages/*.deb /
  69. # ----- Runtime image -----
  70. # A slim image that runs the services, as opposed to the `packages` stage which
  71. # only emits a .deb. Built by hand rather than by installing that .deb, because
  72. # the package pulls in a chain intended for a single-machine install; here the
  73. # database is a separate container and each service must be free to run on its
  74. # own host.
  75. #
  76. # docker buildx build -f packaging/Dockerfile.build \
  77. # --build-arg BASE_IMAGE=smartbotic-automation-build-base:debian13 \
  78. # --build-arg REPO_PASS=<password> \
  79. # --target runtime -t smartbotic-automation:current .
  80. FROM debian:trixie-slim AS runtime
  81. ENV DEBIAN_FRONTEND=noninteractive
  82. ARG REPO_USER=callerai
  83. ARG REPO_PASS
  84. COPY packaging/smartbotics-repo.gpg /usr/share/keyrings/smartbotics-repo.gpg
  85. # libsmartbotic-db-client comes from the SmartBotics repo; everything else is
  86. # Debian. The credential is written and removed inside one layer so it is not
  87. # left in the image.
  88. RUN apt-get update && apt-get install -y --no-install-recommends ca-certificates \
  89. && echo "deb [signed-by=/usr/share/keyrings/smartbotics-repo.gpg] https://repository.smartbotics.ai trixie main" \
  90. > /etc/apt/sources.list.d/smartbotics.list \
  91. && printf "machine repository.smartbotics.ai\nlogin %s\npassword %s\n" \
  92. "$REPO_USER" "$REPO_PASS" > /etc/apt/auth.conf.d/smartbotics.conf \
  93. && chmod 600 /etc/apt/auth.conf.d/smartbotics.conf \
  94. && apt-get update \
  95. && apt-get install -y --no-install-recommends \
  96. libsmartbotic-db-client \
  97. libssl3t64 libprotobuf32t64 libgrpc++1.51t64 libspdlog1.15 libfmt10 \
  98. libuuid1 libcurl4t64 zlib1g libbrotli1 libwebsockets19t64 \
  99. libmariadb3 libpq5 \
  100. # Magick++ and libexif, the image node's engine. The runtime libraries
  101. # only - the node calls them in-process, so none of ImageMagick's
  102. # command-line tools are wanted here.
  103. libmagick++-7.q16-5 libexif12 \
  104. # Fonts, because a text watermark asks Magick for a font by name and
  105. # gets nothing to render with in an image that has none.
  106. fonts-dejavu-core \
  107. tzdata \
  108. && rm -f /etc/apt/auth.conf.d/smartbotics.conf /etc/apt/sources.list.d/smartbotics.list \
  109. && rm -rf /var/lib/apt/lists/*
  110. # Timezone data matters here: cron schedules are evaluated in the workflow's own
  111. # zone through std::chrono::locate_zone, which needs the IANA database present.
  112. # Without tzdata every zone falls back to UTC and a workflow set for 02:00
  113. # Europe/Budapest fires at the wrong hour, silently.
  114. RUN useradd --system --create-home --home-dir /var/lib/smartbotic --shell /usr/sbin/nologin smartbotic
  115. COPY --from=builder /build/build/smartbotic-webserver /usr/bin/
  116. COPY --from=builder /build/build/smartbotic-runner /usr/bin/
  117. COPY --from=builder /build/webui/dist/ /usr/share/smartbotic-automation/webui/
  118. COPY --from=builder /build/nodes/ /usr/share/smartbotic-automation/nodes/
  119. COPY --from=builder /build/config/ /usr/share/smartbotic-automation/config/
  120. # The working directory is what config/ and data/ resolve against.
  121. WORKDIR /var/lib/smartbotic
  122. RUN mkdir -p /var/lib/smartbotic/data /var/lib/smartbotic/config \
  123. && cp -r /usr/share/smartbotic-automation/config/. /var/lib/smartbotic/config/ \
  124. && ln -s /usr/share/smartbotic-automation/nodes /var/lib/smartbotic/nodes \
  125. && chown -R smartbotic:smartbotic /var/lib/smartbotic
  126. USER smartbotic
  127. ENV WEBUI_PATH=/usr/share/smartbotic-automation/webui \
  128. NODES_PATH=/usr/share/smartbotic-automation/nodes \
  129. LOG_LEVEL=info
  130. # No default CMD: compose names the service to run, because one image serves
  131. # both and guessing here would start the wrong one.