Spaces:
Sleeping
Sleeping
| # syntax=docker/dockerfile:1 | |
| # img2threejs β production Dockerfile for a Hugging Face Docker Space. | |
| # | |
| # Build stages: | |
| # 1. fixture β regenerate the test fixture factory through the real | |
| # vendored pipeline (strict gate -> explicit unreviewed | |
| # hosted-preview manifest -> generation). A broken forge | |
| # copy fails here. | |
| # 2. nodesmoke β npm ci (three + esbuild), bundle the fixture exactly as | |
| # the runtime does, and execute it headlessly in node | |
| # (scene-graph smoke: Group, meshes, finite bbox, | |
| # sculptRuntime). A non-executing factory fails the build. | |
| # 3. runtime β python:3.12-slim, non-root UID 1000, tini init, | |
| # uvicorn on 0.0.0.0:7860, /health HEALTHCHECK. | |
| # (esbuild is a statically-linked native binary, so the | |
| # runtime needs no node; node exists only in stage 2.) | |
| # --------------------------------------------------------------------------- | |
| FROM python:3.12-slim-bookworm AS fixture | |
| WORKDIR /build | |
| COPY forge/ forge/ | |
| COPY app/__init__.py app/__init__.py | |
| COPY app/forge_bridge.py app/forge_bridge.py | |
| COPY scripts/build_fixture_factory.py scripts/build_fixture_factory.py | |
| COPY tests/fixtures/canned_spec.json tests/fixtures/canned_spec.json | |
| RUN python3 scripts/build_fixture_factory.py /build/factory_fixture.ts | |
| # --------------------------------------------------------------------------- | |
| FROM node:22-bookworm-slim AS nodesmoke | |
| WORKDIR /build | |
| COPY package.json package-lock.json ./ | |
| RUN npm ci --omit=dev --no-audit --no-fund && npm cache clean --force | |
| COPY --from=fixture /build/factory_fixture.ts /build/factory.ts | |
| COPY app/static/viewer-core.js app/static/viewer-core.js | |
| COPY scripts/node_smoke.mjs scripts/node_smoke.mjs | |
| RUN set -e; \ | |
| factory_export=$(grep -oE 'export function create[A-Za-z0-9]+Model' factory.ts | head -1 | awk '{print $3}'); \ | |
| pascal=$(echo "$factory_export" | sed -E 's/^create//; s/Model$//'); \ | |
| printf 'export { %s as makeModel, create%sLookDevLights as makeLights } from "./factory.ts";\nexport { mountViewer } from "%s";\n' \ | |
| "$factory_export" "$pascal" "/build/app/static/viewer-core.js" > entry.js; \ | |
| node_modules/.bin/esbuild entry.js \ | |
| --bundle --format=esm --target=es2022 --minify --outfile=model.bundle.js; \ | |
| node scripts/node_smoke.mjs /build/model.bundle.js | |
| # --------------------------------------------------------------------------- | |
| FROM python:3.12-slim-bookworm AS runtime | |
| ENV PYTHONUNBUFFERED=1 \ | |
| PYTHONDONTWRITEBYTECODE=1 \ | |
| PIP_NO_CACHE_DIR=1 \ | |
| PIP_DISABLE_PIP_VERSION_CHECK=1 \ | |
| HOME=/home/user | |
| RUN apt-get update \ | |
| && apt-get install -y --no-install-recommends tini curl ca-certificates \ | |
| && rm -rf /var/lib/apt/lists/* \ | |
| && useradd -m -u 1000 user | |
| USER user | |
| WORKDIR /home/user/app | |
| COPY --chown=user requirements.txt ./ | |
| RUN pip install --no-cache-dir -r requirements.txt | |
| COPY --chown=user package.json package-lock.json ./ | |
| COPY --from=nodesmoke --chown=user /build/node_modules ./node_modules | |
| # Runtime source allowlist. Build/test/deploy helpers, fixtures, local | |
| # verification evidence and workspace state never enter the final image. | |
| COPY --chown=user:user app/ ./app/ | |
| COPY --chown=user:user forge/ ./forge/ | |
| COPY --chown=user:user LICENSE ./LICENSE | |
| EXPOSE 7860 | |
| # Local-docker convenience only; the HF runner probes app_port over HTTP. | |
| HEALTHCHECK --interval=30s --timeout=5s --start-period=60s --retries=3 \ | |
| CMD curl -fsS http://localhost:7860/health || exit 1 | |
| ENTRYPOINT ["/usr/bin/tini", "--"] | |
| CMD ["python", "-m", "app.main"] | |