@biffo/cli 0.214.0 → 0.214.1

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.
Files changed (2) hide show
  1. package/dist/index.js +63 -30
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -9126,12 +9126,24 @@ import { Command as Command23 } from "commander";
9126
9126
 
9127
9127
  // src/scripts/check-adr-numbering.ts
9128
9128
  import { existsSync as existsSync32 } from "fs";
9129
- import { join as join31 } from "path";
9129
+ import { join as join32 } from "path";
9130
9130
  import { execa as execa5 } from "execa";
9131
9131
 
9132
9132
  // src/lib/adr-numbering-guard.ts
9133
- import { existsSync as existsSync31, readdirSync as readdirSync13 } from "fs";
9133
+ import { existsSync as existsSync31, readdirSync as readdirSync13, readFileSync as readFileSync24 } from "fs";
9134
+ import { join as join31 } from "path";
9134
9135
  var ADR_FILENAME = /^(\d{4})-.+\.md$/;
9136
+ var ALLOWLIST_FILENAME = ".numbering-allowlist";
9137
+ function readAdrNumberingAllowlist(adrDir) {
9138
+ const path = join31(adrDir, ALLOWLIST_FILENAME);
9139
+ if (!existsSync31(path)) return /* @__PURE__ */ new Set();
9140
+ const numbers = /* @__PURE__ */ new Set();
9141
+ for (const rawLine of readFileSync24(path, "utf8").split("\n")) {
9142
+ const line = rawLine.split("#")[0].trim();
9143
+ if (line) numbers.add(line);
9144
+ }
9145
+ return numbers;
9146
+ }
9135
9147
  function adrNumbersIn(adrDir) {
9136
9148
  const claims = /* @__PURE__ */ new Map();
9137
9149
  if (!existsSync31(adrDir)) return claims;
@@ -9144,12 +9156,20 @@ function adrNumbersIn(adrDir) {
9144
9156
  return claims;
9145
9157
  }
9146
9158
  function findAdrNumberCollisions(adrDir) {
9159
+ const allowlist = readAdrNumberingAllowlist(adrDir);
9147
9160
  const collisions = [];
9148
9161
  for (const [number, files] of [...adrNumbersIn(adrDir).entries()].sort()) {
9149
- if (files.length > 1) collisions.push({ number, files: [...files].sort() });
9162
+ if (files.length > 1 && !allowlist.has(number)) {
9163
+ collisions.push({ number, files: [...files].sort() });
9164
+ }
9150
9165
  }
9151
9166
  return collisions;
9152
9167
  }
9168
+ function findStaleAdrNumberingAllowlistEntries(adrDir) {
9169
+ const allowlist = readAdrNumberingAllowlist(adrDir);
9170
+ const claims = adrNumbersIn(adrDir);
9171
+ return [...allowlist].filter((number) => (claims.get(number)?.length ?? 0) < 2).sort();
9172
+ }
9153
9173
  function formatAdrNumberCollisions(collisions) {
9154
9174
  return collisions.map(
9155
9175
  (c) => ` ADR-${c.number} is claimed by: ${c.files.join(", ")}
@@ -9161,18 +9181,31 @@ function formatAdrNumberCollisions(collisions) {
9161
9181
  // src/scripts/check-adr-numbering.ts
9162
9182
  async function runAdrNumberingCheck() {
9163
9183
  const root = (await execa5("git", ["rev-parse", "--show-toplevel"])).stdout.trim();
9164
- const adrDir = join31(root, "docs", "ADR");
9184
+ const adrDir = join32(root, "docs", "ADR");
9165
9185
  if (!existsSync32(adrDir)) {
9166
9186
  console.log("\u2713 ADR numbering guard: no docs/ADR/ directory \u2014 nothing to compare");
9167
9187
  return;
9168
9188
  }
9169
9189
  const collisions = findAdrNumberCollisions(adrDir);
9190
+ const stale = findStaleAdrNumberingAllowlistEntries(adrDir);
9191
+ let failed = false;
9170
9192
  if (collisions.length > 0) {
9193
+ failed = true;
9171
9194
  console.error("\u2717 ADR numbering guard: two ADRs in docs/ADR/ share a number\n");
9172
9195
  console.error(formatAdrNumberCollisions(collisions));
9173
- console.error("\nSee tabsii-platform#449 for how this class of collision happens.");
9174
- process.exit(1);
9196
+ console.error(
9197
+ `
9198
+ Already accepted? List it in docs/ADR/${ALLOWLIST_FILENAME} instead of leaving this red forever. See tabsii-platform#449 for how this class of collision happens.`
9199
+ );
9200
+ }
9201
+ if (stale.length > 0) {
9202
+ failed = true;
9203
+ console.error(
9204
+ `\u2717 ADR numbering guard: docs/ADR/${ALLOWLIST_FILENAME} names a number that no longer collides: ${stale.join(", ")}
9205
+ Remove the stale entry \u2014 an allowlist nothing checks against just hides the next real one.`
9206
+ );
9175
9207
  }
9208
+ if (failed) process.exit(1);
9176
9209
  console.log("\u2713 ADR numbering guard: OK");
9177
9210
  }
