@intentius/sql-yodeler 0.2.0 → 0.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.
Files changed (308) hide show
  1. package/README.md +3 -3
  2. package/dist/apply-steps/index.d.ts +65 -0
  3. package/dist/apply-steps/index.js +106 -0
  4. package/dist/apply-steps/index.js.map +1 -0
  5. package/dist/approval-mode/index.d.ts +78 -0
  6. package/dist/approval-mode/index.js +105 -0
  7. package/dist/approval-mode/index.js.map +1 -0
  8. package/dist/chant/access.d.ts +87 -0
  9. package/dist/chant/access.js +169 -0
  10. package/dist/chant/access.js.map +1 -0
  11. package/dist/chant/build.js +7 -0
  12. package/dist/chant/build.js.map +1 -1
  13. package/dist/chant/drift.d.ts +17 -1
  14. package/dist/chant/drift.js +55 -2
  15. package/dist/chant/drift.js.map +1 -1
  16. package/dist/chant/plan.d.ts +18 -2
  17. package/dist/chant/plan.js +21 -3
  18. package/dist/chant/plan.js.map +1 -1
  19. package/dist/ci/render.d.ts +333 -0
  20. package/dist/ci/render.js +885 -0
  21. package/dist/ci/render.js.map +1 -0
  22. package/dist/ci/replay.d.ts +17 -0
  23. package/dist/ci/replay.js +69 -0
  24. package/dist/ci/replay.js.map +1 -0
  25. package/dist/cleanup/index.d.ts +86 -0
  26. package/dist/cleanup/index.js +207 -0
  27. package/dist/cleanup/index.js.map +1 -0
  28. package/dist/commands/apply.d.ts +113 -0
  29. package/dist/commands/apply.js +370 -30
  30. package/dist/commands/apply.js.map +1 -1
  31. package/dist/commands/approve.d.ts +13 -0
  32. package/dist/commands/approve.js +239 -13
  33. package/dist/commands/approve.js.map +1 -1
  34. package/dist/commands/checkpoint.d.ts +3 -0
  35. package/dist/commands/checkpoint.js +78 -0
  36. package/dist/commands/checkpoint.js.map +1 -0
  37. package/dist/commands/ci.d.ts +3 -0
  38. package/dist/commands/ci.js +130 -0
  39. package/dist/commands/ci.js.map +1 -0
  40. package/dist/commands/cleanup.d.ts +13 -0
  41. package/dist/commands/cleanup.js +138 -0
  42. package/dist/commands/cleanup.js.map +1 -0
  43. package/dist/commands/config.d.ts +3 -0
  44. package/dist/commands/config.js +136 -0
  45. package/dist/commands/config.js.map +1 -0
  46. package/dist/commands/docs.d.ts +11 -0
  47. package/dist/commands/docs.js +108 -0
  48. package/dist/commands/docs.js.map +1 -0
  49. package/dist/commands/drift.d.ts +13 -1
  50. package/dist/commands/drift.js +52 -5
  51. package/dist/commands/drift.js.map +1 -1
  52. package/dist/commands/index.js +16 -0
  53. package/dist/commands/index.js.map +1 -1
  54. package/dist/commands/init.js +1 -1
  55. package/dist/commands/init.js.map +1 -1
  56. package/dist/commands/lint.js +39 -7
  57. package/dist/commands/lint.js.map +1 -1
  58. package/dist/commands/new.js +133 -34
  59. package/dist/commands/new.js.map +1 -1
  60. package/dist/commands/override.js +48 -16
  61. package/dist/commands/override.js.map +1 -1
  62. package/dist/commands/plan.js +47 -6
  63. package/dist/commands/plan.js.map +1 -1
  64. package/dist/commands/report.d.ts +13 -0
  65. package/dist/commands/report.js +121 -0
  66. package/dist/commands/report.js.map +1 -0
  67. package/dist/commands/revert.d.ts +20 -0
  68. package/dist/commands/revert.js +246 -0
  69. package/dist/commands/revert.js.map +1 -0
  70. package/dist/commands/status.js +32 -5
  71. package/dist/commands/status.js.map +1 -1
  72. package/dist/commands/test.d.ts +3 -0
  73. package/dist/commands/test.js +164 -0
  74. package/dist/commands/test.js.map +1 -0
  75. package/dist/config/base.d.ts +69 -0
  76. package/dist/config/base.js +121 -0
  77. package/dist/config/base.js.map +1 -0
  78. package/dist/config/define.d.ts +240 -3
  79. package/dist/config/define.js +4 -0
  80. package/dist/config/define.js.map +1 -1
  81. package/dist/config/index.d.ts +2 -0
  82. package/dist/config/index.js +2 -0
  83. package/dist/config/index.js.map +1 -1
  84. package/dist/config/load.d.ts +1 -1
  85. package/dist/config/load.js +156 -7
  86. package/dist/config/load.js.map +1 -1
  87. package/dist/config/settings.d.ts +22 -1
  88. package/dist/config/settings.js +34 -2
  89. package/dist/config/settings.js.map +1 -1
  90. package/dist/credentials.d.ts +119 -0
  91. package/dist/credentials.js +119 -0
  92. package/dist/credentials.js.map +1 -0
  93. package/dist/exit.js +1 -1
  94. package/dist/exit.js.map +1 -1
  95. package/dist/history/postgres/ddl.d.ts +18 -0
  96. package/dist/history/postgres/ddl.js +27 -2
  97. package/dist/history/postgres/ddl.js.map +1 -1
  98. package/dist/history/postgres/store.d.ts +2 -0
  99. package/dist/history/postgres/store.js +7 -1
  100. package/dist/history/postgres/store.js.map +1 -1
  101. package/dist/history/rows.d.ts +16 -3
  102. package/dist/history/rows.js +30 -1
  103. package/dist/history/rows.js.map +1 -1
  104. package/dist/history/store.d.ts +2 -0
  105. package/dist/history/store.js +2 -2
  106. package/dist/history/store.js.map +1 -1
  107. package/dist/index.d.ts +8 -1
  108. package/dist/index.js +3 -0
  109. package/dist/index.js.map +1 -1
  110. package/dist/init/adopt.js +1 -1
  111. package/dist/init/adopt.js.map +1 -1
  112. package/dist/init/clickhouse-access.d.ts +64 -0
  113. package/dist/init/clickhouse-access.js +211 -0
  114. package/dist/init/clickhouse-access.js.map +1 -0
  115. package/dist/init/live.d.ts +4 -0
  116. package/dist/init/live.js +16 -0
  117. package/dist/init/live.js.map +1 -1
  118. package/dist/lint/access.d.ts +18 -0
  119. package/dist/lint/access.js +34 -0
  120. package/dist/lint/access.js.map +1 -0
  121. package/dist/lint/check.js +38 -6
  122. package/dist/lint/check.js.map +1 -1
  123. package/dist/lint/config.d.ts +7 -1
  124. package/dist/lint/config.js +11 -5
  125. package/dist/lint/config.js.map +1 -1
  126. package/dist/lint/index.d.ts +2 -0
  127. package/dist/lint/index.js +2 -0
  128. package/dist/lint/index.js.map +1 -1
  129. package/dist/lint/naming.d.ts +62 -0
  130. package/dist/lint/naming.js +138 -0
  131. package/dist/lint/naming.js.map +1 -0
  132. package/dist/lint/report.js +1 -1
  133. package/dist/lint/report.js.map +1 -1
  134. package/dist/lint/routines.d.ts +26 -0
  135. package/dist/lint/routines.js +65 -0
  136. package/dist/lint/routines.js.map +1 -0
  137. package/dist/lint/rules.d.ts +51 -6
  138. package/dist/lint/rules.js +23 -6
  139. package/dist/lint/rules.js.map +1 -1
  140. package/dist/lint/schema-checks.d.ts +63 -0
  141. package/dist/lint/schema-checks.js +128 -0
  142. package/dist/lint/schema-checks.js.map +1 -0
  143. package/dist/lint/statements.d.ts +22 -1
  144. package/dist/lint/statements.js +40 -1
  145. package/dist/lint/statements.js.map +1 -1
  146. package/dist/mcp/tools.d.ts +1 -1
  147. package/dist/mcp/tools.js +1 -1
  148. package/dist/mcp/tools.js.map +1 -1
  149. package/dist/migrations/checkpoint.d.ts +45 -0
  150. package/dist/migrations/checkpoint.js +108 -0
  151. package/dist/migrations/checkpoint.js.map +1 -0
  152. package/dist/migrations/checks.d.ts +19 -1
  153. package/dist/migrations/checks.js +15 -0
  154. package/dist/migrations/checks.js.map +1 -1
  155. package/dist/migrations/create.d.ts +13 -4
  156. package/dist/migrations/create.js +24 -7
  157. package/dist/migrations/create.js.map +1 -1
  158. package/dist/migrations/document.d.ts +10 -0
  159. package/dist/migrations/document.js +1 -0
  160. package/dist/migrations/document.js.map +1 -1
  161. package/dist/migrations/index.d.ts +2 -0
  162. package/dist/migrations/index.js +2 -0
  163. package/dist/migrations/index.js.map +1 -1
  164. package/dist/migrations/routines.d.ts +21 -0
  165. package/dist/migrations/routines.js +30 -0
  166. package/dist/migrations/routines.js.map +1 -0
  167. package/dist/notify/index.d.ts +124 -0
  168. package/dist/notify/index.js +210 -0
  169. package/dist/notify/index.js.map +1 -0
  170. package/dist/notify/verify.d.ts +53 -0
  171. package/dist/notify/verify.js +71 -0
  172. package/dist/notify/verify.js.map +1 -0
  173. package/dist/pr/model.d.ts +7 -1
  174. package/dist/pr/model.js +29 -7
  175. package/dist/pr/model.js.map +1 -1
  176. package/dist/pr/render.d.ts +4 -1
  177. package/dist/pr/render.js +49 -9
  178. package/dist/pr/render.js.map +1 -1
  179. package/dist/pr/tenants.d.ts +37 -0
  180. package/dist/pr/tenants.js +138 -0
  181. package/dist/pr/tenants.js.map +1 -0
  182. package/dist/probe.d.ts +27 -0
  183. package/dist/probe.js +106 -0
  184. package/dist/probe.js.map +1 -0
  185. package/dist/project.d.ts +35 -2
  186. package/dist/project.js +62 -2
  187. package/dist/project.js.map +1 -1
  188. package/dist/replay/clickhouse.d.ts +31 -0
  189. package/dist/replay/clickhouse.js +103 -6
  190. package/dist/replay/clickhouse.js.map +1 -1
  191. package/dist/replay/replay.d.ts +14 -3
  192. package/dist/replay/replay.js +62 -31
  193. package/dist/replay/replay.js.map +1 -1
  194. package/dist/replay/report.js +1 -1
  195. package/dist/replay/report.js.map +1 -1
  196. package/dist/report/audit.d.ts +138 -0
  197. package/dist/report/audit.js +219 -0
  198. package/dist/report/audit.js.map +1 -0
  199. package/dist/report/jsonl.d.ts +21 -0
  200. package/dist/report/jsonl.js +52 -0
  201. package/dist/report/jsonl.js.map +1 -0
  202. package/dist/report/model.d.ts +68 -0
  203. package/dist/report/model.js +127 -0
  204. package/dist/report/model.js.map +1 -0
  205. package/dist/report/render.d.ts +18 -0
  206. package/dist/report/render.js +127 -0
  207. package/dist/report/render.js.map +1 -0
  208. package/dist/schema-docs/render.d.ts +49 -0
  209. package/dist/schema-docs/render.js +359 -0
  210. package/dist/schema-docs/render.js.map +1 -0
  211. package/dist/schemas.d.ts +7 -2
  212. package/dist/schemas.js +7 -2
  213. package/dist/schemas.js.map +1 -1
  214. package/dist/sources/ddl.d.ts +48 -0
  215. package/dist/sources/ddl.js +53 -0
  216. package/dist/sources/ddl.js.map +1 -0
  217. package/dist/sources/index.d.ts +69 -0
  218. package/dist/sources/index.js +170 -0
  219. package/dist/sources/index.js.map +1 -0
  220. package/dist/steps/backfill.d.ts +40 -5
  221. package/dist/steps/backfill.js +128 -8
  222. package/dist/steps/backfill.js.map +1 -1
  223. package/dist/steps/index.d.ts +19 -1
  224. package/dist/steps/index.js +34 -7
  225. package/dist/steps/index.js.map +1 -1
  226. package/dist/steps/kinds.d.ts +42 -4
  227. package/dist/steps/kinds.js +88 -14
  228. package/dist/steps/kinds.js.map +1 -1
  229. package/dist/steps/postgres-migration.d.ts +38 -25
  230. package/dist/steps/postgres-migration.js +49 -32
  231. package/dist/steps/postgres-migration.js.map +1 -1
  232. package/dist/steps/rebuild.d.ts +15 -6
  233. package/dist/steps/rebuild.js +12 -9
  234. package/dist/steps/rebuild.js.map +1 -1
  235. package/dist/tenants/index.d.ts +41 -0
  236. package/dist/tenants/index.js +121 -0
  237. package/dist/tenants/index.js.map +1 -0
  238. package/dist/tenants/target.d.ts +23 -0
  239. package/dist/tenants/target.js +29 -0
  240. package/dist/tenants/target.js.map +1 -0
  241. package/dist/tenants/wave.d.ts +128 -0
  242. package/dist/tenants/wave.js +254 -0
  243. package/dist/tenants/wave.js.map +1 -0
  244. package/dist/testing/define.d.ts +62 -0
  245. package/dist/testing/define.js +32 -0
  246. package/dist/testing/define.js.map +1 -0
  247. package/dist/testing/files.d.ts +5 -0
  248. package/dist/testing/files.js +18 -0
  249. package/dist/testing/files.js.map +1 -0
  250. package/dist/testing/report.d.ts +14 -0
  251. package/dist/testing/report.js +83 -0
  252. package/dist/testing/report.js.map +1 -0
  253. package/dist/testing/run.d.ts +104 -0
  254. package/dist/testing/run.js +328 -0
  255. package/dist/testing/run.js.map +1 -0
  256. package/dist/versioned/approval.d.ts +6 -0
  257. package/dist/versioned/approval.js +11 -5
  258. package/dist/versioned/approval.js.map +1 -1
  259. package/dist/versioned/checks.d.ts +8 -2
  260. package/dist/versioned/checks.js +34 -13
  261. package/dist/versioned/checks.js.map +1 -1
  262. package/dist/versioned/driver.d.ts +2 -2
  263. package/dist/versioned/engine.d.ts +16 -0
  264. package/dist/versioned/engine.js +20 -6
  265. package/dist/versioned/engine.js.map +1 -1
  266. package/dist/versioned/explain.d.ts +2 -0
  267. package/dist/versioned/explain.js +3 -1
  268. package/dist/versioned/explain.js.map +1 -1
  269. package/dist/versioned/index.d.ts +1 -0
  270. package/dist/versioned/index.js +1 -0
  271. package/dist/versioned/index.js.map +1 -1
  272. package/dist/versioned/op.d.ts +6 -1
  273. package/dist/versioned/op.js +7 -2
  274. package/dist/versioned/op.js.map +1 -1
  275. package/dist/versioned/policy.d.ts +33 -2
  276. package/dist/versioned/policy.js +59 -8
  277. package/dist/versioned/policy.js.map +1 -1
  278. package/dist/versioned/report.js +9 -0
  279. package/dist/versioned/report.js.map +1 -1
  280. package/dist/versioned/revert.d.ts +128 -0
  281. package/dist/versioned/revert.js +251 -0
  282. package/dist/versioned/revert.js.map +1 -0
  283. package/dist/versioned/state.d.ts +25 -0
  284. package/dist/versioned/state.js +37 -4
  285. package/dist/versioned/state.js.map +1 -1
  286. package/dist/waves/index.d.ts +224 -0
  287. package/dist/waves/index.js +353 -0
  288. package/dist/waves/index.js.map +1 -0
  289. package/dist/waves/promotion.d.ts +56 -0
  290. package/dist/waves/promotion.js +108 -0
  291. package/dist/waves/promotion.js.map +1 -0
  292. package/dist/waves/record.d.ts +34 -0
  293. package/dist/waves/record.js +20 -0
  294. package/dist/waves/record.js.map +1 -0
  295. package/package.json +11 -7
  296. package/schemas/apply.schema.json +162 -3
  297. package/schemas/checkpoint.schema.json +19 -0
  298. package/schemas/cleanup.schema.json +40 -0
  299. package/schemas/drift.schema.json +26 -1
  300. package/schemas/event.schema.json +36 -0
  301. package/schemas/lint-rules.schema.json +4 -0
  302. package/schemas/lint.schema.json +4 -0
  303. package/schemas/new.schema.json +41 -3
  304. package/schemas/plan.schema.json +157 -0
  305. package/schemas/report.schema.json +114 -0
  306. package/schemas/revert.schema.json +69 -0
  307. package/schemas/status.schema.json +64 -3
  308. package/schemas/test.schema.json +41 -0
