# Pin the Node stages to the builder's own platform ($BUILDPLATFORM). # The Angular build output is platform-independent static files, so there is # no need to run yarn/ng build under qemu emulation for linux/arm64 - doing # so is slow and memory hungry and can OOM the machine during multi-arch # builds (podman builds all --platform targets concurrently). # Declare the automatic build arguments ARG BUILDPLATFORM FROM --platform=$BUILDPLATFORM docker.io/library/node:lts-trixie-slim AS base # Install dependencies only when needed FROM base AS deps WORKDIR /app RUN apt-get update && apt-get install -y --no-install-recommends \ python3 make g++ \ && rm -rf /var/lib/apt/lists/* # Install dependencies based on the preferred package manager COPY overlord/package.json overlord/yarn.lock* overlord/package-lock.json* overlord/pnpm-lock.yaml* ./ RUN \ if [ -f yarn.lock ]; then yarn --frozen-lockfile; \ elif [ -f package-lock.json ]; then npm ci; \ elif [ -f pnpm-lock.yaml ]; then corepack enable pnpm && pnpm i --frozen-lockfile; \ else echo "Lockfile not found." && exit 1; \ fi FROM base AS builder WORKDIR /app COPY --from=deps /app/node_modules ./node_modules COPY /overlord ./ RUN \ if [ -f yarn.lock ]; then yarn run build; \ elif [ -f package-lock.json ]; then npm run build; \ elif [ -f pnpm-lock.yaml ]; then corepack enable pnpm && pnpm run build; \ else echo "Lockfile not found." && exit 1; \ fi FROM docker.io/library/python:3.14-slim AS runner LABEL maintainer="Amritanshu " RUN apt-get update \ && apt-get install -y --no-install-recommends curl locales \ && sed --in-place --expression='s/# en_IN UTF-8/en_IN UTF-8/' /etc/locale.gen \ && dpkg-reconfigure --frontend=noninteractive locales \ && rm -rf /var/lib/apt/lists/* ENV LANG=en_IN ENV LC_ALL=en_IN # Install uv # Ref: https://docs.astral.sh/uv/guides/integration/docker/#installing-uv COPY --from=ghcr.io/astral-sh/uv:latest /uv /uvx /bin/ # Compile bytecode # Ref: https://docs.astral.sh/uv/guides/integration/docker/#compiling-bytecode ENV UV_COMPILE_BYTECODE=1 # Disable development dependencies # Ref: https://docs.astral.sh/uv/guides/integration/docker/#installing-a-project ENV UV_NO_DEV=1 # uv Cache # Ref: https://docs.astral.sh/uv/guides/integration/docker/#caching ENV UV_LINK_MODE=copy WORKDIR /app/ # Place executables in the environment at the front of the path # Ref: https://docs.astral.sh/uv/guides/integration/docker/#using-the-environment ENV PATH="/app/.venv/bin:$PATH" # Install dependencies # Ref: https://docs.astral.sh/uv/guides/integration/docker/#intermediate-layers RUN --mount=type=cache,target=/root/.cache/uv \ --mount=type=bind,source=brewman/uv.lock,target=uv.lock \ --mount=type=bind,source=brewman/pyproject.toml,target=pyproject.toml \ uv sync --locked --no-install-project COPY /brewman ./ # Pass the version from the Makefile. The build context has no usable .git # (git archive stream / shallow clone lacks git metadata), so # hatch-vcs cannot discover the version itself. hatch-vcs calls # setuptools_scm without a dist name, so only the generic # SETUPTOOLS_SCM_PRETEND_VERSION is honored - the # SETUPTOOLS_SCM_PRETEND_VERSION_FOR_ variant is silently ignored. ARG SETUPTOOLS_SCM_PRETEND_VERSION ENV SETUPTOOLS_SCM_PRETEND_VERSION=$SETUPTOOLS_SCM_PRETEND_VERSION # hatch-vcs generates _version.py only inside uv's isolated wheel build, so # the source tree copied here lacks it. Write it ourselves (from the same # build arg) so importing brewman from /app works identically to the # installed wheel package in .venv. RUN printf '__version__ = version = "%s"\n' "$SETUPTOOLS_SCM_PRETEND_VERSION" > brewman/_version.py COPY --from=builder /frontend/browser /app/frontend # Sync the project # Ref: https://docs.astral.sh/uv/guides/integration/docker/#intermediate-layers RUN --mount=type=cache,target=/root/.cache/uv \ --mount=type=bind,source=brewman/uv.lock,target=uv.lock \ --mount=type=bind,source=brewman/pyproject.toml,target=pyproject.toml \ uv sync --locked --no-editable EXPOSE 80 RUN chmod 777 /app/docker-entrypoint.sh \ && ln -s /app/docker-entrypoint.sh /usr/local/bin/docker-entrypoint.sh \ && ln -s /app/docker-entrypoint.sh / ENTRYPOINT ["docker-entrypoint.sh"] # at the end of your Dockerfile, before CMD or after EXPOSE # Kill the main process if the healthcheck fails. This will kill the container and restart policy can restart it. HEALTHCHECK --interval=30s --timeout=5s --start-period=30s --retries=3 \ CMD curl -fsS http://localhost/health || kill -s 15 1 CMD ["gunicorn", "brewman.main:app", "--worker-class", "uvicorn.workers.UvicornWorker", "--config", "./gunicorn.conf.py", "--log-config", "./logging.conf"]