# syntax=docker/dockerfile:1 # check=skip=InvalidDefaultArgInFrom ARG PYTHON_VERSION ARG UV_VERSION # Stage 0: uv binary (workaround — --from doesn't support ARG expansion) FROM ghcr.io/astral-sh/uv:${UV_VERSION} AS uv-bin # =========================================== # Stage 1: Builder # =========================================== FROM python:${PYTHON_VERSION}-slim-bookworm AS builder ENV DEBIAN_FRONTEND=noninteractive \ PYTHONDONTWRITEBYTECODE=1 \ PYTHONUNBUFFERED=1 \ UV_PROJECT_ENVIRONMENT=/venv # Install build dependencies RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \ --mount=type=cache,target=/var/lib/apt,sharing=locked \ apt-get update \ && apt-get install -y --no-install-recommends build-essential # Install uv globally COPY --from=uv-bin /uv /usr/local/bin/uv WORKDIR /tmp COPY pyproject.toml uv.lock Makefile ./ # Install dependencies into /venv RUN --mount=type=cache,target=/root/.cache/uv uv sync COPY scripts/ ./scripts/ COPY .env.example ./ # =========================================== # Stage 2: Testing # =========================================== FROM builder AS test RUN --mount=type=cache,target=/root/.cache/uv uv sync --group dev COPY alfred/ ./alfred COPY scripts ./scripts COPY tests/ ./tests # =========================================== # Stage 3: Runtime # =========================================== FROM python:${PYTHON_VERSION}-slim-bookworm AS runtime ENV PYTHONDONTWRITEBYTECODE=1 \ PYTHONUNBUFFERED=1 \ PYTHONPATH=/home/appuser \ PATH="/venv/bin:$PATH" # Install runtime dependencies RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \ --mount=type=cache,target=/var/lib/apt,sharing=locked \ apt-get update \ && apt-get install -y --no-install-recommends ca-certificates # Create non-root user RUN useradd -m -u 1000 -s /bin/bash appuser # Create data directories RUN mkdir -p /data /logs \ && chown -R appuser:appuser /data /logs USER appuser WORKDIR /home/appuser # Copy venv from builder stage COPY --from=builder /venv /venv # Copy application code COPY --chown=appuser:appuser alfred/ ./alfred COPY --chown=appuser:appuser scripts/ ./scripts COPY --chown=appuser:appuser .env.example ./ COPY --chown=appuser:appuser pyproject.toml ./ VOLUME ["/data", "/logs"] EXPOSE 8000 HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \ CMD python -c "import requests; requests.get('http://localhost:8000/health', timeout=5).raise_for_status()" || exit 1 CMD ["python", "-m", "uvicorn", "alfred.app:app", "--host", "0.0.0.0", "--port", "8000"]