CI / verify (pull_request) Failing after 47s
CI / infrastructure-smoke (pull_request) Skipped
CI / migration-smoke (pull_request) Skipped
CI / image-build (pull_request) Skipped
CI / deploy-smoke (same-origin) (pull_request) Skipped
CI / deploy-smoke (split) (pull_request) Skipped
The production image now contains zero feature modules and is the same for every installation. Installed modules live in module storage that one Core resolver selects from EKKLESIA_MODULES_DIR: the checkout's modules/ when unset, an absolute path that must exist when set (/var/lib/ekklesia/modules in the image). A relative, blank, missing, or unreadable configured directory fails closed with a sanitized error that never contains the value. - Host configuration reads EKKLESIA_MODULES_DIR. The API, migration plan, apply and validate, module validation, module enablement, and Doctor all resolve it the same way; the test-only API override still takes precedence. - Doctor receives Core's migrations directory explicitly instead of deriving it from the modules directory's parent. - The migration runner retains and reports the applied history of a module that is not installed, and plans and applies Core's and present modules' migrations around it. Core's history and every installed module's history stay strict: a missing or changed applied migration, or a downgrade, still fails. No down migrations, no data deletion. - deploy/Dockerfile copies no module and installs nothing for the module template; .dockerignore excludes modules/ and the build refuses a context that contains it; the runtime has an empty root-owned /var/lib/ekklesia/modules and sets EKKLESIA_MODULES_DIR. - deploy/compose.yaml bind-mounts EKKLESIA_MODULES_HOST_DIR read-only into api and migrate, with create_host_path disabled. - scripts/check-image.sh proves the image holds no modules and that a populated modules/ directory leaves its runtime file system identical; CI runs it. - The split deploy smoke now uses the same generic image: it boots with no modules, installs a self-contained test module packaged from the template (test-only), migrates and restarts, removes it (Core runs, planning reports the retained history, data survives), and restores it with its data. Docs: deploy README (module storage, install/update/remove/restore, the runtime modules target), .env.example, API configuration, module development guide, testing, workflow, security, and the architecture overview. Co-Authored-By: Claude Opus 5.5 <[email protected]>
51 lines
2.8 KiB
Docker
51 lines
2.8 KiB
Docker
# TEST-ONLY. Builds the repository's module template as one self-contained installed-module directory
|
|
# for scripts/smoke-deploy.sh, which installs it into a deployment's module storage (ADR 0048). It is
|
|
# not a module package format and defines none: the archive format, signing, and transport of real
|
|
# module packages are deferred. No image is produced; the build's only output is the directory:
|
|
#
|
|
# DOCKER_BUILDKIT=1 docker build --file scripts/lib/test-module-package.Dockerfile \
|
|
# --output type=local,dest=<directory> .
|
|
#
|
|
# writes <directory>/example: the module's package.json, manifest, built artifacts, migrations, and a
|
|
# node_modules holding exactly its production dependencies, including its own copies of the Module
|
|
# SDK, the contracts, and Drizzle. Nothing in it resolves to anything outside it, because the
|
|
# platform lends an installed module nothing.
|
|
#
|
|
# The same pinned Node.js base as deploy/Dockerfile, so the package targets the runtime it is
|
|
# installed into. Drizzle comes from the platform's own lockfile, which is what keeps it the exact
|
|
# version the platform uses: a constraint of the current implementation (ADR 0048), not a contract.
|
|
FROM node:24.18.0-trixie-slim@sha256:ae91dcc111a68c9d2d81ff2a17bda61be126426176fde6fe7d08ab13b7f50573 AS build
|
|
ENV COREPACK_ENABLE_DOWNLOAD_PROMPT=0 \
|
|
PNPM_HOME=/pnpm \
|
|
TURBO_TELEMETRY_DISABLED=1 \
|
|
DO_NOT_TRACK=1
|
|
WORKDIR /opt/source
|
|
COPY package.json ./
|
|
RUN corepack enable && corepack install
|
|
COPY . .
|
|
RUN --mount=type=cache,id=ekklesia-pnpm-store,target=/pnpm/store \
|
|
pnpm install --frozen-lockfile --store-dir /pnpm/store
|
|
RUN pnpm exec turbo run build --filter=@ekklesia/module-template
|
|
# `--legacy` deploys from this workspace without switching it to injected workspace packages; the
|
|
# Module SDK and every other workspace dependency are still copied into the package, built.
|
|
RUN --mount=type=cache,id=ekklesia-pnpm-store,target=/pnpm/store \
|
|
set -eu; \
|
|
pnpm --filter @ekklesia/module-template deploy --prod --legacy --store-dir /pnpm/store \
|
|
/package/example; \
|
|
rm -f /package/example/README.md; \
|
|
# pnpm's hoisted self-reference points back into this workspace; an installed module has none.
|
|
rm -f /package/example/node_modules/.pnpm/node_modules/@ekklesia/module-template; \
|
|
escaping=""; \
|
|
for link in $(find /package/example -type l); do \
|
|
case "$(realpath -m "$link")" in \
|
|
/package/example/*) ;; \
|
|
*) escaping="$escaping $link" ;; \
|
|
esac; \
|
|
done; \
|
|
if [ -n "$escaping" ]; then echo "Links escape the package:$escaping" >&2; exit 1; fi; \
|
|
chmod -R a+rX,go-w /package/example; \
|
|
cd /package/example && node -e 'import("./dist/server/index.js").then((m) => { if (m.default.id !== "example") process.exit(1); })'
|
|
|
|
FROM scratch AS package
|
|
COPY --from=build /package/ /
|