@telorun/cli 0.74.0 → 0.76.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.
Files changed (60) hide show
  1. package/dist/bundle/manifest-text.d.ts +28 -0
  2. package/dist/bundle/manifest-text.d.ts.map +1 -0
  3. package/dist/bundle/manifest-text.js +92 -0
  4. package/dist/bundle/manifest-text.js.map +1 -0
  5. package/dist/bundle/module-payload.d.ts +157 -0
  6. package/dist/bundle/module-payload.d.ts.map +1 -0
  7. package/dist/bundle/module-payload.js +313 -0
  8. package/dist/bundle/module-payload.js.map +1 -0
  9. package/dist/bundle/partition-layers.d.ts.map +1 -1
  10. package/dist/bundle/partition-layers.js +26 -1
  11. package/dist/bundle/partition-layers.js.map +1 -1
  12. package/dist/bundle/payload-drift.d.ts +14 -8
  13. package/dist/bundle/payload-drift.d.ts.map +1 -1
  14. package/dist/bundle/payload-drift.js +23 -3
  15. package/dist/bundle/payload-drift.js.map +1 -1
  16. package/dist/bundle/select-files.d.ts +16 -4
  17. package/dist/bundle/select-files.d.ts.map +1 -1
  18. package/dist/bundle/select-files.js +25 -7
  19. package/dist/bundle/select-files.js.map +1 -1
  20. package/dist/cli.js +2 -0
  21. package/dist/cli.js.map +1 -1
  22. package/dist/commands/publish.d.ts +7 -18
  23. package/dist/commands/publish.d.ts.map +1 -1
  24. package/dist/commands/publish.js +60 -250
  25. package/dist/commands/publish.js.map +1 -1
  26. package/dist/commands/release.d.ts +16 -0
  27. package/dist/commands/release.d.ts.map +1 -0
  28. package/dist/commands/release.js +376 -0
  29. package/dist/commands/release.js.map +1 -0
  30. package/dist/logger.d.ts.map +1 -1
  31. package/dist/logger.js +17 -2
  32. package/dist/logger.js.map +1 -1
  33. package/dist/output.d.ts +40 -0
  34. package/dist/output.d.ts.map +1 -1
  35. package/dist/output.js +86 -2
  36. package/dist/output.js.map +1 -1
  37. package/dist/publishers/npm.d.ts.map +1 -1
  38. package/dist/publishers/npm.js +8 -0
  39. package/dist/publishers/npm.js.map +1 -1
  40. package/dist/release/apply-plan.d.ts +38 -0
  41. package/dist/release/apply-plan.d.ts.map +1 -0
  42. package/dist/release/apply-plan.js +103 -0
  43. package/dist/release/apply-plan.js.map +1 -0
  44. package/dist/release/evidence.d.ts +56 -0
  45. package/dist/release/evidence.d.ts.map +1 -0
  46. package/dist/release/evidence.js +245 -0
  47. package/dist/release/evidence.js.map +1 -0
  48. package/dist/release/ledger-store.d.ts +27 -0
  49. package/dist/release/ledger-store.d.ts.map +1 -0
  50. package/dist/release/ledger-store.js +62 -0
  51. package/dist/release/ledger-store.js.map +1 -0
  52. package/dist/release/render.d.ts +17 -0
  53. package/dist/release/render.d.ts.map +1 -0
  54. package/dist/release/render.js +71 -0
  55. package/dist/release/render.js.map +1 -0
  56. package/dist/release/workspace.d.ts +58 -0
  57. package/dist/release/workspace.d.ts.map +1 -0
  58. package/dist/release/workspace.js +138 -0
  59. package/dist/release/workspace.js.map +1 -0
  60. package/package.json +6 -6
package/dist/output.js CHANGED
@@ -45,20 +45,76 @@ export class Output {
45
45
  this.stdout = format === "text" ? paletteFor(Boolean(stdout.isTTY), env) : PLAIN;
46
46
  this.stderr = paletteFor(Boolean(stderr.isTTY), env);
47
47
  }
48
+ /** Whether a transient progress line is currently on screen, waiting to be
49
+ * overwritten. Owned here because the rule it implies — every other write
50
+ * must erase it first — has to hold for every write method, and a call site
51
+ * that forgot would leave its output glued to the end of a ticker. */
52
+ transient = false;
48
53
  get isJson() {
49
54
  return this.format === "json";
50
55
  }
56
+ /** Erase a pending transient line, so the next write starts on a clean row.
57
+ * `\r` returns to column 0 and `\x1b[2K` clears the row; both are cursor
58
+ * control rather than colour, so they are gated on the stream being a TTY and
59
+ * not on the palette — a `NO_COLOR` terminal still has a cursor. */
60
+ clearTransient() {
61
+ if (!this.transient)
62
+ return;
63
+ this.transient = false;
64
+ this.errStream.write("\r\x1b[2K");
65
+ }
66
+ /** Drop any pending progress line without writing anything else. For a
67
+ * command that finishes without a further write and would otherwise leave the
68
+ * shell prompt sitting after a half-drawn ticker. */
69
+ endProgress() {
70
+ this.clearTransient();
71
+ }
51
72
  /** Human-readable line on stdout. Suppressed under `-o json`, where stdout is
52
73
  * reserved for the payload. */
