# ── Stage 1: dependency resolver ───────────────────────────────────────────── FROM python:3.11-slim AS builder # Install uv COPY --from=ghcr.io/astral-sh/uv:latest /uv /usr/local/bin/uv WORKDIR /app # Copy only dependency manifests first (cache layer) COPY pyproject.toml uv.lock* ./ # Install deps into an isolated prefix so we can copy them cleanly RUN uv sync --frozen --no-dev --no-install-project # ── Stage 2: runtime ────────────────────────────────────────────────────────── FROM python:3.11-slim AS runtime # ── Chromium + matching system ChromeDriver ─────────────────────────────────── RUN apt-get update && apt-get install -y --no-install-recommends \ ca-certificates curl chromium chromium-driver \ && rm -rf /var/lib/apt/lists/* # ── uv + venv from builder ──────────────────────────────────────────────────── COPY --from=ghcr.io/astral-sh/uv:latest /uv /usr/local/bin/uv WORKDIR /app # Make the venv's binaries the default Python ENV PATH="/app/.venv/bin:$PATH" \ PYTHONUNBUFFERED=1 \ PYTHONDONTWRITEBYTECODE=1 \ # Tell undetected-chromedriver where Chrome lives CHROME_BINARY=/usr/bin/chromium \ CHROMEDRIVER_PATH=/usr/bin/chromedriver \ # Always run headless inside Docker HEADLESS=true # ── Application code ────────────────────────────────────────────────────────── COPY . . # Copy this after the application so a host venv can never replace it. COPY --from=builder /app/.venv /app/.venv # Patch Debian's version-matched driver at build time. Runtime jobs require no # ChromeDriver network download. RUN PYTHONPATH=. python scripts/patch_driver.py && mkdir -p data logs # Non-root user for safety RUN useradd -m -u 1001 seed && chown -R seed:seed /app USER seed EXPOSE 8000 CMD ["python", "main.py", "admin"]