File size: 2,055 Bytes
59db361
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3396ad2
 
 
 
 
59db361
 
3396ad2
 
59db361
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
# app/Dockerfile
# Build context is the je-validation project root, NOT app/ — compose sets
# `context: ..`, and the HF Space repo mirrors the same layout with this file
# copied to its root. The context's .dockerignore keeps raw snapshots out.

# Build stage: python base with uv on top; produces /app/.venv only.
FROM python:3.12-slim AS builder

COPY --from=ghcr.io/astral-sh/uv:0.5 /uv /usr/local/bin/uv

ENV UV_COMPILE_BYTECODE=1 \
    UV_LINK_MODE=copy

WORKDIR /app
COPY app/pyproject.toml app/uv.lock ./
RUN uv sync --frozen --no-dev --no-install-project
COPY app/src ./src
RUN uv sync --frozen --no-dev

# Runtime stage: plain python, no uv. The venv is copied to the same /app path
# the builder created it at, so its interpreter links and the editable install
# stay valid.
FROM python:3.12-slim

ENV PYTHONDONTWRITEBYTECODE=1 \
    PYTHONUNBUFFERED=1 \
    PATH="/app/.venv/bin:$PATH" \
    JE_DATA_DIR=/data

RUN groupadd --gid 1000 appuser \
    && useradd --uid 1000 --gid 1000 --create-home appuser

WORKDIR /app
COPY --from=builder --chown=appuser:appuser /app/.venv ./.venv
COPY --chown=appuser:appuser app/src ./src
# Spec + DS-A snapshot + recorded run history baked in so the image is
# self-contained (HF Spaces has no volumes; its disk resets on restart, so
# baked runs are the only ones that survive). Local compose shadows all of
# these with its ../docs and ../data mounts. The deploy script stages a
# curated subset of data/runs/console; local builds bake whatever is there.
COPY --chown=appuser:appuser docs/spec ./docs/spec
COPY --chown=appuser:appuser data/snapshots/ds_a /data/snapshots/ds_a
COPY --chown=appuser:appuser data/runs/console /data/runs/console
RUN chown -R appuser:appuser /data

USER appuser
EXPOSE 8000

HEALTHCHECK --interval=30s --timeout=5s --start-period=10s --retries=3 \
    CMD python -c "import urllib.request; urllib.request.urlopen('http://127.0.0.1:8000/api/health', timeout=3)"

CMD ["uvicorn", "je_validation.console.api:app", \
     "--host", "0.0.0.0", "--port", "8000", "--workers", "1"]