# syntax=docker/dockerfile:1 # check=skip=InvalidDefaultArgInFrom ARG PYTHON_VERSION ARG PYTHON_VERSION_SHORT ARG RUNNER # =========================================== # Stage 1: Builder # =========================================== FROM python:${PYTHON_VERSION}-slim-bookworm AS builder # Re-declare ARGs after FROM to make them available in this stage ARG RUNNER # STFU - No need - Write logs asap ENV DEBIAN_FRONTEND=noninteractive \ PYTHONDONTWRITEBYTECODE=1 \ PYTHONUNBUFFERED=1 # Install build dependencies (needs root) RUN apt-get update && apt-get install -y --no-install-recommends \ build-essential \ && rm -rf /var/lib/apt/lists/* # Install runner globally (needs root) - Save cache for future RUN --mount=type=cache,target=/root/.cache/pip \ pip install $RUNNER # Set working directory for dependency installation WORKDIR /app # Copy dependency files COPY pyproject.toml poetry.lock* uv.lock* Makefile ./ # Install dependencies as root (to avoid permission issues with system packages) RUN --mount=type=cache,target=/root/.cache/pip \ --mount=type=cache,target=/root/.cache/pypoetry \ --mount=type=cache,target=/root/.cache/uv \ if [ "$RUNNER" = "poetry" ]; then \ poetry config virtualenvs.create false && \ poetry install --only main --no-root; \ elif [ "$RUNNER" = "uv" ]; then \ uv pip install --system -r pyproject.toml; \ fi COPY alfred/ ./alfred/ COPY scripts/ ./scripts/ COPY .env.example ./ COPY settings.toml ./ # =========================================== # Stage 2: Testing # =========================================== FROM builder AS test ARG RUNNER RUN --mount=type=cache,target=/root/.cache/pip \ --mount=type=cache,target=/root/.cache/pypoetry \ --mount=type=cache,target=/root/.cache/uv \ if [ "$RUNNER" = "poetry" ]; then \ poetry install --no-root; \ elif [ "$RUNNER" = "uv" ]; then \ uv pip install --system -e .[dev]; \ fi COPY tests/ ./tests # =========================================== # Stage 3: Runtime # =========================================== FROM python:${PYTHON_VERSION}-slim-bookworm AS runtime ARG PYTHON_VERSION_SHORT # TODO: A-t-on encore besoin de toutes les clés ? ENV LLM_PROVIDER=deepseek \ MEMORY_STORAGE_DIR=/data/memory \ PYTHONDONTWRITEBYTECODE=1 \ PYTHONPATH=/home/appuser \ PYTHONUNBUFFERED=1 # Install runtime dependencies (needs root) RUN apt-get update && apt-get install -y --no-install-recommends \ ca-certificates \ && rm -rf /var/lib/apt/lists/* \ && apt-get clean # Create non-root user RUN useradd -m -u 1000 -s /bin/bash appuser # Create data directories (needs root for /data) RUN mkdir -p /data /logs \ && chown -R appuser:appuser /data /logs # Switch to non-root user USER appuser # Set working directory (owned by appuser) WORKDIR /home/appuser # Copy Python packages from builder stage COPY --from=builder /usr/local/lib/python${PYTHON_VERSION_SHORT}/site-packages /usr/local/lib/python${PYTHON_VERSION_SHORT}/site-packages COPY --from=builder /usr/local/bin /usr/local/bin # Copy application code (already owned by appuser) COPY --chown=appuser:appuser alfred/ ./alfred COPY --chown=appuser:appuser scripts/ ./scripts COPY --chown=appuser:appuser .env.example ./ COPY --chown=appuser:appuser pyproject.toml ./ COPY --chown=appuser:appuser settings.toml ./ # Create volumes for persistent data VOLUME ["/data", "/logs"] # Expose port EXPOSE 8000 # Health check 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"]