diff --git a/modules/lab/module.json b/modules/lab/module.json index 5406579d..c9c14770 100644 --- a/modules/lab/module.json +++ b/modules/lab/module.json @@ -104,7 +104,9 @@ ], "env": { "MESH_LAB_WORK": "${dir:work}", - "MESH_LAB_ENV_FILE": "${dir:state}/lab.env" + "MESH_LAB_ENV_FILE": "${dir:state}/lab.env", + "MESH_LAB_FORGE_BINDING": "${dir:state}/package-registry.json", + "MESH_LAB_FORGE_SECRET": "${dir:state}/package-registry.secret" } } ] diff --git a/modules/lab/test/runs.test.ts b/modules/lab/test/runs.test.ts index 441af105..f87bf3d2 100644 --- a/modules/lab/test/runs.test.ts +++ b/modules/lab/test/runs.test.ts @@ -209,7 +209,11 @@ test("each check gets a fresh directory of its own", () => { rmSync(work, { recursive: true, force: true }); }); -test("what earlier checks left is cleared, and what cannot be is named rather than tripped over", () => { +// As root nothing is locked against removal, so the unremovable tree cannot be made: skipped there, before +// it makes anything (the merge check runs as root). +test("what earlier checks left is cleared, and what cannot be is named rather than tripped over", { + skip: process.getuid?.() === 0 ? "running as root, which no directory mode stops from removing anything" : false, +}, () => { const work = mkdtempSync(join(tmpdir(), "lab-work-")); for (const d of ["check/src", "check-setup/mesh-lab", "check-abc", "run-x", "keep"]) mkdirSync(join(work, d), { recursive: true }); // A tree this account cannot empty: a directory it may not write, with something in it. @@ -220,9 +224,30 @@ test("what earlier checks left is cleared, and what cannot be is named rather th chmodSync(join(work, "check-stuck", "locked"), 0o755); for (const gone of ["check", "check-setup", "check-abc"]) assert.equal(existsSync(join(work, gone)), false, gone); assert.ok(existsSync(join(work, "run-x")) && existsSync(join(work, "keep"))); - if (process.getuid?.() !== 0) { - assert.equal(said.length, 1); - assert.match(said[0] as string, /check-stuck, left by an earlier check, could not be removed/); - } + assert.equal(said.length, 1); + assert.match(said[0] as string, /check-stuck, left by an earlier check, could not be removed/); rmSync(work, { recursive: true, force: true }); }); + +// And as root too: what earlier checks left is cleared. +test("leftover checks are cleared, whoever runs the check", () => { + const work = mkdtempSync(join(tmpdir(), "lab-work-")); + for (const d of ["check/src", "check-setup/mesh-lab", "check-abc", "run-x"]) mkdirSync(join(work, d), { recursive: true }); + assert.deepEqual(clearChecks(work), []); + for (const gone of ["check", "check-setup", "check-abc"]) assert.equal(existsSync(join(work, gone)), false, gone); + assert.ok(existsSync(join(work, "run-x"))); + rmSync(work, { recursive: true, force: true }); +}); + +// The lab's tools read the bound credential, so the runtime is given that file by the tools bundle's env +// (ADR 0192), as gitea and umami are given theirs: the runtime's account may then read exactly it, and no +// other credential changes hands (issue 624). +test("the lab's tools bundle names its bound credential and binding in its env", () => { + const m = JSON.parse(readFileSync(join(import.meta.dirname, "..", "module.json"), "utf8")) as { + secrets: Record; binds: Record; + build: { artifacts: { name: string; env?: Record }[] }; + }; + const env = m.build.artifacts.find((a) => a.name === "code")?.env ?? {}; + assert.equal(env.MESH_LAB_FORGE_SECRET, m.secrets["npm-package-registry"]); + assert.equal(env.MESH_LAB_FORGE_BINDING, m.binds["npm-package-registry"]); +});