mirror of
https://github.com/vercel-labs/scriptc.git
synced 2026-10-21 05:50:55 +08:00
- library-mode, library-contract (cache dir plus the tmpdir npm root), and library-asyncfree now carry a plain/san path segment: both flavors run the same ordinary builds, and the nightly's concurrent both-flavor recipe raced ar/ranlib in the shared dirs ("can't open file ... lib.lib.a"); the per-build -san tag stays, it distinguishes explicitly-sanitized builds within one suite
- the same flavor split lands on npm-static's fixed npm-static-workspace dir (the two flavors even build with different sanitize flags there), provenance's shared outDir, and diagnostics' $TMPDIR/scriptc-diag scratch (some entries emit backend artifacts before failing)
- dec-oracle publishes in the differential/llvm/linux/windows lanes are now atomic (write tmp, rename): the content-keyed path is shared across flavors and lanes, and a truncate-and-rewrite could expose a torn oracle to a concurrent reader
- every other suite already keys its build dirs by a content hash that includes the flavor, and the CAS build cache folds the sanitized flags into its key, so those stay as they are
- proof: three consecutive concurrent both-flavor library-mode runs from a wiped cache all pass 45/45 with zero ar/ranlib failures, and a concurrent both-flavor run of all four library suites passes 125/125 in each flavor
111 lines
5.2 KiB
TypeScript
111 lines
5.2 KiB
TypeScript
/* --provenance-sources (EXPERIMENTAL): the provenance-assisted static
|
|
* compilation pipeline, offline — the fixture manifest
|
|
* (SCRIPTC_PROVENANCE_MANIFEST) stands in for the live attestation +
|
|
* source fetch, so these tests exercise everything AFTER the network:
|
|
* source-entry mapping, the registry chokepoints (preflight edges, tsgo
|
|
* "paths", the npm-import bypass), the @__PURE__ dead-const elision, the
|
|
* per-package attribution, and the island fallback notes.
|
|
*
|
|
* The differential contract is the thesis itself: the binary compiled
|
|
* from the ATTESTED SOURCE (static, no island) must byte-match the
|
|
* binary island-running the PUBLISHED dist — and both must byte-match
|
|
* Node running the published package.
|
|
*/
|
|
import { execFile } from "node:child_process";
|
|
import { mkdirSync } from "node:fs";
|
|
import { join } from "node:path";
|
|
import { promisify } from "node:util";
|
|
import { afterEach, describe, expect, test } from "vitest";
|
|
import {
|
|
analyze,
|
|
compile,
|
|
resolveProvenanceSources,
|
|
setProvenanceSources,
|
|
} from "@scriptc/compiler";
|
|
|
|
const execFileAsync = promisify(execFile);
|
|
const repoRoot = join(import.meta.dirname, "../..");
|
|
const fixtureDir = join(repoRoot, "tests/fixtures/provenance");
|
|
const entry = join(fixtureDir, "cases/greet/main.ts");
|
|
/* Suite-flavor segment: the plain and SCRIPTC_SAN=1 suites both run these
|
|
* same (unsanitized) builds and may run concurrently — the suite lock is
|
|
* per flavor — so they must never share a build dir. */
|
|
const flavor = process.env["SCRIPTC_SAN"] === "1" ? "san" : "plain";
|
|
const outDir = join(repoRoot, "node_modules/.cache/scriptc-tests/provenance", flavor);
|
|
|
|
const EXPECTED = "hello, world\nHELLO, COMPILER!\nhello, chain!\n";
|
|
|
|
async function buildAndRun(name: string, dynamic: boolean): Promise<string> {
|
|
mkdirSync(outDir, { recursive: true });
|
|
const outPath = join(outDir, name);
|
|
// Pinned: the provenance thesis compares a source-static binary against
|
|
// a dist-island binary — holding both on the C lane keeps that byte
|
|
// comparison about PROVENANCE, never about which backend each build drew.
|
|
const result = await compile(entry, { outPath, outDir, dynamic, backend: "c" });
|
|
if (!result.ok) {
|
|
throw new Error(result.diagnostics.map((d) => `${d.code}: ${d.message}`).join("\n"));
|
|
}
|
|
const { stdout } = await execFileAsync(outPath, [], { encoding: "utf8" });
|
|
return stdout;
|
|
}
|
|
|
|
afterEach(() => {
|
|
setProvenanceSources(null);
|
|
delete process.env["SCRIPTC_PROVENANCE_MANIFEST"];
|
|
});
|
|
|
|
describe("provenance sources", () => {
|
|
test("attested source compiles STATIC and byte-matches the island's published dist", async () => {
|
|
// Flag off: the published dist embeds in the island (--dynamic).
|
|
const island = await buildAndRun("greet-island", true);
|
|
expect(island).toBe(EXPECTED);
|
|
|
|
// Flag on: the manifest maps greeter to its source tree; the driver
|
|
// compiles WITHOUT --dynamic — possible only because the source
|
|
// became program modules (a bare npm import would fence a static
|
|
// build), which IS the assertion that nothing islanded.
|
|
process.env["SCRIPTC_PROVENANCE_MANIFEST"] = join(fixtureDir, "manifest.json");
|
|
const sources = await resolveProvenanceSources(entry);
|
|
expect(sources.packages).toHaveLength(1);
|
|
const pkg = sources.packages[0]!;
|
|
expect(pkg.name).toBe("greeter");
|
|
expect(pkg.version).toBe("1.2.3");
|
|
expect(pkg.entries["greeter"]).toMatch(/attested-src\/greeter\/src\/index\.ts$/);
|
|
setProvenanceSources(sources);
|
|
const fromSource = await buildAndRun("greet-static", false);
|
|
expect(fromSource).toBe(island);
|
|
});
|
|
|
|
test("per-package attribution counts the source static; the @__PURE__ dead const elides", async () => {
|
|
process.env["SCRIPTC_PROVENANCE_MANIFEST"] = join(fixtureDir, "manifest.json");
|
|
setProvenanceSources(await resolveProvenanceSources(entry));
|
|
const { coverage } = analyze(entry);
|
|
expect(coverage.preflightFailed).toBe(false);
|
|
// No blockers anywhere: the dead const elided instead of fencing.
|
|
expect(coverage.diagnostics).toHaveLength(0);
|
|
expect(coverage.provenanceElided ?? []).toHaveLength(1);
|
|
expect(coverage.provenanceElided![0]!.message).toContain("'Legacy'");
|
|
// Attribution: every counted statement of the greeter source is
|
|
// static (the elided declaration never counts — it lowers to nothing).
|
|
const srcStats = [...(coverage.statsByFile ?? [])].filter(([f]) => f.includes("attested-src/greeter/"));
|
|
expect(srcStats.length).toBeGreaterThan(0);
|
|
for (const [, s] of srcStats) {
|
|
expect(s.failed).toBe(0);
|
|
expect(s.island).toBe(0);
|
|
}
|
|
});
|
|
|
|
test("an unmappable package falls back to the island with a note, never a failure", async () => {
|
|
process.env["SCRIPTC_PROVENANCE_MANIFEST"] = join(fixtureDir, "manifest-broken.json");
|
|
const sources = await resolveProvenanceSources(entry);
|
|
expect(sources.packages).toHaveLength(0);
|
|
expect(sources.notes).toHaveLength(1);
|
|
expect(sources.notes[0]).toContain("greeter");
|
|
expect(sources.notes[0]).toContain("island path used");
|
|
setProvenanceSources(sources);
|
|
// The island path still works under the flag — the fallback contract.
|
|
const island = await buildAndRun("greet-fallback", true);
|
|
expect(island).toBe(EXPECTED);
|
|
});
|
|
});
|