# ── Build stage ────────────────────────────────────────────────────── FROM python:3.12-slim AS builder WORKDIR /build RUN apt-get update && apt-get install -y --no-install-recommends \ gcc libpq-dev curl \ && rm -rf /var/lib/apt/lists/* # Copy pyproject.toml for both api and scanner COPY apps/api/pyproject.toml ./api/pyproject.toml COPY apps/scanner/pyproject.toml ./scanner/pyproject.toml # Install API dependencies RUN pip install --no-cache-dir --prefix=/install api/. # Install Scanner dependencies (Playwright + Chromium) # PYTHONPATH needed because --prefix=/install doesn't auto-set site-packages path RUN pip install --no-cache-dir --prefix=/install scanner/. \ && PYTHONPATH=/install/lib/python3.12/site-packages \ /install/bin/playwright install chromium --with-deps # ── Runtime stage ──────────────────────────────────────────────────── FROM python:3.12-slim WORKDIR /app RUN apt-get update && apt-get install -y --no-install-recommends \ libpq5 curl tini supervisor \ && rm -rf /var/lib/apt/lists/* \ && apt-get clean # Copy installed dependencies from builder COPY --from=builder /install /usr/local # Copy application code COPY apps/api/src ./src COPY apps/scanner/src ./src_scanner COPY supervisord.conf /etc/supervisord.conf # Move scanner source into api structure RUN if [ -d src_scanner ]; then \ cp -r src_scanner/* src/ 2>/dev/null || true; \ fi # Healthcheck for API HEALTHCHECK --interval=30s --timeout=5s --start-period=30s --retries=3 \ CMD curl -f http://localhost:8000/health || exit 1 # Use tini as init system for proper signal handling ENTRYPOINT ["/usr/bin/tini", "--"] # supervisord manages multiple processes CMD ["supervisord", "-c", "/etc/supervisord.conf"]