# Two images from one recipe: the one modules are COMPILED in, and the one they RUN in. # # **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. 23 of the 28 MB of libraries were that compiler. It was defended # in a comment, which made a workaround look like a decision: the earlier attempt to prune the build # tools produced a smaller image that nothing could be built on, and the answer to that is two # images rather than one image that is bad at both jobs. # # Kept in one recipe deliberately. They must agree about the operating system, the language version # and the library, and two files drift. `build.artifacts` in module.json names a stage each. # ---- toolchain: what a module is compiled in ------------------------------------------------- FROM node:22-bookworm-slim AS toolchain # git, because a dependency named by a git URL is fetched by git and this image does not carry it. # Only here: what it is needed for happens at build time, and an image that can clone is an image # that can be made to clone. RUN apt-get update \ && apt-get install -y --no-install-recommends git ca-certificates \ && rm -rf /var/lib/apt/lists/* WORKDIR /app COPY package.json package-lock.json ./ # Development dependencies included: the compiler is one, and so is the toolkit's own. RUN npm install --no-audit --no-fund # **And then compile the toolkit, because npm did not.** It declares a `prepare` script, the hook a # package manager is supposed to run after installing from git, and this one does not run it — so # the package arrives as sources with every entry point pointing at a compiled directory that is not # there. Done explicitly rather than relying on a hook firing, which would break the day it stopped. RUN npm --prefix node_modules/@novox/mesh-sdk install --no-audit --no-fund \ && npm --prefix node_modules/@novox/mesh-sdk run build \ && npm --prefix node_modules/@novox/mesh-sdk prune --omit=dev COPY tsconfig.json ./ COPY src ./src RUN npm run build # ---- what the running image needs, and nothing else ------------------------------------------- # Its own stage so the toolchain image keeps its build tools while the runtime image does not. The # prune has to happen somewhere, and doing it in the toolchain stage would take the compiler out of # the image whose whole purpose is to have one. FROM toolchain AS lean RUN npm prune --omit=dev # ---- runtime: what a module runs in ----------------------------------------------------------- FROM node:22-bookworm-slim AS runtime WORKDIR /app COPY package.json ./ COPY --from=lean /app/node_modules ./node_modules COPY --from=toolchain /app/dist ./dist ENTRYPOINT ["node", "dist/main.js"]