diff --git a/modules/mesh-catalog/heard.ts b/modules/mesh-catalog/heard.ts index daff5043..4dab234f 100644 --- a/modules/mesh-catalog/heard.ts +++ b/modules/mesh-catalog/heard.ts @@ -37,6 +37,12 @@ export interface Built { /** A pull request's merge check or a branch tried before one (to-be 45 §9, issue 686): nothing merged. */ "check-outcome"?: unknown; check?: unknown; + /** The repository's default branch as the build seat read the forge at the build (ADR 0238); empty from a seat older than the rule. */ + trunk?: string; + /** Whether the commit is on `trunk`. */ + "on-trunk"?: boolean; + /** The branches the commit is on. */ + branches?: string[]; } /** The events a build outcome reaches this module on: the build seat's, as it happens, and the control plane's replay. */ @@ -54,9 +60,19 @@ export const BUILD_EVENTS = [ /** * Whether a build outcome is a module-version to place in the graph, and why not when it is not. * - * The same rule the controller keeps when it takes a build in: a merge check, a try and a dry run built - * nothing that merged, and a failure made nothing. Placing any of them would make an unmerged head the - * version the catalogue calls current. + * It mirrors two of the rules the controller keeps before it registers a build (mesh-controller + * cmd/mesh-controller/main.go `builds.Built`, build.go `publishable`): a merge check, a try and a dry run are + * never published, and **only a commit on its repository's trunk is** (ADR 0238) — a branch built through + * the `build` verb is checked, never a module's version. Where the seat names no trunk (one older than the + * rule) it is placed, as the controller lets it through. A failure made nothing and is passed over too. + * + * Not mirrored, because the catalogue cannot see what they read: the branch a module follows when it is not + * the default one (the controller's `follows`; such a module's builds off the default branch are passed over + * here and reach the graph by the replay), ADR 0266's rule that a module registers only from the repository + * it is built from, a superseded request, a build in the scratch namespace, one that failed its gate, and a + * build whose source is unchanged. Each needs the controller's records. This is safe for what the mesh + * sends, which is chosen from the controller's own module table and never from this graph; that the graph + * can still hold a build the controller refused under those rules is a follow-up (hq issue 712). */ export function placeable(body: Built): | { placed: "yes"; module: string; commit: string } @@ -70,6 +86,9 @@ export function placeable(body: Built): if (body.failed) { return { placed: "no", why: "a failed build made no module-version", fault: false }; } + if (body.trunk && !body["on-trunk"] && !(body.branches ?? []).includes(body.trunk)) { + return { placed: "no", why: `the commit is not on its repository's trunk ${body.trunk}: checked, never published (ADR 0238)`, fault: false }; + } if (!body.module || !body.commit) { return { placed: "no", why: "it named no module or no commit", fault: true }; } diff --git a/modules/mesh-catalog/test/heard.test.ts b/modules/mesh-catalog/test/heard.test.ts index c2630f9e..e00daa1b 100644 --- a/modules/mesh-catalog/test/heard.test.ts +++ b/modules/mesh-catalog/test/heard.test.ts @@ -11,6 +11,9 @@ const walkBuild = { path: "modules/mailu", ref: "main", on: "shanks", + trunk: "main", + "on-trunk": true, + branches: ["main"], module: "mailu", commit: "e1c8965ddbf1cf9af9cf1eed7a72ae9ffa6d5a87", manifest: { module: "mailu", unhealthy: { severity: "urgent", why: "mail stops" } }, @@ -52,3 +55,18 @@ test("an outcome that names no module or commit is said as the builder's fault", assert.equal(placing.placed, "no"); if (placing.placed === "no") assert.equal(placing.fault, true); }); + +test("a branch built through the build verb is passed over; a commit on the trunk, or from a seat that names none, is placed (ADR 0238)", () => { + const branch = { ...walkBuild, ref: "feature", "on-trunk": false, branches: ["feature"] }; + const placing = placeable(branch); + assert.equal(placing.placed, "no"); + if (placing.placed === "no") { + assert.match(placing.why, /not on its repository's trunk main/); + assert.equal(placing.fault, false); + } + // On the trunk by its branches although the seat did not set on-trunk. + assert.equal(placeable({ ...branch, branches: ["feature", "main"] }).placed, "yes"); + // A seat older than the rule says no trunk: placed, as the controller lets it through. + const { trunk, "on-trunk": _on, branches, ...older } = walkBuild; + assert.equal(placeable(older).placed, "yes"); +});