@@ -0,0 +1,3 @@
1
+ import type { Command } from "../command.js";
2
+ /** `yodel ci`; the exit codes are in `help`. */
3
+ export declare const ci: Command;
@@ -0,0 +1,130 @@
1
+ import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
2
+ import { dirname, join, resolve } from "node:path";
3
+ import { guarded } from "../env-args.js";
4
+ import { loadProject, YodelError } from "../project.js";
5
+ const usage = "usage: yodel ci [--check] [--dir <project>]\n yodel ci --replay [--dir <project>] [-- <yodel lint options>]";
6
+ const help = `${usage}
7
+
8
+ Renders the project's CI pipelines for GitHub Actions, GitLab CI and
9
+ Forgejo Actions from chant.config.ts (the environments, in profile order),
10
+ yodel.config.ts (waves, ci) and ops/ (each environment's migrate and watch
11
+ Ops), each declared with chant's lexicons:
12
+
13
+ yodel-pr on a pull request: yodel ci --check, yodel lint, the replay
14
+ check into a throwaway server, and the plan comment per
15
+ environment
16
+ yodel-apply on a push to main: one job per wave (yodel-waves.json)
17
+ yodel-apply-plans
18
+ on a pull request, when a wave's approval is pr-review:
19
+ chant run wave --record-plans records the plan each wave
20
+ makes at the head, for the review to approve (on GitLab, the
21
+ merge request job yodel-apply-record-plans)
22
+ watch-<env> each environment's drift watch, on its schedule
23
+
24
+ The pipelines come from the installed yodel, so upgrading yodel and running
25
+ yodel ci again brings its fixes to the project; yodel ci --check then fails
26
+ until the new files are committed.
27
+
28
+ yodel.config.ts ci:
29
+ forges the forges to render for (default github, gitlab and forgejo)
30
+ jobs a module (such as "ci/jobs.ts") exporting the project's own jobs,
31
+ declared with chant: actions (github lexicon Jobs, for GitHub and
32
+ Forgejo) and gitlab (gitlab lexicon Jobs). They are added to the
33
+ pull request pipeline after yodel's jobs, on every render.
34
+ image the CI image every job runs in, pinned by its digest
35
+ resume { schedule: "<cron>" }: a scheduled yodel-apply-resume job that
36
+ starts again an apply wave whose approval has arrived (chant run
37
+ resume); it approves nothing. GitHub uses the job's token
38
+ (actions: write), Forgejo the CHANT_FORGE_TOKEN secret
39
+ (write:repository), GitLab a pipeline schedule you create, with
40
+ GITLAB_TOKEN (api scope, Developer)
41
+
42
+ Options:
43
+ --check write nothing; exit 1 naming each file that differs from
44
+ what yodel ci renders
45
+ --replay the lint job's replay check on this machine: a throwaway
46
+ server started with Docker from the job's image, the
47
+ replay profile pointed at it, yodel lint --replay replay
48
+ --reset, and the server removed. Options after -- go to
49
+ yodel lint. The exit code is yodel lint's
50
+ -C, --dir <dir> the project directory (default: the working directory)
51
+
52
+ Exit codes:
53
+ 0 written, or (--check) every file is up to date
54
+ 1 (--check) a file is stale or missing; anything else failed
55
+ `;
56
+ function parseCiArgs(args) {
57
+ const out = { dir: process.cwd(), check: false, replay: false, rest: [] };
58
+ for (let i = 0; i < args.length; i++) {
59
+ const a = args[i];
60
+ if (a === "--") {
61
+ out.rest = args.slice(i + 1);
62
+ break;
63
+ }
64
+ if (a === "--check")
65
+ out.check = true;
66
+ else if (a === "--replay")
67
+ out.replay = true;
68
+ else if (a === "--dir" || a === "-C") {
69
+ const v = args[++i];
70
+ if (v === undefined)
71
+ throw new YodelError(`${a} needs a directory\n${usage}`);
72
+ out.dir = v;
73
+ }
74
+ else if (a?.startsWith("--dir="))
75
+ out.dir = a.slice("--dir=".length);
76
+ else
77
+ throw new YodelError(`unknown argument ${String(a)}\n${usage}`);
78
+ }
79
+ if (out.check && out.replay)
80
+ throw new YodelError(`--check and --replay are two things; give one\n${usage}`);
81
+ if (out.rest.length > 0 && !out.replay)
82
+ throw new YodelError(`options after -- are for --replay\n${usage}`);
83
+ return out;
84
+ }
85
+ /** `yodel ci`; the exit codes are in `help`. */
86
+ export const ci = {
87
+ name: "ci",
88
+ summary: "render the project's CI pipelines (GitHub, GitLab, Forgejo)",
89
+ implemented: true,
90
+ run: (args, io) => guarded("ci", io, args, help, async () => {
91
+ const a = parseCiArgs(args);
92
+ // The project, as every command reads it: chant.config.ts and a checked yodel.config.ts.
93
+ const dir = (await loadProject(resolve(a.dir))).dir;
94
+ if (a.replay) {
95
+ const { replayLocally } = await import("../ci/replay.js");
96
+ return replayLocally(dir, a.rest);
97
+ }
98
+ // Loaded here: the forge lexicons are for this command alone.
99
+ const { render } = await import("../ci/render.js");
100
+ let files;
101
+ try {
102
+ files = await render(dir);
103
+ }
104
+ catch (e) {
105
+ throw new YodelError(e instanceof Error ? e.message : String(e));
106
+ }
107
+ const stale = [];
108
+ for (const file of files) {
109
+ const abs = join(dir, file.path);
110
+ const current = existsSync(abs) ? readFileSync(abs, "utf8") : undefined;
111
+ if (current === file.content)
112
+ continue;
113
+ if (a.check) {
114
+ stale.push(file.path);
115
+ continue;
116
+ }
117
+ mkdirSync(dirname(abs), { recursive: true });
118
+ writeFileSync(abs, file.content);
119
+ io.out(`wrote ${file.path}\n`);
120
+ }
121
+ if (a.check && stale.length > 0) {
122
+ io.err(`stale, run yodel ci: ${stale.join(", ")}\n`);
123
+ return 1;
124
+ }
125
+ if (a.check)
126
+ io.out(`${String(files.length)} CI files are up to date\n`);
127
+ return 0;
128
+ }),
129
+ };
130
+ //# sourceMappingURL=ci.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ci.js","sourceRoot":"","sources":["../../src/commands/ci.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,13 @@
1
+ import type { Command } from "../command.js";
2
+ import { type Retained } from "../cleanup/index.js";
3
+ /** `yodel cleanup --json` (schemas/cleanup.schema.json). */
4
+ export declare function cleanupJson(r: {
5
+ environment: string;
6
+ now: string;
7
+ retained: readonly Retained[];
8
+ planDigest?: string | undefined;
9
+ approve?: string | undefined;
10
+ dropped: readonly string[];
11
+ }): Record<string, unknown>;
12
+ /** `yodel cleanup <env>`; the exit codes are in `help`. */
13
+ export declare const cleanup: Command;
@@ -0,0 +1,138 @@
1
+ import { parseArgs } from "node:util";
2
+ import { guarded } from "../env-args.js";
3
+ import { loadProject, resolveEnvironment, YodelError } from "../project.js";
4
+ import { appliedBy, withLock } from "../history/index.js";
5
+ import { closeVersioned, findMigrateOp, gitLedger, NOT_APPROVED, openVersioned, verifyApproval } from "../versioned/index.js";
6
+ import { cleanupDigest, describeRetained, dropRetained, findRetained, retainedServer } from "../cleanup/index.js";
7
+ const usage = "usage: yodel cleanup <env> [--list] [--op <name>] [--json] [--dir <project>]";
8
+ const help = `${usage}
9
+
10
+ Lists what steps kept in <env> on purpose, and drops what is past its
11
+ retention date, behind the environment's approval:
12
+
13
+ - a ClickHouse rebuild's old table, <table>__chant_old, kept after the swap
14
+ (7 days, or the step's retain);
15
+ - a Postgres rename's or type change's old column, kept after the switch
16
+ when the step set retain (yodel new --retain), with the trigger and
17
+ function that keep a renamed column written.
18
+
19
+ Each one's date is in its own comment, and the server's clock decides
20
+ whether it has passed. Nothing is dropped before its date.
21
+
22
+ When something is due, the drop is a plan like an apply's: its digest
23
+ covers each due object (a table by its UUID, a column by its table and
24
+ attribute number) and its date. Without an approval of the environment's
25
+ migrations Op gate for that digest, yodel cleanup lists what it would drop,
26
+ prints the digest and the chant approve command, and drops nothing (exit
27
+ 3). Approve it, run yodel cleanup <env> again, and it drops them under the
28
+ apply lock, after reading them again: if anything due changed, the digest
29
+ differs and the approval does not count.
30
+
31
+ Options:
32
+ --list list only; never drop, even with an approval
33
+ --op <name> the migrations Op, when more than one applies <env>
34
+ --json print the list (and what was dropped) as JSON
35
+ -C, --dir <dir> the project directory (default: the working directory)
36
+
37
+ Exit codes:
38
+ 0 nothing is due (and nothing was dropped), the due objects were dropped,
39
+ or --list
40
+ 3 something is due and waits for an approval; nothing was dropped
41
+ 5 another apply holds the lock
42
+ 1 an error: a bad config, an unknown environment, a server it cannot read
43
+ `;
44
+ function parseCleanupArgs(args) {
45
+ let parsed;
46
+ try {
47
+ parsed = parseArgs({ args, allowPositionals: true, strict: true, options: { list: { type: "boolean" }, op: { type: "string" }, json: { type: "boolean" }, dir: { type: "string", short: "C" } } });
48
+ }
49
+ catch (e) {
50
+ throw new YodelError(`${e instanceof Error ? e.message : String(e)}\n${usage}`);
51
+ }
52
+ const [env, ...rest] = parsed.positionals;
53
+ if (env === undefined || rest.length > 0)
54
+ throw new YodelError(usage);
55
+ const v = parsed.values;
56
+ return { env, list: v.list === true, op: typeof v.op === "string" ? v.op : undefined, json: v.json === true, dir: typeof v.dir === "string" ? v.dir : process.cwd() };
57
+ }
58
+ /** `yodel cleanup --json` (schemas/cleanup.schema.json). */
59
+ export function cleanupJson(r) {
60
+ return {
61
+ environment: r.environment,
62
+ now: r.now,
63
+ retained: r.retained.map((x) => ({ kind: x.kind, name: x.name, of: x.of, ...(x.retainUntil ? { retainUntil: x.retainUntil } : {}), due: x.due, identity: x.identity, ...(x.trigger ? { trigger: x.trigger } : {}) })),
64
+ due: r.retained.filter((x) => x.due).length,
65
+ ...(r.planDigest ? { planDigest: r.planDigest } : {}),
66
+ ...(r.approve ? { approve: r.approve } : {}),
67
+ dropped: r.dropped,
68
+ };
69
+ }
70
+ /** `yodel cleanup <env>`; the exit codes are in `help`. */
71
+ export const cleanup = {
72
+ name: "cleanup",
73
+ summary: "drop the old tables and columns steps kept, once past their date",
74
+ implemented: true,
75
+ run: (args, io) => guarded("cleanup", io, args, help, async () => {
76
+ const a = parseCleanupArgs(args);
77
+ const project = await loadProject(a.dir);
78
+ const env = resolveEnvironment(project, a.env);
79
+ const venv = await openVersioned(project, env);
80
+ try {
81
+ return await cleanupWith(project, env, venv, a, io);
82
+ }
83
+ finally {
84
+ await closeVersioned(venv);
85
+ }
86
+ }),
87
+ };
88
+ async function cleanupWith(project, env, venv, a, io) {
89
+ const server = retainedServer(venv);
90
+ const { now, retained } = await findRetained(server);
91
+ const dueCount = retained.filter((r) => r.due).length;
92
+ const lines = [`Kept by steps in ${env.name} (server time ${now}):`];
93
+ if (retained.length === 0)
94
+ lines[0] = `Nothing kept by steps in ${env.name}: no old table or old column waits for its retention date.`;
95
+ for (const r of retained)
96
+ lines.push(` ${describeRetained(r)}`);
97
+ const report = (extra, tail = []) => {
98
+ if (a.json)
99
+ io.out(`${JSON.stringify(cleanupJson({ environment: env.name, now, retained, dropped: [], ...extra }), null, 2)}\n`);
100
+ else
101
+ io.out(`${[...lines, ...tail].join("\n")}\n`);
102
+ };
103
+ if (dueCount === 0 || a.list) {
104
+ report({}, retained.length > 0 && dueCount === 0 ? ["", "Nothing is due: nothing dropped."] : []);
105
+ return 0;
106
+ }
107
+ const digest = await cleanupDigest(env.name, retained);
108
+ const op = await findMigrateOp(project, env, a.op);
109
+ const ledger = await gitLedger(project.dir);
110
+ const approve = `chant approve ${op.name} ${op.gate} --plan ${digest}`;
111
+ try {
112
+ await verifyApproval(ledger, op, digest);
113
+ }
114
+ catch (e) {
115
+ if (!(e instanceof YodelError) || e.exitCode !== NOT_APPROVED)
116
+ throw e;
117
+ report({ planDigest: digest, approve }, ["", `Plan digest: ${digest}`, `${String(dueCount)} due; nothing dropped without an approval. Approve it with:`, ` ${approve}`, `then run yodel cleanup ${env.name} again.`]);
118
+ return NOT_APPROVED;
119
+ }
120
+ const lock = await venv.driver.acquireLock(appliedBy());
121
+ if (lock.notice && !a.json)
122
+ io.err(`lock: ${lock.notice}\n`);
123
+ const dropped = await withLock(lock, venv.lockTtlMs, async () => {
124
+ // Read again under the lock: the approval counts only for what is due now.
125
+ const again = await findRetained(server);
126
+ const now = await cleanupDigest(env.name, again.retained);
127
+ const by = await verifyApproval(ledger, op, now);
128
+ if (!a.json)
129
+ io.err(`approval: ${op.name} / ${op.gate} for ${now}, by ${by.resolvedBy} at ${by.timestamp}\n`);
130
+ return dropRetained(server, again.retained, (l) => {
131
+ if (!a.json)
132
+ io.err(`${l}\n`);
133
+ });
134
+ });
135
+ report({ planDigest: digest, dropped }, ["", `Dropped ${String(dropped.length)}: ${dropped.join(", ")}`]);
136
+ return 0;
137
+ }
138
+ //# sourceMappingURL=cleanup.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cleanup.js","sourceRoot":"","sources":["../../src/commands/cleanup.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AAEtC,OAAO,EAAE,OAAO,EAAE,MAAM,gBAAgB,CAAC;AACzC,OAAO,EAAE,WAAW,EAAE,kBAAkB,EAAE,UAAU,EAAkC,MAAM,eAAe,CAAC;AAC5G,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AAC1D,OAAO,EAAE,cAAc,EAAE,aAAa,EAAE,SAAS,EAAE,YAAY,EAAE,aAAa,EAAE,cAAc,EAAqB,MAAM,uBAAuB,CAAC;AACjJ,OAAO,EAAE,aAAa,EAAE,gBAAgB,EAAE,YAAY,EAAE,YAAY,EAAE,cAAc,EAAiB,MAAM,qBAAqB,CAAC;AAEjI,MAAM,KAAK,GAAG,8EAA8E,CAAC;AAE7F,MAAM,IAAI,GAAG,GAAG,KAAK;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAmCpB,CAAC;AAUF,SAAS,gBAAgB,CAAC,IAAc;IACtC,IAAI,MAAoC,CAAC;IACzC,IAAI,CAAC;QACH,MAAM,GAAG,SAAS,CAAC,EAAE,IAAI,EAAE,gBAAgB,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,EAAE,IAAI,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,EAAE,EAAE,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,EAAE,IAAI,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,EAAE,GAAG,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,KAAK,EAAE,GAAG,EAAE,EAAE,EAAE,CAAC,CAAC;IACrM,CAAC;IAAC,OAAO,CAAC,EAAE,CAAC;QACX,MAAM,IAAI,UAAU,CAAC,GAAG,CAAC,YAAY,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,KAAK,KAAK,EAAE,CAAC,CAAC;IAClF,CAAC;IACD,MAAM,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,GAAG,MAAM,CAAC,WAAW,CAAC;IAC1C,IAAI,GAAG,KAAK,SAAS,IAAI,IAAI,CAAC,MAAM,GAAG,CAAC;QAAE,MAAM,IAAI,UAAU,CAAC,KAAK,CAAC,CAAC;IACtE,MAAM,CAAC,GAAG,MAAM,CAAC,MAAM,CAAC;IACxB,OAAO,EAAE,GAAG,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,KAAK,IAAI,EAAE,EAAE,EAAE,OAAO,CAAC,CAAC,EAAE,KAAK,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,KAAK,IAAI,EAAE,GAAG,EAAE,OAAO,CAAC,CAAC,GAAG,KAAK,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,CAAC;AACxK,CAAC;AAED,4DAA4D;AAC5D,MAAM,UAAU,WAAW,CAAC,CAAiK;IAC3L,OAAO;QACL,WAAW,EAAE,CAAC,CAAC,WAAW;QAC1B,GAAG,EAAE,CAAC,CAAC,GAAG;QACV,QAAQ,EAAE,CAAC,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,EAAE,EAAE,CAAC,CAAC,EAAE,EAAE,GAAG,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,EAAE,WAAW,EAAE,CAAC,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,GAAG,EAAE,CAAC,CAAC,GAAG,EAAE,QAAQ,EAAE,CAAC,CAAC,QAAQ,EAAE,GAAG,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC;QACrN,GAAG,EAAE,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,MAAM;QAC3C,GAAG,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,EAAE,UAAU,EAAE,CAAC,CAAC,UAAU,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QACrD,GAAG,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QAC5C,OAAO,EAAE,CAAC,CAAC,OAAO;KACnB,CAAC;AACJ,CAAC;AAED,2DAA2D;AAC3D,MAAM,CAAC,MAAM,OAAO,GAAY;IAC9B,IAAI,EAAE,SAAS;IACf,OAAO,EAAE,kEAAkE;IAC3E,WAAW,EAAE,IAAI;IACjB,GAAG,EAAE,CAAC,IAAI,EAAE,EAAE,EAAE,EAAE,CAChB,OAAO,CAAC,SAAS,EAAE,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,IAAI,EAAE;QAC5C,MAAM,CAAC,GAAG,gBAAgB,CAAC,IAAI,CAAC,CAAC;QACjC,MAAM,OAAO,GAAG,MAAM,WAAW,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC;QACzC,MAAM,GAAG,GAAG,kBAAkB,CAAC,OAAO,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC;QAC/C,MAAM,IAAI,GAAG,MAAM,aAAa,CAAC,OAAO,EAAE,GAAG,CAAC,CAAC;QAC/C,IAAI,CAAC;YACH,OAAO,MAAM,WAAW,CAAC,OAAO,EAAE,GAAG,EAAE,IAAI,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC;QACtD,CAAC;gBAAS,CAAC;YACT,MAAM,cAAc,CAAC,IAAI,CAAC,CAAC;QAC7B,CAAC;IACH,CAAC,CAAC;CACL,CAAC;AAEF,KAAK,UAAU,WAAW,CAAC,OAAgB,EAAE,GAAgB,EAAE,IAAkB,EAAE,CAAc,EAAE,EAAM;IACvG,MAAM,MAAM,GAAG,cAAc,CAAC,IAAI,CAAC,CAAC;IACpC,MAAM,EAAE,GAAG,EAAE,QAAQ,EAAE,GAAG,MAAM,YAAY,CAAC,MAAM,CAAC,CAAC;IACrD,MAAM,QAAQ,GAAG,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,MAAM,CAAC;IACtD,MAAM,KAAK,GAAG,CAAC,oBAAoB,GAAG,CAAC,IAAI,iBAAiB,GAAG,IAAI,CAAC,CAAC;IACrE,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC;QAAE,KAAK,CAAC,CAAC,CAAC,GAAG,4BAA4B,GAAG,CAAC,IAAI,4DAA4D,CAAC;IACvI,KAAK,MAAM,CAAC,IAAI,QAAQ;QAAE,KAAK,CAAC,IAAI,CAAC,KAAK,gBAAgB,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;IACjE,MAAM,MAAM,GAAG,CAAC,KAAoE,EAAE,OAAiB,EAAE,EAAE,EAAE;QAC3G,IAAI,CAAC,CAAC,IAAI;YAAE,EAAE,CAAC,GAAG,CAAC,GAAG,IAAI,CAAC,SAAS,CAAC,WAAW,CAAC,EAAE,WAAW,EAAE,GAAG,CAAC,IAAI,EAAE,GAAG,EAAE,QAAQ,EAAE,OAAO,EAAE,EAAE,EAAE,GAAG,KAAK,EAAE,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC;;YAC5H,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,KAAK,EAAE,GAAG,IAAI,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACrD,CAAC,CAAC;IACF,IAAI,QAAQ,KAAK,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,CAAC;QAC7B,MAAM,CAAC,EAAE,EAAE,QAAQ,CAAC,MAAM,GAAG,CAAC,IAAI,QAAQ,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,EAAE,kCAAkC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;QAClG,OAAO,CAAC,CAAC;IACX,CAAC;IAED,MAAM,MAAM,GAAG,MAAM,aAAa,CAAC,GAAG,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC;IACvD,MAAM,EAAE,GAAG,MAAM,aAAa,CAAC,OAAO,EAAE,GAAG,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC;IACnD,MAAM,MAAM,GAAG,MAAM,SAAS,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;IAC5C,MAAM,OAAO,GAAG,iBAAiB,EAAE,CAAC,IAAI,IAAI,EAAE,CAAC,IAAI,WAAW,MAAM,EAAE,CAAC;IACvE,IAAI,CAAC;QACH,MAAM,cAAc,CAAC,MAAM,EAAE,EAAE,EAAE,MAAM,CAAC,CAAC;IAC3C,CAAC;IAAC,OAAO,CAAC,EAAE,CAAC;QACX,IAAI,CAAC,CAAC,CAAC,YAAY,UAAU,CAAC,IAAI,CAAC,CAAC,QAAQ,KAAK,YAAY;YAAE,MAAM,CAAC,CAAC;QACvE,MAAM,CAAC,EAAE,UAAU,EAAE,MAAM,EAAE,OAAO,EAAE,EAAE,CAAC,EAAE,EAAE,gBAAgB,MAAM,EAAE,EAAE,GAAG,MAAM,CAAC,QAAQ,CAAC,6DAA6D,EAAE,KAAK,OAAO,EAAE,EAAE,0BAA0B,GAAG,CAAC,IAAI,SAAS,CAAC,CAAC,CAAC;QACvN,OAAO,YAAY,CAAC;IACtB,CAAC;IAED,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,WAAW,CAAC,SAAS,EAAE,CAAC,CAAC;IACxD,IAAI,IAAI,CAAC,MAAM,IAAI,CAAC,CAAC,CAAC,IAAI;QAAE,EAAE,CAAC,GAAG,CAAC,SAAS,IAAI,CAAC,MAAM,IAAI,CAAC,CAAC;IAC7D,MAAM,OAAO,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,IAAI,CAAC,SAAS,EAAE,KAAK,IAAI,EAAE;QAC9D,2EAA2E;QAC3E,MAAM,KAAK,GAAG,MAAM,YAAY,CAAC,MAAM,CAAC,CAAC;QACzC,MAAM,GAAG,GAAG,MAAM,aAAa,CAAC,GAAG,CAAC,IAAI,EAAE,KAAK,CAAC,QAAQ,CAAC,CAAC;QAC1D,MAAM,EAAE,GAAG,MAAM,cAAc,CAAC,MAAM,EAAE,EAAE,EAAE,GAAG,CAAC,CAAC;QACjD,IAAI,CAAC,CAAC,CAAC,IAAI;YAAE,EAAE,CAAC,GAAG,CAAC,aAAa,EAAE,CAAC,IAAI,MAAM,EAAE,CAAC,IAAI,QAAQ,GAAG,QAAQ,EAAE,CAAC,UAAU,OAAO,EAAE,CAAC,SAAS,IAAI,CAAC,CAAC;QAC9G,OAAO,YAAY,CAAC,MAAM,EAAE,KAAK,CAAC,QAAQ,EAAE,CAAC,CAAC,EAAE,EAAE;YAChD,IAAI,CAAC,CAAC,CAAC,IAAI;gBAAE,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QAChC,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IACH,MAAM,CAAC,EAAE,UAAU,EAAE,MAAM,EAAE,OAAO,EAAE,EAAE,CAAC,EAAE,EAAE,WAAW,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,KAAK,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC;IAC1G,OAAO,CAAC,CAAC;AACX,CAAC"}
@@ -0,0 +1,3 @@
1
+ import type { Command } from "../command.js";
2
+ /** `yodel config check`; the exit codes are in `help`. */
3
+ export declare const config: Command;
@@ -0,0 +1,136 @@
1
+ import { parseArgs } from "node:util";
2
+ import { environmentSettings } from "../config/settings.js";
3
+ import { CONFIG_FILE } from "../config/load.js";
4
+ import { credentialsOf, describePassword, roleOf, ROLES } from "../credentials.js";
5
+ import { guarded } from "../env-args.js";
6
+ import { EXIT_REFUSED } from "../exit.js";
7
+ import { writeProbe } from "../probe.js";
8
+ import { loadProject, resolveEnvironment, YodelError } from "../project.js";
9
+ import { serverAddress } from "../server.js";
10
+ const USAGE = "usage: yodel config check [<env>...] [--write-probe] [--dir <project>]";
11
+ const help = `${USAGE}
12
+
13
+ Prints each environment's settings and where each came from: the server,
14
+ the role this process connects as (YODEL_CREDENTIALS), the reader's and the
15
+ writer's user and password (the variable each is read from and whether it
16
+ is set here, or the token source that mints it), the history's database,
17
+ the topology, and the lock's TTL and wait. Every environment, or the ones
18
+ named. Never prints a password.
19
+
20
+ A profile made with credentials() in chant.config.ts carries a reader and a
21
+ writer. Every yodel command refuses the project, exit 4, when an
22
+ environment's reader and writer read their user or password from the same
23
+ variable, or their users are the same name.
24
+
25
+ Options:
26
+ --write-probe try a write in each environment with the credentials this
27
+ process holds, and exit 4 when the server lets one
28
+ through: run in a pull request job, it shows the job
29
+ holds no writer. The write is rolled back (Postgres) or
30
+ dropped at once (ClickHouse).
31
+ -C, --dir <dir> the project directory (default: the working directory)
32
+
33
+ Exit codes:
34
+ 0 printed; with --write-probe, no environment let the write through
35
+ 4 refused: an environment's reader and writer are one identity, or with
36
+ --write-probe, a write went through
37
+ 1 an error: a bad config, an unknown environment, a server that does not
38
+ answer, or a probe that could not tell
39
+ `;
40
+ const isEnvRef = (v) => typeof v === "object" && v !== null && typeof v.env === "string";
41
+ function roleView(c, role, env) {
42
+ const r = c[role];
43
+ const value = env[r.user.env];
44
+ const set = value !== undefined && value !== "";
45
+ return {
46
+ user: { variable: r.user.env, set, ...(set ? { value } : {}) },
47
+ password: isEnvRef(r.password) ? { source: r.password.env, set: (env[r.password.env] ?? "") !== "" } : { source: describePassword(r.password) },
48
+ };
49
+ }
50
+ function view(project, name, env) {
51
+ const e = resolveEnvironment(project, name);
52
+ const profile = e.profile;
53
+ const c = credentialsOf(profile);
54
+ const settings = environmentSettings(project, name, env);
55
+ const own = project.yodel?.config.environments?.[name];
56
+ const historySource = own?.history?.database !== undefined ? CONFIG_FILE : env.YODEL_HISTORY_DATABASE ? "YODEL_HISTORY_DATABASE" : "default";
57
+ const ys = env.YODEL_CREDENTIALS;
58
+ const user = profile.user;
59
+ const password = profile.password;
60
+ return {
61
+ environment: name,
62
+ server: serverAddress(project.dialect, String(profile.url)),
63
+ role: roleOf(name, ys),
64
+ roleSource: ys ? `YODEL_CREDENTIALS=${ys}` : "YODEL_CREDENTIALS unset",
65
+ credentials: c
66
+ ? { split: true, reader: roleView(c, "reader", env), writer: roleView(c, "writer", env) }
67
+ : { split: false, ...(isEnvRef(user) ? { user: user.env } : {}), ...(password ? { password: describePassword(password) } : {}) },
68
+ history: { database: settings.historyDatabase, source: historySource },
69
+ ...(project.dialect === "clickhouse" ? { topology: { value: describeTopology(settings.topology), source: settings.topologySource } } : {}),
70
+ lockTtlSeconds: settings.lockTtlMs / 1000,
71
+ lockWaitSeconds: settings.lockWaitMs / 1000,
72
+ };
73
+ }
74
+ const describeTopology = (t) => (t.cluster ? `${t.kind}:${t.cluster}` : t.kind);
75
+ function render(v) {
76
+ const lines = [`${v.environment} (${v.server})`, ` connects as ${v.role} (${v.roleSource})`];
77
+ const c = v.credentials;
78
+ if (c.split) {
79
+ for (const role of ROLES) {
80
+ const r = c[role];
81
+ const user = `${r.user.variable} (${r.user.set ? `set: ${r.user.value ?? ""}` : "not set here"})`;
82
+ const password = r.password.set === undefined ? r.password.source : `${r.password.source} (${r.password.set ? "set" : "not set here"})`;
83
+ lines.push(` ${role.padEnd(13)} user ${user}, password ${password}`);
84
+ }
85
+ }
86
+ else {
87
+ lines.push(` credentials user ${c.user ?? "none"}, password ${c.password ?? "none"}: one set for every job; credentials() in chant.config.ts gives a reader and a writer`);
88
+ }
89
+ lines.push(` history ${v.history.database} (${v.history.source})`);
90
+ if (v.topology)
91
+ lines.push(` topology ${v.topology.value} (${v.topology.source})`);
92
+ lines.push(` lock ttl ${String(v.lockTtlSeconds)}s, wait ${String(v.lockWaitSeconds)}s`);
93
+ if (v.probe)
94
+ lines.push(` write probe ${v.probe.wrote ? "WROTE" : "cannot write"}: ${v.probe.detail}`);
95
+ return `${lines.join("\n")}\n`;
96
+ }
97
+ async function check(args, io) {
98
+ let parsed;
99
+ try {
100
+ parsed = parseArgs({ args, allowPositionals: true, strict: true, options: { "write-probe": { type: "boolean" }, dir: { type: "string", short: "C" } } });
101
+ }
102
+ catch (e) {
103
+ throw new YodelError(`${e instanceof Error ? e.message : String(e)}\n${USAGE}`);
104
+ }
105
+ const { values, positionals } = parsed;
106
+ const project = await loadProject(typeof values.dir === "string" ? values.dir : process.cwd());
107
+ const names = positionals.length > 0 ? positionals : Object.keys(project.config.sql?.profiles ?? {});
108
+ const views = [];
109
+ for (const name of names) {
110
+ const v = view(project, name, process.env);
111
+ if (values["write-probe"] === true)
112
+ v.probe = await writeProbe(project, resolveEnvironment(project, name));
113
+ views.push(v);
114
+ }
115
+ io.out(views.map(render).join("\n"));
116
+ const wrote = views.filter((v) => v.probe?.wrote);
117
+ if (wrote.length === 0)
118
+ return 0;
119
+ for (const v of wrote) {
120
+ io.err(`yodel config: refused: ${v.environment}: the credentials this process holds can write (${v.probe?.detail ?? ""}). A pull request job must hold the reader only: check which secrets it maps, and the reader's grants.\n`);
121
+ }
122
+ return EXIT_REFUSED;
123
+ }
124
+ /** `yodel config check`; the exit codes are in `help`. */
125
+ export const config = {
126
+ name: "config",
127
+ summary: "print each environment's settings, and prove a job cannot write",
128
+ implemented: true,
129
+ run: (args, io) => guarded("config", io, args, help, async () => {
130
+ const [sub, ...rest] = args;
131
+ if (sub !== "check")
132
+ throw new YodelError(USAGE);
133
+ return check(rest, io);
134
+ }),
135
+ };
136
+ //# sourceMappingURL=config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/commands/config.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,11 @@
1
+ import type { Command } from "../command.js";
2
+ import { type Project } from "../project.js";
3
+ import { type DocsInput } from "../schema-docs/render.js";
4
+ /** Where `yodel docs` writes, relative to the project, without --out. */
5
+ export declare const DOCS_DIR = "schema-docs";
6
+ export declare const DOCS_HTML = "index.html";
7
+ export declare const DOCS_ERD = "erd.md";
8
+ /** What `yodel docs` documents for the project: the build, or a migration's recorded schema. */
9
+ export declare function docsInput(project: Project, migration?: string): Promise<DocsInput>;
10
+ /** `yodel docs`; the exit codes are in `help`. */
11
+ export declare const docs: Command;
@@ -0,0 +1,108 @@
1
+ import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
2
+ import { basename, join, relative, resolve } from "node:path";
3
+ import { parseArgs } from "node:util";
4
+ import { guarded } from "../env-args.js";
5
+ import { loadProject, YodelError } from "../project.js";
6
+ import { buildSchema } from "../chant/build.js";
7
+ import { migrationsDir, readChain } from "../versioned/index.js";
8
+ import { erdMarkdown, schemaHtml } from "../schema-docs/render.js";
9
+ /** Where `yodel docs` writes, relative to the project, without --out. */
10
+ export const DOCS_DIR = "schema-docs";
11
+ export const DOCS_HTML = "index.html";
12
+ export const DOCS_ERD = "erd.md";
13
+ const usage = "usage: yodel docs [--out <dir>] [--migration <id>] [--dir <project>]";
14
+ const help = `${usage}
15
+
16
+ Writes a reference of the schema, offline: no server is read.
17
+
18
+ <out>/index.html every table, view, index and database or schema: its
19
+ columns, keys, relations, comments, ClickHouse engine,
20
+ sort key, partition key and TTL, its DDL, and the
21
+ migrations in migrations/ that changed it. One file
22
+ with its styles inline and no scripts.
23
+ <out>/erd.md a Mermaid entity-relationship diagram of the tables and
24
+ views, which GitHub, GitLab and Forgejo render: foreign
25
+ keys, what a view reads, the table a materialized view
26
+ writes to, the table a Distributed table serves.
27
+
28
+ The schema is the build of the declared schema (chant build), or with
29
+ --migration <id> the schema that migration recorded. The same schema gives
30
+ the same files, so they can be committed and reviewed in a pull request.
31
+
32
+ Options:
33
+ --out <dir> where to write (default: ${DOCS_DIR} in the project)
34
+ --migration <id> document the schema migration <id> recorded, and its
35
+ history up to it
36
+ -C, --dir <dir> the project directory (default: the working directory)
37
+
38
+ Exit codes:
39
+ 0 written
40
+ 1 nothing written: a bad config, a declaration chant cannot build, an
41
+ unknown migration id
42
+ 4 refused: the migrations directory is not one chain (a fork, a missing
43
+ parent)
44
+ `;
45
+ function parseDocsArgs(args) {
46
+ let parsed;
47
+ try {
48
+ parsed = parseArgs({ args, strict: true, options: { out: { type: "string" }, migration: { type: "string" }, dir: { type: "string", short: "C" } } });
49
+ }
50
+ catch (e) {
51
+ throw new YodelError(`${e instanceof Error ? e.message : String(e)}\n${usage}`);
52
+ }
53
+ const s = (v) => (typeof v === "string" ? v : undefined);
54
+ return { out: s(parsed.values.out), migration: s(parsed.values.migration), dir: s(parsed.values.dir) ?? process.cwd() };
55
+ }
56
+ /** The project's name for the title: package.json's name, else the directory's. */
57
+ function projectTitle(project) {
58
+ try {
59
+ const pkg = JSON.parse(readFileSync(join(project.dir, "package.json"), "utf8"));
60
+ if (typeof pkg.name === "string" && pkg.name && !pkg.name.includes("{{"))
61
+ return pkg.name;
62
+ }
63
+ catch {
64
+ // no package.json: the directory's name
65
+ }
66
+ return basename(project.dir);
67
+ }
68
+ /** What `yodel docs` documents for the project: the build, or a migration's recorded schema. */
69
+ export async function docsInput(project, migration) {
70
+ const chain = existsSync(migrationsDir(project)) ? readChain(project) : [];
71
+ const steps = chain.map((m) => ({ id: m.id, steps: m.document.steps }));
72
+ const title = projectTitle(project);
73
+ if (migration !== undefined) {
74
+ const at = chain.findIndex((m) => m.id === migration);
75
+ const found = chain[at];
76
+ if (!found)
77
+ throw new YodelError(`no migration ${migration} in ${relative(process.cwd(), migrationsDir(project)) || migrationsDir(project)}`);
78
+ return { title, output: found.document.schema, source: `the schema migration ${migration} recorded`, migrations: steps.slice(0, at + 1) };
79
+ }
80
+ const build = await buildSchema(project);
81
+ try {
82
+ return { title, output: build.output, source: "chant build of the declared schema", migrations: steps };
83
+ }
84
+ finally {
85
+ build.dispose();
86
+ }
87
+ }
88
+ /** `yodel docs`; the exit codes are in `help`. */
89
+ export const docs = {
90
+ name: "docs",
91
+ summary: "write an HTML reference and a Mermaid ERD of the schema",
92
+ implemented: true,
93
+ run: (args, io) => guarded("docs", io, args, help, async () => {
94
+ const { out, migration, dir } = parseDocsArgs(args);
95
+ const project = await loadProject(dir);
96
+ const input = await docsInput(project, migration);
97
+ const outDir = resolve(out ?? join(project.dir, DOCS_DIR));
98
+ mkdirSync(outDir, { recursive: true });
99
+ writeFileSync(join(outDir, DOCS_HTML), schemaHtml(input));
100
+ writeFileSync(join(outDir, DOCS_ERD), erdMarkdown(input));
101
+ const rel = relative(project.dir, outDir);
102
+ const where = rel === "" ? "." : rel.startsWith("..") ? outDir : rel;
103
+ const count = input.output.objects.length;
104
+ io.out(`Wrote ${join(where, DOCS_HTML)} and ${join(where, DOCS_ERD)} (${String(count)} object${count === 1 ? "" : "s"}, from ${input.source})\n`);
105
+ return 0;
106
+ }),
107
+ };
108
+ //# sourceMappingURL=docs.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"docs.js","sourceRoot":"","sources":["../../src/commands/docs.ts"],"names":[],"mappings":"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"}
@@ -1,6 +1,9 @@
1
1
  import type { Command } from "../command.js";
