From 8ee6db7d9a27fad899e1d79df516ded80eb074f9 Mon Sep 17 00:00:00 2001 From: jochen Date: Fri, 23 Oct 2026 15:25:33 +0200 Subject: [PATCH] =?UTF-8?q?Remove=20the=20toolchain=20image=20module;=20no?= =?UTF-8?q?de-tools=20is=20the=20repository's=20only=20module=20(issue=209?= =?UTF-8?q?79,=20S1;=20ADR=200524=20=C2=A75)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Only build-agent built inside that image, and it goes in R4g. --- Dockerfile | 85 ------------------- README.md | 7 +- module.json | 33 ------- node-tools/internal/meshtest/meshtest_test.go | 29 +++++++ 4 files changed, 31 insertions(+), 123 deletions(-) delete mode 100644 Dockerfile delete mode 100644 module.json diff --git a/Dockerfile b/Dockerfile deleted file mode 100644 index 5ab16d2..0000000 --- a/Dockerfile +++ /dev/null @@ -1,85 +0,0 @@ -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"] diff --git a/README.md b/README.md index 21e1f68..22b50b7 100644 --- a/README.md +++ b/README.md @@ -1,13 +1,11 @@ # mesh-tools -Two modules in one repository (novox/hq ADR 0069), one piece of software: +One module, `node-tools`; the toolchain image module that stood beside it is retired (novox/hq ADR 0524 §5): - **`node-tools`** (`node-tools/`) — the node's **tool runtime** as a module (ADR 0175, to-be 38 WP3): one process per machine the host runs from this bundle, serving every assigned module's tools and every held seat's verbs on the bus, and answering MCP on the machine's loopback — the console (design 34). The code, its tests and the `mesh` client all live there. -- **`mesh-tools`** (this directory) — the two images TypeScript bundles are compiled in and a module's - own *service* may still run in. Built from the same code; no longer how tools reach a node. The runtime: @@ -49,8 +47,7 @@ MESH_BROKER_URL a plain URL instead of the credential, for the bootstrap `node dist/main.js`. On a node the controller composes the variables and the host supervises the process like any other host-side workload (novox/hq to-be 38). As `node-tools` the same process is -the console: MCP on `127.0.0.1:4270` (or `MESH_CONSOLE_LISTEN`). The container (`Dockerfile`) is how -a module's own *service* may still be built; it is no longer how tools reach a node. +the console: MCP on `127.0.0.1:4270` (or `MESH_CONSOLE_LISTEN`). ## `mesh` — the tools for whoever is on a machine diff --git a/module.json b/module.json deleted file mode 100644 index dd0e830..0000000 --- a/module.json +++ /dev/null @@ -1,33 +0,0 @@ -{ - "module": "mesh-tools", - "version": "1", - "slug": "tools", - "build": { - "artifacts": [ - { - "name": "build", - "kind": "image", - "from": "Dockerfile", - "target": "toolchain" - }, - { - "name": "runtime", - "kind": "image", - "from": "Dockerfile", - "target": "runtime" - } - ], - "on": [ - { - "arg": "NODE_BASE", - "image": "node@sha256:48e4b67d85f87bd551df43704e24d252f56cc5f8e9718841aace50f19948f0f9" - }, - { - "arg": "MESH_SDK", - "module": "mesh-sdk", - "artifact": "lib" - } - ] - }, - "resources": [] -} diff --git a/node-tools/internal/meshtest/meshtest_test.go b/node-tools/internal/meshtest/meshtest_test.go index 374cd93..8da2521 100644 --- a/node-tools/internal/meshtest/meshtest_test.go +++ b/node-tools/internal/meshtest/meshtest_test.go @@ -90,3 +90,32 @@ func TestBundlesCannotBeLaunchedWithoutNodeOrTheSDKAndTheSkipSaysWhy(t *testing. t.Errorf("node and the package present said %q", why) } } + +// The checker runs node-tools' Go level by Go's convention and its Node level by package.json's test script +// (novox/hq ADR 0529): no script of the repository's own runs beside them, and no module but node-tools is built from +// here (ADR 0524 §5, the toolchain image retired). +func TestTheRepositoryHoldsNoCheckScriptAndNodeToolsIsItsOnlyModule(t *testing.T) { + root := filepath.Join("..", "..", "..") + if _, err := os.Stat(filepath.Join(root, "merge-check.sh")); err == nil { + t.Errorf("merge-check.sh is back: the checker's levels run the tests") + } + var found []string + err := filepath.WalkDir(root, func(p string, d os.DirEntry, err error) error { + if err != nil { + return err + } + if d.IsDir() && (d.Name() == "node_modules" || d.Name() == ".git") { + return filepath.SkipDir + } + if !d.IsDir() && d.Name() == "module.json" { + found = append(found, filepath.ToSlash(p)) + } + return nil + }) + if err != nil { + t.Fatal(err) + } + if len(found) != 1 || !strings.HasSuffix(found[0], "/node-tools/module.json") { + t.Errorf("the module manifests are %v, want node-tools' alone", found) + } +}