FROM alpine:3.20 AS devvibecoder-secrets # Secrets: the app's .env is fetched from the platform at build time with the # deploy's token. This stage is thrown away; only .env is copied out of it. COPY .devvibecoder/handshake/ /handshake/ RUN sh /handshake/fetch_env.sh /devvibecoder-out && rm -rf /handshake FROM oven/bun:1-alpine AS frontend WORKDIR /app COPY frontend/package.json frontend/bun.loc[k] frontend/package-lock.jso[n] ./ RUN if [ -f bun.lock ]; then bun install --frozen-lockfile; else bun install; fi COPY frontend/ ./ ENV VITE_API_URL=/swatiyadav_tutoring_website_react_app_69b3012a/api ENV VITE_BASE_PATH=/swatiyadav_tutoring_website_react_app_69b3012a/ RUN bunx vite build --base=/swatiyadav_tutoring_website_react_app_69b3012a/ FROM python:3.13-slim AS backend WORKDIR /app/backend COPY --from=ghcr.io/astral-sh/uv:0.8 /uv /bin/uv COPY backend/pyproject.tom[l] backend/uv.loc[k] backend/requirements.tx[t] ./ RUN if [ -f uv.lock ]; then uv export --frozen --no-dev --no-hashes -o /tmp/req.txt && uv pip install --system --no-cache -r /tmp/req.txt; elif [ -f pyproject.toml ]; then uv pip install --system --no-cache .; else uv pip install --system --no-cache -r requirements.txt; fi RUN uv pip install --system --no-cache uvicorn COPY backend/ ./ FROM python:3.13-slim WORKDIR /app RUN apt-get update && apt-get install -y --no-install-recommends nginx supervisor wget && rm -rf /var/lib/apt/lists/* COPY --from=backend /usr/local/lib/python3.13/site-packages /usr/local/lib/python3.13/site-packages COPY --from=backend /usr/local/bin /usr/local/bin COPY --from=backend /app/backend /app/backend COPY --from=frontend /app/dist /usr/share/nginx/html COPY nginx.conf /etc/nginx/nginx.conf COPY supervisord.conf /etc/supervisord.conf COPY quit_on_failure.py /app/quit_on_failure.py RUN rm -f /etc/nginx/sites-enabled/default /etc/nginx/conf.d/default.conf # Secrets: the app's .env, fetched from the platform in the devvibecoder-secrets stage above. Nothing here is committed. COPY --from=devvibecoder-secrets /devvibecoder-out/.env /app/.env RUN rm -rf /app/.devvibecoder/handshake RUN echo '#!/bin/sh' > /app/entrypoint.sh && echo 'set -e' >> /app/entrypoint.sh && echo 'for f in /app/.env /app/backend/.env ./.env; do [ -f "$f" ] || continue; while IFS= read -r line || [ -n "$line" ]; do case "$line" in \#*) continue;; *=*) key=${line%%=*}; val=${line#*=}; case "$val" in \"*\") val=${val#\"}; val=${val%\"};; esac; export "$key=$val";; esac; done < "$f"; done' >> /app/entrypoint.sh && echo 'cd /app/backend' >> /app/entrypoint.sh && echo 'if [ ! -f main.py ]; then' >> /app/entrypoint.sh && echo ' for m in app.main src.main backend.main api.main app.api.main; do' >> /app/entrypoint.sh && echo ' p=$(echo "$m" | tr . /);' >> /app/entrypoint.sh && echo ' if [ -f "$p.py" ]; then' >> /app/entrypoint.sh && echo ' echo "from $m import app" > main.py;' >> /app/entrypoint.sh && echo ' echo "entrypoint: no main.py; re-exporting app from $m";' >> /app/entrypoint.sh && echo ' break;' >> /app/entrypoint.sh && echo ' fi;' >> /app/entrypoint.sh && echo ' done;' >> /app/entrypoint.sh && echo 'fi' >> /app/entrypoint.sh && echo 'python -c "from app.db import init_db; init_db()" 2>/dev/null || true' >> /app/entrypoint.sh && echo 'exec "$@"' >> /app/entrypoint.sh && chmod +x /app/entrypoint.sh ENV PYTHONUNBUFFERED=1 EXPOSE 8000 HEALTHCHECK --interval=30s --timeout=5s --start-period=20s --retries=3 \ CMD wget -q --spider http://127.0.0.1:8000/_nginx_health || exit 1 ENTRYPOINT ["/app/entrypoint.sh"] CMD ["supervisord", "-c", "/etc/supervisord.conf"]