Files
2026-07-13 13:12:00 +08:00

71 lines
2.8 KiB
JavaScript

// Integrity checks for .github/areas.json -- the single source of truth for both
// issue triage and PR reviewer assignment. Run offline: `node .github/workflows/areas.test.js`
// (cwd = repo root). No network. Guards the invariants the two workflows rely on.
const fs = require("fs");
const path = require("path");
const areas = JSON.parse(fs.readFileSync(path.resolve(".github/areas.json"), "utf8")).areas;
const maint = new Set(
fs.readFileSync(path.resolve(".github/MAINTAINER"), "utf8")
.split("\n").map((l) => l.replace(/#.*/, "").trim().toLowerCase()).filter(Boolean)
);
// The 8 comp:* labels that exist in the repo (gh cannot add a label that does not
// exist, and there is no label-sync). Every area label must be one of these.
const ALLOWED_LABELS = new Set([
"comp:server", "comp:runner", "comp:repr", "comp:web-ui",
"comp:tui", "comp:policies", "comp:harnesses", "comp:infra",
]);
let failures = 0;
function assert(name, cond, detail) {
console.log(`${cond ? "PASS" : "FAIL"} ${name}${detail ? " -- " + detail : ""}`);
if (!cond) failures++;
}
// Every owner is a known maintainer.
for (const a of areas)
for (const o of a.owners || [])
assert(`owner @${o} (area ${a.key}) is in MAINTAINER`, maint.has(o.toLowerCase()));
// Every label is one of the real comp:* labels.
for (const a of areas)
assert(`area ${a.key} label ${a.label} is a real comp:*`, ALLOWED_LABELS.has(a.label));
// Every area has >= 2 owners (the 2+ codeowner requirement).
for (const a of areas) {
const n = (a.owners || []).length;
assert(`area ${a.key} has >= 2 owners`, n >= 2, `${n} owner(s)`);
}
// Every area has a definition and at least one path.
for (const a of areas) {
assert(`area ${a.key} has a definition`, typeof a.definition === "string" && a.definition.length > 0);
assert(`area ${a.key} has paths`, Array.isArray(a.paths) && a.paths.length > 0);
}
// Path resolution (last-match-wins startsWith) sends representative files to the
// expected area -- especially the web/ carve-out ordering and harness prefixes.
function resolve(fn) {
let match = null;
for (const a of areas) for (const p of a.paths) if (fn.startsWith(p)) match = a;
return match;
}
const cases = [
["omnigent/inner/foo.py", "inner"],
["omnigent/inner/claude_sdk_executor.py", "harness-claude"],
["omnigent/inner/kimi_executor.py", "harness-kimi"],
["omnigent/inner/kiro_native_harness.py", "harness-kiro"],
["web/src/main.tsx", "web"],
["web/ios/App.swift", "mobile-app"],
["web/electron/main.ts", "desktop-app"],
["omnigent/server/api.py", "server"],
];
for (const [fn, key] of cases) {
const m = resolve(fn);
assert(`${fn} -> ${key}`, m && m.key === key, m ? m.key : "(unmatched)");
}
console.log(failures ? `\n${failures} FAILURE(S)` : "\nAll areas.json integrity checks passed.");
process.exitCode = failures ? 1 : 0;