@geonosis/release 1.3.0 → 2.0.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.
@@ -1,19 +1,41 @@
1
1
  import {
2
2
  CannotRun,
3
+ DEFAULT_REGISTRY,
4
+ MIGRATIONS_NEXT,
5
+ PUBLISHED_NEXT,
6
+ SCHEMA_NEXT,
7
+ SMOKE_NEXT,
8
+ formatBaseline,
3
9
  formatDeployed,
4
10
  formatMigrations,
5
11
  formatPlan,
6
12
  formatProve,
13
+ formatPublished,
14
+ formatSchema,
15
+ formatSnapshot,
16
+ formatSweep,
7
17
  formatVerdict,
18
+ migrationsEnvelope,
8
19
  prove,
9
20
  proveOver,
21
+ publishedEnvelope,
22
+ readReleaseConfig,
23
+ recordBaseline,
10
24
  runDeployed,
11
25
  runMigrations,
12
- runPlan
13
- } from "./chunk-D5IC766I.js";
26
+ runPlan,
27
+ runPublished,
28
+ runSchema,
29
+ runSnapshot,
30
+ schemaEnvelope,
31
+ sweepEnvelope,
32
+ sweepSmoke,
33
+ writeEnvelope
34
+ } from "./chunk-KQIWTXQP.js";
14
35
 
15
36
  // src/release-cli.ts
16
- import { resolve } from "path";
37
+ import { existsSync } from "fs";
38
+ import { join, resolve } from "path";
17
39
  var USAGE = `geonosis-release <command> [options]
18
40
 
19
41
  migrations --since <ref> [--dialect <name>] [--root <dir>] [--json]
@@ -30,6 +52,17 @@ var USAGE = `geonosis-release <command> [options]
30
52
 
31
53
  Exit 0 clean, 1 refusals, 2 the run could not be made.
32
54
 
55
+ schema [--root <dir>] [--json]
56
+
57
+ The wall no oxlint rule can see. Over every migration under every directory
58
+ geonosis.json \u2192 release.schema.domains declares: a statement naming a table another
59
+ domain declared is a cross-domain dependency the import graph does not show, and a
60
+ current_setting('\u2026') outside release.schema.sessionVariables writes one consumer's
61
+ session vocabulary into a package everyone installs.
62
+
63
+ Options only, empty defaults; the block declared and empty is a refusal naming both.
64
+ Whole-directory, not diff-scoped \u2014 a domain's migrations are applied as a set.
65
+
33
66
  plan [--root <dir>] [--i-accept-an-unproven-smoke] [--json]
34
67
 
35
68
  The contract as data \u2014 geonosis.json \u2192 release.steps and release.proof \u2014 validated
