mirror of
https://github.com/vercel-labs/scriptc.git
synced 2026-10-25 06:24:27 +08:00
The sanitized native emitter peaks near 13 GB while it emits the compiler and then compiles the 348 MB module with clang under AddressSanitizer, which together exceed the 16 GB runner. Back the rebuild-emit job with a 24 GiB swap file and lift its stack limit, since the instrumented emitter's recursion can exceed 8 MiB.
95 lines
4.2 KiB
TypeScript
95 lines
4.2 KiB
TypeScript
import { mkdirSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from "node:fs";
|
|
import { tmpdir } from "node:os";
|
|
import { dirname, join } from "node:path";
|
|
import { load } from "js-yaml";
|
|
import { expect, test } from "vitest";
|
|
import { verifyBootstrapSeed } from "../../scripts/bootstrap-seed.mjs";
|
|
|
|
test("bootstrap artifacts require a complete seed from the same revision, lane, and path", () => {
|
|
const directory = mkdtempSync(join(tmpdir(), "scriptc-seed-contract-"));
|
|
try {
|
|
const metadata = { schema: 1, revision: "revision", sanitize: true, directory };
|
|
writeFileSync(join(directory, "bootstrap-seed.json"), JSON.stringify(metadata));
|
|
expect(() => verifyBootstrapSeed(directory, "revision", true)).toThrow("missing");
|
|
for (const path of [
|
|
"distribution/bin/scriptc",
|
|
"distribution/bin/scriptc.json",
|
|
".scriptc/distribution-seed/cli.ir.json",
|
|
".scriptc/distribution-seed/cli.ll",
|
|
".scriptc/distribution-seed/compiler.ffi.json",
|
|
]) {
|
|
const file = join(directory, path);
|
|
mkdirSync(dirname(file), { recursive: true });
|
|
writeFileSync(file, "fixture");
|
|
}
|
|
expect(() => verifyBootstrapSeed(directory, "revision", true)).not.toThrow();
|
|
expect(() => verifyBootstrapSeed(directory, "other", true)).toThrow("identity");
|
|
expect(() => verifyBootstrapSeed(directory, "revision", false)).toThrow("identity");
|
|
expect(() => verifyBootstrapSeed(directory, undefined, true)).toThrow("identity");
|
|
writeFileSync(
|
|
join(directory, "bootstrap-seed.json"),
|
|
JSON.stringify({ ...metadata, directory: "/different" }),
|
|
);
|
|
expect(() => verifyBootstrapSeed(directory, "revision", true)).toThrow("identity");
|
|
} finally {
|
|
rmSync(directory, { recursive: true, force: true });
|
|
}
|
|
});
|
|
|
|
test("CI restores each seed only into its matching instrumented bootstrap lane", () => {
|
|
const workflow = load(
|
|
readFileSync(new URL("../../.github/workflows/ci.yml", import.meta.url), "utf8"),
|
|
) as {
|
|
jobs: Record<
|
|
string,
|
|
{
|
|
needs?: string | string[];
|
|
strategy: { matrix: { flavor?: string[]; include?: { flavor: string }[] } };
|
|
env: Record<string, string>;
|
|
steps: {
|
|
uses?: string;
|
|
if?: string;
|
|
with?: Record<string, unknown>;
|
|
env?: Record<string, string>;
|
|
run?: string;
|
|
}[];
|
|
}
|
|
>;
|
|
};
|
|
const seed = workflow.jobs.bootstrap_seed!;
|
|
const checks = workflow.jobs.bootstrap!;
|
|
const dependencies = Array.isArray(checks.needs) ? checks.needs : [checks.needs];
|
|
expect(dependencies).toContain("bootstrap_seed");
|
|
expect(
|
|
seed.steps
|
|
.filter((step) => step.uses === "./.github/actions/native-packages")
|
|
.map((step) => step.with?.target),
|
|
).toEqual(["linux-x64-gnu", "wasm32-wasi"]);
|
|
for (const step of checks.steps.filter(
|
|
(step) => step.uses === "./.github/actions/native-packages",
|
|
)) {
|
|
expect(step.if).toBe("matrix.phase == 'commands'");
|
|
}
|
|
// Each bootstrap job restores the seed of a flavor the seed job builds.
|
|
const checkFlavors = new Set(checks.strategy.matrix.include?.map((job) => job.flavor));
|
|
expect([...checkFlavors].toSorted()).toEqual(seed.strategy.matrix.flavor);
|
|
expect(seed.env.SCRIPTC_SAN).toBe(checks.env.SCRIPTC_SAN);
|
|
const prepare = seed.steps.find((step) => step.run === "node scripts/bootstrap-seed.mjs")!;
|
|
const execute = checks.steps.find((step) =>
|
|
step.run?.split("\n").includes("node scripts/ci-native-bootstrap.mjs"),
|
|
)!;
|
|
expect(prepare.env?.SCRIPTC_BOOTSTRAP_SEED_DIRECTORY).toBe("${{ runner.temp }}/native-bootstrap");
|
|
expect(execute.env?.SCRIPTC_BOOTSTRAP_SEED_DIRECTORY).toBe(
|
|
prepare.env?.SCRIPTC_BOOTSTRAP_SEED_DIRECTORY,
|
|
);
|
|
// Runner paths are available in step contexts, not job-level env.
|
|
for (const job of [seed, checks]) {
|
|
expect(Object.values(job.env).some((value) => value.includes("runner."))).toBe(false);
|
|
}
|
|
const upload = seed.steps.find((step) => step.uses === "actions/upload-artifact@v4")!;
|
|
const download = checks.steps.find((step) => step.uses === "actions/download-artifact@v4")!;
|
|
expect(upload.with?.name).toBe("bootstrap-seed-${{ matrix.flavor }}-${{ github.sha }}");
|
|
expect(download.with?.name).toBe(upload.with?.name);
|
|
expect(upload.with?.["if-no-files-found"]).toBe("error");
|
|
});
|