9178
9211
 
@@ -9460,8 +9493,8 @@ async function runOwnershipCheck(argv) {
9460
9493
  const { stdout } = await execa7("git", ["diff", "--cached", "--name-status"], { cwd: root });
9461
9494
  ({ changed: changedFiles, deleted: deletedFiles } = parseNameStatus(stdout));
9462
9495
  if (messageFile) {
9463
- const { readFileSync: readFileSync26, existsSync: existsSync37 } = await import("fs");
9464
- if (existsSync37(messageFile)) commitMessage = readFileSync26(messageFile, "utf8");
9496
+ const { readFileSync: readFileSync27, existsSync: existsSync37 } = await import("fs");
9497
+ if (existsSync37(messageFile)) commitMessage = readFileSync27(messageFile, "utf8");
9465
9498
  }
9466
9499
  } else {
9467
9500
  const base = process.env["GITHUB_BASE_REF"] ?? args[0];
@@ -9563,12 +9596,12 @@ ${BOLD}If the divergence is deliberate${OFF}
9563
9596
 
9564
9597
  // src/scripts/check-plugin-collisions.ts
9565
9598
  import { existsSync as existsSync34 } from "fs";
9566
- import { join as join33 } from "path";
9599
+ import { join as join34 } from "path";
9567
9600
  import { execa as execa8 } from "execa";
9568
9601
 
9569
9602
  // src/lib/plugin-collision-guard.ts
9570
9603
  import { existsSync as existsSync33, readdirSync as readdirSync14, statSync as statSync7 } from "fs";
9571
- import { join as join32 } from "path";
9604
+ import { join as join33 } from "path";
9572
9605
  var PYTEST_SPECIAL = /* @__PURE__ */ new Set(["conftest.py"]);
9573
9606
  var IGNORED_DIRS = /* @__PURE__ */ new Set([".venv", "node_modules", "__pycache__", ".git", "dist", "build"]);
9574
9607
  function subdirectories(dir) {
@@ -9576,19 +9609,19 @@ function subdirectories(dir) {
9576
9609
  return readdirSync14(dir).filter((entry) => {
9577
9610
  if (IGNORED_DIRS.has(entry) || entry.startsWith(".")) return false;
9578
9611
  try {
9579
- return statSync7(join32(dir, entry)).isDirectory();
9612
+ return statSync7(join33(dir, entry)).isDirectory();
9580
9613
  } catch {
9581
9614
  return false;
9582
9615
  }
9583
9616
  });
9584
9617
  }
9585
9618
  function regularPackagesOf(pluginDir2) {
9586
- return subdirectories(pluginDir2).filter((name) => existsSync33(join32(pluginDir2, name, "__init__.py"))).sort();
9619
+ return subdirectories(pluginDir2).filter((name) => existsSync33(join33(pluginDir2, name, "__init__.py"))).sort();
9587
9620
  }
9588
9621
  function bareTestModulesOf(pluginDir2) {
9589
- const testsDir = join32(pluginDir2, "tests");
9622
+ const testsDir = join33(pluginDir2, "tests");
9590
9623
  if (!existsSync33(testsDir)) return [];
9591
- if (existsSync33(join32(testsDir, "__init__.py"))) return [];
9624
+ if (existsSync33(join33(testsDir, "__init__.py"))) return [];
9592
9625
  return readdirSync14(testsDir).filter((f) => f.endsWith(".py") && !PYTEST_SPECIAL.has(f)).sort();
9593
9626
  }
9594
9627
  function findCollisions(servicesDir, pluginDirs) {
@@ -9597,7 +9630,7 @@ function findCollisions(servicesDir, pluginDirs) {
9597
9630
  const gather = (kind, namesOf) => {
9598
9631
  const claims = /* @__PURE__ */ new Map();
9599
9632
  for (const plugin of plugins) {
9600
- for (const name of namesOf(join32(servicesDir, plugin))) {
9633
+ for (const name of namesOf(join33(servicesDir, plugin))) {
9601
9634
  claims.set(name, [...claims.get(name) ?? [], plugin]);
9602
9635
  }
9603
9636
  }
@@ -9635,7 +9668,7 @@ function formatCollisions(collisions) {
9635
9668
  // src/scripts/check-plugin-collisions.ts
9636
9669
  async function runPluginCollisionCheck() {
9637
9670
  const root = (await execa8("git", ["rev-parse", "--show-toplevel"])).stdout.trim();
9638
- const servicesDir = join33(root, "services");
9671
+ const servicesDir = join34(root, "services");
9639
9672
  if (!existsSync34(servicesDir)) {
9640
9673
  console.log("\u2713 plugin collision guard: no services/ directory \u2014 nothing to compare");
9641
9674
  return;
@@ -9656,8 +9689,8 @@ async function runPluginCollisionCheck() {
9656
9689
  import { execa as execa9 } from "execa";
9657
9690
 
9658
9691
  // src/lib/plugin-terraform-guard.ts
9659
- import { existsSync as existsSync35, readFileSync as readFileSync24, readdirSync as readdirSync15 } from "fs";
9660
- import { dirname as dirname9, join as join34, relative as relative6, sep as sep3 } from "path";
9692
+ import { existsSync as existsSync35, readFileSync as readFileSync25, readdirSync as readdirSync15 } from "fs";
9693
+ import { dirname as dirname9, join as join35, relative as relative6, sep as sep3 } from "path";
9661
9694
  var SKIP_DIRS = /* @__PURE__ */ new Set(["node_modules", ".git", ".worktrees", "dist", ".venv", "__pycache__"]);
9662
9695
  var PLUGIN_MANIFEST_FILE2 = "biffo.plugin.json";
9663
9696
  function findPluginManifests(root) {
@@ -9672,9 +9705,9 @@ function findPluginManifests(root) {
9672
9705
  for (const entry of entries) {
9673
9706
  if (entry.isDirectory()) {
9674
9707
  if (SKIP_DIRS.has(entry.name)) continue;
9675
- walk(join34(dir, entry.name));
9708
+ walk(join35(dir, entry.name));
9676
9709
  } else if (entry.isFile() && entry.name === PLUGIN_MANIFEST_FILE2) {
9677
- found.push(relative6(root, join34(dir, entry.name)).split(sep3).join("/"));
9710
+ found.push(relative6(root, join35(dir, entry.name)).split(sep3).join("/"));
9678
9711
  }
9679
9712
  }
9680
9713
  };
@@ -9684,7 +9717,7 @@ function findPluginManifests(root) {
9684
9717
  function readSubscriptions(absManifestPath) {
9685
9718
  let parsed;
9686
9719
  try {
9687
- parsed = JSON.parse(readFileSync24(absManifestPath, "utf8"));
9720
+ parsed = JSON.parse(readFileSync25(absManifestPath, "utf8"));
9688
9721
  } catch {
9689
9722
  return null;
9690
9723
  }
@@ -9699,14 +9732,14 @@ function readSubscriptions(absManifestPath) {
9699
9732
  }
9700
9733
  function checkPluginTerraform(root) {
9701
9734
  const violations = [];
9702
- const coreManifest = existsSync35(join34(root, CORE_MANIFEST_FILE)) ? readCoreManifest(root) : null;
9735
+ const coreManifest = existsSync35(join35(root, CORE_MANIFEST_FILE)) ? readCoreManifest(root) : null;
9703
9736
  for (const manifest of findPluginManifests(root)) {
9704
9737
  if (coreManifest && !isTemplateOwned(manifest, coreManifest)) continue;
9705
- const absManifest = join34(root, manifest);
9738
+ const absManifest = join35(root, manifest);
9706
9739
  const subscriptions = readSubscriptions(absManifest);
9707
9740
  if (subscriptions === null) continue;
9708
9741
  const pluginDir2 = dirname9(absManifest);
9709
- if (existsSync35(join34(pluginDir2, "terraform"))) continue;
9742
+ if (existsSync35(join35(pluginDir2, "terraform"))) continue;
9710
9743
  const relPluginDir = relative6(root, pluginDir2).split(sep3).join("/");
9711
9744
  violations.push({
9712
9745
  manifest,
@@ -9859,8 +9892,8 @@ function rawArgsAfter(subcommand) {
9859
9892
  }
9860
9893
 
9861
9894
  // src/commands/doctor.ts
9862
- import { existsSync as existsSync36, readFileSync as readFileSync25 } from "fs";
9863
- import { join as join35, resolve as resolve18 } from "path";
9895
+ import { existsSync as existsSync36, readFileSync as readFileSync26 } from "fs";
9896
+ import { join as join36, resolve as resolve18 } from "path";
9864
9897
  import chalk21 from "chalk";
9865
9898
  import { Command as Command24 } from "commander";
9866
9899
 
@@ -10035,10 +10068,10 @@ async function runDoctor(options, deps = { git: new GitAdapter() }) {
10035
10068
  return runDoctorChecks(facts);
10036
10069
  }
10037
10070
  function readLocalCoreVersion(cwd) {
10038
- const path = join35(cwd, INSTANCE_CORE_FILE);
10071
+ const path = join36(cwd, INSTANCE_CORE_FILE);
10039
10072
  if (!existsSync36(path)) return null;
10040
10073
  try {
10041
- return parseCoreRecord(readFileSync25(path, "utf8"));
10074
+ return parseCoreRecord(readFileSync26(path, "utf8"));
10042
10075
  } catch {
10043
10076
  return null;
10044
10077
  }
@@ -10053,10 +10086,10 @@ function parseCoreRecord(contents) {
10053
10086
  }
10054
10087
  }
10055
10088
  function readFossil(cwd) {
10056
- const path = join35(cwd, CORE_VERSION_FILE);
10089
+ const path = join36(cwd, CORE_VERSION_FILE);
10057
10090
  if (!existsSync36(path)) return null;
10058
10091
  try {
10059
- const value = readFileSync25(path, "utf8").trim();
10092
+ const value = readFileSync26(path, "utf8").trim();
10060
10093
  return value === "" ? null : value;
10061
10094
  } catch {
10062
10095
  return null;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@biffo/cli",
3
- "version": "0.214.0",
3
+ "version": "0.214.1",
4
4
  "description": "Biffo project scaffolding CLI",
5
5
  "license": "MIT",
6
6
  "type": "module",