2
+ import { type Drift } from "../chant/drift.js";
2
3
  import { type BuildOutput } from "../chant/build.js";
3
- import type { Environment, Project } from "../project.js";
4
+ import { type ServerInfo } from "../server.js";
5
+ import type { Environment, Project, SqlDialect } from "../project.js";
6
+ import { type IssueAction } from "../pr/issue.js";
4
7
  /**
5
8
  * The schema the environment's history says it has: the recorded schema
6
9
  * of the newest migration, in chain order, that the history records as
@@ -10,5 +13,14 @@ export declare function recordedSchema(project: Project, env: Environment): Prom
10
13
  migration: string;
11
14
  schema: BuildOutput;
12
15
  } | undefined>;
16
+ /** The document `yodel drift --json` prints (schemas/drift.schema.json): the drift, and what the run made of it. */
17
+ export declare function driftJson(result: Drift, parts: {
18
+ dialect: SqlDialect;
19
+ server?: ServerInfo | undefined;
20
+ drifted: boolean;
21
+ fingerprint: string;
22
+ issue?: IssueAction | undefined;
23
+ issueError?: string | undefined;
24
+ }): Record<string, unknown>;
13
25
  /** `yodel drift <env>`; the exit codes are in `help`. */
14
26
  export declare const drift: Command;