The lab stands in for the builder on an upstream artifact too: the reference the manifest pins

hello-web's server is somebody else's image the mesh would copy in (ADR 0096); the
loader refused it as an artifact nobody stocked.
This commit is contained in:
2026-09-21 22:33:44 +02:00
parent 107257faf4
commit 2530ca762e
2 changed files with 26 additions and 2 deletions
+14
View File
@@ -85,3 +85,17 @@ test("a manifest the catalogue does not have is refused by name", () => {
assert.throws(() => catalogueModule("nothing", held), /no manifest for nothing/);
} finally { restore(); }
});
test("an upstream artifact resolves to the reference the manifest pins, as the machine pulls it", () => {
const web = {
module: "thing", version: "1",
resources: [{ id: "server", type: "container", name: "web", artifact: "server" }],
build: { artifacts: [{ name: "server", kind: "upstream", from: "alpine@" + digest("e") }] },
};
const restore = aCatalogueWith(web);
try {
const m = JSON.parse(catalogueModule("thing", held)) as { resources: Record<string, unknown>[] };
assert.equal(m.resources[0]!["image"], "alpine@" + digest("e"));
assert.equal(m.resources[0]!["artifact"], undefined);
} finally { restore(); }
});
+12 -2
View File
@@ -306,12 +306,22 @@ export interface ForTheLab {
export function catalogueModule(module: string, held: HeldImage[], lab: ForTheLab = {}): string {
const m = JSON.parse(readFileSync(catalogueManifest(module), "utf8")) as {
resources?: { id: string; type: string; image?: string; artifact?: string; ports?: string[]; env?: Record<string, string> }[];
build?: unknown;
build?: { artifacts?: { name: string; kind: string; from?: string }[] };
};
const artifacts: Record<string, string> = { runtime: `mesh-runtime-${module}`, ...(lab.artifacts ?? {}) };
// An upstream artifact is somebody else's image, which the mesh's builder copies into its own
// registry (ADR 0096). The lab stands in for the builder by using the reference the manifest
// pins, which the machine pulls over its uplink — the same bytes, without the copy.
const upstream = new Map<string, string>();
for (const a of m.build?.artifacts ?? []) {
if (a.kind === "upstream" && a.from) upstream.set(a.name, a.from);
}
for (const r of m.resources ?? []) {
if (r.type !== "container") continue;
if (typeof r.artifact === "string") {
if (typeof r.artifact === "string" && upstream.has(r.artifact) && !lab.artifacts?.[r.artifact]) {
r.image = onTheMachine(upstream.get(r.artifact)!, held);
delete r.artifact;
} else if (typeof r.artifact === "string") {
const repository = artifacts[r.artifact];
assert.ok(repository,
`${module}'s container '${r.id}' names the "${r.artifact}" artifact, which the mesh would ` +