chore: initialize from frontend template 4dc033c
This commit is contained in:
@@ -0,0 +1,213 @@
|
||||
import { spawn } from "node:child_process";
|
||||
import { lstat, mkdir, mkdtemp, readFile, readdir, rename, rm, writeFile } from "node:fs/promises";
|
||||
import { tmpdir } from "node:os";
|
||||
import path from "node:path";
|
||||
|
||||
import { afterEach, describe, expect, it } from "vitest";
|
||||
|
||||
import {
|
||||
loadCiGateContract,
|
||||
parseCiGateContract,
|
||||
} from "../../scripts/contracts/ci-gates.ts";
|
||||
import { generateCiWorkflow } from "../../scripts/generate-ci-workflow.ts";
|
||||
import { validatePackageScriptGraph } from "../../scripts/lib/package-script-graph.ts";
|
||||
|
||||
const roots: string[] = [];
|
||||
|
||||
afterEach(async () => {
|
||||
await Promise.all(roots.splice(0).map((root) => rm(root, { recursive: true, force: true })));
|
||||
});
|
||||
|
||||
describe("selective Task 3 contract closure", () => {
|
||||
it("builds a private offline aggregate-cgroup provider launch without argv secrets", async () => {
|
||||
const {
|
||||
encodeProviderBwrapInput,
|
||||
encodeProviderScopeFrame,
|
||||
formatProviderCgroupUnitName,
|
||||
systemctlKillProviderArguments,
|
||||
systemdRunProviderArguments,
|
||||
} = await import("../../scripts/lib/provider-cgroup.ts");
|
||||
const unit = formatProviderCgroupUnitName(
|
||||
"vulnerability",
|
||||
42,
|
||||
"0123456789abcdef01234567",
|
||||
);
|
||||
const command = "node provider.mjs --token command-secret";
|
||||
const credential = "credential-secret";
|
||||
const launch = systemdRunProviderArguments(
|
||||
unit,
|
||||
1_800_000,
|
||||
1_200,
|
||||
"/trusted/node",
|
||||
"/workspace/scripts/lib/provider-scope-wrapper.ts",
|
||||
"/exact/report.json",
|
||||
12,
|
||||
34,
|
||||
);
|
||||
|
||||
expect(launch).toEqual(expect.arrayContaining([
|
||||
"--scope",
|
||||
"--property=MemoryMax=1073741824",
|
||||
"--property=MemorySwapMax=0",
|
||||
"--property=TasksMax=64",
|
||||
"--property=CPUQuota=100%",
|
||||
"--property=KillMode=control-group",
|
||||
"/trusted/node",
|
||||
"/workspace/scripts/lib/provider-scope-wrapper.ts",
|
||||
]));
|
||||
expect(launch.join("\0")).not.toContain(command);
|
||||
expect(launch.join("\0")).not.toContain(credential);
|
||||
expect(
|
||||
encodeProviderBwrapInput(
|
||||
["--unshare-net", "--bind", "/exact/report.json", "/exact/report.json"],
|
||||
{ PROVIDER_COMMAND: command, PROVIDER_CREDENTIAL: credential },
|
||||
),
|
||||
).toEqual(expect.any(Buffer));
|
||||
expect(systemctlKillProviderArguments(unit)).toEqual([
|
||||
"--user",
|
||||
"kill",
|
||||
"--kill-whom=all",
|
||||
"--signal=SIGKILL",
|
||||
unit,
|
||||
]);
|
||||
const frame = encodeProviderScopeFrame({
|
||||
bwrapInput: Buffer.from("private-bwrap-vector\0"),
|
||||
reportPath: "/exact/report.json",
|
||||
reportDev: 12,
|
||||
reportIno: 34,
|
||||
});
|
||||
expect(frame.readUInt32BE(0)).toBe(frame.byteLength - 4);
|
||||
expect(frame.subarray(4).toString("utf8")).toContain(
|
||||
Buffer.from("private-bwrap-vector\0").toString("base64"),
|
||||
);
|
||||
expect(launch.join("\0")).not.toContain("private-bwrap-vector");
|
||||
});
|
||||
|
||||
it("removes only the pinned raw inode during parent-loss cleanup", async () => {
|
||||
const { cleanupOwnedProviderReport } = await import(
|
||||
"../../scripts/lib/provider-raw-cleanup.ts"
|
||||
);
|
||||
const root = await mkdtemp(path.join(tmpdir(), "provider-raw-cleanup-"));
|
||||
roots.push(root);
|
||||
const reportPath = path.join(root, "raw.json");
|
||||
const originalPath = path.join(root, "original.json");
|
||||
await writeFile(reportPath, "owned\n");
|
||||
const identity = await lstat(reportPath);
|
||||
await rename(reportPath, originalPath);
|
||||
await writeFile(reportPath, "unrelated\n");
|
||||
|
||||
await expect(cleanupOwnedProviderReport({
|
||||
reportPath,
|
||||
reportDev: identity.dev,
|
||||
reportIno: identity.ino,
|
||||
})).resolves.toBe(false);
|
||||
await expect(readFile(reportPath, "utf8")).resolves.toBe("unrelated\n");
|
||||
|
||||
await rm(reportPath);
|
||||
await rename(originalPath, reportPath);
|
||||
await expect(cleanupOwnedProviderReport({
|
||||
reportPath,
|
||||
reportDev: identity.dev,
|
||||
reportIno: identity.ino,
|
||||
})).resolves.toBe(true);
|
||||
await expect(lstat(reportPath)).rejects.toMatchObject({ code: "ENOENT" });
|
||||
await expect(readdir(root)).resolves.toEqual([]);
|
||||
});
|
||||
|
||||
it("uses early liveness EOF to clean the pinned raw file without waiting for a command frame", async () => {
|
||||
const root = await mkdtemp(path.join(tmpdir(), "provider-scope-eof-"));
|
||||
roots.push(root);
|
||||
const reportPath = path.join(root, "raw.json");
|
||||
await writeFile(reportPath, "partial\n");
|
||||
const identity = await lstat(reportPath);
|
||||
const child = spawn(process.execPath, [
|
||||
path.resolve("scripts/lib/provider-scope-wrapper.ts"),
|
||||
"1",
|
||||
reportPath,
|
||||
String(identity.dev),
|
||||
String(identity.ino),
|
||||
], { stdio: ["pipe", "pipe", "pipe"] });
|
||||
const completion = waitForChildResult(child);
|
||||
await new Promise<void>((resolve) => setTimeout(resolve, 75));
|
||||
expect(child.exitCode).toBeNull();
|
||||
await expect(readFile(reportPath, "utf8")).resolves.toBe("partial\n");
|
||||
child.stdin.end();
|
||||
const result = await within(completion, 1_000, "provider scope EOF close");
|
||||
expect(result).toEqual({ code: 125, signal: null });
|
||||
await expect(lstat(reportPath)).rejects.toMatchObject({ code: "ENOENT" });
|
||||
await expect(readdir(root)).resolves.toEqual([]);
|
||||
});
|
||||
|
||||
it("tracks npm run-script dependencies instead of bypassing the graph", () => {
|
||||
expect(validatePackageScriptGraph({ root: "npm run-script missing" }, "root"))
|
||||
.toContain("package script missing: root -> missing");
|
||||
});
|
||||
|
||||
it("accepts only the canonical exact-count authority and rejects orphan retention", async () => {
|
||||
const canonical = await loadCiGateContract(process.cwd());
|
||||
expect(canonical.gates).toHaveLength(26);
|
||||
expect(canonical.commands).toHaveLength(81);
|
||||
expect(canonical.gates.reduce((sum, gate) => sum + gate.commandIds.length, 0)).toBe(93);
|
||||
expect(canonical.artifacts).toHaveLength(105);
|
||||
expect(canonical.stages).toHaveLength(5);
|
||||
expect(canonical.retention.classes).toHaveLength(5);
|
||||
|
||||
const orphan = JSON.parse(JSON.stringify(canonical)) as Record<string, any>;
|
||||
orphan.retention.classes.push({ id: "unused", policy: "never referenced" });
|
||||
expect(() => parseCiGateContract(orphan)).toThrow(/five canonical retention|orphan retention/u);
|
||||
});
|
||||
|
||||
it("rejects the retired validate-candidate-archive grammar", async () => {
|
||||
const canonical = JSON.parse(
|
||||
JSON.stringify(await loadCiGateContract(process.cwd())),
|
||||
) as Record<string, any>;
|
||||
canonical.jobs.find((job: Record<string, any>) => job.id === "vulnerability_provider")
|
||||
.steps.splice(4, 0, {
|
||||
kind: "validate-candidate-archive",
|
||||
archivePath: ".release/candidate/release-candidate.tar.gz",
|
||||
});
|
||||
expect(() => parseCiGateContract(canonical)).toThrow(
|
||||
/invalid discriminator|forbidden|canonical job step sequence/iu,
|
||||
);
|
||||
});
|
||||
|
||||
it("publishes a generated workflow as exactly 0644 under a restrictive umask", async () => {
|
||||
const root = await mkdtemp(path.join(tmpdir(), "ci-workflow-mode-"));
|
||||
roots.push(root);
|
||||
await mkdir(path.join(root, ".gitea/workflows"), { recursive: true });
|
||||
const previous = process.umask(0o777);
|
||||
try {
|
||||
const contract = await loadCiGateContract(process.cwd());
|
||||
await generateCiWorkflow({ root, contract, check: false });
|
||||
} finally {
|
||||
process.umask(previous);
|
||||
}
|
||||
const target = path.join(root, ".gitea/workflows/quality-gates.yml");
|
||||
const metadata = await lstat(target);
|
||||
expect(metadata.mode & 0o777).toBe(0o644);
|
||||
expect((await readFile(target, "utf8")).startsWith("# GENERATED FILE")).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
async function waitForChildResult(
|
||||
child: ReturnType<typeof spawn>,
|
||||
): Promise<Readonly<{ code: number | null; signal: NodeJS.Signals | null }>> {
|
||||
return await new Promise((resolve, reject) => {
|
||||
child.once("error", reject);
|
||||
child.once("close", (code, signal) => resolve({ code, signal }));
|
||||
});
|
||||
}
|
||||
|
||||
async function within<T>(operation: Promise<T>, timeoutMs: number, label: string): Promise<T> {
|
||||
let timer: NodeJS.Timeout | undefined;
|
||||
try {
|
||||
return await Promise.race([
|
||||
operation,
|
||||
new Promise<never>((_resolve, reject) => {
|
||||
timer = setTimeout(() => reject(new Error(`${label} timed out`)), timeoutMs);
|
||||
}),
|
||||
]);
|
||||
} finally {
|
||||
if (timer) clearTimeout(timer);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user