diff --git a/tools/toolchain/moon.yml b/tools/toolchain/moon.yml index d67dd25e..ce653bbe 100644 --- a/tools/toolchain/moon.yml +++ b/tools/toolchain/moon.yml @@ -39,12 +39,14 @@ tasks: # edit to one must invalidate this task — otherwise it stays cached and the # assertion silently stops re-running against the thing it constrains. # ci.yml + pgtest.go: a test asserts their two Postgres image digests are - # equal. flake.nix + devenv.nix: version-guard-core.test.ts lifts the real + # equal. release.yml: a test pins the release-pr main-ref guard. + # flake.nix + devenv.nix: version-guard-core.test.ts lifts the real # version.txt guards out of both and asserts they are still extractable. inputs: - '*.ts' - '/bun.lock' - '/.github/workflows/ci.yml' + - '/.github/workflows/release.yml' - '/go/internal/pgtest/pgtest.go' - '/flake.nix' - '/devenv.nix' diff --git a/tools/toolchain/release-guard-core.test.ts b/tools/toolchain/release-guard-core.test.ts new file mode 100644 index 00000000..e451065d --- /dev/null +++ b/tools/toolchain/release-guard-core.test.ts @@ -0,0 +1,53 @@ +// This test is CAPABLE OF FAILING: an absent or unparseable job block must fail +// rather than turn an uncheckable release guard into a pass. + +import { describe, expect, test } from "bun:test"; + +const releasePrBlock = (workflow: string): string => { + const matches = workflow.match( + /^ {2}release-pr:\n([\s\S]*?)(?=^ {2}\S|(?![\s\S]))/m, + ); + if (matches === null) + throw new Error( + "release-pr block could not be located; the privileged contents-write tag-minting job is unchecked", + ); + expect( + matches, + "release-pr must be present so the privileged contents-write tag-minting job stays guarded", + ).toHaveLength(2); + const [, block] = matches; + if (block === undefined) + throw new Error( + "release-pr block could not be extracted; the privileged contents-write tag-minting job is unchecked", + ); + expect( + block.length, + "release-pr must contain a substantial job block so the privileged contents-write tag-minting job is actually checked", + ).toBeGreaterThan(100); + return block; +}; + +const readReleasePrBlock = async (): Promise => { + const root = new URL("../../", import.meta.url).pathname; + return releasePrBlock( + await Bun.file(`${root}.github/workflows/release.yml`).text(), + ); +}; + +describe("the release-pr main-ref guard", () => { + test("keeps the privileged tag-minting job restricted to main", async () => { + const block = await readReleasePrBlock(); + expect( + block, + "release-pr must require main because it holds contents: write and mints release tags", + ).toMatch(/^ {4}if: github\.ref == 'refs\/heads\/main'$/m); + }); + + test("pins release-please to the main target branch", async () => { + const block = await readReleasePrBlock(); + expect( + block, + "release-pr must pass target-branch: main because it holds contents: write and mints release tags", + ).toMatch(/^ {10}target-branch: main$/m); + }); +});