diff --git a/src/rebuild.ts b/src/rebuild.ts index 5a73332..ddeee70 100644 --- a/src/rebuild.ts +++ b/src/rebuild.ts @@ -46,7 +46,21 @@ export function planned(env: NodeJS.ProcessEnv = process.env): Build[] { } const control = where["mesh-control"]; if (control) { - builds.push({ what: "control plane image", in: control, argv: ["make", "image"] }); + // **Every image the lab runs, not only the control plane's.** + // + // On 2026-09-01 a suite ran with a control-plane image built that minute and a provisioner + // image built the day before. The rotation test failed against a real database, and the + // failure looked exactly like the change under test being wrong — the provisioner was + // creating logins by a naming rule that had been replaced. + // + // This is the same fault the builder line below was added for, one target along. A rebuild + // that covers most of what a run uses is worse than one that covers none, because the run + // that follows it is believed. + builds.push({ + what: "images", + in: control, + argv: ["make", "image", "builder-image", "provisioner-image", "proxy-image"], + }); const builder = env["MESH_LAB_BUILDER"]; if (builder) { // Both of these parse manifests. Building one and not the other is the eleven-hour-old diff --git a/test/rebuild.test.ts b/test/rebuild.test.ts index 012bb36..f3d0eaa 100644 --- a/test/rebuild.test.ts +++ b/test/rebuild.test.ts @@ -13,11 +13,29 @@ test("the control plane's image and builder are always built together", () => { MESH_LAB_BUILDER: "/repo/control/build/mesh-builder", }); const what = builds.map((b) => b.what); - assert.ok(what.includes("control plane image"), "the image was not built"); + assert.ok(what.includes("images"), "the images were not built"); assert.ok(what.includes("builder"), "the builder was not built"); for (const build of builds) assert.equal(build.in, "/repo/control"); }); +// Every image the lab runs, not only the control plane's. +// +// On 2026-09-01 a run had a control-plane image built that minute and a provisioner image built +// the day before. A test against a real database failed, and it looked exactly like the change +// under test being wrong: the provisioner was creating logins by a naming rule that had been +// replaced hours earlier. novox/hq 04-ISSUES/005 again, one target along. +// +// Named individually rather than by counting, because the failure this guards is a target that +// exists and is not run — which a count would not notice. +test("every image the lab runs is rebuilt, not only the control plane's", () => { + const builds = planned({ MESH_LAB_MODULES: "/repo/control/examples/modules" }); + const images = builds.find((b) => b.what === "images"); + assert.ok(images, "no image build at all"); + for (const target of ["image", "builder-image", "provisioner-image", "proxy-image"]) { + assert.ok(images.argv.includes(target), `${target} is never built, so the lab runs a stale one`); + } +}); + // A repository this run was not pointed at is not built, and not claimed. test("only what this run was pointed at is built", () => { assert.deepEqual(planned({}), []);