e7738de6d2
CI / Deep Native Runtime Cases (1/6) (push) Has been skipped
CI / Native Preflight (push) Failing after 1s
CI / Native Runtime Cases (1/2) (push) Failing after 0s
CI / Native Runtime Cases (2/2) (push) Failing after 1s
CI / Native Metadata Reports (push) Failing after 0s
CI / Native Direct Backend Artifacts (push) Failing after 0s
CI / Native Sanitizer Smoke (push) Failing after 1s
CI / Command Contract Snapshots (push) Failing after 1s
CI / Deep Conformance Suite (push) Has been skipped
CI / Graph Build Perf (push) Failing after 1s
CI / Deep Native Preflight (push) Has been skipped
CI / Deep Native Runtime Cases (2/6) (push) Has been skipped
CI / Deep Native Runtime Cases (3/6) (push) Has been skipped
CI / Conformance Suite (push) Failing after 1s
CI / Workspace Checks (push) Failing after 0s
CI / Deep Native Runtime Cases (5/6) (push) Has been skipped
CI / Deep Native Runtime Cases (6/6) (push) Has been skipped
CI / Deep Native Runtime Cases (4/6) (push) Has been skipped
CI / Deep Graph Build Perf (push) Has been skipped
144 lines
4.2 KiB
TypeScript
144 lines
4.2 KiB
TypeScript
#!/usr/bin/env -S node --experimental-strip-types --disable-warning=ExperimentalWarning
|
|
import { execFileSync } from "node:child_process";
|
|
import { existsSync, lstatSync, readFileSync, readdirSync } from "node:fs";
|
|
import { dirname, isAbsolute, join, relative, resolve, sep } from "node:path";
|
|
|
|
const skippedDirs = new Set([
|
|
".git",
|
|
".next",
|
|
".turbo",
|
|
".zero",
|
|
"dist",
|
|
"node_modules",
|
|
"target",
|
|
"zig-cache",
|
|
"zig-out",
|
|
]);
|
|
|
|
function usage() {
|
|
console.error("usage: repository-graph-verify-projection [--root <path>] [--target <target>]");
|
|
}
|
|
|
|
let root = process.cwd();
|
|
let target = "";
|
|
for (let i = 2; i < process.argv.length; i++) {
|
|
const arg = process.argv[i];
|
|
if (arg === "--root") {
|
|
const value = process.argv[++i];
|
|
if (!value) {
|
|
usage();
|
|
process.exit(2);
|
|
}
|
|
root = value;
|
|
} else if (arg === "--target") {
|
|
const value = process.argv[++i];
|
|
if (!value) {
|
|
usage();
|
|
process.exit(2);
|
|
}
|
|
target = value;
|
|
} else if (arg === "--help" || arg === "-h") {
|
|
usage();
|
|
process.exit(0);
|
|
} else {
|
|
usage();
|
|
process.exit(2);
|
|
}
|
|
}
|
|
|
|
const repoRoot = process.cwd();
|
|
const nativeZeroBin = resolve(repoRoot, ".zero/bin/zero");
|
|
const zeroBin = process.env.ZERO_BIN
|
|
? resolve(repoRoot, process.env.ZERO_BIN)
|
|
: existsSync(nativeZeroBin) ? nativeZeroBin : resolve(repoRoot, "bin/zero");
|
|
const absoluteRoot = resolve(root);
|
|
|
|
if (!existsSync(zeroBin)) {
|
|
console.error("repository graph verify-projection requires a built Zero compiler from the repository root");
|
|
process.exit(1);
|
|
}
|
|
|
|
if (!existsSync(absoluteRoot)) {
|
|
console.error(`repository graph verify-projection root does not exist: ${absoluteRoot}`);
|
|
process.exit(1);
|
|
}
|
|
|
|
function findStores(path: string, stores: string[]) {
|
|
const stat = lstatSync(path);
|
|
if (stat.isSymbolicLink()) return;
|
|
if (stat.isFile()) {
|
|
if (path.endsWith("/zero.graph") || path === "zero.graph") stores.push(path);
|
|
return;
|
|
}
|
|
if (!stat.isDirectory()) return;
|
|
|
|
for (const entry of readdirSync(path, { withFileTypes: true })) {
|
|
if (entry.isDirectory() && skippedDirs.has(entry.name)) continue;
|
|
const child = join(path, entry.name);
|
|
if (entry.isSymbolicLink()) continue;
|
|
if (entry.isFile() && entry.name === "zero.graph") stores.push(child);
|
|
else if (entry.isDirectory()) findStores(child, stores);
|
|
}
|
|
}
|
|
|
|
const stores: string[] = [];
|
|
findStores(absoluteRoot, stores);
|
|
stores.sort();
|
|
|
|
if (stores.length === 0) {
|
|
console.log("repository graph verify-projection ok (0 stores)");
|
|
process.exit(0);
|
|
}
|
|
|
|
let failures = 0;
|
|
for (const store of stores) {
|
|
const input = sourceInputForStore(store);
|
|
const display = relative(repoRoot, input) || ".";
|
|
console.log(`repository graph verify-projection: ${display}`);
|
|
const args = ["verify-projection"];
|
|
if (target) args.push("--target", target);
|
|
args.push(input);
|
|
try {
|
|
execFileSync(zeroBin, args, { stdio: "inherit" });
|
|
} catch {
|
|
failures++;
|
|
const targetArgs = target ? ` --target ${target}` : "";
|
|
console.error(`repository graph verify-projection failed: bin/zero verify-projection${targetArgs} ${display}`);
|
|
}
|
|
}
|
|
|
|
if (failures > 0) {
|
|
console.error(`repository graph verify-projection failed (${failures}/${stores.length} stores)`);
|
|
process.exit(1);
|
|
}
|
|
|
|
console.log(`repository graph verify-projection ok (${stores.length} ${stores.length === 1 ? "store" : "stores"})`);
|
|
|
|
function sourceInputForStore(store: string) {
|
|
const root = dirname(store);
|
|
if (existsSync(join(root, "zero.toml")) || existsSync(join(root, "zero.json"))) return root;
|
|
|
|
const text = readFileSync(store, "utf8");
|
|
const projections = text.matchAll(/^projection path:"((?:\\.|[^"])*)" text:/gm);
|
|
for (const projection of projections) {
|
|
const source = sourceInputForProjection(root, projection[1]);
|
|
if (source) return source;
|
|
}
|
|
return root;
|
|
}
|
|
|
|
function sourceInputForProjection(root: string, projection: string) {
|
|
let path = "";
|
|
try {
|
|
path = JSON.parse(`"${projection}"`);
|
|
} catch {
|
|
return "";
|
|
}
|
|
if (!path) return "";
|
|
const source = resolve(root, path);
|
|
const local = relative(root, source);
|
|
if (!local || local === ".." || local.startsWith(`..${sep}`) || isAbsolute(local)) return "";
|
|
if (dirname(source) !== root) return "";
|
|
return existsSync(source) ? source : "";
|
|
}
|