@@ -53,13 +86,67 @@ var USAGE = `geonosis-release <command> [options]
53
86
  tree declares in the wrangler configs geonosis.json names and release.secrets.
54
87
  \`versions upload\` applies no triggers, and a secret can be put outside the gate
55
88
  entirely \u2014 so declared and deployed drift apart silently, with a green pipeline.
89
+
90
+ published --group [--registry <url>] [--wait <seconds>] [--root <dir>] [--json]
91
+
92
+ Every workspace's local version against the version the registry answers, with no
93
+ sampling. \`changeset publish\` exiting zero is not the release; the GROUP resolving
94
+ from npm is \u2014 a six-package sample announced a version while the one unchecked entry
95
+ point read one release behind, and the consumer's bump aborted on it and left a
96
+ nested duplicate (#147).
97
+
98
+ The registry is asked over HTTP, never through npm or bun: their manifest CACHE is
99
+ the thing that lied. --wait polls through the few minutes in which a freshly
100
+ published name still 404s.
101
+
102
+ Exit 0 the whole group is there, 1 a gap, 2 the registry could not be asked.
103
+
104
+ smoke snapshot <name> --from <their tree> [--replace] [--root <dir>] [--json]
105
+ [--typecheck <cmd>] [--lint <cmd>] [--doctor <cmd>]
106
+
107
+ Copy a consumer tree, read-only, into .geonosis/consumer-snapshots/<name>/ \u2014 the
108
+ thing a release must be read against. Installed packages, build output and the git
109
+ history stay behind; the manager comes off the lockfile; the versions every manifest
110
+ declares and the commands the smoke will run are recorded beside the copy.
111
+
112
+ The bytes are a private tree's (D-048), so it refuses to write where git would let
113
+ them be committed.
114
+
115
+ smoke baseline --snapshot <name> [--rc-dir <workspace>] [--install <cmd>] [--json]
116
+ smoke run --snapshot <name> [--rc-dir <workspace>] [--install <cmd>] [--json]
117
+
118
+ Pack every workspace package of the release candidate (\`pnpm pack\` \u2014 a tarball is
119
+ what a consumer installs, and a release that shipped no declarations shipped them in
120
+ the tree and not in the tarball), point a COPY of the snapshot's manifests at those
121
+ tarballs, install with the snapshot's own manager, and run the snapshot's own
122
+ typecheck, lint and doctor.
123
+
124
+ \`baseline\` records what those three answered at the versions the consumer is on.
125
+ \`run\` compares against it and refuses only what THIS release broke, in their
126
+ compiler's and their doctor's own lines. Exit 0 nothing new, 1 a new failure,
127
+ 2 the run could not be made \u2014 including a phase the baseline cannot be compared to.
56
128
  `;
57
- var VALUED = /* @__PURE__ */ new Set(["--dialect", "--root", "--runner", "--since"]);
58
- var FLAGS = /* @__PURE__ */ new Set(["--check", "--i-accept-an-unproven-smoke", "--json"]);
129
+ var VALUED = /* @__PURE__ */ new Set([
130
+ "--dialect",
131
+ "--doctor",
132
+ "--from",
133
+ "--install",
134
+ "--lint",
135
+ "--rc-dir",
136
+ "--registry",
137
+ "--root",
138
+ "--runner",
139
+ "--since",
140
+ "--snapshot",
141
+ "--typecheck",
142
+ "--wait"
143
+ ]);
144
+ var FLAGS = /* @__PURE__ */ new Set(["--check", "--group", "--i-accept-an-unproven-smoke", "--json", "--replace"]);
59
145
  var DIALECTS = /* @__PURE__ */ new Set(["mikro-orm-ts", "postgres", "sqlite"]);
60
146
  var parseArgs = (argv) => {
61
147
  const read = {};
62
148
  const flags = /* @__PURE__ */ new Set();
149
+ const rest = [];
63
150
  let json = false;
64
151
  for (let index = 1; index < argv.length; index += 1) {
65
152
  const flag = argv[index] ?? "";
@@ -68,13 +155,17 @@ var parseArgs = (argv) => {
68
155
  flags.add(flag);
69
156
  continue;
70
157
  }
158
+ if (!flag.startsWith("--")) {
159
+ rest.push(flag);
160
+ continue;
161
+ }
71
162
  if (!VALUED.has(flag)) throw new CannotRun(`unknown argument "${flag}"`);
72
163
  const value = argv[index + 1];
73
164
  if (value === void 0 || value.startsWith("--")) throw new CannotRun(`${flag} needs a value`);
74
165
  read[flag] = value;
75
166
  index += 1;
76
167
  }
77
- return { command: argv[0] ?? "", flags, json, read };
168
+ return { command: argv[0] ?? "", flags, json, read, rest };
78
169
  };
79
170
  var rootOf = (parsed, cwd) => resolve(cwd, parsed.read["--root"] ?? cwd);
