Feat/deploy container image #48
+6
-4
@@ -54,10 +54,12 @@ FROM toolchain AS build
|
||||
COPY . .
|
||||
RUN --mount=type=cache,id=ekklesia-pnpm-store,target=/pnpm/store \
|
||||
pnpm install --frozen-lockfile --store-dir /pnpm/store
|
||||
# The same gates `pnpm verify` runs for modules and migrations, against exactly the module set in this
|
||||
# context. Each script builds first; the final build is a Turbo cache hit that keeps the intent
|
||||
# explicit.
|
||||
RUN pnpm modules:validate && pnpm migrations:validate && pnpm build
|
||||
# The same gates `pnpm verify` runs for boundaries, modules, and migrations, against exactly the
|
||||
# module set in this context. `pnpm boundaries` comes first and reads only sources: among its rules,
|
||||
# a module that imports something at run time without declaring it a production dependency fails
|
||||
# here, because the production-only install below would leave it unresolvable at API startup. Each
|
||||
# validate script builds first; the final build is a Turbo cache hit that keeps the intent explicit.
|
||||
RUN pnpm boundaries && pnpm modules:validate && pnpm migrations:validate && pnpm build
|
||||
# What the runtime resolves at startup, at the paths it resolves them from:
|
||||
# - every workspace package's `dist/` (every package exports only from `dist/`), which includes
|
||||
# the Dashboard and display host assets under `surfaces/shell/dist/browser/`;
|
||||
|
||||
+9
-1
@@ -27,7 +27,7 @@ this file.
|
||||
| `base` | `node:24.18.0-trixie-slim`, pinned by its multi-architecture index digest. Debian, so glibc, because module dependencies such as sharp ship glibc prebuilt binaries. Every stage starts here. |
|
||||
| `toolchain` | Enables pnpm through Corepack, at exactly the root `packageManager` version. |
|
||||
| `manifests` | Collects the lockfile, workspace configuration, `.npmrc`, and every workspace `package.json` at its repository path. |
|
||||
| `build` | `pnpm install --frozen-lockfile`, then `pnpm modules:validate`, `pnpm migrations:validate`, and `pnpm build`, against exactly the modules in the context. Never uses host `node_modules`: `.dockerignore` excludes them. |
|
||||
| `build` | `pnpm install --frozen-lockfile`, then `pnpm boundaries`, `pnpm modules:validate`, `pnpm migrations:validate`, and `pnpm build`, against exactly the modules in the context. Never uses host `node_modules`: `.dockerignore` excludes them. |
|
||||
| `prod-deps` | The same lockfile with `--prod`: production dependencies only. |
|
||||
| `runtime` | Production dependencies plus built outputs, owned by root and run as the unprivileged `node` user (uid 1000). No pnpm, no compiler, no curl. |
|
||||
|
||||
@@ -62,6 +62,14 @@ set `DOCKER_BUILDKIT=1` if your Docker CLI has no buildx plugin.
|
||||
No secret is a build argument, and none enters any layer. `.env` files are excluded from the build
|
||||
context entirely, and no registry credentials are needed.
|
||||
|
||||
**A module missing a production dependency fails the build, not the API.** The runtime receives
|
||||
production dependencies only, so a module that imports a package at run time but declares it only
|
||||
as a development or peer dependency would build and then fail at startup. `pnpm boundaries` runs
|
||||
in the build stage and rejects that module first
|
||||
(`runtime-imports-are-production-dependencies`, see
|
||||
[Repository boundaries](../docs/architecture/repository-boundaries.md)). The same applies to every
|
||||
other boundary rule: a module the platform's own CI would reject cannot be built into an image.
|
||||
|
||||
**A platform-only image does not exercise native module dependencies.** With no modules in the
|
||||
context, nothing like sharp is installed, so the CI image build proves nothing about them. An
|
||||
instance build that includes such a module must smoke-test the built image itself: start it,
|
||||
|
||||
Reference in New Issue
Block a user