import { describe, expect, test } from "bun:test"; import fs from "node:fs"; import path from "node:path"; import { createHash } from "node:crypto"; const ROOT = path.resolve(import.meta.dir, ".."); const read = (relative: string) => fs.readFileSync(path.join(ROOT, relative), "utf8"); describe("release and CI hardening", () => { test("every workflow has explicit permissions and immutable action refs", () => { const workflowRoot = path.join(ROOT, ".github", "workflows"); for (const name of fs.readdirSync(workflowRoot).filter((entry) => entry.endsWith(".yml"))) { const source = fs.readFileSync(path.join(workflowRoot, name), "utf8"); expect(source, `${name} must declare top-level permissions`).toMatch(/^permissions:\s*$/m); for (const match of source.matchAll(/\buses:\s*[^\s@]+@([^\s#]+)/g)) { expect(match[1], `${name} contains a mutable action ref`).toMatch(/^[a-f0-9]{40}$/); } } }); test("paid eval secrets cannot run against fork PR code", () => { const source = read(".github/workflows/evals.yml"); const guard = "github.event.pull_request.head.repo.full_name == github.repository"; expect(source.match(new RegExp(guard.replaceAll(".", "\\."), "g"))?.length).toBeGreaterThanOrEqual(3); }); test("npm package is an explicit small runtime-control surface", () => { const pkg = JSON.parse(read("package.json")); expect(pkg.version).toBe(read("VERSION").trim()); expect(pkg.gstack).toEqual({ packageRole: "runtime-control", runtimeVersion: "2.0.0", skillApi: "2.0" }); expect(pkg.bin).toEqual({ gstack: "./bin/gstack", "gstack-runtime-bootstrap": "./runtime/runtime-bootstrap.mjs", }); expect(pkg.files).toEqual(["bin/gstack", "runtime", "README.md", "LICENSE", "VERSION"]); expect(pkg.dependencies["puppeteer-core"]).toBeUndefined(); expect(pkg.dependencies.playwright).toBe("npm:playwright-core@^1.58.2"); }); test("runtime identity is aligned independently of the legacy four-slot release counter", () => { for (const file of ["runtime/index.js", "runtime/install.js", "runtime/runtime-bootstrap.mjs"]) { expect(read(file), file).toContain('"2.0.0"'); } expect(read("docs/gstack-2/RELEASE-INTEGRITY.md")).toContain("intentionally different namespaces"); }); test("release workflow emits all six signed byte-counted artifacts", () => { const workflow = read(".github/workflows/release-artifacts.yml"); for (const target of ["darwin-arm64", "darwin-x64", "linux-arm64", "linux-x64", "windows-arm64", "windows-x64"]) { expect(workflow).toContain(`target: ${target}`); } expect(workflow).toContain("cosign sign-blob --yes --bundle"); expect(workflow).toContain("actions/attest-build-provenance@"); expect(workflow).toContain("versions/current.json"); expect(workflow).not.toContain('active="$GSTACK_HOME/versions/2.0.0"'); expect(workflow).toContain(".gstack-runtime-browsers"); expect(workflow).toContain("--browser managed"); // Exercise both the bundled browser and the explicit Chromium channel. Keep // this semantic: the workflow intentionally loops over launch options so a // harmless refactor does not invalidate release hardening. expect(workflow).toMatch(/for \(const options of \[\{ headless: true \}, \{ headless: true, channel: ["']chromium["'] \}\]\)/); expect(workflow).toContain("chromium.launch(options)"); expect(workflow).toContain("await browser.close()"); expect(workflow).not.toContain("--with-deps"); expect(workflow).toContain(".gstack-runtime-tools/bun"); expect(workflow).toContain('"$GSTACK_HOME/bin/bun" --version'); expect(workflow).toContain("BUN-LICENSE-1.3.14.md"); expect(workflow).toContain("command -v bun"); expect(workflow).toContain("GSTACK_NODE=\"$node_command\""); expect(workflow).toContain("pathToFileURL(p).href"); expect(workflow).toContain('path").join(process.env.GITHUB_WORKSPACE,".gstack-runtime-smoke.html")'); expect(workflow).not.toContain("goto about:blank"); expect(read("scripts/gstack2/runtime-install-smoke.sh")) .toContain('./setup --home "$HOME_DIR" --browser managed --install-now --yes --json'); const manifest = read(".github/scripts/create-runtime-release-manifest.mjs"); expect(manifest).toContain("bytes: stat.size"); expect(manifest).toContain('certificateOidcIssuer: "https://token.actions.githubusercontent.com"'); }); test("redistributed Bun is pinned and carries the exact tagged license inventory", () => { const workflow = read(".github/workflows/release-artifacts.yml"); expect(workflow).toContain("bun-version: 1.3.14"); const license = read("runtime/licenses/BUN-LICENSE-1.3.14.md"); expect(createHash("sha256").update(license).digest("hex")) .toBe("2cb858b2db8fc793bca2093489c5bc8eee615d002cc4924254904044c27a0afa"); const source = read("runtime/licenses/BUN-SOURCE.md"); expect(source).toContain("2c6160ec8fb853f7e8f97d9b249e756c9b0ac44860a68b6bf4f1b0bcbc5c3741"); expect(source).toContain("bun-v1.3.14"); const installer = read("runtime/install.js"); expect(installer).toContain('entry("runtime")'); expect(installer).toContain('entry(managedBunRelativePath(), "managed-bun", true)'); expect(installer).not.toContain('entry("node_modules/playwright-core")'); const browser = read("browse/src/cli.ts"); expect(browser).toContain("Every installed/compiled client must use the adjacent Node-compatible daemon"); expect(browser).toContain("export function resolveServerLaunchTarget("); expect(browser).toContain("server-node.mjs not found. Rebuild the managed browser runtime"); }); test("Windows setup lane installs, doctors, and uninstalls rather than only building", () => { const workflow = read(".github/workflows/windows-setup-e2e.yml"); expect(workflow).toContain("--dry-run --capabilities browser --browser managed"); expect(workflow).toContain("--install-now --yes --capabilities browser --browser managed"); expect(workflow).toContain("doctor --json"); expect(workflow).toContain("runtime/cli.js uninstall"); }); test("physical-iOS docs match the immutable five-iteration artifact", () => { const artifact = JSON.parse(read("docs/gstack-2/evidence/ios-physical-device-2026-07-20T17-49-19-302Z.json")); expect(artifact.passed).toBe(true); expect(artifact.requiredIterations).toBe(5); expect(artifact.passedIterations).toBe(5); expect(artifact.iterations).toHaveLength(5); expect(artifact.iterations.every((iteration: { passed: boolean }) => iteration.passed)).toBe(true); for (const file of ["STATUS.md", "TEST-EVIDENCE.md", "ARCHITECTURE.md", "HOST-COMPATIBILITY.md", "IOS-PHYSICAL-DEVICE.md"]) { expect(read(`docs/gstack-2/${file}`), file).toContain("ios-physical-device-2026-07-20T17-49-19-302Z.json"); } }); test("public-tool decisions stay inside the accepted architecture", () => { const adr = read("docs/gstack-2/adr/0001-public-infrastructure-tools.md"); expect(adr).toContain("Vercel Agent Skills CLI"); expect(adr).toContain("Sigstore Cosign"); expect(adr).toContain("No cloud-browser provider"); }); test("unavailable governance/static gates are explicit rather than claimed green", () => { const policy = read("docs/gstack-2/RELEASE-INTEGRITY.md"); expect(policy).toContain("not claimed by the current six-artifact release matrix"); expect(policy).toContain("typecheck as not yet enforceable"); expect(policy).toContain("No `CODEOWNERS` file is invented"); expect(read(".github/workflows/quality-gate.yml")).toContain("gate-secret-scan.mjs"); }); });