80
171
  var migrations = (parsed, cwd) => {
@@ -84,15 +175,42 @@ var migrations = (parsed, cwd) => {
84
175
  if (dialect !== void 0 && !DIALECTS.has(dialect)) {
85
176
  throw new CannotRun(`--dialect must be one of ${[...DIALECTS].toSorted().join(", ")}`);
86
177
  }
178
+ const root = resolve(cwd, parsed.read["--root"] ?? cwd);
179
+ const startedAt = Date.now();
87
180
  const report = runMigrations({
88
181
  ...dialect === void 0 ? {} : { dialect },
89
- root: resolve(cwd, parsed.read["--root"] ?? cwd),
182
+ root,
90
183
  since
91
184
  });
92
185
  process.stdout.write(
93
186
  parsed.json ? `${JSON.stringify(report, void 0, 2)}
94
187
  ` : formatMigrations(report)
95
188
  );
189
+ const at = writeEnvelope({
190
+ envelope: migrationsEnvelope(report, Date.now() - startedAt),
191
+ next: MIGRATIONS_NEXT,
192
+ root
193
+ });
194
+ if (!parsed.json) process.stdout.write(`envelope: ${at}
195
+ `);
196
+ if (report.unreadable.length > 0) return 2;
197
+ return report.ok ? 0 : 1;
198
+ };
199
+ var schema = (parsed, cwd) => {
200
+ const root = rootOf(parsed, cwd);
201
+ const startedAt = Date.now();
202
+ const report = runSchema({ root });
203
+ process.stdout.write(
204
+ parsed.json ? `${JSON.stringify(report, void 0, 2)}
205
+ ` : formatSchema(report)
206
+ );
207
+ const at = writeEnvelope({
208
+ envelope: schemaEnvelope(report, Date.now() - startedAt),
209
+ next: SCHEMA_NEXT,
210
+ root
211
+ });
212
+ if (!parsed.json) process.stdout.write(`envelope: ${at}
213
+ `);
96
214
  if (report.unreadable.length > 0) return 2;
97
215
  return report.ok ? 0 : 1;
98
216
  };
@@ -134,6 +252,137 @@ var deployed = (parsed, cwd) => {
134
252
  );
135
253
  return report.ok ? 0 : 1;
136
254
  };
