From c3d5ec1d55854213b2c3fc8043374e06b19d7651 Mon Sep 17 00:00:00 2001 From: jochen Date: Mon, 14 Sep 2026 21:05:28 +0200 Subject: [PATCH] Build each repository from its own ref, and build the provider lavinmq needs building now, so the step that assigns it builds it first. And the ref is per repository rather than one value for all of them: a change under test lives in one repository, and building the others from that branch would prove it against itself. Claude-Session: https://claude.ai/code/session_01D6qtiYU3P9jk3pnAXyAFyx --- test/integration/fresh-mesh.test.ts | 35 +++++++++++++++++++++++------ 1 file changed, 28 insertions(+), 7 deletions(-) diff --git a/test/integration/fresh-mesh.test.ts b/test/integration/fresh-mesh.test.ts index 70627ce..7b95aa5 100644 --- a/test/integration/fresh-mesh.test.ts +++ b/test/integration/fresh-mesh.test.ts @@ -74,7 +74,7 @@ const BASE = { module: "mesh-tools", repo: "mesh-tools", path: "" }; * record of, so it provides nothing to anything. A module asking for `amqp` is asking for a * provider in the graph, and this is it. No build: its image is upstream. */ -const PROVIDER = { module: "lavinmq" }; +const PROVIDER = { module: "lavinmq", repo: "mesh-catalog", path: "modules/lavinmq" }; /** Named once, because the step title is also how later steps say what they waited on. */ const NEEDS = "the mesh runs a broker for that module to talk to"; const MODULE = { module: "amqp-ping", repo: "mesh-catalog", path: "modules/amqp-ping" }; @@ -101,8 +101,21 @@ function forgeUrl(repo: string): string { if (override) return override; return source.replace(/[^/]+\.git$/, `${repo}.git`); } -/** A branch is acceptable for an ordinary build; only genesis insists on a commit (ADR 0071). */ -const buildRef = process.env["MESH_LAB_BUILD_REF"] ?? "main"; +/** + * What to build, per repository. + * + * A branch is acceptable for an ordinary build; only genesis insists on a commit (ADR 0071). Per + * repository rather than one value for all of them, because a change under test usually lives in + * one repository and the rest should be built from what everyone else has — building them all from + * a feature branch would prove that branch against itself. + * + * MESH_LAB_BUILD_REF the default for every repository + * MESH_LAB_BUILD_REF_MESH_CATALOG ...overridden for one + */ +function refFor(repo: string): string { + const override = process.env[`MESH_LAB_BUILD_REF_${repo.toUpperCase().replaceAll("-", "_")}`]; + return override ?? process.env["MESH_LAB_BUILD_REF"] ?? "main"; +} /** * The shared base's manifest, on this workstation. @@ -290,7 +303,7 @@ before(async () => { // needs that record, is where it surfaced. await registerModule(BASE.module, baseManifest); const built = await mesh( - `build ${forgeUrl(BASE.repo)} --ref ${buildRef} --wait 1200s`, 1_500_000); + `build ${forgeUrl(BASE.repo)} --ref ${refFor(BASE.repo)} --wait 1200s`, 1_500_000); assert.doesNotMatch(built, /failed/i, built); return built; }); @@ -300,7 +313,7 @@ before(async () => { "the mesh builds the shared base from source", async () => { await registerModule(MODULE.module, resolve(catalogDir, MODULE.module, "module.json")); const built = await mesh( - `build ${forgeUrl(MODULE.repo)} --path ${MODULE.path} --ref ${buildRef} --wait 1200s`, + `build ${forgeUrl(MODULE.repo)} --path ${MODULE.path} --ref ${refFor(MODULE.repo)} --wait 1200s`, 1_500_000); assert.doesNotMatch(built, /failed/i, built); // The point of the whole step: what came out is named by a digest this mesh's registry @@ -320,10 +333,18 @@ before(async () => { // with something to offer — the mesh's own plumbing, not an entry in its graph. A module that // wants a broker wants one the mesh knows about. // - // `lavinmq` is that module, and it needs no building: its image is upstream, so this is a - // register and an assign, and it is a fair test of provisioning rather than of building. + // `lavinmq` is that module. It was first added here on the belief that it needed no building — + // its broker is an upstream image — and the mesh refused it: two of its three containers named a + // placeholder digest, "which is never a real image". That was right. The broker is upstream, but + // the module is not only the broker: it carries a run-once bootstrap that writes the broker's + // configuration, a provisioner that grants each consumer its own vhost and user, tools and an + // event consumer. All of that is its own code and has to be built like anything else. await step(NEEDS, "the mesh builds a module standing on that base", async () => { await registerModule(PROVIDER.module, resolve(catalogDir, PROVIDER.module, "module.json")); + const built = await mesh( + `build ${forgeUrl(PROVIDER.repo)} --path ${PROVIDER.path} --ref ${refFor(PROVIDER.repo)} --wait 1200s`, + 1_500_000); + assert.doesNotMatch(built, /failed/i, built); await mesh(`assign ${CONTROL} ${PROVIDER.module}`); await mesh(`push ${CONTROL}`, 600_000); for (let i = 0; i < 60; i++) {