-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathDockerfile
More file actions
104 lines (86 loc) · 4.29 KB
/
Copy pathDockerfile
File metadata and controls
104 lines (86 loc) · 4.29 KB
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
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
# syntax=docker/dockerfile:1.7
# Default image for the SimpleModule reference app — the host plus the bundled
# modules that a single web process needs (auth/users, dashboard, permissions,
# settings, file storage, feature flags, audit log, branding, site lock).
#
# Background tasks are deliberately not part of it: Celery is a second process
# plus a broker, which is the opposite of a standalone image. The worker/beat
# services in docker-compose.yml build docker/worker.Dockerfile for that.
#
# docker build -t simple-module-python .
# docker run --rm -p 8000:8000 simple-module-python
#
# Standalone by design: SQLite under /app/data, no Postgres and no Redis
# needed to boot. `docker-compose.yml`'s `app` service is the same image with
# a named volume; worker/beat (Celery) stay opt-in.
#
# One builder stage carries both uv and Node because the Vite build imports
# `modules.generated.{ts,css}`, which `smpy host gen-pages` emits from the
# *installed Python modules* — a Node-only stage would have nothing to read.
FROM ghcr.io/astral-sh/uv:python3.12-bookworm AS builder
ENV UV_LINK_MODE=copy \
UV_COMPILE_BYTECODE=1 \
PYTHONUNBUFFERED=1
WORKDIR /app
# Node 24 — same major as NODE_VERSION in .github/workflows/pr.yml, so the
# image builds the bundle CI validates.
RUN curl -fsSL https://deb.nodesource.com/setup_24.x | bash - \
&& apt-get install -y --no-install-recommends nodejs \
&& rm -rf /var/lib/apt/lists/*
# Dependency layer: every workspace member's manifest, resolved before the
# full source arrives. `uv.lock` is gitignored in this repo, so it's an
# optional glob and the sync deliberately isn't `--frozen`.
COPY pyproject.toml uv.lock* ./
COPY framework/ framework/
COPY modules/ modules/
COPY host/pyproject.toml host/
RUN --mount=type=cache,target=/root/.cache/uv \
uv sync --all-packages --no-dev --no-install-workspace
# npm workspaces span host/client_app, packages/* and modules/* — every
# member's package.json must exist before `npm ci` will honour the lockfile.
COPY package.json package-lock.json ./
COPY packages/ packages/
COPY host/client_app/package.json host/client_app/
RUN --mount=type=cache,target=/root/.npm npm ci
# --no-install-package drops the Celery module from this image: with no entry
# point installed, discovery never sees it, so nothing here needs a broker and
# the bundle carries none of its pages. Drop the flag (and point
# SM_BG_TASKS_BROKER_URL at a real Redis) to run tasks from the web process.
COPY . .
RUN --mount=type=cache,target=/root/.cache/uv \
uv sync --all-packages --no-dev --no-install-package simple-module-background-tasks
# Page manifest + generated module imports first, then the production bundle
# into host/static/dist (with its .vite/manifest.json and precompressed
# .gz/.br siblings, which the host serves from the /static mount).
# The venv binary directly rather than `uv run`, which re-resolves and re-syncs
# the environment on every invocation — the layer above already installed
# exactly what this image should contain.
RUN /app/.venv/bin/smpy host gen-pages --host-dir=host/client_app
RUN npm run build
# node_modules is a build-time artifact only; the runtime serves static files.
RUN rm -rf node_modules host/client_app/node_modules
FROM python:3.12-slim-bookworm AS runtime
ENV PYTHONUNBUFFERED=1 \
PYTHONDONTWRITEBYTECODE=1 \
PATH="/app/.venv/bin:$PATH"
# Containers serve the built bundle; development mode would emit asset tags
# pointing at a Vite dev server that isn't in this image.
ENV SM_ENVIRONMENT=production
# Absolute sqlite path: /app/data is the volume mount point, so the DB is
# cwd-independent and survives restarts whenever a volume is attached.
ENV SM_DATABASE_URL=sqlite+aiosqlite:////app/data/app.db
# curl backs the HEALTHCHECK below.
RUN apt-get update \
&& apt-get install -y --no-install-recommends curl ca-certificates \
&& rm -rf /var/lib/apt/lists/*
COPY --from=builder /app /app
RUN mkdir -p /app/data \
&& useradd --system --uid 10001 --home /app --shell /usr/sbin/nologin app \
&& chown -R app:app /app
USER app
WORKDIR /app
EXPOSE 8000
HEALTHCHECK --interval=30s --timeout=5s --start-period=20s --retries=3 \
CMD curl -fsS http://localhost:8000/health || exit 1
ENTRYPOINT ["/app/docker/entrypoint.sh"]
CMD ["uvicorn", "host.main:app", "--host", "0.0.0.0", "--port", "8000"]