@geonosis/release 1.1.0 → 1.2.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/README.md CHANGED
@@ -17,6 +17,10 @@ geonosis-release migrations --since <ref> [--dialect <name>] [--root <dir>] [--j
17
17
 
18
18
  Exit **0** clean · **1** refusals · **2** the run could not be made.
19
19
 
20
+ ## What it reads
21
+
22
+ The files added since `--since <ref>` — committed, **staged, and untracked** — under every configured `release.migrations[].dir`. A migration that exists and narrows is a refusal whatever git thinks of it: the local gate a person runs before committing is the one window in which it is still free to fix, and a gate that only speaks after the commit speaks after the cheap moment has passed. A run that finds no migration file anywhere says `NONE … nothing was read`, never `OK`.
23
+
20
24
  ## `migrations` — expand-only, enforced
21
25
 
22
26
  For every file the diff ADDED under a directory `geonosis.json` names, in the dialect it names it
@@ -191,9 +191,9 @@ var arrayTable = (into, path) => {
191
191
  var parseToml = (source) => {
192
192
  const out = {};
193
193
  let here = out;
194
- const lines = source.split("\n");
195
- for (let index = 0; index < lines.length; index += 1) {
196
- const line = (lines[index] ?? "").split("#")[0]?.trim() ?? "";
194
+ const lines2 = source.split("\n");
195
+ for (let index = 0; index < lines2.length; index += 1) {
196
+ const line = (lines2[index] ?? "").split("#")[0]?.trim() ?? "";
197
197
  if (line === "") continue;
198
198
  if (line.startsWith("[[") && line.endsWith("]]")) {
199
199
  here = arrayTable(out, line.slice(2, -2).trim().split("."));
@@ -208,9 +208,9 @@ var parseToml = (source) => {
208
208
  let text = line.slice(at + 1);
209
209
  while ([...text].filter((one) => one === "[").length > [...text].filter((one) => one === "]").length) {
210
210
  index += 1;
211
- if (index >= lines.length) throw new CannotRun("an unterminated array in the TOML config");
211
+ if (index >= lines2.length) throw new CannotRun("an unterminated array in the TOML config");
212
212
  text += `
213
- ${(lines[index] ?? "").split("#")[0] ?? ""}`;
213
+ ${(lines2[index] ?? "").split("#")[0] ?? ""}`;
214
214
  }
215
215
  put(here, line.slice(0, at).trim().split("."), value(text));
216
216
  }
@@ -353,7 +353,13 @@ var git = (root, args) => {
353
353
  throw new CannotRun(`git ${args.join(" ")} failed: ${(error.message ?? "").trim()}`);
354
354
  }
355
355
  };
356
- var addedSince = (root, since) => git(root, ["diff", "--name-only", "--diff-filter=A", `${since}..HEAD`]).split("\n").filter((line) => line !== "");
356
+ var lines = (out) => out.split("\n").filter((line) => line !== "");
357
+ var addedSince = (root, since) => {
358
+ const committed = lines(git(root, ["diff", "--name-only", "--diff-filter=A", `${since}..HEAD`]));
359
+ const staged = lines(git(root, ["diff", "--name-only", "--diff-filter=A", "--cached"]));
360
+ const untracked = lines(git(root, ["ls-files", "--others", "--exclude-standard"]));
361
+ return [.../* @__PURE__ */ new Set([...committed, ...staged, ...untracked])].toSorted();
362
+ };
357
363
  var gitOk = (root, args) => {
358
364
  const run = spawnSync("git", [...args], { cwd: root, encoding: "utf8" });
359
365
  return run.error === void 0 && run.status === 0;
@@ -604,12 +610,17 @@ var EXTENSION = {
604
610
  var under = (dir, file) => file === dir || file.startsWith(dir.endsWith("/") ? dir : `${dir}/`);
605
611
  var fromTypeScript = (path, source, entry) => {
606
612
  try {
607
- return addSqlIn(source, entry.phases ?? ["up"]).flatMap(
608
- (one) => refusalsIn(path, one.sql, one.line)
609
- );
613
+ return {
614
+ refusals: addSqlIn(source, entry.phases ?? ["up"]).flatMap(
615
+ (one) => refusalsIn(path, one.sql, one.line)
616
+ )
617
+ };
610
618
  } catch (error) {
611
619
  if (error instanceof Unreadable) {
612
- throw new CannotRun(`${path} holds ${error.message} \u2014 this reader cannot read it`);
620
+ return {
621
+ refusals: [],
622
+ unreadable: `${path} holds ${error.message} \u2014 this reader cannot read it`
623
+ };
613
624
  }
614
625
  throw error;
615
626
  }
@@ -636,7 +647,9 @@ var runMigrations = (input) => {
636
647
  const refusals = [];
637
648
  const markers = [];
638
649
  const squawk = [];
650
+ const unreadable = [];
639
651
  const forSquawk = [];
652
+ const states = /* @__PURE__ */ new Map();
640
653
  for (const { entry, file } of matched) {
641
654
  const source = readFileSync4(resolve5(input.root, file), "utf8");
642
655
  const found = markersIn(source);
@@ -646,11 +659,18 @@ var runMigrations = (input) => {
646
659
  });
647
660
  markers.push(...bad);
648
661
  const excused = found.length > 0 && bad.length === 0;
662
+ states.set(file, excused ? "excused" : "clean");
649
663
  if (!excused) {
650
- refusals.push(
651
- ...entry.dialect === "mikro-orm-ts" ? fromTypeScript(file, source, entry) : refusalsIn(file, source)
652
- );
664
+ const mine = entry.dialect === "mikro-orm-ts" ? fromTypeScript(file, source, entry) : { refusals: refusalsIn(file, source) };
665
+ if (mine.unreadable !== void 0) {
666
+ unreadable.push({ path: file, why: mine.unreadable });
667
+ states.set(file, "unreadable");
668
+ continue;
669
+ }
670
+ refusals.push(...mine.refusals);
671
+ if (mine.refusals.length > 0) states.set(file, "refused");
653
672
  }
673
+ if (bad.length > 0) states.set(file, "refused");
654
674
  if (entry.dialect !== "sqlite")
655
675
  forSquawk.push({ entry, file, sql: sqlFor(file, source, entry) });
656
676
  }
@@ -670,6 +690,7 @@ var runMigrations = (input) => {
670
690
  if (!found.ok) {
671
691
  clean = false;
672
692
  squawk.push({ findings: found.findings, path: one.file });
693
+ if (states.get(one.file) === "clean") states.set(one.file, "findings");
673
694
  }
674
695
  }
675
696
  } finally {
@@ -677,14 +698,18 @@ var runMigrations = (input) => {
677
698
  }
678
699
  }
679
700
  return {
701
+ dirs: entries.map((one) => one.dir),
702
+ files: matched.map(({ file }) => ({ path: file, state: states.get(file) ?? "clean" })),
680
703
  linted: forSquawk.length,
681
704
  markers: markers.toSorted((a, b) => a.path.localeCompare(b.path) || a.line - b.line),
682
- ok: refusals.length === 0 && markers.length === 0 && clean,
683
- read: matched.length,
705
+ ok: refusals.length === 0 && markers.length === 0 && clean && unreadable.length === 0,
706
+ read: matched.length - unreadable.length,
707
+ since: input.since,
684
708
  refusals: refusals.toSorted(
685
709
  (a, b) => a.path.localeCompare(b.path) || a.line - b.line || a.verb.localeCompare(b.verb)
686
710
  ),
687
- squawk
711
+ squawk,
712
+ unreadable
688
713
  };
689
714
  };
690
715
  var WHY = `
@@ -695,21 +720,35 @@ is safe now:
695
720
  -- contract-migration: <what expanded it, and why nothing reads it any more> since:<ref>
696
721
  `;
697
722
  var formatMigrations = (report) => {
723
+ if (report.files.length === 0) {
724
+ return `NONE migrations: no migration file was added since ${report.since} \u2014 committed, staged or untracked \u2014 under ${report.dirs.join(", ")}; nothing was read
725
+ `;
726
+ }
698
727
  if (report.ok) {
699
728
  return `OK migrations: expand-only (${String(report.read)} read, ${String(report.linted)} linted by squawk)
700
729
  `;
701
730
  }
702
731
  const said = report.markers.map((one) => ` ${one.path}: ${one.why} (line ${String(one.line)})
703
732
  `);
704
- const lines = report.refusals.map(
733
+ const lines2 = report.refusals.map(
705
734
  (one) => ` ${one.path}: ${one.verb} (line ${String(one.line)})
706
735
  `
707
736
  );
708
737
  const found = report.squawk.map((one) => ` ${one.path}:
709
738
  ${one.findings}
739
+ `);
740
+ const cannot = report.unreadable.map((one) => ` ${one.why}
710
741
  `);
711
742
  const why = report.markers.length + report.refusals.length === 0 ? "" : WHY;
712
- return `${said.join("")}${lines.join("")}${why}${found.join("")}`;
743
+ return `${said.join("")}${lines2.join("")}${cannot.join("")}${why}${found.join("")}${accounting(report)}`;
744
+ };
745
+ var accounting = (report) => {
746
+ const total = report.files.length;
747
+ const by = (state) => report.files.filter((one) => one.state === state).length;
748
+ const rows = report.files.map((one) => ` ${one.state.padEnd(10)} ${one.path}
749
+ `);
750
+ return `${rows.join("")}migrations: ${String(report.read)} of ${String(total)} files read \u2014 ${String(by("refused"))} refused, ${String(by("findings"))} with squawk findings, ${String(by("excused"))} excused, ${String(by("clean"))} clean${report.unreadable.length === 0 ? "" : `, ${String(report.unreadable.length)} unreadable`}
751
+ `;
713
752
  };
714
753
 
715
754
  // src/plan.ts
@@ -871,7 +910,7 @@ var stubPath = () => {
871
910
  return path;
872
911
  };
873
912
  var prove = async (cwd, stub = stubPath()) => {
874
- const lines = [];
913
+ const lines2 = [];
875
914
  let ok = true;
876
915
  for (const plant of PLANTS) {
877
916
  const verdict = await proveOver({
@@ -881,10 +920,10 @@ var prove = async (cwd, stub = stubPath()) => {
881
920
  timeoutMs: 3e4
882
921
  });
883
922
  if (verdict.ok) ok = false;
884
- lines.push(` ${verdict.ok ? "MISSED" : "PROVEN"} ${plant.says}`);
923
+ lines2.push(` ${verdict.ok ? "MISSED" : "PROVEN"} ${plant.says}`);
885
924
  if (plant.name !== "promoted" && verdict.steps.includes("promote")) {
886
925
  ok = false;
887
- lines.push(" MISSED a refused smoke was followed by a promote request");
926
+ lines2.push(" MISSED a refused smoke was followed by a promote request");
888
927
  }
889
928
  }
890
929
  const honest = await proveOver({
@@ -894,10 +933,10 @@ var prove = async (cwd, stub = stubPath()) => {
894
933
  timeoutMs: 3e4
895
934
  });
896
935
  if (!honest.ok) ok = false;
897
- lines.push(
936
+ lines2.push(
898
937
  ` ${honest.ok ? "PROVEN" : "MISSED"} a smoke that answered the version it overrode to is not refused`
899
938
  );
900
- return { lines, ok };
939
+ return { lines: lines2, ok };
901
940
  };
902
941
  var formatProve = (outcome) => `${outcome.lines.join("\n")}
903
942
 
package/dist/index.d.ts CHANGED
@@ -37,16 +37,30 @@ type MarkerRefusal = {
37
37
  path: string;
38
38
  why: string;
39
39
  };
40
+ type FileState = 'clean' | 'excused' | 'findings' | 'refused' | 'unreadable';
40
41
  type MigrationsReport = {
42
+ /** The configured directories the run looked under. */
43
+ dirs: string[];
44
+ /** Every file the diff added under a configured dir, by name and state — a clean file included. */
45
+ files: {
46
+ path: string;
47
+ state: FileState;
48
+ }[];
41
49
  linted: number;
42
50
  markers: MarkerRefusal[];
43
51
  ok: boolean;
44
52
  read: number;
45
53
  refusals: Refusal[];
54
+ since: string;
46
55
  squawk: {
47
56
  findings: string;
48
57
  path: string;
49
58
  }[];
59
+ /** Files this reader could not read: named, never a stop for the others. */
60
+ unreadable: {
61
+ path: string;
62
+ why: string;
63
+ }[];
50
64
  };
51
65
 
52
66
  declare const parseReleaseConfig: (raw: unknown) => ReleaseConfig;
package/dist/index.js CHANGED
@@ -28,7 +28,7 @@ import {
28
28
  runMigrations,
29
29
  runPlan,
30
30
  statementsOf
31
- } from "./chunk-2JLVQK7Y.js";
31
+ } from "./chunk-BOTEFN3W.js";
32
32
  export {
33
33
  DEPLOYED_FILE,
34
34
  NARROWING,
@@ -10,7 +10,7 @@ import {
10
10
  runDeployed,
11
11
  runMigrations,
12
12
  runPlan
13
- } from "./chunk-2JLVQK7Y.js";
13
+ } from "./chunk-BOTEFN3W.js";
14
14
 
15
15
  // src/release-cli.ts
16
16
  import { resolve } from "path";
@@ -93,6 +93,7 @@ var migrations = (parsed, cwd) => {
93
93
  parsed.json ? `${JSON.stringify(report, void 0, 2)}
94
94
  ` : formatMigrations(report)
95
95
  );
96
+ if (report.unreadable.length > 0) return 2;
96
97
  return report.ok ? 0 : 1;
97
98
  };
98
99
  var plan = (parsed, cwd) => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@geonosis/release",
3
- "version": "1.1.0",
3
+ "version": "1.2.0",
4
4
  "types": "./dist/index.d.ts",
5
5
  "description": "The release contract with its proofs: an expand-only migration gate over three dialects, a smoke that must name the version that answered it, and declared ≠ deployed.",
6
6
  "keywords": [