import { readFileSync } from "node:fs"; import path from "node:path"; import { describe, expect, it } from "vitest"; import { parse } from "yaml"; const workflowText = readFileSync( path.resolve(import.meta.dirname, "../../../.github/workflows/publish-server-image.yml"), "utf8", ); interface WorkflowJob { if?: string; needs?: string; "runs-on"?: string; strategy?: { matrix?: { arch?: string[]; include?: Array> } }; } const workflow = parse(workflowText) as { jobs: { validate: WorkflowJob; build: WorkflowJob; publish: WorkflowJob }; }; describe("server image publish workflow", () => { it("builds amd64 natively on the Linux runner and keeps arm64 paused, not emulated", () => { expect(workflowText).not.toContain("setup-qemu-action"); expect(workflowText).toContain("arm64 is paused"); const build = workflow.jobs.build; expect(build["runs-on"]).toContain("matrix.runner"); expect(build.strategy?.matrix?.arch).toEqual(["amd64"]); const runners = Object.fromEntries( (build.strategy?.matrix?.include ?? []) .filter((entry) => entry.arch !== undefined) .map((entry) => [entry.arch, entry.runner]), ); expect(runners).toEqual({ amd64: ["self-hosted", "evoo-ci", "Linux"] }); }); it("publishes one verified manifest per image after the build", () => { const publish = workflow.jobs.publish; expect(publish.needs).toBe("build"); expect(workflowText).toContain("push-by-digest=true"); expect(workflowText).toContain("docker buildx imagetools create"); expect(workflowText).toContain('if [[ "${#sources[@]}" -ne 1 ]]'); expect(workflowText).toContain("for want in linux/amd64; do"); expect(workflowText).toContain("actions/attest-build-provenance@"); }); it("names every image under the lowercase repository namespace", () => { expect(workflowText).not.toContain("ghcr.io/${{ github.repository }}"); const references = workflowText.match(/ghcr\.io\/\$\{\{ steps\.ns\.outputs\.repo \}\}\//g) ?? []; expect(references.length).toBeGreaterThanOrEqual(6); expect(workflowText).toContain("repo=${GITHUB_REPOSITORY,,}"); }); it("keeps pull requests read-only and every action pinned to a commit", () => { const validate = workflow.jobs.validate; const build = workflow.jobs.build; const publish = workflow.jobs.publish; expect(validate.if).toBe("github.event_name == 'pull_request'"); expect(build.if).toBe("github.event_name != 'pull_request'"); expect(publish.if).toBe("github.event_name != 'pull_request'"); expect(workflowText).toContain("push: false"); for (const match of workflowText.matchAll(/uses:\s+([^\s#]+)/g)) { expect(match[1], match[1]).toMatch(/@[0-9a-f]{40}$/); } }); });