1a88ab3199
The v0.8 milestone: installable on any Debian-based server via docker (Gitea registry image, honest sandbox degrade), deb (amd64+arm64, advisory postinst), and arch-aware binary — with release consistency enforced by pre-commit hooks + Gitea Actions (3-runner fleet on CT 113, machine-made releases with the full 8-asset matrix + registry image), the CLI carrying the full suite (guide, channel-aware doctor), and the admin runbook (deploy/README.md, evidence-backed recipes). ---ci--- phase: 6 milestone: v0.8 status: complete requirements: covered: [REQ-8-001, REQ-8-002, REQ-8-003, REQ-8-004, REQ-8-005] partial: [] ---/ci---
112 lines
4.8 KiB
Docker
112 lines
4.8 KiB
Docker
# Nextcraft — single-port docker image (v0.8 P2, REQ-8-002, R-16..R-19)
|
|
#
|
|
# Topology: uvicorn :8420 serves BOTH the API (/v1/*, /health) and the
|
|
# exported web UI (everything else) — the same single-port production
|
|
# shape as `nextcraft deploy` (D-039). Deps are pre-loaded in image
|
|
# layers; runtime is unprivileged by default (honest sandbox degrade:
|
|
# ai_service/sandbox/capability.py probes + api/sandboxes.py 503s with
|
|
# remediation — D-068/G-208).
|
|
#
|
|
# Build context is the repo root. Build args:
|
|
# NEXTCRAFT_VERSION — stamped into the embedded CLI (`--version`)
|
|
#
|
|
# Stages:
|
|
# 1. web-export — node:24-bookworm: pnpm workspace install, static
|
|
# export (NEXT_PUBLIC_AI_SERVICE_URL=self), SEA CLI binary build
|
|
# 2. venv — python:3.11-bookworm: production deps only (no [dev])
|
|
# 3. runtime — python:3.11-slim-bookworm: non-root, venv + package +
|
|
# export + CLI, /health healthcheck, state volume
|
|
|
|
# -- stage 1: web export + CLI binary ---------------------------------------
|
|
FROM node:24-bookworm AS web-export
|
|
ARG NEXTCRAFT_VERSION=0.0.0-dev
|
|
WORKDIR /repo
|
|
|
|
# Toolchain: corepack ships with node 24 (deprecated but present).
|
|
RUN corepack enable pnpm
|
|
|
|
# Workspace manifests first (layer-cacheable install).
|
|
COPY package.json pnpm-workspace.yaml pnpm-lock.yaml .npmrc tsconfig.json ./
|
|
COPY apps/web/package.json apps/web/
|
|
COPY apps/cli/package.json apps/cli/
|
|
COPY packages/types/package.json packages/types/
|
|
COPY packages/ui/package.json packages/ui/
|
|
COPY packages/mock-data/package.json packages/mock-data/
|
|
RUN pnpm install --frozen-lockfile
|
|
|
|
# Sources (web build needs all workspaces; CLI build needs its tree).
|
|
COPY apps ./apps
|
|
COPY packages ./packages
|
|
|
|
# Static export (same command as the deploy path): the export lands in
|
|
# apps/web/out and is served same-origin by uvicorn.
|
|
ENV NEXT_PUBLIC_AI_SERVICE_URL=self
|
|
RUN pnpm --filter web build
|
|
|
|
# Embedded CLI (D-066): doctor/verify/guide work in-container; the daemon
|
|
# commands refuse honestly (see apps/cli/src — NC_CONTAINER detection).
|
|
RUN pnpm --filter @nextcraft/cli build:binary "$NEXTCRAFT_VERSION"
|
|
|
|
# -- stage 2: python venv ----------------------------------------------------
|
|
FROM python:3.11-bookworm AS venv
|
|
WORKDIR /repo
|
|
# The full source tree installs the package (bootstrap.sh's mechanism
|
|
# without [dev]: production deps only — the image runs the app, it never
|
|
# lints/tests it; the runner lanes own that).
|
|
COPY apps/ai-service /repo/apps/ai-service
|
|
RUN python3 -m venv /opt/venv && \
|
|
/opt/venv/bin/pip install --no-cache-dir --upgrade pip && \
|
|
/opt/venv/bin/pip install --no-cache-dir /repo/apps/ai-service
|
|
|
|
# -- stage 3: runtime --------------------------------------------------------
|
|
FROM python:3.11-slim-bookworm AS runtime
|
|
WORKDIR /opt/nextcraft
|
|
|
|
# Non-root user; home carries the state volume (~/.nextcraft semantics —
|
|
# config.py anchors db/sandboxes under $HOME/.nextcraft, R-17).
|
|
RUN useradd --create-home --uid 10001 nextcraft
|
|
|
|
# venv from stage 2 (production deps only).
|
|
COPY --from=venv /opt/venv /opt/venv
|
|
ENV PATH=/opt/venv/bin:$PATH
|
|
|
|
# The installed ai_service package is IN the venv's site-packages; the
|
|
# source tree rides along for transparency (PYTHONPATH prefers the
|
|
# installed package).
|
|
COPY --from=venv /repo/apps/ai-service /opt/nextcraft/ai-service-src
|
|
RUN rm -rf /opt/nextcraft/ai-service-src/.venv
|
|
|
|
# Web export from stage 1.
|
|
COPY --from=web-export /repo/apps/web/out /opt/nextcraft/web-out
|
|
ENV AI_WEB_STATIC_DIR=/opt/nextcraft/web-out
|
|
|
|
# Image marker: NC_CONTAINER=1 (context for the CLI container-mode
|
|
# branches and the degrade boot notice; the capability PROBE — not this
|
|
# marker — decides degrade, D-068).
|
|
ENV NC_CONTAINER=1
|
|
|
|
# Embedded CLI (D-066).
|
|
COPY --from=web-export /repo/apps/cli/dist/nextcraft-linux-x64 /usr/local/bin/nextcraft
|
|
RUN chmod 0755 /usr/local/bin/nextcraft
|
|
|
|
# App dir + state: db/sandboxes under /home/nextcraft/.nextcraft (R-17).
|
|
# The state root is pre-created with correct ownership BEFORE the VOLUME
|
|
# declaration — an anonymous volume copies the image's directory CONTENTS
|
|
# and ownership at first use, so the app (uid 10001) can mkdir its data/
|
|
# and sandboxes/ subdirs on a clean boot (rc5 smoke-gate finding: the
|
|
# bare useradd home did not carry a writable .nextcraft).
|
|
RUN mkdir -p /home/nextcraft/.nextcraft/data \
|
|
&& chown -R nextcraft:nextcraft /opt/nextcraft /home/nextcraft/.nextcraft
|
|
WORKDIR /opt/nextcraft/ai-service-src
|
|
USER nextcraft
|
|
ENV AI_HOST=0.0.0.0 AI_PORT=8420
|
|
|
|
EXPOSE 8420
|
|
# slim has no curl — the healthcheck speaks python (stdlib urllib).
|
|
HEALTHCHECK --interval=30s --timeout=5s --start-period=15s --retries=3 \
|
|
CMD ["python3", "-c", "import urllib.request,sys; sys.exit(0 if urllib.request.urlopen('http://127.0.0.1:8420/health', timeout=4).status == 200 else 1)"]
|
|
|
|
VOLUME ["/home/nextcraft/.nextcraft"]
|
|
|
|
CMD ["python3", "-m", "uvicorn", "ai_service.main:app", "--host", "0.0.0.0", "--port", "8420"]
|