mesh/merge-gate pass: builds mesh-tools, sent nowhere; no bus step; every machine composes with the change as it did without (4 of 4 compose)
mesh/repo-check pass: its merge-check.sh passed
mesh/delivery delivered
mesh/delivery-group group fix/385-an-untestable-module-fails-the-check delivered: every member is delivered
A catalogue's TypeScript check fetches nothing and reads this image's node_modules. Once a module it cannot check fails rather than passing with a note, mesh-catalog, model-usage, mongodb and mssql could never merge again. They are development dependencies, so the runtime image stays as it is.
86 lines
5.2 KiB
Docker
86 lines
5.2 KiB
Docker
ARG NODE_BASE=node:22-bookworm-slim
|
|
# The mesh-tools module: the two images every TypeScript module is COMPILED in and may RUN in. The
|
|
# runtime itself ships as the node-tools module's bundle (node-tools/, novox/hq ADR 0175, to-be 38
|
|
# WP3); these images are the toolchain for TypeScript bundles and the base a module's own service
|
|
# may still be built on. They are no longer how tools reach a node.
|
|
#
|
|
# **They were the same image, and that was a mistake.** A module's recipe starts from this and
|
|
# invokes the compiler out of it, so the compiler had to be here — and because the same image was
|
|
# also what every module ran in, every running container on every machine carried a TypeScript
|
|
# compiler it would never invoke. The answer is separate stages rather than one image bad at both
|
|
# jobs. `build.artifacts` in module.json names the published stage each — `toolchain` and `runtime`.
|
|
#
|
|
# The `deps` stage is neither published nor named there: it is where the mesh's package registry is
|
|
# reached, so it is where — and only where — the credential to reach it exists. The toolchain copies
|
|
# resolved node_modules out of it, so the credential is in no image any machine ever holds
|
|
# (novox/hq ADR 0076). This is the buildkit-secret's job done without buildkit, because a machine's
|
|
# docker may carry no buildx.
|
|
|
|
# ---- deps: node_modules resolved from the mesh's registry, credential and all ----------------
|
|
FROM ${NODE_BASE} AS deps
|
|
RUN apt-get update \
|
|
&& apt-get install -y --no-install-recommends git ca-certificates \
|
|
&& rm -rf /var/lib/apt/lists/*
|
|
WORKDIR /app
|
|
COPY node-tools/package.json ./
|
|
# The builder writes .npmrc into the build context; it authenticates to the mesh's package registry
|
|
# for the @novox scope, which is where @novox/mesh-sdk resolves. This stage is not published, so the
|
|
# credential travels no further than here. Development dependencies included: the compiler is one.
|
|
COPY .npmrc ./.npmrc
|
|
RUN npm install --no-audit --no-fund
|
|
# **The SDK this image carries is the one the mesh last published** (novox/hq issue 212). The range in
|
|
# package.json is resolved once and the layer above is cached, so a release reached no toolchain until
|
|
# that file changed. The exact version arrives as a build argument from the SDK module's published
|
|
# package (module.json `build.on`), so a new release is a new argument, this layer runs again — and
|
|
# the planner orders this module after the SDK, so a release rebuilds the toolchain and, after it,
|
|
# every bundle compiled in it (issue 211).
|
|
ARG MESH_SDK=@novox/mesh-sdk@latest
|
|
RUN npm install --no-audit --no-fund "${MESH_SDK}"
|
|
|
|
# ---- compiling: the runtime's own code built, WITHOUT the credential -------------------------
|
|
FROM ${NODE_BASE} AS compiling
|
|
WORKDIR /app
|
|
COPY node-tools/package.json ./
|
|
# The resolved libraries, but not the .npmrc that resolved them.
|
|
COPY --from=deps /app/node_modules ./node_modules
|
|
COPY node-tools/tsconfig.json ./
|
|
COPY node-tools/src ./src
|
|
RUN npm run build
|
|
|
|
# ---- what a running bundle needs, and nothing else -------------------------------------------
|
|
# Its own stage so the toolchain image keeps its build tools while the runtime image does not.
|
|
FROM compiling AS lean
|
|
RUN npm prune --omit=dev
|
|
|
|
# ---- toolchain: what a TypeScript bundle is compiled in ---------------------------------------
|
|
# The compiler, and esbuild (a development dependency) at /app/node_modules/esbuild: the builder
|
|
# bundles every entrypoint and launcher into one file with it (novox/hq ADR 0193), so a bundle
|
|
# carries what it imports and not this image's node_modules.
|
|
# Beside the compiler, at /app/runtime, what every TypeScript bundle runs with: the production
|
|
# dependencies the SDK and the runtime need, and a package.json saying the compiled files are ES
|
|
# modules. The builder copies this directory whole into a compiled bundle (novox/hq ADR 0188 §5),
|
|
# so a bundle unpacked on a machine starts — a `.js` without that package.json is read as
|
|
# CommonJS, and an import of `nats` without node_modules beside it resolves to nothing.
|
|
#
|
|
# **The client libraries a catalogue module depends on are here too** (novox/hq issue 385): pg, mongodb and
|
|
# mssql, development dependencies of node-tools, so the runtime stage prunes them. A catalogue's
|
|
# merge-check-typescript.sh fetches nothing and copies this image's node_modules beside a module; a module
|
|
# depending on a package that is not here cannot be type-checked or tested, and fails its check.
|
|
FROM compiling AS toolchain
|
|
COPY --from=lean /app/node_modules /app/runtime/node_modules
|
|
RUN printf '{"type":"module","private":true}\n' > /app/runtime/package.json
|
|
# **git, because a pull request's merge check runs in this image too** (novox/hq ADR 0237): a repository's
|
|
# own merge-check.sh that declares this toolchain runs here, and a suite that reads a repository's history
|
|
# (the lab's last-run record) ran into "spawnSync git ENOENT". Nothing compiled here calls it.
|
|
RUN apt-get update \
|
|
&& apt-get install -y --no-install-recommends git \
|
|
&& rm -rf /var/lib/apt/lists/*
|
|
|
|
# ---- runtime: what a module's own service may run in ------------------------------------------
|
|
FROM ${NODE_BASE} AS runtime
|
|
WORKDIR /app
|
|
COPY node-tools/package.json ./
|
|
COPY --from=lean /app/node_modules ./node_modules
|
|
COPY --from=compiling /app/dist ./dist
|
|
ENTRYPOINT ["node", "dist/main.js"]
|