@geonosis/release 1.3.0 → 1.4.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.
@@ -14,6 +14,86 @@ import {
14
14
 
15
15
  // src/release-cli.ts
16
16
  import { resolve } from "path";
17
+
18
+ // src/envelope.ts
19
+ import { mkdirSync, readFileSync, writeFileSync } from "fs";
20
+ import { dirname, join } from "path";
21
+ import { fileURLToPath } from "url";
22
+ var ENVELOPES_DIR = ".geonosis/envelopes";
23
+ var envelopePath = (root, tool) => join(root, ENVELOPES_DIR, `${tool}.json`);
24
+ var UnbalancedEnvelope = class extends Error {
25
+ constructor(message) {
26
+ super(message);
27
+ this.name = "UnbalancedEnvelope";
28
+ }
29
+ };
30
+ var isCount = (value) => Number.isSafeInteger(value) && value >= 0;
31
+ var unbalancedMessage = (envelope, next) => `${envelope.tool}: considered ${envelope.considered} but accounts for ${envelope.read + envelope.refused.length + envelope.excused.length} \u2014 ${envelope.read} read + ${envelope.refused.length} refused + ${envelope.excused.length} excused. A run that has lost count of its own inputs cannot say what it measured, so no verdict was rendered and no envelope was written. Next: ${next}`;
32
+ var writeEnvelope = ({
33
+ envelope,
34
+ next,
35
+ root
36
+ }) => {
37
+ if (envelope.tool.trim() === "") {
38
+ throw new UnbalancedEnvelope(
39
+ `an envelope with no tool name cannot be filed or reported against. Next: ${next}`
40
+ );
41
+ }
42
+ if (envelope.version.trim() === "") {
43
+ throw new UnbalancedEnvelope(
44
+ `${envelope.tool}: an envelope that cannot name the build that wrote it dates nothing, and a stale one reads exactly like a fresh one. Next: ${next}`
45
+ );
46
+ }
47
+ if (!isCount(envelope.considered) || !isCount(envelope.read)) {
48
+ throw new UnbalancedEnvelope(
49
+ `${envelope.tool}: considered ${envelope.considered} and read ${envelope.read} \u2014 a census is a whole number of things, and arithmetic over anything else balances by accident. Next: ${next}`
50
+ );
51
+ }
52
+ if (envelope.considered !== envelope.read + envelope.refused.length + envelope.excused.length) {
53
+ throw new UnbalancedEnvelope(unbalancedMessage(envelope, next));
54
+ }
55
+ const at = envelopePath(root, envelope.tool);
56
+ mkdirSync(dirname(at), { recursive: true });
57
+ writeFileSync(at, `${JSON.stringify(envelope, void 0, 2)}
58
+ `);
59
+ return at;
60
+ };
61
+ var UNKNOWN = "unknown";
62
+ var versionIn = (dir) => {
63
+ try {
64
+ const manifest = JSON.parse(readFileSync(join(dir, "package.json"), "utf8"));
65
+ return typeof manifest.version === "string" ? manifest.version : void 0;
66
+ } catch {
67
+ return void 0;
68
+ }
69
+ };
70
+ var versionOf = (moduleUrl) => {
71
+ let dir = dirname(fileURLToPath(moduleUrl));
72
+ for (; ; ) {
73
+ const found = versionIn(dir);
74
+ if (found !== void 0) return found;
75
+ const up = dirname(dir);
76
+ if (up === dir) return UNKNOWN;
77
+ dir = up;
78
+ }
79
+ };
80
+ var MIGRATIONS_TOOL = "release-migrations";
81
+ var MIGRATIONS_NEXT = "geonosis-release migrations --since <ref> --json and compare `files` against `unreadable` \u2014 every added file leaves by exactly one door, and a file in one list and not the other is the accounting bug this refuses over";
82
+ var migrationsEnvelope = (report, durationMs) => ({
83
+ considered: report.files.length,
84
+ durationMs,
85
+ excused: report.files.filter((one) => one.state === "excused").map((one) => ({
86
+ path: one.path,
87
+ reason: "a contract-migration marker this gate could believe"
88
+ })),
89
+ findings: [...report.refusals, ...report.markers, ...report.squawk],
90
+ read: report.files.filter((one) => one.state !== "excused" && one.state !== "unreadable").length,
91
+ refused: report.unreadable.map((one) => ({ path: one.path, reason: one.why })),
92
+ tool: MIGRATIONS_TOOL,
93
+ version: versionOf(import.meta.url)
94
+ });
95
+
96
+ // src/release-cli.ts
17
97
  var USAGE = `geonosis-release <command> [options]
18
98
 
19
99
  migrations --since <ref> [--dialect <name>] [--root <dir>] [--json]
@@ -84,15 +164,24 @@ var migrations = (parsed, cwd) => {
84
164
  if (dialect !== void 0 && !DIALECTS.has(dialect)) {
85
165
  throw new CannotRun(`--dialect must be one of ${[...DIALECTS].toSorted().join(", ")}`);
86
166
  }
167
+ const root = resolve(cwd, parsed.read["--root"] ?? cwd);
168
+ const startedAt = Date.now();
87
169
  const report = runMigrations({
88
170
  ...dialect === void 0 ? {} : { dialect },
89
- root: resolve(cwd, parsed.read["--root"] ?? cwd),
171
+ root,
90
172
  since
91
173
  });
92
174
  process.stdout.write(
93
175
  parsed.json ? `${JSON.stringify(report, void 0, 2)}
94
176
  ` : formatMigrations(report)
95
177
  );
178
+ const at = writeEnvelope({
179
+ envelope: migrationsEnvelope(report, Date.now() - startedAt),
180
+ next: MIGRATIONS_NEXT,
181
+ root
182
+ });
183
+ if (!parsed.json) process.stdout.write(`envelope: ${at}
184
+ `);
96
185
  if (report.unreadable.length > 0) return 2;
97
186
  return report.ok ? 0 : 1;
98
187
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@geonosis/release",
3
- "version": "1.3.0",
3
+ "version": "1.4.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": [
@@ -44,7 +44,8 @@
44
44
  }
45
45
  },
46
46
  "devDependencies": {
47
- "squawk-cli": "2.63.0"
47
+ "squawk-cli": "2.63.0",
48
+ "@geonosis/ratchet": "1.4.0"
48
49
  },
49
50
  "engines": {
50
51
  "node": ">=22"