# syntax=docker/dockerfile:1.20 ARG PY_IMAGE=python:3.13-alpine3.22 # Stage 1: Builder FROM ${PY_IMAGE} AS builder COPY --from=ghcr.io/astral-sh/uv:latest /uv /uvx /bin/ WORKDIR /app ENV PYTHONDONTWRITEBYTECODE=1 \ PYTHONUNBUFFERED=1 \ PYTHONOPTIMIZE=2 \ UV_COMPILE_BYTECODE=1 \ UV_LINK_MODE=copy \ UV_PROJECT_ENVIRONMENT=/opt/venv COPY pyproject.toml uv.lock ./ RUN uv sync --frozen --no-dev --no-cache # Stage 2: Runtime FROM ${PY_IMAGE} AS runtime ENV PYTHONDONTWRITEBYTECODE=1 \ PYTHONUNBUFFERED=1 \ PYTHONOPTIMIZE=2 \ PATH="/opt/venv/bin:$PATH" \ PYTHONPATH="/app:$PYTHONPATH" WORKDIR /app RUN apk add --no-cache --virtual .runtime-deps \ curl && \ rm -rf /var/cache/apk/* RUN adduser -D -g '' app && chown -R app:app /app USER app COPY --from=builder --chown=app:app /opt/venv /opt/venv COPY --chown=app:app ./src/ ./ EXPOSE 8080 HEALTHCHECK --interval=30s --timeout=5s --start-period=5s --retries=3 \ CMD curl -fsS http://localhost:8080/healthcheck || exit 1 CMD [ "web_api_cli", "/app/config.toml" ] # Stage 3: Testing FROM builder AS tests ENV PYTHONDONTWRITEBYTECODE=1 \ PYTHONUNBUFFERED=1 \ PYTHONOPTIMIZE=2 \ PATH="/opt/venv/bin:$PATH" \ PYTHONPATH="/app:$PYTHONPATH" WORKDIR /app COPY ./src ./src COPY ./tests ./tests RUN uv sync --group tests --frozen --no-cache RUN mkdir -p /app/cov RUN mkdir /app/cov/html CMD [ "sh", "-c", "coverage run --source=src -m pytest -v && coverage report > /app/cov/coverage.txt && coverage xml -o /app/cov/coverage.xml && coverage html -d /app/cov/html" ] # Stage 4: Migrations FROM builder AS migrations ENV PYTHONDONTWRITEBYTECODE=1 \ PYTHONUNBUFFERED=1 \ PYTHONOPTIMIZE=2 \ PATH="/opt/venv/bin:$PATH" \ PYTHONPATH="/app:$PYTHONPATH" WORKDIR /app RUN mkdir -p ./src/template_project COPY ./src/template_project ./src/template_project COPY ./alembic.ini ./ RUN uv sync --group migrations --frozen --no-cache CMD [ "alembic", "upgrade", "head" ]