mirror of
https://github.com/garrytan/gstack.git
synced 2026-09-21 12:20:48 +02:00
121 lines
6.5 KiB
TypeScript
121 lines
6.5 KiB
TypeScript
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();
|
|
});
|
|
|
|
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('chromium.launch({ headless: true, channel: "chromium" })');
|
|
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("goto about:blank");
|
|
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)');
|
|
const browser = read("browse/src/cli.ts");
|
|
expect(browser).toContain("Every installed/compiled client must use the adjacent Node-compatible daemon");
|
|
expect(browser).toContain("if (IS_COMPILED && !NODE_SERVER_SCRIPT)");
|
|
});
|
|
|
|
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");
|
|
expect(workflow).toContain("--install-now --yes --capabilities browser");
|
|
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");
|
|
});
|
|
});
|