255
+ var published = async (parsed, cwd) => {
256
+ if (!parsed.flags.has("--group")) {
257
+ throw new CannotRun("published needs --group \u2014 the whole workspace, or nothing");
258
+ }
259
+ const wait = parsed.read["--wait"];
260
+ if (wait !== void 0 && !/^\d+(?:\.\d+)?$/.test(wait)) {
261
+ throw new CannotRun(`--wait takes a number of seconds, not "${wait}"`);
262
+ }
263
+ const root = rootOf(parsed, cwd);
264
+ const startedAt = Date.now();
265
+ const report = await runPublished({
266
+ registry: parsed.read["--registry"] ?? DEFAULT_REGISTRY,
267
+ root,
268
+ waitSeconds: wait === void 0 ? 0 : Number(wait)
269
+ });
270
+ process.stdout.write(
271
+ parsed.json ? `${JSON.stringify(report, void 0, 2)}
272
+ ` : formatPublished(report)
273
+ );
274
+ const at = writeEnvelope({
275
+ envelope: publishedEnvelope(report, Date.now() - startedAt),
276
+ next: PUBLISHED_NEXT,
277
+ root
278
+ });
279
+ if (!parsed.json) process.stdout.write(`envelope: ${at}
280
+ `);
281
+ if (report.unreachable > 0) return 2;
282
+ return report.ok ? 0 : 1;
283
+ };
284
+ var namedCommands = (parsed) => ({
285
+ ...parsed.read["--doctor"] === void 0 ? {} : { doctor: parsed.read["--doctor"] },
286
+ ...parsed.read["--lint"] === void 0 ? {} : { lint: parsed.read["--lint"] },
287
+ ...parsed.read["--typecheck"] === void 0 ? {} : { typecheck: parsed.read["--typecheck"] }
288
+ });
289
+ var snapshot = (parsed, root) => {
290
+ const name = parsed.rest[1];
291
+ if (name === void 0) {
292
+ throw new CannotRun("smoke snapshot needs a name: smoke snapshot <name> --from <their tree>");
293
+ }
294
+ const from = parsed.read["--from"];
295
+ if (from === void 0) {
296
+ throw new CannotRun("smoke snapshot needs --from <the consumer tree to copy>");
297
+ }
298
+ const record = runSnapshot({
299
+ exclude: readReleaseConfig(root).smoke?.exclude ?? [],
300
+ from,
301
+ name,
302
+ named: namedCommands(parsed),
303
+ replace: parsed.flags.has("--replace"),
304
+ root
305
+ });
306
+ process.stdout.write(
307
+ parsed.json ? `${JSON.stringify(record, void 0, 2)}
308
+ ` : formatSnapshot(record)
309
+ );
310
+ return 0;
311
+ };
312
+ var wantedSnapshots = (parsed, root) => {
313
+ const configured = readReleaseConfig(root).smoke?.snapshots ?? [];
314
+ const named = parsed.read["--snapshot"];
315
+ const install = parsed.read["--install"];
316
+ const withFlag = (one) => {
317
+ const chosen = install ?? one.install;
318
+ return { ...chosen === void 0 ? {} : { install: chosen }, name: one.name };
319
+ };
320
+ if (named === void 0) return configured.map(withFlag);
321
+ return [withFlag(configured.find((one) => one.name === named) ?? { name: named })];
322
+ };
323
+ var rcOf = (parsed, root, cwd) => {
324
+ const named = parsed.read["--rc-dir"];
325
+ if (named !== void 0) return resolve(cwd, named);
326
+ if (existsSync(join(root, "package.json"))) return root;
327
+ throw new CannotRun(
328
+ "nothing here is a release candidate \u2014 pass --rc-dir <the workspace whose packages this release publishes>, because a smoke with nothing packed compares a tree to itself"
329
+ );
330
+ };
331
+ var baseline = (parsed, root, cwd) => {
332
+ const wanted = wantedSnapshots(parsed, root);
333
+ const one = wanted[0];
334
+ if (parsed.read["--snapshot"] === void 0 || one === void 0) {
335
+ throw new CannotRun("smoke baseline needs --snapshot <name>");
336
+ }
337
+ const rc = parsed.read["--rc-dir"];
338
+ const recorded = recordBaseline({
339
+ ...one.install === void 0 ? {} : { install: one.install },
340
+ name: one.name,
341
+ ...rc === void 0 ? {} : { rc: resolve(cwd, rc) },
342
+ root
343
+ });
344
+ process.stdout.write(
345
+ parsed.json ? `${JSON.stringify(recorded, void 0, 2)}
346
+ ` : formatBaseline(one.name, recorded)
347
+ );
348
+ return 0;
349
+ };
350
+ var smokeRun = (parsed, root, cwd) => {
351
+ const startedAt = Date.now();
352
+ const wanted = wantedSnapshots(parsed, root);
353
+ if (wanted.length === 0) {
354
+ throw new CannotRun(
355
+ "nothing here names a consumer to read this release against \u2014 geonosis.json \u2192 release.smoke.snapshots names them, or pass --snapshot <name>. A release check over zero consumers is not a green release check."
356
+ );
357
+ }
358
+ const swept = sweepSmoke({
359
+ rc: rcOf(parsed, root, cwd),
360
+ root,
361
+ skipMissing: parsed.read["--snapshot"] === void 0,
362
+ wanted
363
+ });
364
+ process.stdout.write(
365
+ parsed.json ? `${JSON.stringify(swept, void 0, 2)}
366
+ ` : formatSweep(swept)
367
+ );
368
+ const at = writeEnvelope({
369
+ envelope: sweepEnvelope(swept, Date.now() - startedAt),
370
+ next: SMOKE_NEXT,
371
+ root
372
+ });
373
+ if (!parsed.json) process.stdout.write(`envelope: ${at}
374
+ `);
375
+ if (swept.comparisons.some((one) => one.refused.length > 0)) return 2;
376
+ return swept.comparisons.every((one) => one.ok) ? 0 : 1;
377
+ };
378
+ var smoke = (parsed, cwd) => {
379
+ const root = rootOf(parsed, cwd);
380
+ const verb = parsed.rest[0];
381
+ if (verb === "snapshot") return snapshot(parsed, root);
382
+ if (verb === "baseline") return baseline(parsed, root, cwd);
383
+ if (verb === "run") return smokeRun(parsed, root, cwd);
384
+ throw new CannotRun(`smoke has snapshot, baseline and run \u2014 "${verb ?? ""}" is none of them`);
385
+ };
137
386
  var CONFIG_SHAPE = `geonosis-release reads geonosis.json
138
387
 
139
388
  geonosis.json \u2192 "release" every key optional; a repo that configured nothing gets nothing
@@ -143,6 +392,13 @@ geonosis.json \u2192 "release" every key optional; a repo that configured n
143
392
  phases string[]? "up" and/or "down" (default ["up"])
144
393
  squawk object? { exclude: string[] } \u2014 the rule ids to leave out
145
394
  proof object? { mustAssertVersion: boolean }
395
+ schema object? the walls no oxlint rule can see (#168)
396
+ domains Entry[]? { dir, name, tables } \u2014 whose migrations, and whose tables
397
+ sessionVariables string[]? the session variable names DDL may name
398
+ smoke object? the consumer trees this release is read against
399
+ exclude string[]? what a snapshot leaves behind, beyond the built and the installed
400
+ snapshots Entry[]? { name, install? } \u2014 the recorded snapshot, and the command that
401
+ tree is installed with when the manager's default is not it
146
402
  secrets string[]? the secret names a deployment carries
147
403
  steps string[]? the release steps
148
404
  workers string[]? the workers it deploys
@@ -150,7 +406,10 @@ geonosis.json \u2192 "release" every key optional; a repo that configured n
150
406
  wranglerEnv string? the named environment block to read out of them
151
407
 
152
408
  A key outside either list is refused BY NAME, with the accepted set. This shape had to be read out
153
- of a bundled chunk file by a consumer once (#75).`;
409
+ of a bundled chunk file by a consumer once (#75).
410
+
411
+ published reads no geonosis.json at all: its inputs are every package.json under the root \u2014 "name"
412
+ and "version", and "private" to tell a package a publish is a claim about from one it is not.`;
154
413
  var main = async () => {
155
414
  const argv = process.argv.slice(2);
156
415
  if (argv.includes("--print-config-shape")) {
@@ -163,12 +422,17 @@ var main = async () => {
163
422
  return 0;
164
423
  }
165
424
  const parsed = parseArgs(argv);
425
+ if (parsed.command === "smoke") return smoke(parsed, process.cwd());
426
+ const stray = parsed.rest[0];
427
+ if (stray !== void 0) throw new CannotRun(`unknown argument "${stray}"`);
166
428
  if (parsed.command === "migrations") return migrations(parsed, process.cwd());
167
429
  if (parsed.command === "plan") return plan(parsed, process.cwd());
430
+ if (parsed.command === "schema") return schema(parsed, process.cwd());
168
431
  if (parsed.command === "prove") return proving(parsed, process.cwd());
169
432
  if (parsed.command === "deployed") return deployed(parsed, process.cwd());
433
+ if (parsed.command === "published") return published(parsed, process.cwd());
170
434
  throw new CannotRun(
171
- `no command called "${parsed.command}" \u2014 this bin has migrations, plan, prove, deployed`
435
+ `no command called "${parsed.command}" \u2014 this bin has migrations, plan, prove, deployed, published`
172
436
  );
173
437
  };
174
438
  try {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@geonosis/release",
3
- "version": "1.3.0",
3
+ "version": "2.0.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": "2.0.0"
48
49
  },
49
50
  "engines": {
50
51
  "node": ">=22"