Spaces:
Running
Running
| # Stage 1: Frontend Builder | |
| FROM node:22-alpine AS frontend-builder | |
| WORKDIR /app/frontend | |
| COPY frontend/package*.json ./ | |
| RUN npm ci --legacy-peer-deps || npm install --legacy-peer-deps | |
| COPY frontend/ ./ | |
| # Config web Firebase (publique par design : elle est servie telle quelle dans le | |
| # bundle JS ; la sécurité repose sur les domaines autorisés + règles Firebase). | |
| # Le .dockerignore exclut les .env (remédiation secrets 2026-07-05), donc Vite ne | |
| # peut plus la lire depuis frontend/.env : sans ces build args, le bundle prod | |
| # sort sans config → auth null → tous les boutons de connexion gelés. | |
| ARG VITE_FIREBASE_API_KEY | |
| ARG VITE_FIREBASE_AUTH_DOMAIN | |
| ARG VITE_FIREBASE_PROJECT_ID | |
| ARG VITE_FIREBASE_STORAGE_BUCKET | |
| ARG VITE_FIREBASE_MESSAGING_SENDER_ID | |
| ARG VITE_FIREBASE_APP_ID | |
| ARG VITE_FIREBASE_MEASUREMENT_ID | |
| ENV VITE_FIREBASE_API_KEY=$VITE_FIREBASE_API_KEY \ | |
| VITE_FIREBASE_AUTH_DOMAIN=$VITE_FIREBASE_AUTH_DOMAIN \ | |
| VITE_FIREBASE_PROJECT_ID=$VITE_FIREBASE_PROJECT_ID \ | |
| VITE_FIREBASE_STORAGE_BUCKET=$VITE_FIREBASE_STORAGE_BUCKET \ | |
| VITE_FIREBASE_MESSAGING_SENDER_ID=$VITE_FIREBASE_MESSAGING_SENDER_ID \ | |
| VITE_FIREBASE_APP_ID=$VITE_FIREBASE_APP_ID \ | |
| VITE_FIREBASE_MEASUREMENT_ID=$VITE_FIREBASE_MEASUREMENT_ID | |
| RUN npm run build | |
| # Stage 2: Python Builder | |
| FROM python:3.12-slim-bookworm AS builder | |
| ENV PYTHONDONTWRITEBYTECODE=1 | |
| ENV PYTHONUNBUFFERED=1 | |
| # hadolint ignore=DL3008 | |
| RUN apt-get update && apt-get install -y --no-install-recommends \ | |
| build-essential=12.* \ | |
| libpq-dev=15.* \ | |
| gcc=4:12.2.* \ | |
| libsndfile1=1.2.0* \ | |
| ffmpeg=7:5.1.* \ | |
| zlib1g \ | |
| && rm -rf /var/lib/apt/lists/* | |
| WORKDIR /app | |
| RUN python -m venv /opt/venv | |
| ENV PATH="/opt/venv/bin:$PATH" | |
| # This image is CPU-only (Cloud Run web service + the 8 jobs): pre-install | |
| # torch from the PyTorch CPU index so the lockfile's `torch==` pin is already | |
| # satisfied by the +cpu wheel — the multi-GB nvidia-* CUDA wheels are never | |
| # pulled. Same trick as CI (ci.yml "Install Dependencies"). | |
| COPY requirements-web.txt . | |
| # DL3013: the version IS pinned — the command-substitution resolves to | |
| # `torch==<version>` from the lockfile; hadolint can't see through it. The | |
| # ignore must sit on the line directly above the instruction it covers. | |
| # hadolint ignore=DL3013 | |
| RUN pip install --no-cache-dir --index-url https://download.pytorch.org/whl/cpu \ | |
| "$(grep -E '^torch==' requirements-web.txt)" \ | |
| && pip install --no-cache-dir -r requirements-web.txt | |
| # Stage 3: Runtime | |
| FROM python:3.12-slim-bookworm | |
| # hadolint ignore=DL3008 | |
| RUN apt-get update && apt-get install -y --no-install-recommends \ | |
| libpq5=15.* \ | |
| curl=7.88.1* \ | |
| libsndfile1=1.2.0* \ | |
| ffmpeg=7:5.1.* \ | |
| zlib1g \ | |
| && rm -rf /var/lib/apt/lists/* | |
| # -m creates /home/appuser: gunicorn's control server writes under $HOME, and a | |
| # home-less system user (`useradd -r` without -m) made it fail at startup with | |
| # "Control server error: [Errno 13] Permission denied: '/home/appuser'". | |
| RUN groupadd -r appuser && useradd -r -m -g appuser appuser | |
| WORKDIR /app | |
| # Copy assets & venv | |
| COPY --from=builder /opt/venv /opt/venv | |
| COPY --from=frontend-builder /app/frontend/dist /app/frontend/dist | |
| ENV PATH="/opt/venv/bin:$PATH" | |
| ENV PYTHONPATH="/app:/app/backend:/app/backend/api:$PYTHONPATH" | |
| # Copy source files | |
| COPY --chown=appuser:appuser . . | |
| # Aggregate React assets with Django statics | |
| RUN python backend/api/manage.py collectstatic --noinput | |
| RUN mkdir -p data/raw data/processed data/models data/artifacts && chown -R appuser:appuser /app | |
| USER appuser | |
| EXPOSE 7860 | |
| HEALTHCHECK --interval=30s --timeout=10s --start-period=60s --retries=3 \ | |
| CMD curl -fs http://127.0.0.1:7860/health || exit 1 | |
| CMD ["supervisord", "-c", "deploy/supervisord.conf"] | |