53
74
  line(text = "") {
54
- if (this.format === "text")
55
- this.outStream.write(`${text}\n`);
75
+ if (this.format !== "text")
76
+ return;
77
+ // Cleared even though this writes to the OTHER stream: both usually land on
78
+ // one terminal, so a stdout line written over a pending stderr ticker would
79
+ // start halfway across the row.
80
+ this.clearTransient();
81
+ this.outStream.write(`${text}\n`);
56
82
  }
57
83
  /** Human-readable line on stderr. Written in BOTH formats — stderr is not the
58
84
  * machine contract, and silencing it loses the reason a command failed. */
59
85
  errLine(text = "") {
86
+ this.clearTransient();
60
87
  this.errStream.write(`${text}\n`);
61
88
  }
89
+ /**
90
+ * A progress tick on stderr, written only in text format and only to a TTY.
91
+ *
92
+ * The distinction this draws is between a **diagnostic** and a **progress
93
+ * indicator**, and it is why the rule is not the one `errLine` follows.
94
+ * `errLine` must write in every format because silencing it would lose the
95
+ * reason a command failed — that is error swallowing. A tick explains nothing.
96
+ * It is an affordance of the human-formatted mode: something for a person
97
+ * watching a long operation, so that a two-minute build does not look hung.
98
+ *
99
+ * BOTH conditions, because either one alone leaves a case wrong. `-o json`
100
+ * says the output is a contract, and decorating that run with prose the caller
101
+ * did not ask for is noise in their terminal whether or not they are looking
102
+ * at it — the format is the caller's statement of intent, not a guess about
103
+ * where the bytes land. And a text run redirected into a file or a CI log has
104
+ * nobody watching either, where sixty ticks bury the output the reader came
105
+ * for.
106
+ */
107
+ progress(text) {
108
+ if (this.format !== "text" || !this.errStream.isTTY)
109
+ return;
110
+ this.clearTransient();
111
+ // No newline: this line is TRANSIENT. The next tick overwrites it and any
112
+ // real output erases it, so a 59-module run leaves behind only the findings
113
+ // rather than 59 ticks interleaved with them — which is the whole reason a
114
+ // ticker is bearable at this length at all.
115
+ this.errStream.write(truncate(text, this.errStream.columns));
116
+ this.transient = true;
117
+ }
62
118
  /** Emit a command's structured RESULT ENVELOPE. A no-op in text mode.
63
119
  *
64
120
  * Every command that owns its stdout calls this exactly once, including when
@@ -75,6 +131,10 @@ export class Output {
75
131
  * whose wire protocol is framed per event precisely because it shares a
76
132
  * stream. */
77
133
  emit(payload) {
134
+ // Unconditionally, including under `text` where nothing is written: every
135
+ // command reports its result exactly once, so this is the lifecycle point at
136
+ // which a leftover ticker has to go.
137
+ this.clearTransient();
78
138
  if (this.format === "json")
79
139
  this.outStream.write(serialize(payload));
80
140
  }
@@ -86,6 +146,7 @@ export class Output {
86
146
  * must keep working under the default `text` format. One serializer with
87
147
  * `emit`, so the CLI has a single JSON encoding rather than three. */
88
148
  document(payload) {
149
+ this.clearTransient();
89
150
  this.outStream.write(serialize(payload));
90
151
  }
91
152
  /** Write verbatim content to stdout, unconditionally and with no framing.
@@ -96,9 +157,30 @@ export class Output {
96
157
  * — the `--json` path wraps them in a document instead and never reaches
97
158
  * here. */
98
159
  raw(content) {
160
+ this.clearTransient();
99
161
  this.outStream.write(content);
100
162
  }
101
163
  }
164
+ /** Clip to the terminal width, measuring PRINTABLE characters so the escapes a
165
+ * palette wrapped the text in do not count toward it. A line that wraps
166
+ * occupies two rows and the erase sequence clears one. */
167
+ function truncate(text, columns) {
168
+ if (!columns || columns < 8)
169
+ return text;
170
+ let printable = 0;
171
+ for (let i = 0; i < text.length; i++) {
172
+ // Skip a CSI sequence wholesale: `\x1b[` … a final byte in `@`–`~`.
173
+ if (text[i] === "\x1b" && text[i + 1] === "[") {
174
+ i += 2;
175
+ while (i < text.length && !/[@-~]/.test(text[i]))
176
+ i++;
177
+ continue;
178
+ }
179
+ if (++printable > columns - 1)
180
+ return `${text.slice(0, i)}…`;
181
+ }
182
+ return text;
183
+ }
102
184
  function serialize(payload) {
103
185
  return `${JSON.stringify(payload, null, 2)}\n`;
104
186
  }
@@ -140,4 +222,6 @@ export const outLine = (text = "") => current.line(text);
140
222
  export const outErrLine = (text = "") => current.errLine(text);
141
223
  export const outEmit = (payload) => current.emit(payload);
142
224
  export const outDocument = (payload) => current.document(payload);
225
+ export const outProgress = (text) => current.progress(text);
226
+ export const outEndProgress = () => current.endProgress();
143
227
  //# sourceMappingURL=output.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"output.js","sourceRoot":"","sources":["../src/output.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"output.js","sourceRoot":"","sources":["../src/output.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"npm.d.ts","sourceRoot":"","sources":["../../src/publishers/npm.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAa,mBAAmB,EAAE,MAAM,gBAAgB,CAAC;AAErE,eAAO,MAAM,YAAY,EAAE,mBAgD1B,CAAC"}
