@biffo/cli 0.305.1 → 0.306.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +318 -78
- package/package.json +1 -1
- package/scripts/verify.sh +1 -0
package/dist/index.js
CHANGED
|
@@ -12335,8 +12335,8 @@ async function runOwnershipCheck(argv) {
|
|
|
12335
12335
|
const { stdout } = await execa("git", ["diff", "--cached", "--name-status"], { cwd: root });
|
|
12336
12336
|
({ changed: changedFiles, deleted: deletedFiles } = parseNameStatus(stdout));
|
|
12337
12337
|
if (messageFile) {
|
|
12338
|
-
const { readFileSync:
|
|
12339
|
-
if (existsSync59(messageFile)) commitMessage =
|
|
12338
|
+
const { readFileSync: readFileSync48, existsSync: existsSync59 } = await import("fs");
|
|
12339
|
+
if (existsSync59(messageFile)) commitMessage = readFileSync48(messageFile, "utf8");
|
|
12340
12340
|
}
|
|
12341
12341
|
} else {
|
|
12342
12342
|
const base = process.env["GITHUB_BASE_REF"] ?? args[0];
|
|
@@ -13396,9 +13396,244 @@ baseline was ${String(ratchet.baseline)}, now ${String(ratchet.count)}. See biff
|
|
|
13396
13396
|
}
|
|
13397
13397
|
}
|
|
13398
13398
|
|
|
13399
|
+
// src/lib/ownership-header-claim-guard.ts
|
|
13400
|
+
import { readFileSync as readFileSync39 } from "fs";
|
|
13401
|
+
import { join as join58 } from "path";
|
|
13402
|
+
var OWNERSHIP_HEADER_SWEEP_DIRS = [
|
|
13403
|
+
"scripts",
|
|
13404
|
+
".githooks",
|
|
13405
|
+
".github",
|
|
13406
|
+
"services",
|
|
13407
|
+
"cli",
|
|
13408
|
+
"infra"
|
|
13409
|
+
];
|
|
13410
|
+
var STRICT_MARKER = /(?<!`)(?:INSTANCE-OWNED|NOT a template file)(?!`)/;
|
|
13411
|
+
var SELF_REFERENTIAL_CLAIM = /\b(?:[Tt]his|[Ii]t)\b(?:\s+\S+){0,3}\s+(?:is|are|stays|remains)\s+(?:\*\*)?(template-owned|user-owned)\b/;
|
|
13412
|
+
var EM_DASH_FIRST_LINE_CLAIM = /[—–]\s*(template-owned|user-owned)\b/;
|
|
13413
|
+
var MAX_HEADER_LINES = 10;
|
|
13414
|
+
function claimFor(matchedPhrase) {
|
|
13415
|
+
return /^template-owned$/i.test(matchedPhrase) ? "template" : "user";
|
|
13416
|
+
}
|
|
13417
|
+
function claimInText(text) {
|
|
13418
|
+
const candidates = [];
|
|
13419
|
+
const strict = STRICT_MARKER.exec(text);
|
|
13420
|
+
if (strict)
|
|
13421
|
+
candidates.push({ matchedPhrase: strict[0], claim: claimFor(strict[0]), index: strict.index });
|
|
13422
|
+
const grammatical = SELF_REFERENTIAL_CLAIM.exec(text);
|
|
13423
|
+
if (grammatical) {
|
|
13424
|
+
const phrase = grammatical[1];
|
|
13425
|
+
candidates.push({ matchedPhrase: phrase, claim: claimFor(phrase), index: grammatical.index });
|
|
13426
|
+
}
|
|
13427
|
+
const firstLine = text.split("\n", 1)[0] ?? "";
|
|
13428
|
+
const emDash = EM_DASH_FIRST_LINE_CLAIM.exec(firstLine);
|
|
13429
|
+
if (emDash) {
|
|
13430
|
+
const phrase = emDash[1];
|
|
13431
|
+
candidates.push({ matchedPhrase: phrase, claim: claimFor(phrase), index: emDash.index });
|
|
13432
|
+
}
|
|
13433
|
+
if (candidates.length === 0) return null;
|
|
13434
|
+
candidates.sort((a, b) => a.index - b.index);
|
|
13435
|
+
return candidates[0] ?? null;
|
|
13436
|
+
}
|
|
13437
|
+
function fileExtension(path) {
|
|
13438
|
+
const dot = path.lastIndexOf(".");
|
|
13439
|
+
const slash = path.lastIndexOf("/");
|
|
13440
|
+
return dot > slash ? path.slice(dot).toLowerCase() : "";
|
|
13441
|
+
}
|
|
13442
|
+
function isCommentLine(line) {
|
|
13443
|
+
return line.trim().startsWith("#");
|
|
13444
|
+
}
|
|
13445
|
+
function lineAt(lines, i) {
|
|
13446
|
+
return lines[i] ?? "";
|
|
13447
|
+
}
|
|
13448
|
+
function leadingHashCommentEnd(lines, start) {
|
|
13449
|
+
let i = start;
|
|
13450
|
+
while (i < lines.length) {
|
|
13451
|
+
if (isCommentLine(lineAt(lines, i))) {
|
|
13452
|
+
i++;
|
|
13453
|
+
continue;
|
|
13454
|
+
}
|
|
13455
|
+
if (lineAt(lines, i).trim() === "") {
|
|
13456
|
+
let j = i + 1;
|
|
13457
|
+
while (j < lines.length && lineAt(lines, j).trim() === "") j++;
|
|
13458
|
+
if (j < lines.length && isCommentLine(lineAt(lines, j))) {
|
|
13459
|
+
i = j;
|
|
13460
|
+
continue;
|
|
13461
|
+
}
|
|
13462
|
+
return i;
|
|
13463
|
+
}
|
|
13464
|
+
return i;
|
|
13465
|
+
}
|
|
13466
|
+
return i;
|
|
13467
|
+
}
|
|
13468
|
+
function defaultHeaderRange(lines) {
|
|
13469
|
+
const start = lineAt(lines, 0).startsWith("#!") ? 1 : 0;
|
|
13470
|
+
const end = leadingHashCommentEnd(lines, start);
|
|
13471
|
+
return end > start ? [start, end] : null;
|
|
13472
|
+
}
|
|
13473
|
+
function pythonHeaderRange(lines) {
|
|
13474
|
+
let i = lineAt(lines, 0).startsWith("#!") ? 1 : 0;
|
|
13475
|
+
while (i < lines.length && lineAt(lines, i).trim() === "") i++;
|
|
13476
|
+
if (i >= lines.length) return null;
|
|
13477
|
+
const first = lineAt(lines, i);
|
|
13478
|
+
const trimmed = first.trimStart();
|
|
13479
|
+
const quote = trimmed.startsWith('"""') ? '"""' : trimmed.startsWith("'''") ? "'''" : null;
|
|
13480
|
+
if (quote) {
|
|
13481
|
+
const afterOpen = first.slice(first.indexOf(quote) + 3);
|
|
13482
|
+
if (afterOpen.includes(quote)) return [i, i + 1];
|
|
13483
|
+
let j = i + 1;
|
|
13484
|
+
while (j < lines.length && !lineAt(lines, j).includes(quote)) j++;
|
|
13485
|
+
return [i, Math.min(j + 1, lines.length)];
|
|
13486
|
+
}
|
|
13487
|
+
const end = leadingHashCommentEnd(lines, i);
|
|
13488
|
+
return end > i ? [i, end] : null;
|
|
13489
|
+
}
|
|
13490
|
+
function jsHeaderRange(lines) {
|
|
13491
|
+
let i = 0;
|
|
13492
|
+
while (i < lines.length && (lineAt(lines, i).trim() === "" || lineAt(lines, i).trim().startsWith("import "))) {
|
|
13493
|
+
i++;
|
|
13494
|
+
}
|
|
13495
|
+
if (i >= lines.length) return null;
|
|
13496
|
+
const first = lineAt(lines, i).trim();
|
|
13497
|
+
if (first.startsWith("/*")) {
|
|
13498
|
+
let j = i;
|
|
13499
|
+
while (j < lines.length && !lineAt(lines, j).includes("*/")) j++;
|
|
13500
|
+
return [i, Math.min(j + 1, lines.length)];
|
|
13501
|
+
}
|
|
13502
|
+
if (first.startsWith("//")) {
|
|
13503
|
+
let j = i;
|
|
13504
|
+
while (j < lines.length && (lineAt(lines, j).trim().startsWith("//") || lineAt(lines, j).trim() === ""))
|
|
13505
|
+
j++;
|
|
13506
|
+
return [i, j];
|
|
13507
|
+
}
|
|
13508
|
+
return null;
|
|
13509
|
+
}
|
|
13510
|
+
function headerRange(path, lines) {
|
|
13511
|
+
const ext = fileExtension(path);
|
|
13512
|
+
let range;
|
|
13513
|
+
if (ext === ".py") range = pythonHeaderRange(lines);
|
|
13514
|
+
else if (ext === ".ts" || ext === ".tsx" || ext === ".js" || ext === ".mjs")
|
|
13515
|
+
range = jsHeaderRange(lines);
|
|
13516
|
+
else if (ext === ".md")
|
|
13517
|
+
range = [0, Math.min(15, lines.length)];
|
|
13518
|
+
else if (ext === ".json")
|
|
13519
|
+
return null;
|
|
13520
|
+
else range = defaultHeaderRange(lines);
|
|
13521
|
+
if (!range) return null;
|
|
13522
|
+
const [start, end] = range;
|
|
13523
|
+
return [start, Math.min(end, start + MAX_HEADER_LINES)];
|
|
13524
|
+
}
|
|
13525
|
+
function findJsonDescriptionClaim(path, content) {
|
|
13526
|
+
let data;
|
|
13527
|
+
try {
|
|
13528
|
+
data = JSON.parse(content);
|
|
13529
|
+
} catch {
|
|
13530
|
+
return null;
|
|
13531
|
+
}
|
|
13532
|
+
if (typeof data !== "object" || data === null) return null;
|
|
13533
|
+
const description = data["description"];
|
|
13534
|
+
if (typeof description !== "string") return null;
|
|
13535
|
+
const match = /INSTANCE-OWNED|NOT a template file|user-owned|template-owned/i.exec(description);
|
|
13536
|
+
if (!match) return null;
|
|
13537
|
+
const lineIndex = content.split("\n").findIndex((l) => l.includes('"description"'));
|
|
13538
|
+
return {
|
|
13539
|
+
path,
|
|
13540
|
+
claim: claimFor(match[0]),
|
|
13541
|
+
matchedPhrase: match[0],
|
|
13542
|
+
line: lineIndex === -1 ? 1 : lineIndex + 1
|
|
13543
|
+
};
|
|
13544
|
+
}
|
|
13545
|
+
function findHeaderClaim(path, content) {
|
|
13546
|
+
if (fileExtension(path) === ".json") return findJsonDescriptionClaim(path, content);
|
|
13547
|
+
const lines = content.split("\n");
|
|
13548
|
+
const range = headerRange(path, lines);
|
|
13549
|
+
if (!range) return null;
|
|
13550
|
+
const [start, end] = range;
|
|
13551
|
+
const windowText = lines.slice(start, end).join("\n");
|
|
13552
|
+
const found = claimInText(windowText);
|
|
13553
|
+
if (!found) return null;
|
|
13554
|
+
const line = start + windowText.slice(0, found.index).split("\n").length;
|
|
13555
|
+
return { path, claim: found.claim, matchedPhrase: found.matchedPhrase, line };
|
|
13556
|
+
}
|
|
13557
|
+
function sweepOwnershipHeaderClaims(root, options = {}) {
|
|
13558
|
+
const tracked = options.git ? gitTrackedFiles(root, options.git) : gitTrackedFiles(root);
|
|
13559
|
+
if (!tracked) return [];
|
|
13560
|
+
const files = [...tracked].filter((p) => OWNERSHIP_HEADER_SWEEP_DIRS.some((dir) => p === dir || p.startsWith(`${dir}/`))).sort();
|
|
13561
|
+
const hits = [];
|
|
13562
|
+
for (const rel of files) {
|
|
13563
|
+
let content;
|
|
13564
|
+
try {
|
|
13565
|
+
content = readFileSync39(join58(root, rel), "utf8");
|
|
13566
|
+
} catch {
|
|
13567
|
+
continue;
|
|
13568
|
+
}
|
|
13569
|
+
const hit = findHeaderClaim(rel, content);
|
|
13570
|
+
if (hit) hits.push(hit);
|
|
13571
|
+
}
|
|
13572
|
+
return hits;
|
|
13573
|
+
}
|
|
13574
|
+
function matchesReleased(relPath, manifest) {
|
|
13575
|
+
return (manifest.released ?? []).some((p) => relPath === p || relPath.startsWith(p));
|
|
13576
|
+
}
|
|
13577
|
+
function checkOwnershipHeaderClaims(hits, manifest) {
|
|
13578
|
+
const out = [];
|
|
13579
|
+
for (const hit of hits) {
|
|
13580
|
+
const manifestSaysTemplateOwned = isTemplateOwned(hit.path, manifest);
|
|
13581
|
+
const released = matchesReleased(hit.path, manifest);
|
|
13582
|
+
const agrees = hit.claim === "template" ? manifestSaysTemplateOwned || released : !manifestSaysTemplateOwned && !released;
|
|
13583
|
+
if (!agrees) {
|
|
13584
|
+
out.push({ ...hit, manifestSaysTemplateOwned });
|
|
13585
|
+
}
|
|
13586
|
+
}
|
|
13587
|
+
return out;
|
|
13588
|
+
}
|
|
13589
|
+
|
|
13590
|
+
// src/scripts/check-ownership-header-claim.ts
|
|
13591
|
+
async function runOwnershipHeaderClaimCheck() {
|
|
13592
|
+
const root = (await execa("git", ["rev-parse", "--show-toplevel"])).stdout.trim();
|
|
13593
|
+
const ownership = classifyRepoOwnership(root);
|
|
13594
|
+
if (ownership === "satellite") {
|
|
13595
|
+
console.log(
|
|
13596
|
+
"\u2713 ownership-header-claim guard: skipped \u2014 this repo carries no core-manifest.json, so there is no ownership authority to check a header claim against."
|
|
13597
|
+
);
|
|
13598
|
+
return;
|
|
13599
|
+
}
|
|
13600
|
+
const manifest = readCoreManifest(root);
|
|
13601
|
+
const hits = sweepOwnershipHeaderClaims(root);
|
|
13602
|
+
console.log(
|
|
13603
|
+
`examined ${hits.length} file(s) carrying an ownership-style header claim under scripts/, .githooks/, .github/, services/, cli/, infra/`
|
|
13604
|
+
);
|
|
13605
|
+
if (hits.length === 0) {
|
|
13606
|
+
console.error(
|
|
13607
|
+
"\u2717 ownership-header-claim guard: swept zero files \u2014 refusing a false green rather than assuming every ownership-style header vanished."
|
|
13608
|
+
);
|
|
13609
|
+
process.exit(1);
|
|
13610
|
+
return;
|
|
13611
|
+
}
|
|
13612
|
+
const disagreements = checkOwnershipHeaderClaims(hits, manifest);
|
|
13613
|
+
if (disagreements.length > 0) {
|
|
13614
|
+
console.error(
|
|
13615
|
+
`\u2717 ownership-header-claim guard: ${disagreements.length} of ${hits.length} file(s) carry a header claim core-manifest.json disagrees with (#1911):
|
|
13616
|
+
`
|
|
13617
|
+
);
|
|
13618
|
+
for (const d of disagreements) {
|
|
13619
|
+
console.error(
|
|
13620
|
+
` ${d.path}:${d.line} header says "${d.matchedPhrase}" (${d.claim}-owned), but core-manifest.json says ${d.manifestSaysTemplateOwned ? "template-owned" : "user-owned"}`
|
|
13621
|
+
);
|
|
13622
|
+
}
|
|
13623
|
+
console.error(
|
|
13624
|
+
"\nEither fix the header to match core-manifest.json, or fix core-manifest.json to match reality \u2014 never leave the two disagreeing."
|
|
13625
|
+
);
|
|
13626
|
+
process.exit(1);
|
|
13627
|
+
return;
|
|
13628
|
+
}
|
|
13629
|
+
console.log(
|
|
13630
|
+
`\u2713 ownership-header-claim guard: examined ${hits.length} file(s), all agree with core-manifest.json`
|
|
13631
|
+
);
|
|
13632
|
+
}
|
|
13633
|
+
|
|
13399
13634
|
// src/scripts/check-pipe-trap.ts
|
|
13400
|
-
import { readFileSync as
|
|
13401
|
-
import { join as
|
|
13635
|
+
import { readFileSync as readFileSync40, readdirSync as readdirSync23 } from "fs";
|
|
13636
|
+
import { join as join59, relative as relative9 } from "path";
|
|
13402
13637
|
|
|
13403
13638
|
// src/lib/pipe-trap-guard.ts
|
|
13404
13639
|
var STATUS_BEARING = [
|
|
@@ -13494,7 +13729,7 @@ function findPipeTraps(source) {
|
|
|
13494
13729
|
function shellFiles(root) {
|
|
13495
13730
|
const out = [];
|
|
13496
13731
|
for (const dir of ["scripts", ".githooks"]) {
|
|
13497
|
-
const full =
|
|
13732
|
+
const full = join59(root, dir);
|
|
13498
13733
|
let entries;
|
|
13499
13734
|
try {
|
|
13500
13735
|
entries = readdirSync23(full, { withFileTypes: true });
|
|
@@ -13504,7 +13739,7 @@ function shellFiles(root) {
|
|
|
13504
13739
|
for (const entry of entries) {
|
|
13505
13740
|
if (!entry.isFile()) continue;
|
|
13506
13741
|
if (dir === "scripts" && !entry.name.endsWith(".sh")) continue;
|
|
13507
|
-
out.push(
|
|
13742
|
+
out.push(join59(full, entry.name));
|
|
13508
13743
|
}
|
|
13509
13744
|
}
|
|
13510
13745
|
return out;
|
|
@@ -13520,7 +13755,7 @@ async function runPipeTrapCheck() {
|
|
|
13520
13755
|
process.exit(1);
|
|
13521
13756
|
}
|
|
13522
13757
|
const findings = files.flatMap(
|
|
13523
|
-
(file) => findPipeTraps(
|
|
13758
|
+
(file) => findPipeTraps(readFileSync40(file, "utf8")).map(
|
|
13524
13759
|
(t) => `${relative9(root, file)}:${t.line} ${t.text}
|
|
13525
13760
|
${t.reason}`
|
|
13526
13761
|
)
|
|
@@ -13538,11 +13773,11 @@ async function runPipeTrapCheck() {
|
|
|
13538
13773
|
|
|
13539
13774
|
// src/scripts/check-plugin-allowlist-convention.ts
|
|
13540
13775
|
import { existsSync as existsSync50 } from "fs";
|
|
13541
|
-
import { join as
|
|
13776
|
+
import { join as join61 } from "path";
|
|
13542
13777
|
|
|
13543
13778
|
// src/lib/plugin-allowlist-convention.ts
|
|
13544
|
-
import { readFileSync as
|
|
13545
|
-
import { join as
|
|
13779
|
+
import { readFileSync as readFileSync41 } from "fs";
|
|
13780
|
+
import { join as join60 } from "path";
|
|
13546
13781
|
var COMPUTE_MAIN_TF = "modules/cloud/aws/compute/main.tf";
|
|
13547
13782
|
var PLUGIN_TEMPLATE_MAIN_TF = "modules/plugins/_template/main.tf";
|
|
13548
13783
|
var ALLOWLIST_MAIN_TF = "modules/cloud/aws/plugin-allowlist/main.tf";
|
|
@@ -13553,7 +13788,7 @@ var PLUGIN = "<plugin>";
|
|
|
13553
13788
|
var ACCOUNT = "<account>";
|
|
13554
13789
|
function read(repoRoot, relative11) {
|
|
13555
13790
|
try {
|
|
13556
|
-
return
|
|
13791
|
+
return readFileSync41(join60(repoRoot, relative11), "utf8");
|
|
13557
13792
|
} catch {
|
|
13558
13793
|
throw new Error(`plugin-allowlist drift guard: cannot read ${relative11}`);
|
|
13559
13794
|
}
|
|
@@ -13656,7 +13891,7 @@ var REQUIRED_SOURCES = [
|
|
|
13656
13891
|
];
|
|
13657
13892
|
async function runPluginAllowlistConventionCheck() {
|
|
13658
13893
|
const root = (await execa("git", ["rev-parse", "--show-toplevel"])).stdout.trim();
|
|
13659
|
-
const missing = REQUIRED_SOURCES.filter((relative11) => !existsSync50(
|
|
13894
|
+
const missing = REQUIRED_SOURCES.filter((relative11) => !existsSync50(join61(root, relative11)));
|
|
13660
13895
|
if (missing.length === REQUIRED_SOURCES.length) {
|
|
13661
13896
|
console.log(
|
|
13662
13897
|
`\xB7 Plugin-allowlist convention guard: not applicable \u2014 ${missing.join(", ")} not found under ${root}. This is not a template/instance tree (a satellite repo never carries the plugin-allowlist Terraform modules). Skipping.`
|
|
@@ -13688,11 +13923,11 @@ async function runPluginAllowlistConventionCheck() {
|
|
|
13688
13923
|
|
|
13689
13924
|
// src/scripts/check-plugin-collisions.ts
|
|
13690
13925
|
import { existsSync as existsSync52 } from "fs";
|
|
13691
|
-
import { join as
|
|
13926
|
+
import { join as join63 } from "path";
|
|
13692
13927
|
|
|
13693
13928
|
// src/lib/plugin-collision-guard.ts
|
|
13694
13929
|
import { existsSync as existsSync51, readdirSync as readdirSync24, statSync as statSync15 } from "fs";
|
|
13695
|
-
import { join as
|
|
13930
|
+
import { join as join62 } from "path";
|
|
13696
13931
|
var PYTEST_SPECIAL = /* @__PURE__ */ new Set(["conftest.py"]);
|
|
13697
13932
|
var IGNORED_DIRS = /* @__PURE__ */ new Set([".venv", "node_modules", "__pycache__", ".git", "dist", "build"]);
|
|
13698
13933
|
function subdirectories(dir) {
|
|
@@ -13700,19 +13935,19 @@ function subdirectories(dir) {
|
|
|
13700
13935
|
return readdirSync24(dir).filter((entry) => {
|
|
13701
13936
|
if (IGNORED_DIRS.has(entry) || entry.startsWith(".")) return false;
|
|
13702
13937
|
try {
|
|
13703
|
-
return statSync15(
|
|
13938
|
+
return statSync15(join62(dir, entry)).isDirectory();
|
|
13704
13939
|
} catch {
|
|
13705
13940
|
return false;
|
|
13706
13941
|
}
|
|
13707
13942
|
});
|
|
13708
13943
|
}
|
|
13709
13944
|
function regularPackagesOf(pluginDir2) {
|
|
13710
|
-
return subdirectories(pluginDir2).filter((name) => existsSync51(
|
|
13945
|
+
return subdirectories(pluginDir2).filter((name) => existsSync51(join62(pluginDir2, name, "__init__.py"))).sort();
|
|
13711
13946
|
}
|
|
13712
13947
|
function bareTestModulesOf(pluginDir2) {
|
|
13713
|
-
const testsDir =
|
|
13948
|
+
const testsDir = join62(pluginDir2, "tests");
|
|
13714
13949
|
if (!existsSync51(testsDir)) return [];
|
|
13715
|
-
if (existsSync51(
|
|
13950
|
+
if (existsSync51(join62(testsDir, "__init__.py"))) return [];
|
|
13716
13951
|
return readdirSync24(testsDir).filter((f) => f.endsWith(".py") && !PYTEST_SPECIAL.has(f)).sort();
|
|
13717
13952
|
}
|
|
13718
13953
|
function findCollisions(servicesDir, pluginDirs) {
|
|
@@ -13721,7 +13956,7 @@ function findCollisions(servicesDir, pluginDirs) {
|
|
|
13721
13956
|
const gather = (kind, namesOf) => {
|
|
13722
13957
|
const claims = /* @__PURE__ */ new Map();
|
|
13723
13958
|
for (const plugin of plugins) {
|
|
13724
|
-
for (const name of namesOf(
|
|
13959
|
+
for (const name of namesOf(join62(servicesDir, plugin))) {
|
|
13725
13960
|
claims.set(name, [...claims.get(name) ?? [], plugin]);
|
|
13726
13961
|
}
|
|
13727
13962
|
}
|
|
@@ -13759,7 +13994,7 @@ function formatCollisions(collisions) {
|
|
|
13759
13994
|
// src/scripts/check-plugin-collisions.ts
|
|
13760
13995
|
async function runPluginCollisionCheck() {
|
|
13761
13996
|
const root = (await execa("git", ["rev-parse", "--show-toplevel"])).stdout.trim();
|
|
13762
|
-
const servicesDir =
|
|
13997
|
+
const servicesDir = join63(root, "services");
|
|
13763
13998
|
if (!existsSync52(servicesDir)) {
|
|
13764
13999
|
console.log("\u2713 plugin collision guard: no services/ directory \u2014 nothing to compare");
|
|
13765
14000
|
return;
|
|
@@ -13777,8 +14012,8 @@ async function runPluginCollisionCheck() {
|
|
|
13777
14012
|
}
|
|
13778
14013
|
|
|
13779
14014
|
// src/lib/plugin-terraform-guard.ts
|
|
13780
|
-
import { existsSync as existsSync53, readFileSync as
|
|
13781
|
-
import { dirname as dirname10, join as
|
|
14015
|
+
import { existsSync as existsSync53, readFileSync as readFileSync42, readdirSync as readdirSync25 } from "fs";
|
|
14016
|
+
import { dirname as dirname10, join as join64, relative as relative10, sep as sep4 } from "path";
|
|
13782
14017
|
var SKIP_DIRS4 = /* @__PURE__ */ new Set(["node_modules", ".git", ".worktrees", "dist", ".venv", "__pycache__"]);
|
|
13783
14018
|
var PLUGIN_MANIFEST_FILE2 = "biffo.plugin.json";
|
|
13784
14019
|
function findPluginManifests(root) {
|
|
@@ -13793,9 +14028,9 @@ function findPluginManifests(root) {
|
|
|
13793
14028
|
for (const entry of entries) {
|
|
13794
14029
|
if (entry.isDirectory()) {
|
|
13795
14030
|
if (SKIP_DIRS4.has(entry.name)) continue;
|
|
13796
|
-
walk2(
|
|
14031
|
+
walk2(join64(dir, entry.name));
|
|
13797
14032
|
} else if (entry.isFile() && entry.name === PLUGIN_MANIFEST_FILE2) {
|
|
13798
|
-
found.push(relative10(root,
|
|
14033
|
+
found.push(relative10(root, join64(dir, entry.name)).split(sep4).join("/"));
|
|
13799
14034
|
}
|
|
13800
14035
|
}
|
|
13801
14036
|
};
|
|
@@ -13805,7 +14040,7 @@ function findPluginManifests(root) {
|
|
|
13805
14040
|
function readSubscriptions(absManifestPath) {
|
|
13806
14041
|
let parsed;
|
|
13807
14042
|
try {
|
|
13808
|
-
parsed = JSON.parse(
|
|
14043
|
+
parsed = JSON.parse(readFileSync42(absManifestPath, "utf8"));
|
|
13809
14044
|
} catch {
|
|
13810
14045
|
return null;
|
|
13811
14046
|
}
|
|
@@ -13820,14 +14055,14 @@ function readSubscriptions(absManifestPath) {
|
|
|
13820
14055
|
}
|
|
13821
14056
|
function checkPluginTerraform(root) {
|
|
13822
14057
|
const violations = [];
|
|
13823
|
-
const coreManifest = existsSync53(
|
|
14058
|
+
const coreManifest = existsSync53(join64(root, CORE_MANIFEST_FILE)) ? readCoreManifest(root) : null;
|
|
13824
14059
|
for (const manifest of findPluginManifests(root)) {
|
|
13825
14060
|
if (coreManifest && !isTemplateOwned(manifest, coreManifest)) continue;
|
|
13826
|
-
const absManifest =
|
|
14061
|
+
const absManifest = join64(root, manifest);
|
|
13827
14062
|
const subscriptions = readSubscriptions(absManifest);
|
|
13828
14063
|
if (subscriptions === null) continue;
|
|
13829
14064
|
const pluginDir2 = dirname10(absManifest);
|
|
13830
|
-
if (existsSync53(
|
|
14065
|
+
if (existsSync53(join64(pluginDir2, "terraform"))) continue;
|
|
13831
14066
|
const relPluginDir = relative10(root, pluginDir2).split(sep4).join("/");
|
|
13832
14067
|
violations.push({
|
|
13833
14068
|
manifest,
|
|
@@ -13859,11 +14094,11 @@ async function runPluginTerraformCheck() {
|
|
|
13859
14094
|
|
|
13860
14095
|
// src/scripts/check-plugin-tool-supply.ts
|
|
13861
14096
|
import { existsSync as existsSync55 } from "fs";
|
|
13862
|
-
import { join as
|
|
14097
|
+
import { join as join66 } from "path";
|
|
13863
14098
|
|
|
13864
14099
|
// src/lib/plugin-tool-supply-audit.ts
|
|
13865
|
-
import { existsSync as existsSync54, readFileSync as
|
|
13866
|
-
import { join as
|
|
14100
|
+
import { existsSync as existsSync54, readFileSync as readFileSync43, readdirSync as readdirSync26, statSync as statSync16 } from "fs";
|
|
14101
|
+
import { join as join65 } from "path";
|
|
13867
14102
|
|
|
13868
14103
|
// src/lib/openrouter-model-snapshot.ts
|
|
13869
14104
|
var OPENROUTER_MODEL_SNAPSHOT_FETCHED_AT = "2026-08-10T06:39:01Z";
|
|
@@ -14280,7 +14515,7 @@ function listDirs(root) {
|
|
|
14280
14515
|
}
|
|
14281
14516
|
return entries.filter((e) => {
|
|
14282
14517
|
try {
|
|
14283
|
-
return statSync16(
|
|
14518
|
+
return statSync16(join65(root, e)).isDirectory();
|
|
14284
14519
|
} catch {
|
|
14285
14520
|
return false;
|
|
14286
14521
|
}
|
|
@@ -14296,7 +14531,7 @@ function walkFiles2(root, accept, skipDir) {
|
|
|
14296
14531
|
return;
|
|
14297
14532
|
}
|
|
14298
14533
|
for (const entry of entries) {
|
|
14299
|
-
const p =
|
|
14534
|
+
const p = join65(dir, entry);
|
|
14300
14535
|
let st;
|
|
14301
14536
|
try {
|
|
14302
14537
|
st = statSync16(p);
|
|
@@ -14322,14 +14557,14 @@ function pluginPythonFiles(pluginDir2) {
|
|
|
14322
14557
|
);
|
|
14323
14558
|
}
|
|
14324
14559
|
function pluginTerraformFiles(pluginDir2) {
|
|
14325
|
-
const tfDir =
|
|
14560
|
+
const tfDir = join65(pluginDir2, "terraform");
|
|
14326
14561
|
let entries;
|
|
14327
14562
|
try {
|
|
14328
14563
|
entries = readdirSync26(tfDir);
|
|
14329
14564
|
} catch {
|
|
14330
14565
|
return [];
|
|
14331
14566
|
}
|
|
14332
|
-
return entries.filter((e) => e.endsWith(".tf")).map((e) =>
|
|
14567
|
+
return entries.filter((e) => e.endsWith(".tf")).map((e) => join65(tfDir, e)).sort();
|
|
14333
14568
|
}
|
|
14334
14569
|
function extractManifestTools(manifestText) {
|
|
14335
14570
|
let parsed;
|
|
@@ -14581,8 +14816,8 @@ function isSnapshotStale(fetchedAt, now) {
|
|
|
14581
14816
|
function normalizeModelId(id) {
|
|
14582
14817
|
return id.endsWith(":online") ? id.slice(0, -":online".length) : id;
|
|
14583
14818
|
}
|
|
14584
|
-
var CONFIG_PY_PATH =
|
|
14585
|
-
var ORCHESTRATION_SCHEMA_PATH =
|
|
14819
|
+
var CONFIG_PY_PATH = join65("services", "api", "src", "api", "config.py");
|
|
14820
|
+
var ORCHESTRATION_SCHEMA_PATH = join65(
|
|
14586
14821
|
"services",
|
|
14587
14822
|
"api",
|
|
14588
14823
|
"src",
|
|
@@ -14594,8 +14829,8 @@ function auditDeclaredModelIds(repoRoot, options = {}) {
|
|
|
14594
14829
|
const knownModelIds = options.knownModelIds ?? OPENROUTER_MODEL_IDS;
|
|
14595
14830
|
const snapshotFetchedAt = options.snapshotFetchedAt ?? OPENROUTER_MODEL_SNAPSHOT_FETCHED_AT;
|
|
14596
14831
|
const now = options.now ?? /* @__PURE__ */ new Date();
|
|
14597
|
-
const configPath =
|
|
14598
|
-
const orchestrationPath =
|
|
14832
|
+
const configPath = join65(repoRoot, CONFIG_PY_PATH);
|
|
14833
|
+
const orchestrationPath = join65(repoRoot, ORCHESTRATION_SCHEMA_PATH);
|
|
14599
14834
|
const configMissing = !existsSync54(configPath);
|
|
14600
14835
|
const orchestrationSchemaMissing = !existsSync54(orchestrationPath);
|
|
14601
14836
|
const knownSet = new Set(knownModelIds);
|
|
@@ -14615,13 +14850,13 @@ function auditDeclaredModelIds(repoRoot, options = {}) {
|
|
|
14615
14850
|
};
|
|
14616
14851
|
let settingsBlind = false;
|
|
14617
14852
|
if (!configMissing) {
|
|
14618
|
-
const settingsFields = extractSettingsModelFields(
|
|
14853
|
+
const settingsFields = extractSettingsModelFields(readFileSync43(configPath, "utf8"));
|
|
14619
14854
|
if (settingsFields.length === 0) settingsBlind = true;
|
|
14620
14855
|
for (const { field, value } of settingsFields) record(`${CONFIG_PY_PATH}#${field}`, value);
|
|
14621
14856
|
}
|
|
14622
14857
|
let curatedFieldsBlind = false;
|
|
14623
14858
|
if (!orchestrationSchemaMissing) {
|
|
14624
|
-
const curated = extractCuratedModelFields(
|
|
14859
|
+
const curated = extractCuratedModelFields(readFileSync43(orchestrationPath, "utf8"));
|
|
14625
14860
|
if (curated.rawFieldCount > 0 && curated.fields.every((f) => f.defaultValue === null && f.optionValues.length === 0)) {
|
|
14626
14861
|
curatedFieldsBlind = true;
|
|
14627
14862
|
}
|
|
@@ -14668,7 +14903,7 @@ function auditDeclaredModelIds(repoRoot, options = {}) {
|
|
|
14668
14903
|
function discoverPluginDirs(pluginsRoot) {
|
|
14669
14904
|
return listDirs(pluginsRoot).filter((name) => {
|
|
14670
14905
|
try {
|
|
14671
|
-
return statSync16(
|
|
14906
|
+
return statSync16(join65(pluginsRoot, name, "biffo.plugin.json")).isFile();
|
|
14672
14907
|
} catch {
|
|
14673
14908
|
return false;
|
|
14674
14909
|
}
|
|
@@ -14681,8 +14916,8 @@ function auditPluginToolSupply(pluginsRoot) {
|
|
|
14681
14916
|
let terraformBlind = false;
|
|
14682
14917
|
let totalDeclaredTools = 0;
|
|
14683
14918
|
for (const name of pluginNames) {
|
|
14684
|
-
const pluginDir2 =
|
|
14685
|
-
const manifestText =
|
|
14919
|
+
const pluginDir2 = join65(pluginsRoot, name);
|
|
14920
|
+
const manifestText = readFileSync43(join65(pluginDir2, "biffo.plugin.json"), "utf8");
|
|
14686
14921
|
const manifest = extractManifestTools(manifestText);
|
|
14687
14922
|
if (manifest.parseError) {
|
|
14688
14923
|
findings.push({
|
|
@@ -14700,13 +14935,13 @@ function auditPluginToolSupply(pluginsRoot) {
|
|
|
14700
14935
|
totalDeclaredTools += manifest.tools.length;
|
|
14701
14936
|
const pySources = pluginPythonFiles(pluginDir2).map((f) => ({
|
|
14702
14937
|
file: f,
|
|
14703
|
-
text:
|
|
14938
|
+
text: readFileSync43(f, "utf8")
|
|
14704
14939
|
}));
|
|
14705
14940
|
const resolver = buildSymbolResolver(pySources);
|
|
14706
14941
|
const registry = extractToolRegistryEntries(pySources, resolver);
|
|
14707
14942
|
if (registry.rawToolDefinitionCount > 0 && registry.entries.length === 0) registryBlind = true;
|
|
14708
14943
|
const tfFiles = pluginTerraformFiles(pluginDir2);
|
|
14709
|
-
const tfText = tfFiles.map((f) =>
|
|
14944
|
+
const tfText = tfFiles.map((f) => readFileSync43(f, "utf8")).join("\n");
|
|
14710
14945
|
const terraform = extractTerraformEnvKeys(tfText);
|
|
14711
14946
|
if (terraform.rawMarkerCount > 0 && terraform.resolvedBlockCount === 0) terraformBlind = true;
|
|
14712
14947
|
for (const toolName of manifest.tools) {
|
|
@@ -14780,7 +15015,7 @@ function auditPluginToolSupply(pluginsRoot) {
|
|
|
14780
15015
|
requiredEnvVars: envResult.envVars,
|
|
14781
15016
|
missingEnvVars: anyWired ? [] : envResult.envVars,
|
|
14782
15017
|
status: anyWired ? "ok" : "missing-env",
|
|
14783
|
-
detail: anyWired ? `${entry.predicate}() is satisfiable: at least one of ${JSON.stringify(envResult.envVars)} is wired in Terraform` : `${entry.predicate}() reads ${JSON.stringify(envResult.envVars)} \u2014 NONE of these are wired by any environment_variables block under ${
|
|
15018
|
+
detail: anyWired ? `${entry.predicate}() is satisfiable: at least one of ${JSON.stringify(envResult.envVars)} is wired in Terraform` : `${entry.predicate}() reads ${JSON.stringify(envResult.envVars)} \u2014 NONE of these are wired by any environment_variables block under ${join65(pluginDir2, "terraform")}, so this deployment can never supply it`
|
|
14784
15019
|
});
|
|
14785
15020
|
}
|
|
14786
15021
|
}
|
|
@@ -14813,7 +15048,7 @@ function auditPluginToolSupply(pluginsRoot) {
|
|
|
14813
15048
|
async function runPluginToolSupplyCheck() {
|
|
14814
15049
|
const root = (await execa("git", ["rev-parse", "--show-toplevel"])).stdout.trim();
|
|
14815
15050
|
let allOk = true;
|
|
14816
|
-
const pluginsRoot =
|
|
15051
|
+
const pluginsRoot = join66(root, "services", "_plugins");
|
|
14817
15052
|
if (!existsSync55(pluginsRoot)) {
|
|
14818
15053
|
console.log("\u2713 plugin tool-supply guard: no services/_plugins/ \u2014 nothing to audit");
|
|
14819
15054
|
} else {
|
|
@@ -14844,7 +15079,7 @@ async function runPluginToolSupplyCheck() {
|
|
|
14844
15079
|
console.log(`\u2713 plugin tool-supply guard: ${report.summary}`);
|
|
14845
15080
|
}
|
|
14846
15081
|
}
|
|
14847
|
-
const servicesApiRoot =
|
|
15082
|
+
const servicesApiRoot = join66(root, "services", "api");
|
|
14848
15083
|
if (!existsSync55(servicesApiRoot)) {
|
|
14849
15084
|
console.log("\u2713 plugin model-id guard: no services/api/ \u2014 nothing to audit");
|
|
14850
15085
|
} else {
|
|
@@ -15019,10 +15254,10 @@ async function runReleaseSubjectCheck(argv) {
|
|
|
15019
15254
|
}
|
|
15020
15255
|
|
|
15021
15256
|
// src/scripts/check-shared-file-reduction.ts
|
|
15022
|
-
import { readFileSync as
|
|
15257
|
+
import { readFileSync as readFileSync45 } from "fs";
|
|
15023
15258
|
|
|
15024
15259
|
// src/lib/shared-file-reduction-guard.ts
|
|
15025
|
-
import { readFileSync as
|
|
15260
|
+
import { readFileSync as readFileSync44 } from "fs";
|
|
15026
15261
|
var LEAF_TEST_CALLS = /* @__PURE__ */ new Set(["it", "test"]);
|
|
15027
15262
|
var SUITE_CALLS = /* @__PURE__ */ new Set(["describe", "suite"]);
|
|
15028
15263
|
var TEST_FILE_PATTERN = /\.(test|spec)\.(ts|tsx|mts|cts|js|jsx|mjs|cjs)$/;
|
|
@@ -15149,7 +15384,7 @@ function formatReductionReport(report) {
|
|
|
15149
15384
|
// src/scripts/check-shared-file-reduction.ts
|
|
15150
15385
|
function readStdin() {
|
|
15151
15386
|
try {
|
|
15152
|
-
return
|
|
15387
|
+
return readFileSync45(0, "utf8");
|
|
15153
15388
|
} catch {
|
|
15154
15389
|
return "";
|
|
15155
15390
|
}
|
|
@@ -15166,15 +15401,15 @@ function pairsFromTsv(tsv) {
|
|
|
15166
15401
|
const [target, existingPath, incomingPath] = fields;
|
|
15167
15402
|
pairs.push({
|
|
15168
15403
|
target,
|
|
15169
|
-
existing:
|
|
15170
|
-
incoming:
|
|
15404
|
+
existing: readFileSync45(existingPath, "utf8"),
|
|
15405
|
+
incoming: readFileSync45(incomingPath, "utf8")
|
|
15171
15406
|
});
|
|
15172
15407
|
}
|
|
15173
15408
|
return pairs;
|
|
15174
15409
|
}
|
|
15175
15410
|
function loadAccepted(manifestPath) {
|
|
15176
15411
|
if (!manifestPath) return {};
|
|
15177
|
-
const parsed = JSON.parse(
|
|
15412
|
+
const parsed = JSON.parse(readFileSync45(manifestPath, "utf8"));
|
|
15178
15413
|
return parsed.acceptedReductions ?? {};
|
|
15179
15414
|
}
|
|
15180
15415
|
async function runSharedFileReductionCheck(args) {
|
|
@@ -15182,13 +15417,13 @@ async function runSharedFileReductionCheck(args) {
|
|
|
15182
15417
|
let accepted;
|
|
15183
15418
|
try {
|
|
15184
15419
|
if (args.pairs) {
|
|
15185
|
-
pairs = pairsFromTsv(args.pairs === "-" ? readStdin() :
|
|
15420
|
+
pairs = pairsFromTsv(args.pairs === "-" ? readStdin() : readFileSync45(args.pairs, "utf8"));
|
|
15186
15421
|
} else if (args.target && args.existing && args.incoming) {
|
|
15187
15422
|
pairs = [
|
|
15188
15423
|
{
|
|
15189
15424
|
target: args.target,
|
|
15190
|
-
existing:
|
|
15191
|
-
incoming:
|
|
15425
|
+
existing: readFileSync45(args.existing, "utf8"),
|
|
15426
|
+
incoming: readFileSync45(args.incoming, "utf8")
|
|
15192
15427
|
}
|
|
15193
15428
|
];
|
|
15194
15429
|
} else {
|
|
@@ -15223,11 +15458,11 @@ async function runSharedFileReductionCheck(args) {
|
|
|
15223
15458
|
|
|
15224
15459
|
// src/scripts/check-skeleton-drift.ts
|
|
15225
15460
|
import { existsSync as existsSync56, readdirSync as readdirSync28 } from "fs";
|
|
15226
|
-
import { join as
|
|
15461
|
+
import { join as join68 } from "path";
|
|
15227
15462
|
|
|
15228
15463
|
// src/lib/skeleton-drift-guard.ts
|
|
15229
|
-
import { readFileSync as
|
|
15230
|
-
import { join as
|
|
15464
|
+
import { readFileSync as readFileSync46, readdirSync as readdirSync27, statSync as statSync17 } from "fs";
|
|
15465
|
+
import { join as join67 } from "path";
|
|
15231
15466
|
var isWorkflow = (rel) => rel.startsWith(".github/workflows/") && (rel.endsWith(".yml") || rel.endsWith(".yaml"));
|
|
15232
15467
|
var isRootLayout = (rel) => rel.endsWith("src/app/layout.tsx");
|
|
15233
15468
|
var uncommented = (contents) => contents.split("\n").filter((line) => !/^\s*(\/\/|\/\*|\*)/.test(line)).join("\n");
|
|
@@ -15291,7 +15526,7 @@ function walk(dir, base = dir) {
|
|
|
15291
15526
|
}
|
|
15292
15527
|
for (const entry of entries) {
|
|
15293
15528
|
if (entry === ".venv" || entry === "node_modules" || entry === ".git") continue;
|
|
15294
|
-
const abs =
|
|
15529
|
+
const abs = join67(dir, entry);
|
|
15295
15530
|
let isDir;
|
|
15296
15531
|
try {
|
|
15297
15532
|
isDir = statSync17(abs).isDirectory();
|
|
@@ -15313,7 +15548,7 @@ function auditSkeleton(skeletonRoot, name, rules = SKELETON_RULES) {
|
|
|
15313
15548
|
if (!rule.appliesTo(rel)) continue;
|
|
15314
15549
|
let contents;
|
|
15315
15550
|
try {
|
|
15316
|
-
contents =
|
|
15551
|
+
contents = readFileSync46(join67(skeletonRoot, rel), "utf8");
|
|
15317
15552
|
} catch {
|
|
15318
15553
|
continue;
|
|
15319
15554
|
}
|
|
@@ -15342,23 +15577,23 @@ function formatViolations2(violations) {
|
|
|
15342
15577
|
|
|
15343
15578
|
// src/scripts/check-skeleton-drift.ts
|
|
15344
15579
|
function discoverSkeletons(root) {
|
|
15345
|
-
const skeletonsDir =
|
|
15580
|
+
const skeletonsDir = join68(root, "_skeletons");
|
|
15346
15581
|
let entries;
|
|
15347
15582
|
try {
|
|
15348
15583
|
entries = readdirSync28(skeletonsDir, { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) => e.name);
|
|
15349
15584
|
} catch {
|
|
15350
15585
|
return [];
|
|
15351
15586
|
}
|
|
15352
|
-
return entries.filter((name) => existsSync56(
|
|
15587
|
+
return entries.filter((name) => existsSync56(join68(skeletonsDir, name, ".github", "workflows", "ci.yml"))).sort();
|
|
15353
15588
|
}
|
|
15354
15589
|
async function runSkeletonDriftCheck() {
|
|
15355
15590
|
const root = (await execa("git", ["rev-parse", "--show-toplevel"])).stdout.trim();
|
|
15356
15591
|
const skeletons = discoverSkeletons(root);
|
|
15357
15592
|
let filesConsidered = 0;
|
|
15358
15593
|
for (const name of skeletons) {
|
|
15359
|
-
const skeletonRoot =
|
|
15594
|
+
const skeletonRoot = join68(root, "_skeletons", name);
|
|
15360
15595
|
filesConsidered += findWorkflowFiles(skeletonRoot).length;
|
|
15361
|
-
if (existsSync56(
|
|
15596
|
+
if (existsSync56(join68(skeletonRoot, "apps", "frontend", "src", "app", "layout.tsx"))) {
|
|
15362
15597
|
filesConsidered += 1;
|
|
15363
15598
|
}
|
|
15364
15599
|
}
|
|
@@ -15366,7 +15601,7 @@ async function runSkeletonDriftCheck() {
|
|
|
15366
15601
|
`audited ${skeletons.length} skeleton(s) (${skeletons.join(", ") || "none"}), ${filesConsidered} file(s) considered, under ${root}/_skeletons`
|
|
15367
15602
|
);
|
|
15368
15603
|
if (skeletons.length === 0) {
|
|
15369
|
-
if (!existsSync56(
|
|
15604
|
+
if (!existsSync56(join68(root, "_skeletons"))) {
|
|
15370
15605
|
console.log(
|
|
15371
15606
|
`\xB7 Skeleton-drift guard: no _skeletons/ directory under ${root} \u2014 this is not a repo that ships scaffolding (a satellite repo never carries one). Not applicable; skipping.`
|
|
15372
15607
|
);
|
|
@@ -15378,7 +15613,7 @@ async function runSkeletonDriftCheck() {
|
|
|
15378
15613
|
process.exit(1);
|
|
15379
15614
|
}
|
|
15380
15615
|
const violations = skeletons.flatMap(
|
|
15381
|
-
(name) => auditSkeleton(
|
|
15616
|
+
(name) => auditSkeleton(join68(root, "_skeletons", name), name)
|
|
15382
15617
|
);
|
|
15383
15618
|
if (violations.length > 0) {
|
|
15384
15619
|
console.error("\u2717 Skeleton-drift guard: drift found between this repo and its scaffolding\n");
|
|
@@ -15414,7 +15649,7 @@ async function runTerraformInputCheck() {
|
|
|
15414
15649
|
|
|
15415
15650
|
// src/commands/check.ts
|
|
15416
15651
|
var checkCommand = new Command24("check").description(
|
|
15417
|
-
"Repo guards (ownership, release subject, plugin terraform, plugin collisions, eventbridge-log-permissions, plugin-tool-supply, core-direct-paths, instance-adoption, orphan-ratchet, cognito-invite-template, lambda-output, pipe-trap, codeql-suppression, skeleton-drift, terraform-input, plugin-allowlist-convention, migration-body-change, distribution-inventory, distribution-remote-state, api-gateway-integration) run in CI and git hooks, plus out-of-band audits (branch protection, plugin-staleness)"
|
|
15652
|
+
"Repo guards (ownership, ownership-header-claim, release subject, plugin terraform, plugin collisions, eventbridge-log-permissions, plugin-tool-supply, core-direct-paths, instance-adoption, orphan-ratchet, cognito-invite-template, lambda-output, pipe-trap, codeql-suppression, skeleton-drift, terraform-input, plugin-allowlist-convention, migration-body-change, distribution-inventory, distribution-remote-state, api-gateway-integration) run in CI and git hooks, plus out-of-band audits (branch protection, plugin-staleness)"
|
|
15418
15653
|
);
|
|
15419
15654
|
checkCommand.command("ownership").description("Refuse changes to template-owned paths in an instance (#370)").argument("[base]", "Base branch to diff against; defaults to $GITHUB_BASE_REF").option("--staged <messageFile>", "Check staged changes instead of a branch diff (commit hook)").allowExcessArguments(true).action(async () => {
|
|
15420
15655
|
await runOwnershipCheck(rawArgsAfter("ownership"));
|
|
@@ -15503,6 +15738,11 @@ checkCommand.command("orphan-ratchet").description(
|
|
|
15503
15738
|
await runOrphanRatchetCheck(opts);
|
|
15504
15739
|
}
|
|
15505
15740
|
);
|
|
15741
|
+
checkCommand.command("ownership-header-claim").description(
|
|
15742
|
+
`Refuse a file whose own header comment claims INSTANCE-OWNED/template-owned/user-owned/"NOT a template file" while core-manifest.json's real longest-prefix-match answer disagrees (#1911) \u2014 scripts/verify-deployed.checks lived this exact gap (#1706/#1707) until a human happened to notice by hand; nothing else compared the two documents.`
|
|
15743
|
+
).action(async () => {
|
|
15744
|
+
await runOwnershipHeaderClaimCheck();
|
|
15745
|
+
});
|
|
15506
15746
|
checkCommand.command("cognito-invite-template").description(
|
|
15507
15747
|
"Refuse a Cognito invite_message_template missing a required member or placeholder (#356) \u2014 terraform validate is silent on this, so a fresh deploy fails on its first apply"
|
|
15508
15748
|
).action(async () => {
|
|
@@ -15574,8 +15814,8 @@ function rawArgsAfter(subcommand) {
|
|
|
15574
15814
|
}
|
|
15575
15815
|
|
|
15576
15816
|
// src/commands/doctor.ts
|
|
15577
|
-
import { existsSync as existsSync57, readFileSync as
|
|
15578
|
-
import { join as
|
|
15817
|
+
import { existsSync as existsSync57, readFileSync as readFileSync47 } from "fs";
|
|
15818
|
+
import { join as join69, resolve as resolve20 } from "path";
|
|
15579
15819
|
import chalk21 from "chalk";
|
|
15580
15820
|
import { Command as Command25 } from "commander";
|
|
15581
15821
|
|
|
@@ -15970,10 +16210,10 @@ async function runDoctorFixBranches(cwd, facts, deps = { git: new GitAdapter(),
|
|
|
15970
16210
|
return reapAllBareBranches(cwd, facts.branches, facts.worktrees, facts.currentBranch, deps);
|
|
15971
16211
|
}
|
|
15972
16212
|
function readLocalCoreVersion(cwd) {
|
|
15973
|
-
const path =
|
|
16213
|
+
const path = join69(cwd, INSTANCE_CORE_FILE);
|
|
15974
16214
|
if (!existsSync57(path)) return null;
|
|
15975
16215
|
try {
|
|
15976
|
-
return extractVersionField(
|
|
16216
|
+
return extractVersionField(readFileSync47(path, "utf8"));
|
|
15977
16217
|
} catch {
|
|
15978
16218
|
return null;
|
|
15979
16219
|
}
|
|
@@ -15993,10 +16233,10 @@ function extractVersionField(contents) {
|
|
|
15993
16233
|
return match?.[1] ?? null;
|
|
15994
16234
|
}
|
|
15995
16235
|
function readFossil(cwd) {
|
|
15996
|
-
const path =
|
|
16236
|
+
const path = join69(cwd, CORE_VERSION_FILE);
|
|
15997
16237
|
if (!existsSync57(path)) return null;
|
|
15998
16238
|
try {
|
|
15999
|
-
const value =
|
|
16239
|
+
const value = readFileSync47(path, "utf8").trim();
|
|
16000
16240
|
return value === "" ? null : value;
|
|
16001
16241
|
} catch {
|
|
16002
16242
|
return null;
|
|
@@ -16526,11 +16766,11 @@ import { Command as Command27 } from "commander";
|
|
|
16526
16766
|
|
|
16527
16767
|
// src/lib/packaged-scripts.ts
|
|
16528
16768
|
import { existsSync as existsSync58 } from "fs";
|
|
16529
|
-
import { dirname as dirname11, join as
|
|
16769
|
+
import { dirname as dirname11, join as join70 } from "path";
|
|
16530
16770
|
function findPackagedScript(startDir, relativePath) {
|
|
16531
16771
|
let dir = startDir;
|
|
16532
16772
|
for (; ; ) {
|
|
16533
|
-
const candidate =
|
|
16773
|
+
const candidate = join70(dir, relativePath);
|
|
16534
16774
|
if (existsSync58(candidate)) return candidate;
|
|
16535
16775
|
const parent = dirname11(dir);
|
|
16536
16776
|
if (parent === dir) return null;
|
package/package.json
CHANGED
package/scripts/verify.sh
CHANGED
|
@@ -1449,6 +1449,7 @@ if [ -f scripts/biffo.sh ]; then
|
|
|
1449
1449
|
run_check plugin-tool-supply sh scripts/biffo.sh check plugin-tool-supply
|
|
1450
1450
|
run_check core-direct-paths sh scripts/biffo.sh check core-direct-paths
|
|
1451
1451
|
run_check orphan-ratchet sh scripts/biffo.sh check orphan-ratchet
|
|
1452
|
+
run_check ownership-header-claim sh scripts/biffo.sh check ownership-header-claim
|
|
1452
1453
|
# #1714 second remediation: the INSTANCE-mode caller, distinct from the
|
|
1453
1454
|
# self-check above. It clones a real template tree pinned to
|
|
1454
1455
|
# biffo.core.json's own version and compares against it -- but this repo
|