@intentius/sql-yodeler 0.0.0-stage → 0.2.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 (324) hide show
  1. package/LICENSE +202 -0
  2. package/README.md +112 -2
  3. package/dist/bin.d.ts +2 -0
  4. package/dist/bin.js +29 -0
  5. package/dist/bin.js.map +1 -0
  6. package/dist/chant/apply.d.ts +59 -0
  7. package/dist/chant/apply.js +116 -0
  8. package/dist/chant/apply.js.map +1 -0
  9. package/dist/chant/build.d.ts +34 -0
  10. package/dist/chant/build.js +48 -0
  11. package/dist/chant/build.js.map +1 -0
  12. package/dist/chant/drift.d.ts +112 -0
  13. package/dist/chant/drift.js +238 -0
  14. package/dist/chant/drift.js.map +1 -0
  15. package/dist/chant/plan.d.ts +45 -0
  16. package/dist/chant/plan.js +61 -0
  17. package/dist/chant/plan.js.map +1 -0
  18. package/dist/chant/process.d.ts +41 -0
  19. package/dist/chant/process.js +87 -0
  20. package/dist/chant/process.js.map +1 -0
  21. package/dist/chant/runnable.d.ts +19 -0
  22. package/dist/chant/runnable.js +47 -0
  23. package/dist/chant/runnable.js.map +1 -0
  24. package/dist/cli.d.ts +8 -0
  25. package/dist/cli.js +99 -0
  26. package/dist/cli.js.map +1 -0
  27. package/dist/command.d.ts +23 -0
  28. package/dist/command.js +13 -0
  29. package/dist/command.js.map +1 -0
  30. package/dist/commands/apply.d.ts +3 -0
  31. package/dist/commands/apply.js +426 -0
  32. package/dist/commands/apply.js.map +1 -0
  33. package/dist/commands/approve.d.ts +5 -0
  34. package/dist/commands/approve.js +111 -0
  35. package/dist/commands/approve.js.map +1 -0
  36. package/dist/commands/drift.d.ts +14 -0
  37. package/dist/commands/drift.js +267 -0
  38. package/dist/commands/drift.js.map +1 -0
  39. package/dist/commands/index.d.ts +3 -0
  40. package/dist/commands/index.js +28 -0
  41. package/dist/commands/index.js.map +1 -0
  42. package/dist/commands/init.d.ts +6 -0
  43. package/dist/commands/init.js +139 -0
  44. package/dist/commands/init.js.map +1 -0
  45. package/dist/commands/lint.d.ts +3 -0
  46. package/dist/commands/lint.js +202 -0
  47. package/dist/commands/lint.js.map +1 -0
  48. package/dist/commands/mcp.d.ts +3 -0
  49. package/dist/commands/mcp.js +50 -0
  50. package/dist/commands/mcp.js.map +1 -0
  51. package/dist/commands/new.d.ts +5 -0
  52. package/dist/commands/new.js +292 -0
  53. package/dist/commands/new.js.map +1 -0
  54. package/dist/commands/override.d.ts +3 -0
  55. package/dist/commands/override.js +118 -0
  56. package/dist/commands/override.js.map +1 -0
  57. package/dist/commands/plan.d.ts +3 -0
  58. package/dist/commands/plan.js +183 -0
  59. package/dist/commands/plan.js.map +1 -0
  60. package/dist/commands/rebase.d.ts +3 -0
  61. package/dist/commands/rebase.js +167 -0
  62. package/dist/commands/rebase.js.map +1 -0
  63. package/dist/commands/repair.d.ts +14 -0
  64. package/dist/commands/repair.js +125 -0
  65. package/dist/commands/repair.js.map +1 -0
  66. package/dist/commands/status.d.ts +7 -0
  67. package/dist/commands/status.js +54 -0
  68. package/dist/commands/status.js.map +1 -0
  69. package/dist/config/define.d.ts +148 -0
  70. package/dist/config/define.js +20 -0
  71. package/dist/config/define.js.map +1 -0
  72. package/dist/config/index.d.ts +11 -0
  73. package/dist/config/index.js +12 -0
  74. package/dist/config/index.js.map +1 -0
  75. package/dist/config/load.d.ts +17 -0
  76. package/dist/config/load.js +162 -0
  77. package/dist/config/load.js.map +1 -0
  78. package/dist/config/settings.d.ts +53 -0
  79. package/dist/config/settings.js +116 -0
  80. package/dist/config/settings.js.map +1 -0
  81. package/dist/env-args.d.ts +24 -0
  82. package/dist/env-args.js +92 -0
  83. package/dist/env-args.js.map +1 -0
  84. package/dist/exit.d.ts +20 -0
  85. package/dist/exit.js +28 -0
  86. package/dist/exit.js.map +1 -0
  87. package/dist/history/client.d.ts +25 -0
  88. package/dist/history/client.js +42 -0
  89. package/dist/history/client.js.map +1 -0
  90. package/dist/history/ddl.d.ts +58 -0
  91. package/dist/history/ddl.js +100 -0
  92. package/dist/history/ddl.js.map +1 -0
  93. package/dist/history/index.d.ts +25 -0
  94. package/dist/history/index.js +26 -0
  95. package/dist/history/index.js.map +1 -0
  96. package/dist/history/lock.d.ts +104 -0
  97. package/dist/history/lock.js +230 -0
  98. package/dist/history/lock.js.map +1 -0
  99. package/dist/history/postgres/ddl.d.ts +22 -0
  100. package/dist/history/postgres/ddl.js +47 -0
  101. package/dist/history/postgres/ddl.js.map +1 -0
  102. package/dist/history/postgres/index.d.ts +12 -0
  103. package/dist/history/postgres/index.js +13 -0
  104. package/dist/history/postgres/index.js.map +1 -0
  105. package/dist/history/postgres/lock.d.ts +48 -0
  106. package/dist/history/postgres/lock.js +95 -0
  107. package/dist/history/postgres/lock.js.map +1 -0
  108. package/dist/history/postgres/store.d.ts +39 -0
  109. package/dist/history/postgres/store.js +68 -0
  110. package/dist/history/postgres/store.js.map +1 -0
  111. package/dist/history/rows.d.ts +96 -0
  112. package/dist/history/rows.js +73 -0
  113. package/dist/history/rows.js.map +1 -0
  114. package/dist/history/store.d.ts +72 -0
  115. package/dist/history/store.js +134 -0
  116. package/dist/history/store.js.map +1 -0
  117. package/dist/history/topology.d.ts +41 -0
  118. package/dist/history/topology.js +54 -0
  119. package/dist/history/topology.js.map +1 -0
  120. package/dist/index.d.ts +10 -0
  121. package/dist/index.js +10 -0
  122. package/dist/index.js.map +1 -0
  123. package/dist/init/adopt.d.ts +70 -0
  124. package/dist/init/adopt.js +143 -0
  125. package/dist/init/adopt.js.map +1 -0
  126. package/dist/init/baseline.d.ts +61 -0
  127. package/dist/init/baseline.js +88 -0
  128. package/dist/init/baseline.js.map +1 -0
  129. package/dist/init/files.d.ts +23 -0
  130. package/dist/init/files.js +77 -0
  131. package/dist/init/files.js.map +1 -0
  132. package/dist/init/index.d.ts +16 -0
  133. package/dist/init/index.js +17 -0
  134. package/dist/init/index.js.map +1 -0
  135. package/dist/init/live.d.ts +39 -0
  136. package/dist/init/live.js +68 -0
  137. package/dist/init/live.js.map +1 -0
  138. package/dist/lint/check.d.ts +88 -0
  139. package/dist/lint/check.js +227 -0
  140. package/dist/lint/check.js.map +1 -0
  141. package/dist/lint/config.d.ts +21 -0
  142. package/dist/lint/config.js +43 -0
  143. package/dist/lint/config.js.map +1 -0
  144. package/dist/lint/index.d.ts +31 -0
  145. package/dist/lint/index.js +43 -0
  146. package/dist/lint/index.js.map +1 -0
  147. package/dist/lint/project-rules.d.ts +19 -0
  148. package/dist/lint/project-rules.js +32 -0
  149. package/dist/lint/project-rules.js.map +1 -0
  150. package/dist/lint/report.d.ts +7 -0
  151. package/dist/lint/report.js +48 -0
  152. package/dist/lint/report.js.map +1 -0
  153. package/dist/lint/rules.d.ts +53 -0
  154. package/dist/lint/rules.js +42 -0
  155. package/dist/lint/rules.js.map +1 -0
  156. package/dist/lint/silences.d.ts +49 -0
  157. package/dist/lint/silences.js +91 -0
  158. package/dist/lint/silences.js.map +1 -0
  159. package/dist/lint/statements.d.ts +38 -0
  160. package/dist/lint/statements.js +120 -0
  161. package/dist/lint/statements.js.map +1 -0
  162. package/dist/mcp/server.d.ts +40 -0
  163. package/dist/mcp/server.js +106 -0
  164. package/dist/mcp/server.js.map +1 -0
  165. package/dist/mcp/tools.d.ts +72 -0
  166. package/dist/mcp/tools.js +178 -0
  167. package/dist/mcp/tools.js.map +1 -0
  168. package/dist/migrations/checks.d.ts +28 -0
  169. package/dist/migrations/checks.js +97 -0
  170. package/dist/migrations/checks.js.map +1 -0
  171. package/dist/migrations/checksum.d.ts +56 -0
  172. package/dist/migrations/checksum.js +72 -0
  173. package/dist/migrations/checksum.js.map +1 -0
  174. package/dist/migrations/create.d.ts +75 -0
  175. package/dist/migrations/create.js +120 -0
  176. package/dist/migrations/create.js.map +1 -0
  177. package/dist/migrations/directory.d.ts +119 -0
  178. package/dist/migrations/directory.js +229 -0
  179. package/dist/migrations/directory.js.map +1 -0
  180. package/dist/migrations/document.d.ts +106 -0
  181. package/dist/migrations/document.js +83 -0
  182. package/dist/migrations/document.js.map +1 -0
  183. package/dist/migrations/index.d.ts +21 -0
  184. package/dist/migrations/index.js +22 -0
  185. package/dist/migrations/index.js.map +1 -0
  186. package/dist/pr/comment.d.ts +32 -0
  187. package/dist/pr/comment.js +99 -0
  188. package/dist/pr/comment.js.map +1 -0
  189. package/dist/pr/forge.d.ts +54 -0
  190. package/dist/pr/forge.js +119 -0
  191. package/dist/pr/forge.js.map +1 -0
  192. package/dist/pr/issue.d.ts +68 -0
  193. package/dist/pr/issue.js +107 -0
  194. package/dist/pr/issue.js.map +1 -0
  195. package/dist/pr/model.d.ts +99 -0
  196. package/dist/pr/model.js +137 -0
  197. package/dist/pr/model.js.map +1 -0
  198. package/dist/pr/render.d.ts +29 -0
  199. package/dist/pr/render.js +279 -0
  200. package/dist/pr/render.js.map +1 -0
  201. package/dist/project.d.ts +62 -0
  202. package/dist/project.js +92 -0
  203. package/dist/project.js.map +1 -0
  204. package/dist/rebase/index.d.ts +121 -0
  205. package/dist/rebase/index.js +390 -0
  206. package/dist/rebase/index.js.map +1 -0
  207. package/dist/rebase/repair.d.ts +41 -0
  208. package/dist/rebase/repair.js +68 -0
  209. package/dist/rebase/repair.js.map +1 -0
  210. package/dist/replay/clickhouse.d.ts +33 -0
  211. package/dist/replay/clickhouse.js +106 -0
  212. package/dist/replay/clickhouse.js.map +1 -0
  213. package/dist/replay/index.d.ts +39 -0
  214. package/dist/replay/index.js +57 -0
  215. package/dist/replay/index.js.map +1 -0
  216. package/dist/replay/postgres.d.ts +31 -0
  217. package/dist/replay/postgres.js +140 -0
  218. package/dist/replay/postgres.js.map +1 -0
  219. package/dist/replay/replay.d.ts +114 -0
  220. package/dist/replay/replay.js +98 -0
  221. package/dist/replay/replay.js.map +1 -0
  222. package/dist/replay/report.d.ts +18 -0
  223. package/dist/replay/report.js +72 -0
  224. package/dist/replay/report.js.map +1 -0
  225. package/dist/replay/shared.d.ts +28 -0
  226. package/dist/replay/shared.js +77 -0
  227. package/dist/replay/shared.js.map +1 -0
  228. package/dist/schemas.d.ts +34 -0
  229. package/dist/schemas.js +38 -0
  230. package/dist/schemas.js.map +1 -0
  231. package/dist/server.d.ts +40 -0
  232. package/dist/server.js +87 -0
  233. package/dist/server.js.map +1 -0
  234. package/dist/steps/backfill.d.ts +132 -0
  235. package/dist/steps/backfill.js +307 -0
  236. package/dist/steps/backfill.js.map +1 -0
  237. package/dist/steps/index.d.ts +52 -0
  238. package/dist/steps/index.js +52 -0
  239. package/dist/steps/index.js.map +1 -0
  240. package/dist/steps/kinds.d.ts +101 -0
  241. package/dist/steps/kinds.js +143 -0
  242. package/dist/steps/kinds.js.map +1 -0
  243. package/dist/steps/postgres-migration.d.ts +95 -0
  244. package/dist/steps/postgres-migration.js +149 -0
  245. package/dist/steps/postgres-migration.js.map +1 -0
  246. package/dist/steps/rebuild.d.ts +97 -0
  247. package/dist/steps/rebuild.js +155 -0
  248. package/dist/steps/rebuild.js.map +1 -0
  249. package/dist/steps/run.d.ts +45 -0
  250. package/dist/steps/run.js +72 -0
  251. package/dist/steps/run.js.map +1 -0
  252. package/dist/topology/render.d.ts +17 -0
  253. package/dist/topology/render.js +17 -0
  254. package/dist/topology/render.js.map +1 -0
  255. package/dist/version.d.ts +2 -0
  256. package/dist/version.js +11 -0
  257. package/dist/version.js.map +1 -0
  258. package/dist/versioned/approval.d.ts +35 -0
  259. package/dist/versioned/approval.js +53 -0
  260. package/dist/versioned/approval.js.map +1 -0
  261. package/dist/versioned/checks.d.ts +29 -0
  262. package/dist/versioned/checks.js +54 -0
  263. package/dist/versioned/checks.js.map +1 -0
  264. package/dist/versioned/clickhouse.d.ts +24 -0
  265. package/dist/versioned/clickhouse.js +42 -0
  266. package/dist/versioned/clickhouse.js.map +1 -0
  267. package/dist/versioned/digest.d.ts +77 -0
  268. package/dist/versioned/digest.js +69 -0
  269. package/dist/versioned/digest.js.map +1 -0
  270. package/dist/versioned/driver.d.ts +51 -0
  271. package/dist/versioned/driver.js +13 -0
  272. package/dist/versioned/driver.js.map +1 -0
  273. package/dist/versioned/engine.d.ts +206 -0
  274. package/dist/versioned/engine.js +370 -0
  275. package/dist/versioned/engine.js.map +1 -0
  276. package/dist/versioned/explain.d.ts +123 -0
  277. package/dist/versioned/explain.js +294 -0
  278. package/dist/versioned/explain.js.map +1 -0
  279. package/dist/versioned/index.d.ts +46 -0
  280. package/dist/versioned/index.js +77 -0
  281. package/dist/versioned/index.js.map +1 -0
  282. package/dist/versioned/op.d.ts +72 -0
  283. package/dist/versioned/op.js +185 -0
  284. package/dist/versioned/op.js.map +1 -0
  285. package/dist/versioned/policy.d.ts +67 -0
  286. package/dist/versioned/policy.js +161 -0
  287. package/dist/versioned/policy.js.map +1 -0
  288. package/dist/versioned/postgres/catalog.d.ts +33 -0
  289. package/dist/versioned/postgres/catalog.js +34 -0
  290. package/dist/versioned/postgres/catalog.js.map +1 -0
  291. package/dist/versioned/postgres/driver.d.ts +71 -0
  292. package/dist/versioned/postgres/driver.js +140 -0
  293. package/dist/versioned/postgres/driver.js.map +1 -0
  294. package/dist/versioned/postgres/executor.d.ts +104 -0
  295. package/dist/versioned/postgres/executor.js +172 -0
  296. package/dist/versioned/postgres/executor.js.map +1 -0
  297. package/dist/versioned/postgres/index.d.ts +11 -0
  298. package/dist/versioned/postgres/index.js +12 -0
  299. package/dist/versioned/postgres/index.js.map +1 -0
  300. package/dist/versioned/postgres/waits.d.ts +46 -0
  301. package/dist/versioned/postgres/waits.js +78 -0
  302. package/dist/versioned/postgres/waits.js.map +1 -0
  303. package/dist/versioned/report.d.ts +25 -0
  304. package/dist/versioned/report.js +84 -0
  305. package/dist/versioned/report.js.map +1 -0
  306. package/dist/versioned/stand-down.d.ts +30 -0
  307. package/dist/versioned/stand-down.js +56 -0
  308. package/dist/versioned/stand-down.js.map +1 -0
  309. package/dist/versioned/state.d.ts +108 -0
  310. package/dist/versioned/state.js +153 -0
  311. package/dist/versioned/state.js.map +1 -0
  312. package/package.json +61 -4
  313. package/schemas/apply.schema.json +269 -0
  314. package/schemas/common.schema.json +213 -0
  315. package/schemas/drift.schema.json +36 -0
  316. package/schemas/init.schema.json +31 -0
  317. package/schemas/lint-rules.schema.json +54 -0
  318. package/schemas/lint.schema.json +290 -0
  319. package/schemas/mcp.schema.json +70 -0
  320. package/schemas/new.schema.json +71 -0
  321. package/schemas/plan.schema.json +371 -0
  322. package/schemas/rebase.schema.json +54 -0
  323. package/schemas/repair.schema.json +20 -0
  324. package/schemas/status.schema.json +310 -0
