mirror of
https://github.com/herdrdev/herdr.git
synced 2026-09-21 16:01:04 +00:00
91 lines
3.8 KiB
TypeScript
91 lines
3.8 KiB
TypeScript
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("preview checks do not require a workstation Windows SDK", () => {
|
|
const checks = preview.jobs.preflight.steps.find((step: any) => step.name === "Run checks");
|
|
expect(checks.run.trim().split("\n")).toEqual(["just ci", "just docs-contract-test"]);
|
|
expect(preview.jobs.build.strategy.matrix.include).toContainEqual({
|
|
target: "x86_64-pc-windows-msvc",
|
|
os: "windows-latest",
|
|
name: "herdr-windows-x86_64.zip",
|
|
});
|
|
expect(preview.jobs.publish.needs).toContain("build");
|
|
});
|
|
|
|
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("release arguments are not interpolated into executable shell text", () => {
|
|
const input = `untrusted'\"$(echo unexpected-command)`;
|
|
for (const args of [
|
|
["preview", input],
|
|
["release-prepare", input, input],
|
|
["release-publish", input, input],
|
|
["release", input, input],
|
|
]) {
|
|
const result = spawnSync("just", ["--dry-run", ...args], { encoding: "utf8" });
|
|
expect(result.status).toBe(0);
|
|
expect(result.stdout + result.stderr).not.toContain(input);
|
|
expect(result.stdout + result.stderr).not.toContain("unexpected-command");
|
|
}
|
|
});
|
|
|
|
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 });
|
|
}
|
|
});
|
|
});
|