@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.
package/dist/index.d.ts CHANGED
@@ -1,3 +1,5 @@
1
+ import { ReportEnvelope } from '@geonosis/ratchet';
2
+
1
3
  /** The dialects the gate can route to. A dialect is never sniffed: it is what the repo declared. */
2
4
  type Dialect = 'mikro-orm-ts' | 'postgres' | 'sqlite';
3
5
  type Phase = 'down' | 'up';
@@ -12,6 +14,45 @@ type MigrationDir = {
12
14
  type ProofConfig = {
13
15
  mustAssertVersion?: boolean;
14
16
  };
17
+ /** One domain's shipped migrations, and the tables that are ITS to name. */
18
+ type SchemaDomain = {
19
+ dir: string;
20
+ name: string;
21
+ tables: string[];
22
+ };
23
+ type SchemaConfig = {
24
+ domains: SchemaDomain[];
25
+ sessionVariables: string[];
26
+ };
27
+ type SchemaFinding = {
28
+ line: number;
29
+ path: string;
30
+ why: string;
31
+ };
32
+ type SchemaReport = {
33
+ dirs: string[];
34
+ files: string[];
35
+ findings: SchemaFinding[];
36
+ ok: boolean;
37
+ read: number;
38
+ unreadable: {
39
+ path: string;
40
+ why: string;
41
+ }[];
42
+ };
43
+ /** One consumer tree a release must be read against, and how that one is installed. */
44
+ type SmokeSnapshotConfig = {
45
+ install?: string;
46
+ name: string;
47
+ };
48
+ /**
49
+ * What the release check asks of consumers. Law 6: a repo that names no snapshot gets no sweep —
50
+ * and is told so, rather than passing over nothing.
51
+ */
52
+ type SmokeConfig = {
53
+ exclude?: string[];
54
+ snapshots?: SmokeSnapshotConfig[];
55
+ };
15
56
  /**
16
57
  * Law 6: every directory, dialect, secret name and worker is an OPTION. The defaults are empty, and
17
58
  * a command that needs one it has not got refuses with 2 rather than measuring nothing and
@@ -20,7 +61,10 @@ type ProofConfig = {
20
61
  type ReleaseConfig = {
21
62
  migrations?: MigrationDir[];
22
63
  proof?: ProofConfig;
64
+ /** Absent when the repo never declared the block — which is not the same as declaring it empty. */
65
+ schema?: SchemaConfig;
23
66
  secrets?: string[];
67
+ smoke?: SmokeConfig;
24
68
  steps?: string[];
25
69
  workers?: string[];
26
70
  wrangler?: string[];
@@ -200,6 +244,106 @@ declare const prove: (cwd: string, stub?: string) => Promise<ProveOutcome>;
200
244
  declare const formatProve: (outcome: ProveOutcome) => string;
201
245
  declare const formatVerdict: (verdict: Verdict) => string;
202
246
 
247
+ type PublishedVerdict = 'ABSENT' | 'BEHIND' | 'MATCH' | 'UNREACHABLE';
248
+ type PublishedLine = {
249
+ at: string;
250
+ latest: string | undefined;
251
+ local: string;
252
+ name: string;
253
+ verdict: PublishedVerdict;
254
+ why: string;
255
+ };
256
+ type PublishedReport = {
257
+ considered: number;
258
+ excused: {
259
+ path: string;
260
+ reason: string;
261
+ }[];
262
+ lines: PublishedLine[];
263
+ ok: boolean;
264
+ registry: string;
265
+ sweeps: number;
266
+ unreachable: number;
267
+ waitedMs: number;
268
+ };
269
+ type Local = {
270
+ at: string;
271
+ name: string;
272
+ version: string;
273
+ };
274
+ type Census = {
275
+ excused: {
276
+ path: string;
277
+ reason: string;
278
+ }[];
279
+ locals: Local[];
280
+ };
281
+ /**
282
+ * Every workspace in the tree, split into the ones a publish is a claim about and the ones it is
283
+ * not. Both halves are kept: a check that quietly dropped the manifests it skipped could not say
284
+ * what its denominator was, and "21 of 21" over a tree of 22 is exactly the sample that announced
285
+ * a release the group had not finished (#147).
286
+ */
287
+ declare const censusOf: (root: string) => Census;
288
+ declare const EXISTS_BUT = "exists but no version matching";
289
+ /**
290
+ * #147 — the release is not the publish exiting zero; it is the GROUP resolving from npm.
291
+ *
292
+ * Every workspace, every sweep, with no sampling: the one entry point nobody checked is the one a
293
+ * consumer's bump reads, and it fails in their tree as a nested duplicate rather than as an error
294
+ * anyone here would see.
295
+ */
296
+ declare const runPublished: ({ pollMs, registry, root, waitSeconds, }: {
297
+ pollMs?: number;
298
+ registry?: string;
299
+ root: string;
300
+ waitSeconds?: number;
301
+ }) => Promise<PublishedReport>;
302
+ declare const formatPublished: (report: PublishedReport) => string;
303
+
304
+ /**
305
+ * What a registry says about one package name.
306
+ *
307
+ * Three answers and not two, because a release check and a bump act differently on each: `absent`
308
+ * is the ~4-minute window in which a name published a moment ago still 404s, `present` with the
309
+ * wanted version missing is a manager's cached manifest or a publish that left a package out, and
310
+ * `unreachable` is not a verdict about the package at all.
311
+ */
312
+ type RegistryAnswer = {
313
+ kind: 'absent';
314
+ name: string;
315
+ } | {
316
+ kind: 'present';
317
+ latest: string | undefined;
318
+ name: string;
319
+ versions: string[];
320
+ } | {
321
+ kind: 'unreachable';
322
+ name: string;
323
+ why: string;
324
+ };
325
+ declare const DEFAULT_REGISTRY = "https://registry.npmjs.org";
326
+ /** `@scope/name` → `@scope%2fname`, which is how a registry addresses a scoped packument. */
327
+ declare const registryPathOf: (name: string) => string;
328
+ /**
329
+ * The registry asked over HTTP, and never through the package manager.
330
+ *
331
+ * `npm view` and `bun pm view` answer out of the MANAGER's manifest cache, which is the thing that
332
+ * lied: a consumer's bump aborted on "no version matching 1.4.0" minutes after 1.4.0 was published,
333
+ * because bun had cached the packument from before it (measured 2026-09-01, #147). A check whose
334
+ * job is to catch a stale read must not be reading the same cache.
335
+ */
336
+ declare const askRegistry: ({ name, registry, timeoutMs, }: {
337
+ name: string;
338
+ registry?: string;
339
+ timeoutMs?: number;
340
+ }) => Promise<RegistryAnswer>;
341
+
342
+ declare const runSchema: ({ root }: {
343
+ root: string;
344
+ }) => SchemaReport;
345
+ declare const formatSchema: (report: SchemaReport) => string;
346
+
203
347
  type Request = {
204
348
  step: 'promote';
205
349
  versionId: string;
@@ -228,6 +372,178 @@ declare class Runner {
228
372
  close(): void;
229
373
  }
230
374
 
375
+ /**
376
+ * Where a consumer's bytes live while the release is being read against them.
377
+ *
378
+ * D-048: a consumer tree is private, and a copy of one that reaches a commit cannot be taken back.
379
+ * The directory is under `.geonosis/`, which every repo in this kit's orbit already ignores, and
380
+ * `smoke snapshot` asks git — in the tree it is about to write into — whether that is still true
381
+ * before it copies a single file.
382
+ */
383
+ declare const SNAPSHOTS_DIR = ".geonosis/consumer-snapshots";
384
+ /** The managers a snapshot can be installed with here, each measured on this machine. */
385
+ type Manager = 'bun' | 'pnpm';
386
+ /**
387
+ * Directories a consumer builds or installs into. They are excluded by name at every depth: the
388
+ * snapshot is the SOURCE of a tree, and the install the smoke runs is the thing under test.
389
+ */
390
+ declare const EXCLUDED_DIRS: string[];
391
+ /** What a phase runs: a script of theirs, a binary their install put there, or nothing at all. */
392
+ type Command = {
393
+ exec: string;
394
+ } | {
395
+ run: string;
396
+ } | {
397
+ why: string;
398
+ };
399
+ type SmokePhase = 'doctor' | 'lint' | 'typecheck';
400
+ declare const PHASES: SmokePhase[];
401
+ type SnapshotRecord = {
402
+ at: string;
403
+ bytes: number;
404
+ commands: Record<SmokePhase, Command>;
405
+ excluded: string[];
406
+ files: number;
407
+ from: string;
408
+ lockfile: string;
409
+ manager: Manager;
410
+ manifests: {
411
+ path: string;
412
+ versions: Record<string, string>;
413
+ }[];
414
+ name: string;
415
+ };
416
+ declare const snapshotDir: (root: string, name: string) => string;
417
+ declare const readSnapshot: (root: string, name: string) => SnapshotRecord;
418
+ type SnapshotInput = {
419
+ exclude?: string[];
420
+ from: string;
421
+ name: string;
422
+ named: Partial<Record<SmokePhase, string>>;
423
+ replace: boolean;
424
+ root: string;
425
+ };
426
+ declare const runSnapshot: (input: SnapshotInput) => SnapshotRecord;
427
+ declare const formatSnapshot: (record: SnapshotRecord) => string;
428
+
429
+ /** What a phase did, or the sentence saying there was nothing of theirs to do it with. */
430
+ type PhaseOutcome = {
431
+ code: number;
432
+ command: string;
433
+ lines: string[];
434
+ ok: boolean;
435
+ phase: SmokePhase;
436
+ } | {
437
+ phase: SmokePhase;
438
+ why: string;
439
+ };
440
+ type Packed = {
441
+ files: string[];
442
+ name: string;
443
+ tarball: string;
444
+ version: string;
445
+ };
446
+ type SmokeBaseline = {
447
+ at: string;
448
+ packed: {
449
+ name: string;
450
+ version: string;
451
+ }[];
452
+ phases: PhaseOutcome[];
453
+ rc: string;
454
+ };
455
+ type SmokeComparison = {
456
+ broken: {
457
+ lines: string[];
458
+ phase: SmokePhase;
459
+ }[];
460
+ findings: string[];
461
+ known: SmokePhase[];
462
+ mended: SmokePhase[];
463
+ name: string;
464
+ ok: boolean;
465
+ outcomes: PhaseOutcome[];
466
+ refused: {
467
+ phase: SmokePhase;
468
+ why: string;
469
+ }[];
470
+ };
471
+ declare const BASELINE_FILE = "baseline.json";
472
+ /**
473
+ * The tarballs, because a tarball is what a consumer installs.
474
+ *
475
+ * Escape (1) lived exactly here: the declarations were in the tree, the build made them, and the
476
+ * `files` list did not carry them — so every kit test passed and the first consumer import was
477
+ * TS7016. Nothing short of packing can see that.
478
+ */
479
+ declare const packRc: (rc: string, into: string) => Packed[];
480
+ /**
481
+ * A tarball that promises declarations and does not carry them, read off the pack itself.
482
+ *
483
+ * The consumer's compiler says the same thing one import later — but only about the packages that
484
+ * consumer imports. This says it about every package in the release.
485
+ */
486
+ declare const promisedButNotPacked: (rc: string, packed: Packed[]) => string[];
487
+ /** The snapshot's manifests, pointed at the tarballs instead of at the versions they name. */
488
+ declare const rewriteManifests: (tree: string, manifests: string[], packed: Packed[]) => {
489
+ name: string;
490
+ path: string;
491
+ }[];
492
+ type RunInput = {
493
+ install?: string;
494
+ name: string;
495
+ rc?: string;
496
+ root: string;
497
+ };
498
+ declare const recordBaseline: (input: RunInput) => SmokeBaseline;
499
+ declare const readBaseline: (root: string, name: string) => SmokeBaseline;
500
+ declare const compareToBaseline: (name: string, baseline: SmokeBaseline, outcomes: PhaseOutcome[], findings: string[]) => SmokeComparison;
501
+ declare const runSmoke: (input: RunInput) => SmokeComparison;
502
+ type SmokeSkip = {
503
+ name: string;
504
+ why: string;
505
+ };
506
+ type SmokeSweep = {
507
+ comparisons: SmokeComparison[];
508
+ skipped: SmokeSkip[];
509
+ };
510
+ /**
511
+ * Every snapshot the release expects, read in turn.
512
+ *
513
+ * A tree that is not on this machine is SKIPPED with the sentence saying so — a laptop without the
514
+ * consumer trees must not fail, it must say what it could not ask. The skip is not free: its three
515
+ * commands stay in the envelope's denominator as excused, so a sweep that asked nothing cannot be
516
+ * read as a sweep that asked everything.
517
+ */
518
+ declare const sweepSmoke: (input: {
519
+ rc?: string;
520
+ root: string;
521
+ /**
522
+ * Whether an absent snapshot is a skip. It is when the repo NAMED the set and this laptop has
523
+ * some of it; it is not when a caller asked for one tree by name — that question was asked and
524
+ * has to be answered, not stepped around.
525
+ */
526
+ skipMissing: boolean;
527
+ wanted: {
528
+ install?: string;
529
+ name: string;
530
+ }[];
531
+ }) => SmokeSweep;
532
+ declare const formatSweep: (sweep: SmokeSweep) => string;
533
+ declare const sweepEnvelope: (sweep: SmokeSweep, durationMs: number) => ReportEnvelope;
534
+ declare const formatSmoke: (comparison: SmokeComparison) => string;
535
+ declare const formatBaseline: (name: string, baseline: SmokeBaseline) => string;
536
+ declare const SMOKE_TOOL = "release-smoke";
537
+ declare const SMOKE_NEXT = "geonosis-release smoke run --snapshot <name> --json and read `outcomes` \u2014 every phase the snapshot named leaves by exactly one door: read, excused with the sentence saying there was nothing of theirs to run, or refused because this run and the baseline are not comparable";
538
+ /**
539
+ * The three commands as a census.
540
+ *
541
+ * The denominator is what the SNAPSHOT named, not what this run managed to do: a smoke that skipped
542
+ * the doctor because the bin was not installed and reported OK over two commands is the shape this
543
+ * whole workstream exists to catch.
544
+ */
545
+ declare const smokeEnvelope: (comparison: SmokeComparison, durationMs: number) => ReportEnvelope;
546
+
231
547
  type Extracted = {
232
548
  line: number;
233
549
  sql: string;
@@ -263,4 +579,4 @@ declare const statementsOf: (sql: string) => string;
263
579
  /** One refusal per verb per file, at the line of that verb's first occurrence. */
264
580
  declare const refusalsIn: (path: string, body: string, firstLine?: number) => Refusal[];
265
581
 
266
- export { DEPLOYED_FILE, type Declared, type Deployed, type DeployedReport, type Dialect, type Drift, type MigrationDir, type MigrationsReport, NARROWING, NOTHING_SAYS, PLANTS, type Phase, type PlanReport, type ProveOutcome, type Refusal, type ReleaseConfig, Runner, STEPS, type Verdict, WHY_VERSION_IDENTITY, addSqlIn, declaredIn, driftBetween, formatDeployed, formatMigrations, formatPlan, formatProve, formatVerdict, markersIn, parseJsonc, parseReleaseConfig, parseToml, prove, proveOver, readDeployed, readReleaseConfig, readWrangler, refusalsIn, runDeployed, runMigrations, runPlan, statementsOf };
582
+ export { BASELINE_FILE, type Command, DEFAULT_REGISTRY, DEPLOYED_FILE, type Declared, type Deployed, type DeployedReport, type Dialect, type Drift, EXCLUDED_DIRS, EXISTS_BUT, type Manager, type MigrationDir, type MigrationsReport, NARROWING, NOTHING_SAYS, PHASES, PLANTS, type Packed, type Phase, type PhaseOutcome, type PlanReport, type ProveOutcome, type PublishedLine, type PublishedReport, type PublishedVerdict, type Refusal, type RegistryAnswer, type ReleaseConfig, Runner, SMOKE_NEXT, SMOKE_TOOL, SNAPSHOTS_DIR, STEPS, type SchemaConfig, type SchemaDomain, type SchemaFinding, type SchemaReport, type SmokeBaseline, type SmokeComparison, type SmokePhase, type SmokeSkip, type SmokeSweep, type SnapshotRecord, type Verdict, WHY_VERSION_IDENTITY, addSqlIn, askRegistry, censusOf, compareToBaseline, declaredIn, driftBetween, formatBaseline, formatDeployed, formatMigrations, formatPlan, formatProve, formatPublished, formatSchema, formatSmoke, formatSnapshot, formatSweep, formatVerdict, markersIn, packRc, parseJsonc, parseReleaseConfig, parseToml, promisedButNotPacked, prove, proveOver, readBaseline, readDeployed, readReleaseConfig, readSnapshot, readWrangler, recordBaseline, refusalsIn, registryPathOf, rewriteManifests, runDeployed, runMigrations, runPlan, runPublished, runSchema, runSmoke, runSnapshot, smokeEnvelope, snapshotDir, statementsOf, sweepEnvelope, sweepSmoke };
package/dist/index.js CHANGED
@@ -1,62 +1,126 @@
1
1
  import {
2
+ BASELINE_FILE,
3
+ DEFAULT_REGISTRY,
2
4
  DEPLOYED_FILE,
5
+ EXCLUDED_DIRS,
6
+ EXISTS_BUT,
3
7
  NARROWING,
4
8
  NOTHING_SAYS,
9
+ PHASES,
5
10
  PLANTS,
6
11
  Runner,
12
+ SMOKE_NEXT,
13
+ SMOKE_TOOL,
14
+ SNAPSHOTS_DIR,
7
15
  STEPS,
8
16
  WHY_VERSION_IDENTITY,
9
17
  addSqlIn,
18
+ askRegistry,
19
+ censusOf,
20
+ compareToBaseline,
10
21
  declaredIn,
11
22
  driftBetween,
23
+ formatBaseline,
12
24
  formatDeployed,
13
25
  formatMigrations,
14
26
  formatPlan,
15
27
  formatProve,
28
+ formatPublished,
29
+ formatSchema,
30
+ formatSmoke,
31
+ formatSnapshot,
32
+ formatSweep,
16
33
  formatVerdict,
17
34
  markersIn,
35
+ packRc,
18
36
  parseJsonc,
19
37
  parseReleaseConfig,
20
38
  parseToml,
39
+ promisedButNotPacked,
21
40
  prove,
22
41
  proveOver,
42
+ readBaseline,
23
43
  readDeployed,
24
44
  readReleaseConfig,
45
+ readSnapshot,
25
46
  readWrangler,
47
+ recordBaseline,
26
48
  refusalsIn,
49
+ registryPathOf,
50
+ rewriteManifests,
27
51
  runDeployed,
28
52
  runMigrations,
29
53
  runPlan,
30
- statementsOf
31
- } from "./chunk-D5IC766I.js";
54
+ runPublished,
55
+ runSchema,
56
+ runSmoke,
57
+ runSnapshot,
58
+ smokeEnvelope,
59
+ snapshotDir,
60
+ statementsOf,
61
+ sweepEnvelope,
62
+ sweepSmoke
63
+ } from "./chunk-KQIWTXQP.js";
32
64
  export {
65
+ BASELINE_FILE,
66
+ DEFAULT_REGISTRY,
33
67
  DEPLOYED_FILE,
68
+ EXCLUDED_DIRS,
69
+ EXISTS_BUT,
34
70
  NARROWING,
35
71
  NOTHING_SAYS,
72
+ PHASES,
36
73
  PLANTS,
37
74
  Runner,
75
+ SMOKE_NEXT,
76
+ SMOKE_TOOL,
77
+ SNAPSHOTS_DIR,
38
78
  STEPS,
39
79
  WHY_VERSION_IDENTITY,
40
80
  addSqlIn,
81
+ askRegistry,
82
+ censusOf,
83
+ compareToBaseline,
41
84
  declaredIn,
42
85
  driftBetween,
86
+ formatBaseline,
43
87
  formatDeployed,
44
88
  formatMigrations,
45
89
  formatPlan,
46
90
  formatProve,
91
+ formatPublished,
92
+ formatSchema,
93
+ formatSmoke,
94
+ formatSnapshot,
95
+ formatSweep,
47
96
  formatVerdict,
48
97
  markersIn,
98
+ packRc,
49
99
  parseJsonc,
50
100
  parseReleaseConfig,
51
101
  parseToml,
102
+ promisedButNotPacked,
52
103
  prove,
53
104
  proveOver,
105
+ readBaseline,
54
106
  readDeployed,
55
107
  readReleaseConfig,
108
+ readSnapshot,
56
109
  readWrangler,
110
+ recordBaseline,
57
111
  refusalsIn,
112
+ registryPathOf,
113
+ rewriteManifests,
58
114
  runDeployed,
59
115
  runMigrations,
60
116
  runPlan,
61
- statementsOf
117
+ runPublished,
118
+ runSchema,
119
+ runSmoke,
120
+ runSnapshot,
121
+ smokeEnvelope,
122
+ snapshotDir,
123
+ statementsOf,
124
+ sweepEnvelope,
125
+ sweepSmoke
62
126
  };