@@ -0,0 +1,54 @@
1
+ /**
2
+ * Where `yodel plan <env> --comment` posts: the forge, the repository, the
3
+ * pull (merge) request and the token, from the CI job's environment, each
4
+ * overridden by a flag.
5
+ *
6
+ * - GitHub Actions: `GITHUB_ACTIONS=true`. `GITHUB_REPOSITORY`, the PR number
7
+ * from the event payload (`GITHUB_EVENT_PATH`: `pull_request.number`, or
8
+ * `refs/pull/<n>/merge` in `GITHUB_REF`), `GITHUB_API_URL` (default
9
+ * https://api.github.com), token `GITHUB_TOKEN`.
10
+ * - GitLab CI: `GITLAB_CI=true`. `CI_API_V4_URL`, `CI_PROJECT_ID`,
11
+ * `CI_MERGE_REQUEST_IID` (a merge request pipeline), token `GITLAB_TOKEN`
12
+ * (a project or personal access token with `api` scope: `CI_JOB_TOKEN`
13
+ * cannot write merge request notes).
14
+ * - Forgejo Actions: `FORGEJO_ACTIONS=true` (runner 7 and later), or
15
+ * `GITEA_ACTIONS=true`, or a GitHub-style job whose API URL ends in
16
+ * `/api/v1`. `FORGEJO_REPOSITORY` / `GITHUB_REPOSITORY`, the PR number from
17
+ * `FORGEJO_EVENT_PATH` / `GITHUB_EVENT_PATH` or the ref, `FORGEJO_API_URL` /
18
+ * `GITHUB_API_URL` (else `<server>/api/v1`), token `FORGEJO_TOKEN` /
19
+ * `GITHUB_TOKEN`.
20
+ *
21
+ * `--token-env <NAME>` names the variable the token is read from; the token
22
+ * itself is never a flag, never printed and never put in an error.
23
+ */
24
+ export type ForgeKind = "github" | "gitlab" | "forgejo";
25
+ export declare const FORGES: readonly ForgeKind[];
26
+ export interface ForgeTarget {
27
+ forge: ForgeKind;
28
+ /** `owner/name` (GitHub, Forgejo), or the project id or path (GitLab). */
29
+ repo: string;
30
+ /** The PR number (GitHub, Forgejo) or MR iid (GitLab). */
31
+ pr: number;
32
+ /** The REST API's base, e.g. https://api.github.com, https://gitlab.com/api/v4, https://codeberg.org/api/v1. */
33
+ apiUrl: string;
34
+ token: string;
35
+ /** The variable the token came from, for messages. */
36
+ tokenEnv: string;
37
+ }
38
+ export interface ForgeFlags {
39
+ forge?: string | undefined;
40
+ repo?: string | undefined;
41
+ pr?: string | undefined;
42
+ apiUrl?: string | undefined;
43
+ tokenEnv?: string | undefined;
44
+ }
45
+ /** Which forge the job runs on, from its environment; `undefined` outside a known CI. */
46
+ export declare function detectForge(env: NodeJS.ProcessEnv): ForgeKind | undefined;
47
+ /** The PR number from a GitHub-style event payload, or from the ref. */
48
+ export declare function prFromEvent(eventPath: string | undefined, ref: string | undefined): number | undefined;
49
+ /** Where a job writes on its forge, without a pull request: the repository, the API and the token. */
50
+ export type ForgeRepo = Omit<ForgeTarget, "pr">;
51
+ /** Resolves the target from flags and the environment, or says what is missing. */
52
+ export declare function resolveForgeTarget(flags: ForgeFlags, env: NodeJS.ProcessEnv): ForgeTarget;
53
+ /** The repository a job writes to (`yodel drift --issue`), from flags and the environment, or says what is missing. */
54
+ export declare function resolveForgeRepo(flags: Omit<ForgeFlags, "pr">, env: NodeJS.ProcessEnv, what?: string): ForgeRepo;
@@ -0,0 +1,119 @@
1
+ /**
2
+ * Where `yodel plan <env> --comment` posts: the forge, the repository, the
3
+ * pull (merge) request and the token, from the CI job's environment, each
4
+ * overridden by a flag.
5
+ *
6
+ * - GitHub Actions: `GITHUB_ACTIONS=true`. `GITHUB_REPOSITORY`, the PR number
7
+ * from the event payload (`GITHUB_EVENT_PATH`: `pull_request.number`, or
8
+ * `refs/pull/<n>/merge` in `GITHUB_REF`), `GITHUB_API_URL` (default
9
+ * https://api.github.com), token `GITHUB_TOKEN`.
10
+ * - GitLab CI: `GITLAB_CI=true`. `CI_API_V4_URL`, `CI_PROJECT_ID`,
11
+ * `CI_MERGE_REQUEST_IID` (a merge request pipeline), token `GITLAB_TOKEN`
12
+ * (a project or personal access token with `api` scope: `CI_JOB_TOKEN`
13
+ * cannot write merge request notes).
14
+ * - Forgejo Actions: `FORGEJO_ACTIONS=true` (runner 7 and later), or
15
+ * `GITEA_ACTIONS=true`, or a GitHub-style job whose API URL ends in
16
+ * `/api/v1`. `FORGEJO_REPOSITORY` / `GITHUB_REPOSITORY`, the PR number from
17
+ * `FORGEJO_EVENT_PATH` / `GITHUB_EVENT_PATH` or the ref, `FORGEJO_API_URL` /
18
+ * `GITHUB_API_URL` (else `<server>/api/v1`), token `FORGEJO_TOKEN` /
19
+ * `GITHUB_TOKEN`.
20
+ *
21
+ * `--token-env <NAME>` names the variable the token is read from; the token
22
+ * itself is never a flag, never printed and never put in an error.
23
+ */
24
+ import { readFileSync } from "node:fs";
25
+ import { YodelError } from "../project.js";
26
+ export const FORGES = ["github", "gitlab", "forgejo"];
27
+ const truthy = (v) => v === "true" || v === "1";
28
+ /** Which forge the job runs on, from its environment; `undefined` outside a known CI. */
29
+ export function detectForge(env) {
30
+ if (truthy(env.GITLAB_CI))
31
+ return "gitlab";
32
+ if (truthy(env.FORGEJO_ACTIONS) || truthy(env.GITEA_ACTIONS))
33
+ return "forgejo";
34
+ if (truthy(env.GITHUB_ACTIONS))
35
+ return /\/api\/v1\/?$/.test(env.GITHUB_API_URL ?? "") ? "forgejo" : "github";
36
+ return undefined;
37
+ }
38
+ /** The PR number from a GitHub-style event payload, or from the ref. */
39
+ export function prFromEvent(eventPath, ref) {
40
+ if (eventPath) {
41
+ try {
42
+ const event = JSON.parse(readFileSync(eventPath, "utf8"));
43
+ const n = event.pull_request?.number ?? (event.issue?.pull_request ? event.issue.number : undefined) ?? (event.pull_request ? event.number : undefined);
44
+ if (typeof n === "number" && Number.isInteger(n) && n > 0)
45
+ return n;
46
+ }
47
+ catch {
48
+ // Unreadable payload: fall back to the ref.
49
+ }
50
+ }
51
+ const m = /^refs\/pull\/(\d+)\/(?:merge|head)$/.exec(ref ?? "");
52
+ return m ? Number(m[1]) : undefined;
53
+ }
54
+ const DEFAULT_TOKEN_ENV = {
55
+ github: ["GITHUB_TOKEN"],
56
+ gitlab: ["GITLAB_TOKEN"],
57
+ forgejo: ["FORGEJO_TOKEN", "GITHUB_TOKEN"],
58
+ };
59
+ /**
60
+ * Resolves the repository, API and token from flags and the environment;
61
+ * `pr` also the pull (merge) request. `what` names the option that needs
62
+ * them, for messages (`--comment`, `--issue`).
63
+ */
64
+ function resolve(flags, env, what, withPr) {
65
+ if (flags.forge !== undefined && !FORGES.includes(flags.forge))
66
+ throw new YodelError(`--forge must be one of ${FORGES.join(", ")}, got ${JSON.stringify(flags.forge)}`);
67
+ const forge = flags.forge ?? detectForge(env);
68
+ if (!forge)
69
+ throw new YodelError(`${what} cannot tell the forge: not a GitHub Actions, GitLab CI or Forgejo Actions job; pass --forge github|gitlab|forgejo with --repo${withPr ? ", --pr" : ""} and --api-url`);
70
+ const pick = (...names) => names.map((n) => env[n]).find((v) => v !== undefined && v !== "");
71
+ let repo = flags.repo;
72
+ let pr = flags.pr !== undefined ? Number(flags.pr) : undefined;
73
+ let apiUrl = flags.apiUrl;
74
+ if (forge === "gitlab") {
75
+ repo ??= pick("CI_PROJECT_ID", "CI_PROJECT_PATH");
76
+ pr ??= pick("CI_MERGE_REQUEST_IID") !== undefined ? Number(pick("CI_MERGE_REQUEST_IID")) : undefined;
77
+ apiUrl ??= pick("CI_API_V4_URL");
78
+ }
79
+ else {
80
+ const p = forge === "forgejo" ? "FORGEJO_" : "GITHUB_";
81
+ repo ??= pick(`${p}REPOSITORY`, "GITHUB_REPOSITORY");
82
+ if (withPr)
83
+ pr ??= prFromEvent(pick(`${p}EVENT_PATH`, "GITHUB_EVENT_PATH"), pick(`${p}REF`, "GITHUB_REF"));
84
+ apiUrl ??= pick(`${p}API_URL`, "GITHUB_API_URL");
85
+ if (!apiUrl && forge === "forgejo") {
86
+ const server = pick("FORGEJO_SERVER_URL", "GITHUB_SERVER_URL");
87
+ if (server)
88
+ apiUrl = `${server.replace(/\/+$/, "")}/api/v1`;
89
+ }
90
+ if (!apiUrl && forge === "github")
91
+ apiUrl = "https://api.github.com";
92
+ }
93
+ const missing = [];
94
+ if (!repo)
95
+ missing.push(forge === "gitlab" ? "the project (--repo, or CI_PROJECT_ID)" : "the repository (--repo owner/name, or GITHUB_REPOSITORY)");
96
+ if (withPr && (pr === undefined || !Number.isInteger(pr) || pr <= 0)) {
97
+ missing.push(forge === "gitlab" ? "the merge request (--pr, or CI_MERGE_REQUEST_IID in a merge request pipeline)" : "the pull request (--pr, or a pull_request event)");
98
+ }
99
+ if (!apiUrl)
100
+ missing.push(forge === "gitlab" ? "the API URL (--api-url, or CI_API_V4_URL)" : "the API URL (--api-url, or FORGEJO_API_URL / GITHUB_API_URL)");
101
+ const names = flags.tokenEnv ? [flags.tokenEnv] : DEFAULT_TOKEN_ENV[forge];
102
+ const tokenEnv = names.find((n) => env[n]) ?? names[0] ?? "";
103
+ const token = env[tokenEnv] ?? "";
104
+ if (!token)
105
+ missing.push(`a token (${names.join(" or ")} is not set${flags.tokenEnv ? "" : "; name another variable with --token-env"})`);
106
+ if (missing.length > 0)
107
+ throw new YodelError(`${what} on ${forge} needs ${missing.join(", ")}`);
108
+ const base = { forge, repo: repo, apiUrl: apiUrl.replace(/\/+$/, ""), token, tokenEnv };
109
+ return withPr ? { ...base, pr: pr } : base;
110
+ }
111
+ /** Resolves the target from flags and the environment, or says what is missing. */
112
+ export function resolveForgeTarget(flags, env) {
113
+ return resolve(flags, env, "--comment", true);
114
+ }
115
+ /** The repository a job writes to (`yodel drift --issue`), from flags and the environment, or says what is missing. */
116
+ export function resolveForgeRepo(flags, env, what = "--issue") {
117
+ return resolve(flags, env, what, false);
118
+ }
119
+ //# sourceMappingURL=forge.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"forge.js","sourceRoot":"","sources":["../../src/pr/forge.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,68 @@
1
+ /**
2
+ * The drift watch's tracking issue (#49): one open issue per project and
3
+ * environment, found by its marker, opened when drift is found, updated
4
+ * while it lasts, and closed, with a comment, when it is gone. The three
5
+ * forges' REST APIs:
6
+ *
7
+ * GitHub GET/POST {api}/repos/{owner}/{repo}/issues (open ones; pull requests left out)
8
+ * PATCH {api}/repos/{owner}/{repo}/issues/{n} body, or state closed
9
+ * POST {api}/repos/{owner}/{repo}/issues/{n}/comments
10
+ * GitLab GET/POST {api}/projects/{id}/issues (state opened)
11
+ * PUT {api}/projects/{id}/issues/{iid} description, or state_event close
12
+ * POST {api}/projects/{id}/issues/{iid}/notes
13
+ * Forgejo as GitHub, under /api/v1, with type=issues and limit
14
+ *
15
+ * The client lists only open issues, so a closed one is never reopened:
16
+ * drift found again after a close opens a new issue.
17
+ */
18
+ import type { ForgeRepo } from "./forge.js";
19
+ export interface ForgeIssue {
20
+ number: number;
21
+ title: string;
22
+ body: string;
23
+ url?: string | undefined;
24
+ }
25
+ export interface IssueClient {
26
+ /** The open issues (not pull requests). */
27
+ open(): Promise<ForgeIssue[]>;
28
+ create(title: string, body: string): Promise<ForgeIssue>;
29
+ update(n: number, title: string, body: string): Promise<ForgeIssue>;
30
+ /** Comments `comment` on the issue, then closes it. */
31
+ close(n: number, comment: string): Promise<void>;
32
+ }
33
+ type Fetch = typeof fetch;
34
+ export declare function issueClient(t: ForgeRepo, f?: Fetch): IssueClient;
35
+ /**
36
+ * The marker an environment's tracking issue carries, so the next run finds
37
+ * it. Keyed by the project's directory in its repository (`.` at the root),
38
+ * so two projects in one repository keep their own issues.
39
+ */
40
+ export declare function driftIssueMarker(project: string, environment: string): string;
41
+ /** What the run found, as the tracking issue needs it. */
42
+ export type DriftOutcome = {
43
+ kind: "drift";
44
+ title: string;
45
+ body: string;
46
+ fingerprint: string;
47
+ } | {
48
+ kind: "clean";
49
+ comment: string;
50
+ }
51
+ /** The drift could not be read, or part of the environment could not: the issue is left as it is. */
52
+ | {
53
+ kind: "unknown";
54
+ why: string;
55
+ };
56
+ export type IssueAction = {
57
+ action: "opened" | "updated" | "unchanged" | "closed";
58
+ issue: ForgeIssue;
59
+ } | {
60
+ action: "none";
61
+ } | {
62
+ action: "left";
63
+ why: string;
64
+ issue?: ForgeIssue;
65
+ };
66
+ /** Brings the environment's tracking issue in line with the outcome; see the module comment. */
67
+ export declare function trackDrift(client: IssueClient, marker: string, outcome: DriftOutcome): Promise<IssueAction>;
68
+ export {};
@@ -0,0 +1,107 @@
1
+ /**
2
+ * The drift watch's tracking issue (#49): one open issue per project and
3
+ * environment, found by its marker, opened when drift is found, updated
4
+ * while it lasts, and closed, with a comment, when it is gone. The three
5
+ * forges' REST APIs:
6
+ *
7
+ * GitHub GET/POST {api}/repos/{owner}/{repo}/issues (open ones; pull requests left out)
8
+ * PATCH {api}/repos/{owner}/{repo}/issues/{n} body, or state closed
9
+ * POST {api}/repos/{owner}/{repo}/issues/{n}/comments
10
+ * GitLab GET/POST {api}/projects/{id}/issues (state opened)
11
+ * PUT {api}/projects/{id}/issues/{iid} description, or state_event close
12
+ * POST {api}/projects/{id}/issues/{iid}/notes
13
+ * Forgejo as GitHub, under /api/v1, with type=issues and limit
14
+ *
15
+ * The client lists only open issues, so a closed one is never reopened:
16
+ * drift found again after a close opens a new issue.
17
+ */
18
+ import { YodelError } from "../project.js";
19
+ const PAGE = 100;
20
+ const MAX_PAGES = 20;
21
+ const repoPath = (repo) => repo.split("/").map(encodeURIComponent).join("/");
22
+ export function issueClient(t, f = fetch) {
23
+ const gitlab = t.forge === "gitlab";
24
+ const headers = gitlab
25
+ ? { "PRIVATE-TOKEN": t.token, Accept: "application/json" }
26
+ : t.forge === "forgejo"
27
+ ? { Authorization: `token ${t.token}`, Accept: "application/json" }
28
+ : { Authorization: `Bearer ${t.token}`, Accept: "application/vnd.github+json", "X-GitHub-Api-Version": "2022-11-28" };
29
+ const base = gitlab ? `${t.apiUrl}/projects/${encodeURIComponent(t.repo)}/issues` : `${t.apiUrl}/repos/${repoPath(t.repo)}/issues`;
30
+ const call = async (method, url, body) => {
31
+ let res;
32
+ try {
33
+ res = await f(url, { method, headers: { ...headers, ...(body ? { "Content-Type": "application/json" } : {}) }, ...(body ? { body: JSON.stringify(body) } : {}) });
34
+ }
35
+ catch (e) {
36
+ throw new YodelError(`${t.forge}: ${method} ${url} failed: ${e instanceof Error ? e.message : String(e)}`);
37
+ }
38
+ const text = await res.text();
39
+ if (!res.ok)
40
+ throw new YodelError(`${t.forge}: ${method} ${url} answered ${String(res.status)}: ${text.slice(0, 300).replaceAll(t.token, "[token]")}`);
41
+ return text ? JSON.parse(text) : {};
42
+ };
43
+ const toIssue = (r) => ({
44
+ number: Number(gitlab ? r.iid : r.number),
45
+ title: typeof r.title === "string" ? r.title : "",
46
+ body: typeof (gitlab ? r.description : r.body) === "string" ? String(gitlab ? r.description : r.body) : "",
47
+ url: typeof r.html_url === "string" ? r.html_url : typeof r.web_url === "string" ? r.web_url : undefined,
48
+ });
49
+ const list = (page) => gitlab
50
+ ? `${base}?state=opened&per_page=${String(PAGE)}&page=${String(page)}`
51
+ : t.forge === "forgejo"
52
+ ? `${base}?state=open&type=issues&limit=${String(PAGE)}&page=${String(page)}`
53
+ : `${base}?state=open&per_page=${String(PAGE)}&page=${String(page)}`;
54
+ return {
55
+ async open() {
56
+ const all = [];
57
+ for (let page = 1; page <= MAX_PAGES; page++) {
58
+ const got = (await call("GET", list(page)));
59
+ if (!Array.isArray(got))
60
+ throw new YodelError(`${t.forge}: listing issues did not return a list`);
61
+ all.push(...got.filter((r) => r.pull_request === undefined || r.pull_request === null).map(toIssue));
62
+ if (got.length < PAGE)
63
+ break;
64
+ }
65
+ return all;
66
+ },
67
+ async create(title, body) {
68
+ return toIssue((await call("POST", base, gitlab ? { title, description: body } : { title, body })));
69
+ },
70
+ async update(n, title, body) {
71
+ return toIssue((await call(gitlab ? "PUT" : "PATCH", `${base}/${String(n)}`, gitlab ? { title, description: body } : { title, body })));
72
+ },
73
+ async close(n, comment) {
74
+ await call("POST", `${base}/${String(n)}/${gitlab ? "notes" : "comments"}`, { body: comment });
75
+ await call(gitlab ? "PUT" : "PATCH", `${base}/${String(n)}`, gitlab ? { state_event: "close" } : { state: "closed" });
76
+ },
77
+ };
78
+ }
79
+ /**
80
+ * The marker an environment's tracking issue carries, so the next run finds
81
+ * it. Keyed by the project's directory in its repository (`.` at the root),
82
+ * so two projects in one repository keep their own issues.
83
+ */
84
+ export function driftIssueMarker(project, environment) {
85
+ const clean = (s) => s.replace(/\s+/g, "_").replace(/-->/g, "");
86
+ return `<!-- yodel:drift project=${clean(project || ".")} env=${clean(environment)} -->`;
87
+ }
88
+ /** Brings the environment's tracking issue in line with the outcome; see the module comment. */
89
+ export async function trackDrift(client, marker, outcome) {
90
+ const found = (await client.open()).filter((i) => i.body.includes(marker)).sort((a, b) => a.number - b.number);
91
+ const issue = found[0];
92
+ if (outcome.kind === "unknown")
93
+ return { action: "left", why: outcome.why, ...(issue ? { issue } : {}) };
94
+ if (outcome.kind === "clean") {
95
+ if (!issue)
96
+ return { action: "none" };
97
+ await client.close(issue.number, outcome.comment);
98
+ return { action: "closed", issue };
99
+ }
100
+ const body = `${marker}\n${outcome.body}`;
101
+ if (!issue)
102
+ return { action: "opened", issue: await client.create(outcome.title, body) };
103
+ if (issue.body.includes(outcome.fingerprint) && issue.title === outcome.title)
104
+ return { action: "unchanged", issue };
105
+ return { action: "updated", issue: await client.update(issue.number, outcome.title, body) };
106
+ }
107
+ //# sourceMappingURL=issue.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"issue.js","sourceRoot":"","sources":["../../src/pr/issue.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,99 @@
1
+ /**
2
+ * The versioned plan (#16): what `yodel plan <env>` shows for a project
3
+ * with migrations, and what the pull request comment renders. Collected once,
4
+ * rendered as text, markdown or JSON (`./render.ts`).
5
+ *
6
+ * The digest is `statusReport`'s, the value `yodel apply <env> --digest` (the
7
+ * migrations Op's Plan step) prints and the gate binds, so the
8
+ * `chant approve <op> <gate> --plan <digest>` the comment prints is the one
9
+ * the post-merge apply needs. Nothing here writes: no lock, no history row,
10
+ * no ledger entry.
11
+ */
12
+ import type { MigrationStep } from "@intentius/chant-lexicon-sql/migration-statements";
13
+ import type { GateLedgerPort } from "@intentius/chant/op/gate";
14
+ import { type Environment, type Project, type SqlDialect } from "../project.js";
15
+ import { type MigrationCheck } from "../migrations/index.js";
16
+ import { type MigrateOpRef, type RuleResult, type VersionedEnv } from "../versioned/index.js";
17
+ import { type LatestApproval, type MoveExplanation } from "../versioned/explain.js";
18
+ import type { DigestSubject } from "../versioned/digest.js";
19
+ import type { ServerInfo } from "../server.js";
20
+ export interface StepView {
21
+ index: number;
22
+ step: MigrationStep;
23
+ /** Succeeded in an earlier run; not sent again. */
24
+ done: boolean;
25
+ /** An Op or manual step apply does not run, and why; any of them refuses the run. */
26
+ refused?: string | undefined;
27
+ /** A backfill step's module, as the migration's directory holds it. */
28
+ source?: {
29
+ file: string;
30
+ text: string;
31
+ } | undefined;
32
+ }
33
+ export interface PendingView {
34
+ id: string;
35
+ outOfOrder: boolean;
36
+ /** Where a migration that ran part way resumes. */
37
+ resumeAt?: number | undefined;
38
+ /** Its pre-migration checks (#52), run before its first statement. */
39
+ checks?: MigrationCheck[] | undefined;
40
+ steps: StepView[];
41
+ }
42
+ export type ApprovalView = {
43
+ kind: "no-op";
44
+ message: string;
45
+ } | {
46
+ kind: "op";
47
+ op: string;
48
+ gate: string;
49
+ /** The command that approves this plan; absent when there is no digest. */
50
+ command?: string | undefined;
51
+ /** The newest approval of the gate in chant's ledger, when it could be read. */
52
+ latest?: (LatestApproval & {
53
+ moved?: MoveExplanation | undefined;
54
+ }) | undefined;
55
+ /** Why the ledger was not read. */
56
+ ledgerNote?: string | undefined;
57
+ };
58
+ export interface VersionedPlan {
59
+ environment: string;
60
+ dialect: SqlDialect;
61
+ /** The server the plan was read from (#11). */
62
+ server?: ServerInfo | undefined;
63
+ /** `<database>.history (<topology> (<where it came from>))`; Postgres has no topology, so only `<database>.history`. */
64
+ history?: string | undefined;
65
+ appliedCount: number;
66
+ pending: PendingView[];
67
+ /** Directory problems, checksum mismatches, out-of-order migrations. */
68
+ problems: string[];
69
+ /** Why `yodel apply` would refuse, with its exit code. */
70
+ refusal?: {
71
+ exitCode: number;
72
+ message: string;
73
+ } | undefined;
74
+ /** The digest the gate binds; absent when apply would refuse or has nothing to do. */
75
+ digest?: string | undefined;
76
+ /** What the digest is taken over, carried in the comment so a later one can say what moved. */
77
+ subject?: DigestSubject | undefined;
78
+ approval?: ApprovalView | undefined;
79
+ /** Each policy rule's outcome over the plan (#53), with any override recorded for its digest; absent when yodel.config.ts declares no policy. */
80
+ policy?: RuleResult[] | undefined;
81
+ /** The comment this one replaces showed another digest: what moved since. */
82
+ sinceLast?: MoveExplanation | undefined;
83
+ notes: string[];
84
+ }
85
+ export interface CollectOptions {
86
+ allowOutOfOrder?: boolean;
87
+ /** The subject the previous comment carried, when updating one. */
88
+ previous?: DigestSubject | undefined;
89
+ /** The gate ledger; default chant's, in the project's git checkout. `null` skips it. */
90
+ ledger?: GateLedgerPort | null;
91
+ /** The migrations Op; default discovered in the project. */
92
+ op?: MigrateOpRef | null;
93
+ opName?: string | undefined;
94
+ processEnv?: NodeJS.ProcessEnv;
95
+ /** For tests: the environment already bound. */
96
+ venv?: VersionedEnv;
97
+ }
98
+ /** Collects the plan; throws only when the project or the server cannot be read at all. */
99
+ export declare function collectVersionedPlan(project: Project, env: Environment, options?: CollectOptions): Promise<VersionedPlan>;
@@ -0,0 +1,137 @@
1
+ /**
2
+ * The versioned plan (#16): what `yodel plan <env>` shows for a project
3
+ * with migrations, and what the pull request comment renders. Collected once,
4
+ * rendered as text, markdown or JSON (`./render.ts`).
5
+ *
6
+ * The digest is `statusReport`'s, the value `yodel apply <env> --digest` (the
7
+ * migrations Op's Plan step) prints and the gate binds, so the
8
+ * `chant approve <op> <gate> --plan <digest>` the comment prints is the one
9
+ * the post-merge apply needs. Nothing here writes: no lock, no history row,
10
+ * no ledger entry.
11
+ */
12
+ import { YodelError } from "../project.js";
13
+ import { scanMigrations } from "../migrations/index.js";
14
+ import { describeTopology } from "../history/index.js";
15
+ import { asBackfill } from "../steps/kinds.js";
16
+ import { EXIT_REFUSED } from "../exit.js";
17
+ import { BROKEN_CHAIN, findMigrateOp, inspect, gitLedger, judgePlan, migrateOpExample, migrationsDir, closeVersioned, openVersioned, outOfOrder, planDigest, sameDigest, statusReport, } from "../versioned/index.js";
18
+ import { currentSubject, explainAgainst, explainMove, latestApproval } from "../versioned/explain.js";
19
+ const viewOf = (m, done = [], outOfOrder = false, resumeAt, refused = []) => ({
20
+ id: m.id,
21
+ outOfOrder,
22
+ resumeAt,
23
+ checks: m.document.checks ?? [],
24
+ steps: m.document.steps.map((step, index) => {
25
+ const file = asBackfill(step)?.options.file;
26
+ const text = file === undefined ? undefined : m.files.extra?.[file];
27
+ return { index, step, done: done.includes(index), refused: refused.find((r) => r.index === index)?.reason, ...(file !== undefined && text !== undefined ? { source: { file, text } } : {}) };
28
+ }),
29
+ });
30
+ /** Collects the plan; throws only when the project or the server cannot be read at all. */
31
+ export async function collectVersionedPlan(project, env, options = {}) {
32
+ const dir = migrationsDir(project);
33
+ const scan = scanMigrations(dir);
34
+ const problems = scan.problems.map((p) => p.message);
35
+ const base = { environment: env.name, dialect: project.dialect, problems, notes: [] };
36
+ const venv = options.venv ?? (await openVersioned(project, env, options.processEnv));
37
+ try {
38
+ if (!scan.ordered) {
39
+ // Nothing can be planned, but the history still says what is applied.
40
+ const { status } = await inspect(venv, [], { sync: false });
41
+ return { ...base, appliedCount: status.applied.length, pending: [], refusal: { exitCode: EXIT_REFUSED, message: BROKEN_CHAIN } };
42
+ }
43
+ return await collectWith(project, env, options, venv, scan.ordered, base, problems);
44
+ }
45
+ finally {
46
+ if (!options.venv)
47
+ await closeVersioned(venv);
48
+ }
49
+ }
50
+ async function collectWith(project, env, options, venv, ordered, base, problems) {
51
+ const report = await statusReport(venv, ordered, { allowOutOfOrder: options.allowOutOfOrder === true, sync: false });
52
+ const { status } = report;
53
+ for (const m of status.mismatches)
54
+ if (!problems.includes(m.message))
55
+ problems.push(m.message);
56
+ const late = outOfOrder(status);
57
+ if (late.length > 0)
58
+ problems.push(`out of order: ${late.map((p) => p.id).join(", ")} (the chain puts ${late.length === 1 ? "it" : "them"} before a migration already applied)`);
59
+ let subject;
60
+ if (report.digest) {
61
+ subject = await currentSubject(venv, status, status.pending);
62
+ // Read twice (the report's, then here): if the server moved in between, carry no subject rather than a wrong one.
63
+ if (!(await sameDigest(await planDigest(subject), report.digest)))
64
+ subject = undefined;
65
+ }
66
+ const plan = {
67
+ ...base,
68
+ ...(report.server ? { server: report.server } : {}),
69
+ history: venv.dialect === "postgres" ? `${venv.location.database}.history` : `${venv.location.database}.history (${report.topology ?? describeTopology(venv.location.topology)})`,
70
+ appliedCount: status.applied.length,
71
+ pending: status.pending.map((p) => viewOf(p.migration, p.partWay?.done ?? [], p.outOfOrder, p.partWay?.resumeAt, p.refusedSteps)),
72
+ refusal: report.refusal,
73
+ digest: report.digest,
74
+ subject,
75
+ };
76
+ if (options.previous && subject) {
77
+ const prev = await planDigest(options.previous);
78
+ if (!(await sameDigest(prev, report.digest)))
79
+ plan.sinceLast = await explainMove({ approvedDigest: prev, now: subject, previous: options.previous, ordered, applied: status.applied });
80
+ }
81
+ plan.approval = await approvalView(project, env, venv, ordered, plan, options);
82
+ if (project.yodel?.config.policy && !report.refusal) {
83
+ const approval = plan.approval;
84
+ plan.policy = await judgePlan({
85
+ policy: project.yodel.config.policy,
86
+ environment: env.name,
87
+ dialect: project.dialect,
88
+ toApply: status.pending,
89
+ digest: report.digest,
90
+ ledger: async () => {
91
+ if (approval.kind !== "op" || options.ledger === null)
92
+ return undefined;
93
+ try {
94
+ return { port: options.ledger ?? (await gitLedger(project.dir)), op: options.op ?? (await findMigrateOp(project, env, options.opName)) };
95
+ }
96
+ catch {
97
+ return undefined;
98
+ }
99
+ },
100
+ });
101
+ }
102
+ return plan;
103
+ }
104
+ async function approvalView(project, env, venv, ordered, plan, options) {
105
+ let op;
106
+ try {
107
+ op = options.op ?? (await findMigrateOp(project, env, options.opName));
108
+ }
109
+ catch (e) {
110
+ if (!(e instanceof YodelError))
111
+ throw e;
112
+ return { kind: "no-op", message: e.message.includes("no Op applies") ? `no Op applies the migrations to ${env.name}; declare one, for example:\n\n${migrateOpExample(env.name)}` : e.message };
113
+ }
114
+ const view = {
115
+ kind: "op",
116
+ op: op.name,
117
+ gate: op.gate,
118
+ command: plan.digest ? `chant approve ${op.name} ${op.gate} --plan ${plan.digest}` : undefined,
119
+ };
120
+ if (options.ledger === null)
121
+ return view;
122
+ try {
123
+ const ledger = options.ledger ?? (await gitLedger(project.dir));
124
+ const latest = await latestApproval(ledger, op, plan.digest);
125
+ if (!latest)
126
+ return view;
127
+ view.latest = latest;
128
+ if (!latest.holds && !latest.sameDigest && latest.digest && plan.subject) {
129
+ view.latest.moved = await explainAgainst(venv, ordered, { approvedDigest: latest.digest, approvedAt: latest.at, now: plan.subject, previous: options.previous });
130
+ }
131
+ }
132
+ catch (e) {
133
+ view.ledgerNote = `chant's gate ledger could not be read (${e instanceof Error ? (e.message.split("\n")[0] ?? "") : String(e)}); fetch the chant/lifecycle branch to see approvals here`;
134
+ }
135
+ return view;
136
+ }
137
+ //# sourceMappingURL=model.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"model.js","sourceRoot":"","sources":["../../src/pr/model.ts"],"names":[],"mappings":"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"}