1
+ {"version":3,"file":"npm.d.ts","sourceRoot":"","sources":["../../src/publishers/npm.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAa,mBAAmB,EAAE,MAAM,gBAAgB,CAAC;AAErE,eAAO,MAAM,YAAY,EAAE,mBAwD1B,CAAC"}
@@ -16,9 +16,17 @@ export const npmPublisher = {
16
16
  return this.readVersion(localPath);
17
17
  },
18
18
  async build(localPath) {
19
+ // Windows ships no executable named `npm` — it is `npm.cmd`, which libuv's
20
+ // PATH search (`.com`/`.exe` only) never finds and which Node refuses to
21
+ // spawn without a shell since CVE-2024-27980. The `execSync` calls in this
22
+ // file already route through `ComSpec` and so were never affected; only a
23
+ // direct spawn needs this. Every argument here is a fixed literal with no
24
+ // space and no cmd metacharacter, so unlike the kernel's installer this one
25
+ // needs no per-token quoting — a dynamic argument added later would.
19
26
  const result = spawnSync("npm", ["--loglevel=error", "run", "build"], {
20
27
  cwd: localPath,
21
28
  encoding: "utf-8",
29
+ shell: process.platform === "win32",
22
30
  });
23
31
  if (result.status !== 0) {
24
32
  const output = [result.stdout, result.stderr].filter(Boolean).join("\n");
@@ -1 +1 @@
1
- {"version":3,"file":"npm.js","sourceRoot":"","sources":["../../src/publishers/npm.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC;AACpD,OAAO,KAAK,EAAE,MAAM,IAAI,CAAC;AACzB,OAAO,KAAK,IAAI,MAAM,MAAM,CAAC;AAG7B,MAAM,CAAC,MAAM,YAAY,GAAwB;IAC/C,IAAI,EAAE,KAAK;IAEX,KAAK,CAAC,WAAW,CAAC,SAAiB;QACjC,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,cAAc,CAAC,CAAC;QACrD,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,YAAY,CAAC,OAAO,EAAE,OAAO,CAAC,CAAwB,CAAC;QACjF,OAAO,GAAG,CAAC,OAAO,CAAC;IACrB,CAAC;IAED,KAAK,CAAC,WAAW,CAAC,SAAiB,EAAE,KAAgB;QACnD,QAAQ,CAAC,eAAe,KAAK,wCAAwC,EAAE;YACrE,GAAG,EAAE,SAAS;YACd,KAAK,EAAE,SAAS;SACjB,CAAC,CAAC;QACH,OAAO,IAAI,CAAC,WAAW,CAAC,SAAS,CAAC,CAAC;IACrC,CAAC;IAED,KAAK,CAAC,KAAK,CAAC,SAAiB;QAC3B,MAAM,MAAM,GAAG,SAAS,CAAC,KAAK,EAAE,CAAC,kBAAkB,EAAE,KAAK,EAAE,OAAO,CAAC,EAAE;YACpE,GAAG,EAAE,SAAS;YACd,QAAQ,EAAE,OAAO;SAClB,CAAC,CAAC;QACH,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACxB,MAAM,MAAM,GAAG,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YACzE,MAAM,IAAI,KAAK,CAAC,kBAAkB,MAAM,EAAE,CAAC,CAAC;QAC9C,CAAC;IACH,CAAC;IAED,KAAK,CAAC,OAAO,CAAC,SAAiB,EAAE,OAAe;QAC9C,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,cAAc,CAAC,CAAC;QACrD,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,YAAY,CAAC,OAAO,EAAE,OAAO,CAAC,CAAqB,CAAC;QAC9E,MAAM,WAAW,GAAG,GAAG,CAAC,IAAI,CAAC;QAE7B,sDAAsD;QACtD,IAAI,CAAC;YACH,QAAQ,CAAC,YAAY,WAAW,IAAI,OAAO,2BAA2B,EAAE;gBACtE,GAAG,EAAE,SAAS;gBACd,KAAK,EAAE,MAAM;aACd,CAAC,CAAC;YACH,wEAAwE;YACxE,OAAO,KAAK,CAAC;QACf,CAAC;QAAC,MAAM,CAAC;YACP,qEAAqE;QACvE,CAAC;QAED,QAAQ,CAAC,8CAA8C,EAAE,EAAE,GAAG,EAAE,SAAS,EAAE,KAAK,EAAE,SAAS,EAAE,CAAC,CAAC;QAC/F,OAAO,IAAI,CAAC;IACd,CAAC;CACF,CAAC"}
1
+ {"version":3,"file":"npm.js","sourceRoot":"","sources":["../../src/publishers/npm.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC;AACpD,OAAO,KAAK,EAAE,MAAM,IAAI,CAAC;AACzB,OAAO,KAAK,IAAI,MAAM,MAAM,CAAC;AAG7B,MAAM,CAAC,MAAM,YAAY,GAAwB;IAC/C,IAAI,EAAE,KAAK;IAEX,KAAK,CAAC,WAAW,CAAC,SAAiB;QACjC,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,cAAc,CAAC,CAAC;QACrD,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,YAAY,CAAC,OAAO,EAAE,OAAO,CAAC,CAAwB,CAAC;QACjF,OAAO,GAAG,CAAC,OAAO,CAAC;IACrB,CAAC;IAED,KAAK,CAAC,WAAW,CAAC,SAAiB,EAAE,KAAgB;QACnD,QAAQ,CAAC,eAAe,KAAK,wCAAwC,EAAE;YACrE,GAAG,EAAE,SAAS;YACd,KAAK,EAAE,SAAS;SACjB,CAAC,CAAC;QACH,OAAO,IAAI,CAAC,WAAW,CAAC,SAAS,CAAC,CAAC;IACrC,CAAC;IAED,KAAK,CAAC,KAAK,CAAC,SAAiB;QAC3B,2EAA2E;QAC3E,yEAAyE;QACzE,2EAA2E;QAC3E,0EAA0E;QAC1E,0EAA0E;QAC1E,4EAA4E;QAC5E,qEAAqE;QACrE,MAAM,MAAM,GAAG,SAAS,CAAC,KAAK,EAAE,CAAC,kBAAkB,EAAE,KAAK,EAAE,OAAO,CAAC,EAAE;YACpE,GAAG,EAAE,SAAS;YACd,QAAQ,EAAE,OAAO;YACjB,KAAK,EAAE,OAAO,CAAC,QAAQ,KAAK,OAAO;SACpC,CAAC,CAAC;QACH,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACxB,MAAM,MAAM,GAAG,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YACzE,MAAM,IAAI,KAAK,CAAC,kBAAkB,MAAM,EAAE,CAAC,CAAC;QAC9C,CAAC;IACH,CAAC;IAED,KAAK,CAAC,OAAO,CAAC,SAAiB,EAAE,OAAe;QAC9C,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,cAAc,CAAC,CAAC;QACrD,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,YAAY,CAAC,OAAO,EAAE,OAAO,CAAC,CAAqB,CAAC;QAC9E,MAAM,WAAW,GAAG,GAAG,CAAC,IAAI,CAAC;QAE7B,sDAAsD;QACtD,IAAI,CAAC;YACH,QAAQ,CAAC,YAAY,WAAW,IAAI,OAAO,2BAA2B,EAAE;gBACtE,GAAG,EAAE,SAAS;gBACd,KAAK,EAAE,MAAM;aACd,CAAC,CAAC;YACH,wEAAwE;YACxE,OAAO,KAAK,CAAC;QACf,CAAC;QAAC,MAAM,CAAC;YACP,qEAAqE;QACvE,CAAC;QAED,QAAQ,CAAC,8CAA8C,EAAE,EAAE,GAAG,EAAE,SAAS,EAAE,KAAK,EAAE,SAAS,EAAE,CAAC,CAAC;QAC/F,OAAO,IAAI,CAAC;IACd,CAAC;CACF,CAAC"}
@@ -0,0 +1,38 @@
1
+ /**
2
+ * Writing a release plan to disk — the half of `telo release apply` that
3
+ * changes files.
4
+ *
5
+ * Split from the command so the command is wiring: `commands/release.ts` reads
6
+ * argv, resolves the registry and prints, and everything that touches a manifest,
7
+ * a changelog or the ledger is here. The seam is the plan itself, which is
8
+ * already a complete description of what to write.
9
+ */
10
+ import { type Ledger, type ModuleKey, type ReleasePlan } from "@telorun/analyzer";
11
+ import { type Workspace } from "./workspace.js";
12
+ /** One module's writes, reported so `apply` can list what it touched. */
13
+ export interface AppliedModule {
14
+ readonly key: ModuleKey;
15
+ readonly from: string;
16
+ readonly to: string;
17
+ /** Workspace-relative paths written for this module. */
18
+ readonly files: readonly string[];
19
+ }
20
+ /**
21
+ * Write every planned module's version and changelog.
22
+ *
23
+ * Returns before the ledger is recorded, because the ledger has to be built from
24
+ * the versions this pass just wrote.
25
+ */
26
+ export declare function writePlannedVersions(workspace: Workspace, plan: ReleasePlan, date: string): AppliedModule[];
27
+ /**
28
+ * The ledger as it will look once this release is published.
29
+ *
30
+ * Built from a SECOND payload pass taken after the versions are on disk, because
31
+ * that is what will actually ship: a dependent's manifest layer carries its
32
+ * dependency's new version and the pin derived from the dependency's new bytes,
33
+ * neither of which existed while the plan was being formed. Recording the
34
+ * pre-bump digest would guarantee the next `check` reported drift on every
35
+ * module in this release.
36
+ */
37
+ export declare function recordLedger(root: string, registry: string, plan: ReleasePlan): Promise<Ledger>;
38
+ //# sourceMappingURL=apply-plan.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"apply-plan.d.ts","sourceRoot":"","sources":["../../src/release/apply-plan.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAML,KAAK,MAAM,EAEX,KAAK,SAAS,EAEd,KAAK,WAAW,EACjB,MAAM,mBAAmB,CAAC;AAM3B,OAAO,EAAgC,KAAK,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAE9E,yEAAyE;AACzE,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,GAAG,EAAE,SAAS,CAAC;IACxB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,wDAAwD;IACxD,QAAQ,CAAC,KAAK,EAAE,SAAS,MAAM,EAAE,CAAC;CACnC;AAED;;;;;GAKG;AACH,wBAAgB,oBAAoB,CAClC,SAAS,EAAE,SAAS,EACpB,IAAI,EAAE,WAAW,EACjB,IAAI,EAAE,MAAM,GACX,aAAa,EAAE,CAQjB;AAoDD;;;;;;;;;GASG;AACH,wBAAsB,YAAY,CAChC,IAAI,EAAE,MAAM,EACZ,QAAQ,EAAE,MAAM,EAChB,IAAI,EAAE,WAAW,GAChB,OAAO,CAAC,MAAM,CAAC,CAuBjB"}
@@ -0,0 +1,103 @@
1
+ /**
2
+ * Writing a release plan to disk — the half of `telo release apply` that
3
+ * changes files.
4
+ *
5
+ * Split from the command so the command is wiring: `commands/release.ts` reads
6
+ * argv, resolves the registry and prints, and everything that touches a manifest,
7
+ * a changelog or the ledger is here. The seam is the plan itself, which is
8
+ * already a complete description of what to write.
9
+ */
10
+ import { prependChangelogRelease, renderChangelogRelease, stampCrateVersion, stampManifestVersion, stampPackageVersion, } from "@telorun/analyzer";
11
+ import * as fs from "node:fs";
12
+ import * as path from "node:path";
13
+ import { ModulePayloadBuilder } from "../bundle/module-payload.js";
14
+ import { destinationFor, digestPayload, imageDigest } from "./evidence.js";
15
+ import { readLedger } from "./ledger-store.js";
16
+ import { loadWorkspace, requireModule } from "./workspace.js";
17
+ /**
18
+ * Write every planned module's version and changelog.
19
+ *
20
+ * Returns before the ledger is recorded, because the ledger has to be built from
21
+ * the versions this pass just wrote.
22
+ */
23
+ export function writePlannedVersions(workspace, plan, date) {
24
+ return plan.modules.map((module) => {
25
+ const discovered = requireModule(workspace, module.key);
26
+ const files = [...stampVersion(discovered.dir, module.to)];
27
+ const changelog = writeChangelog(workspace, discovered.dir, module, date);
28
+ if (changelog)
29
+ files.push(changelog);
30
+ return { key: module.key, from: module.from, to: module.to, files };
31
+ });
32
+ }
33
+ /**
34
+ * Stamp a module's one version into every manifest it owns.
35
+ *
36
+ * A file that carries no version is skipped — a module may own only a
37
+ * `telo.yaml`, and most have no Rust crate. One whose version is written in a
38
+ * shape that cannot be rewritten in place throws instead, because leaving a
39
+ * module's manifests disagreeing about its own version is worse than stopping.
40
+ */
41
+ function stampVersion(dir, version) {
42
+ const targets = [
43
+ ["telo.yaml", stampManifestVersion],
44
+ ["nodejs/package.json", stampPackageVersion],
45
+ ["rust/Cargo.toml", stampCrateVersion],
46
+ ];
47
+ const written = [];
48
+ for (const [relative, stamp] of targets) {
49
+ const file = path.join(dir, relative);
50
+ if (!fs.existsSync(file))
51
+ continue;
52
+ const before = fs.readFileSync(file, "utf8");
53
+ const after = stamp(before, version, relative);
54
+ if (after === undefined || after === before)
55
+ continue;
56
+ fs.writeFileSync(file, after, "utf8");
57
+ written.push(relative);
58
+ }
59
+ return written;
60
+ }
61
+ /** Prepend this release's block, or nothing when no fragment named the module —
62
+ * a bump that came only from propagation or a toolchain move has no prose, and
63
+ * an empty heading would be worse than silence. */
64
+ function writeChangelog(workspace, dir, module, date) {
65
+ if (module.entries.length === 0)
66
+ return undefined;
67
+ const file = path.join(dir, "CHANGELOG.md");
68
+ const existing = fs.existsSync(file) ? fs.readFileSync(file, "utf8") : undefined;
69
+ fs.writeFileSync(file, prependChangelogRelease(existing, renderChangelogRelease({ version: module.to, date, entries: module.entries })), "utf8");
70
+ return path.relative(workspace.root, file);
71
+ }
72
+ /**
73
+ * The ledger as it will look once this release is published.
74
+ *
75
+ * Built from a SECOND payload pass taken after the versions are on disk, because
76
+ * that is what will actually ship: a dependent's manifest layer carries its
77
+ * dependency's new version and the pin derived from the dependency's new bytes,
78
+ * neither of which existed while the plan was being formed. Recording the
79
+ * pre-bump digest would guarantee the next `check` reported drift on every
80
+ * module in this release.
81
+ */
82
+ export async function recordLedger(root, registry, plan) {
83
+ // Re-read the workspace so discovery sees the versions just written.
84
+ const workspace = loadWorkspace(root);
85
+ const planned = new Set(plan.modules.map((module) => module.key));
86
+ const builder = new ModulePayloadBuilder({ cacheRoot: path.join(workspace.root, ".telo") });
87
+ // A module that is not in the plan keeps whatever the ledger already said —
88
+ // which may be nothing, the correct reading for one that has never published.
89
+ // Recording a digest for it here would claim a publish that is not happening.
90
+ const entries = new Map(readLedger(workspace.root).modules);
91
+ for (const module of workspace.modules) {
92
+ if (!planned.has(module.key))
93
+ continue;
94
+ entries.set(module.key, {
95
+ version: module.version,
96
+ layers: module.artifactKind === "image"
97
+ ? { image: await imageDigest(module) }
98
+ : await digestPayload(await builder.payload(module.manifestPath, destinationFor(registry, module))),
99
+ });
100
+ }
101
+ return { registry, modules: entries };
102
+ }
103
+ //# sourceMappingURL=apply-plan.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"apply-plan.js","sourceRoot":"","sources":["../../src/release/apply-plan.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,56 @@
1
+ /**
2
+ * Collecting what the planner needs: one `ModuleEvidence` per module.
3
+ *
4
+ * Three separate readings, because the plan asks three separate questions:
5
+ *
6
+ * - **The payload digest** — built through the shared `ModulePayloadBuilder`, so
7
+ * the number recorded here is the number `telo publish` computes against the
8
+ * registry. That equality is the whole point of the ledger.
9
+ * - **The edge graph** — from the build's own metafile (which module's source
10
+ * got inlined into whose artifact) and from in-repo relative `imports:`. A
11
+ * declared-dependency graph cannot see `--external`, so `@telorun/sdk` —
12
+ * declared by 54 modules, inlined by none — would bump the entire standard
13
+ * library on every SDK change.
14
+ * - **Own changed files** — a git diff, path-scoped. It used to decide the
15
+ * version, which is why its guesswork had to be sound; here it decides only
16
+ * whether a changelog line is requested.
17
+ */
18
+ import { type LayerDigests, type ModuleEvidence } from "@telorun/analyzer";
19
+ import { type ModulePayload } from "../bundle/module-payload.js";
20
+ import type { DiscoveredModule, Workspace } from "./workspace.js";
21
+ export interface EvidenceOptions {
22
+ /** `oci://host/org` — the base each module's destination derives from. */
23
+ readonly registry: string;
24
+ /** Git ref the changed-files reading diffs against. */
25
+ readonly baseRef: string;
26
+ /** Called before each module is built, so a long batch shows progress. */
27
+ readonly onModule?: (module: DiscoveredModule, index: number, total: number) => void;
28
+ }
29
+ /**
30
+ * A module's publish destination: the registry base plus its own directory name.
31
+ *
32
+ * Identity is the ref, never `metadata.name` — this is the same rule the release
33
+ * job has always applied, lifted out of a shell script.
34
+ *
35
+ * This is the ROOT destination, which is a policy rather than a derivation:
36
+ * nothing in the graph can say which repo a module publishes to. The payload
37
+ * builder derives a SIBLING's from it instead of re-applying this rule, so the
38
+ * ref an artifact carries and the destination its dependency is pushed to are
39
+ * the same string by construction.
40
+ */
41
+ export declare function destinationFor(registry: string, module: DiscoveredModule): string;
42
+ /**
43
+ * A built payload reduced to the per-layer digest map the ledger records.
44
+ *
45
+ * ONE function, because there are two write paths into the ledger — `apply`
46
+ * recording what it is about to publish, and `verify --write` re-recording what
47
+ * the registry actually serves — and the gates only work if they produce the
48
+ * same map. Two hand-built copies that drifted would make `verify --write`
49
+ * record numbers `check` cannot reproduce, and every module would then report
50
+ * permanent phantom drift: the exact failure the ledger's "both gates compute
51
+ * the same number" premise rests on not happening.
52
+ */
53
+ export declare function digestPayload(payload: ModulePayload): Promise<LayerDigests>;
54
+ export declare function collectEvidence(workspace: Workspace, options: EvidenceOptions): Promise<ModuleEvidence[]>;
55
+ export declare function imageDigest(module: DiscoveredModule): Promise<string>;
56
+ //# sourceMappingURL=evidence.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"evidence.d.ts","sourceRoot":"","sources":["../../src/release/evidence.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,EAGL,KAAK,YAAY,EACjB,KAAK,cAAc,EAEpB,MAAM,mBAAmB,CAAC;AAM3B,OAAO,EAAwB,KAAK,aAAa,EAAE,MAAM,6BAA6B,CAAC;AACvF,OAAO,KAAK,EAAE,gBAAgB,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAElE,MAAM,WAAW,eAAe;IAC9B,0EAA0E;IAC1E,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,uDAAuD;IACvD,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,0EAA0E;IAC1E,QAAQ,CAAC,QAAQ,CAAC,EAAE,CAAC,MAAM,EAAE,gBAAgB,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;CACtF;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,cAAc,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,gBAAgB,GAAG,MAAM,CAEjF;AAED;;;;;;;;;;GAUG;AACH,wBAAsB,aAAa,CAAC,OAAO,EAAE,aAAa,GAAG,OAAO,CAAC,YAAY,CAAC,CAajF;AAED,wBAAsB,eAAe,CACnC,SAAS,EAAE,SAAS,EACpB,OAAO,EAAE,eAAe,GACvB,OAAO,CAAC,cAAc,EAAE,CAAC,CAe3B;AAmED,wBAAsB,WAAW,CAAC,MAAM,EAAE,gBAAgB,GAAG,OAAO,CAAC,MAAM,CAAC,CA8B3E"}
@@ -0,0 +1,245 @@
1
+ /**
2
+ * Collecting what the planner needs: one `ModuleEvidence` per module.
3
+ *
4
+ * Three separate readings, because the plan asks three separate questions:
5
+ *
6
+ * - **The payload digest** — built through the shared `ModulePayloadBuilder`, so
7
+ * the number recorded here is the number `telo publish` computes against the
8
+ * registry. That equality is the whole point of the ledger.
9
+ * - **The edge graph** — from the build's own metafile (which module's source
10
+ * got inlined into whose artifact) and from in-repo relative `imports:`. A
11
+ * declared-dependency graph cannot see `--external`, so `@telorun/sdk` —
12
+ * declared by 54 modules, inlined by none — would bump the entire standard
13
+ * library on every SDK change.
14
+ * - **Own changed files** — a git diff, path-scoped. It used to decide the
15
+ * version, which is why its guesswork had to be sound; here it decides only
16
+ * whether a changelog line is requested.
17
+ */
18
+ import { MANIFEST_LAYER, layerDigestKey, } from "@telorun/analyzer";
19
+ import { computeFilesIntegrity } from "@telorun/kernel";
20
+ import { selectByPatterns } from "@telorun/glob";
21
+ import { execFileSync } from "node:child_process";
22
+ import * as fs from "node:fs";
23
+ import * as path from "node:path";
24
+ import { ModulePayloadBuilder } from "../bundle/module-payload.js";
25
+ /**
26
+ * A module's publish destination: the registry base plus its own directory name.
27
+ *
28
+ * Identity is the ref, never `metadata.name` — this is the same rule the release
29
+ * job has always applied, lifted out of a shell script.
30
+ *
31
+ * This is the ROOT destination, which is a policy rather than a derivation:
32
+ * nothing in the graph can say which repo a module publishes to. The payload
33
+ * builder derives a SIBLING's from it instead of re-applying this rule, so the
34
+ * ref an artifact carries and the destination its dependency is pushed to are
35
+ * the same string by construction.
36
+ */
37
+ export function destinationFor(registry, module) {
38
+ return `${registry.replace(/\/+$/, "")}/${path.basename(module.dir)}`;
39
+ }
40
+ /**
41
+ * A built payload reduced to the per-layer digest map the ledger records.
42
+ *
43
+ * ONE function, because there are two write paths into the ledger — `apply`
44
+ * recording what it is about to publish, and `verify --write` re-recording what
45
+ * the registry actually serves — and the gates only work if they produce the
46
+ * same map. Two hand-built copies that drifted would make `verify --write`
47
+ * record numbers `check` cannot reproduce, and every module would then report
48
+ * permanent phantom drift: the exact failure the ledger's "both gates compute
49
+ * the same number" premise rests on not happening.
50
+ */
51
+ export async function digestPayload(payload) {
52
+ // The manifest is not one of `partitionLayers`' layers — the transport writes
53
+ // it as its own — but it is the layer that changes when a dependency's version
54
+ // or pin moves, which is most of what propagation is about.
55
+ const layers = {
56
+ [MANIFEST_LAYER]: await computeFilesIntegrity([
57
+ { name: MANIFEST_LAYER, content: Buffer.from(payload.manifest, "utf8") },
58
+ ]),
59
+ };
60
+ for (const layer of payload.layers) {
61
+ layers[layerDigestKey(layer.role, layer.selector)] = await computeFilesIntegrity(layer.files);
62
+ }
63
+ return layers;
64
+ }
65
+ export async function collectEvidence(workspace, options) {
66
+ const byDir = new Map(workspace.modules.map((module) => [path.resolve(module.dir), module]));
67
+ const changed = changedModules(workspace, options.baseRef);
68
+ const builder = new ModulePayloadBuilder({ cacheRoot: path.join(workspace.root, ".telo") });
69
+ const evidence = [];
70
+ for (const [index, module] of workspace.modules.entries()) {
71
+ options.onModule?.(module, index, workspace.modules.length);
72
+ evidence.push(module.artifactKind === "image"
73
+ ? await imageEvidence(module, changed)
74
+ : await registryEvidence(workspace, module, byDir, builder, options, changed));
75
+ }
76
+ return evidence;
77
+ }
78
+ /**
79
+ * A registry artifact module: the layers `telo publish` builds, digested.
80
+ */
81
+ async function registryEvidence(workspace, module, byDir, builder, options, changed) {
82
+ const payload = await builder.payload(module.manifestPath, destinationFor(options.registry, module));
83
+ return {
84
+ key: module.key,
85
+ name: module.name,
86
+ version: module.version,
87
+ artifactKind: "registry",
88
+ layers: await digestPayload(payload),
89
+ inlines: attributeInputs(workspace, payload.buildInputs, module, byDir),
90
+ imports: payload.relativeImports
91
+ .map((entry) => byDir.get(path.resolve(path.dirname(entry.manifestPath)))?.key)
92
+ .filter((key) => key !== undefined && key !== module.key),
93
+ ownFilesChanged: changed.has(module.key),
94
+ };
95
+ }
96
+ /**
97
+ * An image module: the file set its Docker ignore file defines, digested.
98
+ *
99
+ * The ignore file is already the authored statement of what the image contains,
100
+ * so it is read rather than the `COPY` directives parsed. Without a digest here
101
+ * the demoted changed-files rule would be authoritative again for a third of the
102
+ * set — which is precisely the guesswork this design moves off the critical path.
103
+ *
104
+ * The base image is deliberately outside it: a kernel change alters `apps/hub`'s
105
+ * image while this digest does not move. That exposure is closed elsewhere —
106
+ * every app image publishes `:latest` and `:sha-<short>` on each push, so the
107
+ * change reaches the deployed app regardless, and the immutable `:<version>` tag
108
+ * is what a version move is for.
109
+ */
110
+ async function imageEvidence(module, changed) {
111
+ return {
112
+ key: module.key,
113
+ name: module.name,
114
+ version: module.version,
115
+ artifactKind: "image",
116
+ layers: { image: await imageDigest(module) },
117
+ inlines: new Map(),
118
+ imports: [],
119
+ ownFilesChanged: changed.has(module.key),
120
+ };
121
+ }
122
+ /** BuildKit's per-Dockerfile ignore file, then the classic one. Both image
123
+ * modules here carry the former, and looking for only `.dockerignore` would
124
+ * digest every excluded file in both. */
125
+ const DOCKERIGNORE_NAMES = ["Dockerfile.dockerignore", ".dockerignore"];
126
+ export async function imageDigest(module) {
127
+ const ignoreFile = DOCKERIGNORE_NAMES.map((name) => path.join(module.dir, name)).find((file) => fs.existsSync(file));
128
+ const ignored = ignoreFile
129
+ ? fs
130
+ .readFileSync(ignoreFile, "utf8")
131
+ .split("\n")
132
+ .map((line) => line.trim())
133
+ .filter((line) => line !== "" && !line.startsWith("#"))
134
+ : [];
135
+ // An ignore file EXCLUDES, so it goes in `exclude:` rather than being read as
136
+ // a selector — `*` followed by `!*.yaml` means "nothing but the YAML", and
137
+ // treating those as includes would digest exactly the files Docker drops.
138
+ // `exclude` shares the same last-match-wins engine, so the re-include works.
139
+ //
140
+ // The same digest primitive the artifact layers use, so "did this module's
141
+ // payload change?" is one question with one answer regardless of which of the
142
+ // two file sets stands behind it.
143
+ const files = selectByPatterns(listFiles(module.dir), ["**"], {
144
+ applyDefaultIgnore: false,
145
+ exclude: ignored,
146
+ });
147
+ return computeFilesIntegrity([...files].sort().map((file) => ({
148
+ name: file,
149
+ content: fs.readFileSync(path.join(module.dir, file)),
150
+ })));
151
+ }
152
+ function listFiles(root) {
153
+ const found = [];
154
+ const walk = (dir) => {
155
+ for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
156
+ if (entry.name === "node_modules" || entry.name === ".git" || entry.name === ".telo")
157
+ continue;
158
+ const full = path.join(dir, entry.name);
159
+ if (entry.isDirectory())
160
+ walk(full);
161
+ else if (entry.isFile())
162
+ found.push(path.relative(root, full).split(path.sep).join("/"));
163
+ }
164
+ };
165
+ walk(root);
166
+ return found;
167
+ }
168
+ /**
169
+ * Attribute each build input to the workspace module that owns it.
170
+ *
171
+ * A module's own sources are excluded — they are covered by its digest and by
172
+ * the changed-files reading, and listing them as an inline edge would make every
173
+ * module inline itself. An input under no module (a third-party package, a
174
+ * changesets-owned library) is deliberately not attributed: that is the
175
+ * *unattributed* case, reported rather than papered over.
176
+ */
177
+ function attributeInputs(workspace, inputs, module, byDir) {
178
+ const owners = new Map();
179
+ for (const input of inputs) {
180
+ const owner = ownerOf(path.resolve(input), byDir);
181
+ if (!owner || owner.key === module.key)
182
+ continue;
183
+ const list = owners.get(owner.key);
184
+ const relative = path.relative(workspace.root, input).split(path.sep).join("/");
185
+ if (list)
186
+ list.push(relative);
187
+ else
188
+ owners.set(owner.key, [relative]);
189
+ }
190
+ for (const files of owners.values())
191
+ files.sort();
192
+ return owners;
193
+ }
194
+ /** The nearest enclosing module directory, by walking up. Nearest rather than
195
+ * any, so a nested module is credited to itself. */
196
+ function ownerOf(file, byDir) {
197
+ let dir = path.dirname(file);
198
+ for (;;) {
199
+ const owner = byDir.get(dir);
200
+ if (owner)
201
+ return owner;
202
+ const parent = path.dirname(dir);
203
+ if (parent === dir)
204
+ return undefined;
205
+ dir = parent;
206
+ }
207
+ }
208
+ /** Paths under a module that never reach its artifact, so editing one is not a
209
+ * semantic change worth a changelog line. Kept deliberately short: this rule no
210
+ * longer decides a version, so a false positive costs one sentence and the old
211
+ * per-language guesswork bought nothing. */
212
+ const NON_ARTIFACT_PATHS = [/^docs\//, /^plans\//, /^tests\//, /^README\.md$/, /^CHANGELOG\.md$/];
213
+ /**
214
+ * Modules with a change of their own on this branch.
215
+ *
216
+ * No merge base — a shallow clone, a fresh repo — means the reading cannot be
217
+ * taken, and guessing would ask for a changelog entry at random. The digest is
218
+ * unaffected: it compares against the ledger, not against a ref, which is why it
219
+ * needs no merge base in the first place.
220
+ */
221
+ function changedModules(workspace, baseRef) {
222
+ let diff;
223
+ try {
224
+ diff = execFileSync("git", ["diff", "--name-only", `${baseRef}...HEAD`], {
225
+ cwd: workspace.root,
226
+ encoding: "utf8",
227
+ stdio: ["ignore", "pipe", "ignore"],
228
+ });
229
+ }
230
+ catch {
231
+ return new Set();
232
+ }
233
+ const changed = new Set();
234
+ for (const file of diff.split("\n").map((line) => line.trim()).filter(Boolean)) {
235
+ const module = workspace.modules.find((candidate) => file.startsWith(`${candidate.key}/`));
236
+ if (!module)
237
+ continue;
238
+ const rest = file.slice(module.key.length + 1);
239
+ if (NON_ARTIFACT_PATHS.some((pattern) => pattern.test(rest)))
240
+ continue;
241
+ changed.add(module.key);
242
+ }
243
+ return changed;
244
+ }
245
+ //# sourceMappingURL=evidence.js.map