# Praxis v0.2 — Multi-stage Docker image # Stage 1: build the React client (client/dist) # Stage 2: Python server + serve client/dist via FastAPI StaticFiles # # Per RESEARCH.md Q4 / ARCHITECTURE.md §Image Build Pipeline. # Debian-slim (not Alpine) — glibc for numpy/pipecat native extensions. # ── Stage 1: client builder ────────────────────────────────────────── FROM node:22-slim AS client-builder WORKDIR /app/client # Copy manifest first for layer caching (deps change less often than source). COPY client/package.json client/package-lock.json ./ RUN npm ci # Copy client source and build. COPY client/ ./ RUN npm run build # → produces /app/client/dist/ # ── Stage 2: server ────────────────────────────────────────────────── FROM python:3.12-slim AS server WORKDIR /app # Build tools for any source-compilation fallback (numpy/aiohttp wheels # should exist for cp312/linux-amd64, but gcc/g++ + libasound2-dev cover # the R-DEPLOY-01 risk per RESEARCH.md Q4). RUN apt-get update -qq && \ apt-get install -y --no-install-recommends -qq gcc g++ libasound2-dev && \ rm -rf /var/lib/apt/lists/* # Install Python deps before copying source (layer caching). # G-105 FIX: copy pyproject.toml + README.md first, then pip install, # THEN copy source — so deps are cached and source changes don't # invalidate the pip layer. COPY pyproject.toml README.md ./ RUN pip install --no-cache-dir . # Copy server source + scenarios + db modules. COPY server/ ./server/ COPY scenarios/ ./scenarios/ COPY db/ ./db/ # Copy the built client dist from Stage 1. COPY --from=client-builder /app/client/dist ./client/dist # Data directory for SQLite (mounted as a volume in docker-compose.yml). RUN mkdir -p /app/data VOLUME ["/app/data"] EXPOSE 8789 # Run the FastAPI server via the existing entrypoint. CMD ["python", "-m", "server"]