diff options
Diffstat (limited to 'scripts/release-workflows.test.ts')
| -rw-r--r-- | scripts/release-workflows.test.ts | 64 |
1 files changed, 64 insertions, 0 deletions
diff --git a/scripts/release-workflows.test.ts b/scripts/release-workflows.test.ts new file mode 100644 index 00000000..a169204f --- /dev/null +++ b/scripts/release-workflows.test.ts @@ -0,0 +1,64 @@ +import { describe, expect, test } from "bun:test"; +import { mkdtempSync, readFileSync, rmSync, writeFileSync } from "node:fs"; +import { spawnSync } from "node:child_process"; +import { join } from "node:path"; + +const load = (name: string): any => + Bun.YAML.parse(readFileSync(new URL(`../.github/workflows/${name}.yml`, import.meta.url), "utf8")); +const preview = load("preview"); +const release = load("release"); +const adminGate = release.jobs["validate-release-source"].steps[0]; + +describe("official publishing workflow boundaries", () => { + test("publishing is tag-only while normal PR CI remains enabled", () => { + expect(preview.on).toEqual({ push: { tags: ["preview-*"] } }); + expect(release.on).toEqual({ push: { tags: ["v*"] } }); + expect(load("ci").on.pull_request).toBeDefined(); + }); + + test("each publishing job rechecks both actors before using credentials", () => { + for (const [workflow, names] of [ + [preview, ["preflight", "publish"]], + [release, ["validate-release-source", "release", "update-nix-package", "close-released-issues", "update-latest-json"]], + ] as const) { + for (const name of names) { + const job = workflow.jobs[name]; + expect(job.if).toContain("github.event_name == 'push'"); + expect(job.if).toContain("startsWith(github.ref, 'refs/tags/"); + expect(job.steps[0]).toEqual(adminGate); + } + } + expect(adminGate.run).toContain('"$GITHUB_ACTOR" "$GITHUB_TRIGGERING_ACTOR"'); + expect(adminGate.env.GH_TOKEN).toBe("${{ github.token }}"); + expect(adminGate.run).not.toContain("ogulcancelik"); + }); + + test.skipIf(process.platform === "win32")("admin gate permits admins and fails closed for other roles or API errors", () => { + const dir = mkdtempSync("/var/tmp/herdr-admin-gate-"); + try { + writeFileSync(join(dir, "gh"), `#!/bin/sh +case "$2" in + */collaborators/admin-*/permission) echo admin ;; + */collaborators/maintainer/permission) echo maintain ;; + */collaborators/writer/permission) echo write ;; + *) exit 1 ;; +esac +`, { mode: 0o755 }); + for (const [actor, trigger, succeeds] of [ + ["admin-one", "admin-two", true], + ["writer", "admin-two", false], + ["admin-one", "writer", false], + ["admin-one", "maintainer", false], + ["admin-one", "api-error", false], + ] as const) { + const result = spawnSync("bash", ["-c", adminGate.run], { + env: { ...process.env, PATH: `${dir}:${process.env.PATH}`, GITHUB_REPOSITORY: "example/test", GITHUB_ACTOR: actor, GITHUB_TRIGGERING_ACTOR: trigger }, + encoding: "utf8", + }); + expect(result.status === 0).toBe(succeeds); + } + } finally { + rmSync(dir, { recursive: true, force: true }); + } + }); +}); |
