@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.
- package/LICENSE +202 -0
- package/README.md +112 -2
- package/dist/bin.d.ts +2 -0
- package/dist/bin.js +29 -0
- package/dist/bin.js.map +1 -0
- package/dist/chant/apply.d.ts +59 -0
- package/dist/chant/apply.js +116 -0
- package/dist/chant/apply.js.map +1 -0
- package/dist/chant/build.d.ts +34 -0
- package/dist/chant/build.js +48 -0
- package/dist/chant/build.js.map +1 -0
- package/dist/chant/drift.d.ts +112 -0
- package/dist/chant/drift.js +238 -0
- package/dist/chant/drift.js.map +1 -0
- package/dist/chant/plan.d.ts +45 -0
- package/dist/chant/plan.js +61 -0
- package/dist/chant/plan.js.map +1 -0
- package/dist/chant/process.d.ts +41 -0
- package/dist/chant/process.js +87 -0
- package/dist/chant/process.js.map +1 -0
- package/dist/chant/runnable.d.ts +19 -0
- package/dist/chant/runnable.js +47 -0
- package/dist/chant/runnable.js.map +1 -0
- package/dist/cli.d.ts +8 -0
- package/dist/cli.js +99 -0
- package/dist/cli.js.map +1 -0
- package/dist/command.d.ts +23 -0
- package/dist/command.js +13 -0
- package/dist/command.js.map +1 -0
- package/dist/commands/apply.d.ts +3 -0
- package/dist/commands/apply.js +426 -0
- package/dist/commands/apply.js.map +1 -0
- package/dist/commands/approve.d.ts +5 -0
- package/dist/commands/approve.js +111 -0
- package/dist/commands/approve.js.map +1 -0
- package/dist/commands/drift.d.ts +14 -0
- package/dist/commands/drift.js +267 -0
- package/dist/commands/drift.js.map +1 -0
- package/dist/commands/index.d.ts +3 -0
- package/dist/commands/index.js +28 -0
- package/dist/commands/index.js.map +1 -0
- package/dist/commands/init.d.ts +6 -0
- package/dist/commands/init.js +139 -0
- package/dist/commands/init.js.map +1 -0
- package/dist/commands/lint.d.ts +3 -0
- package/dist/commands/lint.js +202 -0
- package/dist/commands/lint.js.map +1 -0
- package/dist/commands/mcp.d.ts +3 -0
- package/dist/commands/mcp.js +50 -0
- package/dist/commands/mcp.js.map +1 -0
- package/dist/commands/new.d.ts +5 -0
- package/dist/commands/new.js +292 -0
- package/dist/commands/new.js.map +1 -0
- package/dist/commands/override.d.ts +3 -0
- package/dist/commands/override.js +118 -0
- package/dist/commands/override.js.map +1 -0
- package/dist/commands/plan.d.ts +3 -0
- package/dist/commands/plan.js +183 -0
- package/dist/commands/plan.js.map +1 -0
- package/dist/commands/rebase.d.ts +3 -0
- package/dist/commands/rebase.js +167 -0
- package/dist/commands/rebase.js.map +1 -0
- package/dist/commands/repair.d.ts +14 -0
- package/dist/commands/repair.js +125 -0
- package/dist/commands/repair.js.map +1 -0
- package/dist/commands/status.d.ts +7 -0
- package/dist/commands/status.js +54 -0
- package/dist/commands/status.js.map +1 -0
- package/dist/config/define.d.ts +148 -0
- package/dist/config/define.js +20 -0
- package/dist/config/define.js.map +1 -0
- package/dist/config/index.d.ts +11 -0
- package/dist/config/index.js +12 -0
- package/dist/config/index.js.map +1 -0
- package/dist/config/load.d.ts +17 -0
- package/dist/config/load.js +162 -0
- package/dist/config/load.js.map +1 -0
- package/dist/config/settings.d.ts +53 -0
- package/dist/config/settings.js +116 -0
- package/dist/config/settings.js.map +1 -0
- package/dist/env-args.d.ts +24 -0
- package/dist/env-args.js +92 -0
- package/dist/env-args.js.map +1 -0
- package/dist/exit.d.ts +20 -0
- package/dist/exit.js +28 -0
- package/dist/exit.js.map +1 -0
- package/dist/history/client.d.ts +25 -0
- package/dist/history/client.js +42 -0
- package/dist/history/client.js.map +1 -0
- package/dist/history/ddl.d.ts +58 -0
- package/dist/history/ddl.js +100 -0
- package/dist/history/ddl.js.map +1 -0
- package/dist/history/index.d.ts +25 -0
- package/dist/history/index.js +26 -0
- package/dist/history/index.js.map +1 -0
- package/dist/history/lock.d.ts +104 -0
- package/dist/history/lock.js +230 -0
- package/dist/history/lock.js.map +1 -0
- package/dist/history/postgres/ddl.d.ts +22 -0
- package/dist/history/postgres/ddl.js +47 -0
- package/dist/history/postgres/ddl.js.map +1 -0
- package/dist/history/postgres/index.d.ts +12 -0
- package/dist/history/postgres/index.js +13 -0
- package/dist/history/postgres/index.js.map +1 -0
- package/dist/history/postgres/lock.d.ts +48 -0
- package/dist/history/postgres/lock.js +95 -0
- package/dist/history/postgres/lock.js.map +1 -0
- package/dist/history/postgres/store.d.ts +39 -0
- package/dist/history/postgres/store.js +68 -0
- package/dist/history/postgres/store.js.map +1 -0
- package/dist/history/rows.d.ts +96 -0
- package/dist/history/rows.js +73 -0
- package/dist/history/rows.js.map +1 -0
- package/dist/history/store.d.ts +72 -0
- package/dist/history/store.js +134 -0
- package/dist/history/store.js.map +1 -0
- package/dist/history/topology.d.ts +41 -0
- package/dist/history/topology.js +54 -0
- package/dist/history/topology.js.map +1 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.js +10 -0
- package/dist/index.js.map +1 -0
- package/dist/init/adopt.d.ts +70 -0
- package/dist/init/adopt.js +143 -0
- package/dist/init/adopt.js.map +1 -0
- package/dist/init/baseline.d.ts +61 -0
- package/dist/init/baseline.js +88 -0
- package/dist/init/baseline.js.map +1 -0
- package/dist/init/files.d.ts +23 -0
- package/dist/init/files.js +77 -0
- package/dist/init/files.js.map +1 -0
- package/dist/init/index.d.ts +16 -0
- package/dist/init/index.js +17 -0
- package/dist/init/index.js.map +1 -0
- package/dist/init/live.d.ts +39 -0
- package/dist/init/live.js +68 -0
- package/dist/init/live.js.map +1 -0
- package/dist/lint/check.d.ts +88 -0
- package/dist/lint/check.js +227 -0
- package/dist/lint/check.js.map +1 -0
- package/dist/lint/config.d.ts +21 -0
- package/dist/lint/config.js +43 -0
- package/dist/lint/config.js.map +1 -0
- package/dist/lint/index.d.ts +31 -0
- package/dist/lint/index.js +43 -0
- package/dist/lint/index.js.map +1 -0
- package/dist/lint/project-rules.d.ts +19 -0
- package/dist/lint/project-rules.js +32 -0
- package/dist/lint/project-rules.js.map +1 -0
- package/dist/lint/report.d.ts +7 -0
- package/dist/lint/report.js +48 -0
- package/dist/lint/report.js.map +1 -0
- package/dist/lint/rules.d.ts +53 -0
- package/dist/lint/rules.js +42 -0
- package/dist/lint/rules.js.map +1 -0
- package/dist/lint/silences.d.ts +49 -0
- package/dist/lint/silences.js +91 -0
- package/dist/lint/silences.js.map +1 -0
- package/dist/lint/statements.d.ts +38 -0
- package/dist/lint/statements.js +120 -0
- package/dist/lint/statements.js.map +1 -0
- package/dist/mcp/server.d.ts +40 -0
- package/dist/mcp/server.js +106 -0
- package/dist/mcp/server.js.map +1 -0
- package/dist/mcp/tools.d.ts +72 -0
- package/dist/mcp/tools.js +178 -0
- package/dist/mcp/tools.js.map +1 -0
- package/dist/migrations/checks.d.ts +28 -0
- package/dist/migrations/checks.js +97 -0
- package/dist/migrations/checks.js.map +1 -0
- package/dist/migrations/checksum.d.ts +56 -0
- package/dist/migrations/checksum.js +72 -0
- package/dist/migrations/checksum.js.map +1 -0
- package/dist/migrations/create.d.ts +75 -0
- package/dist/migrations/create.js +120 -0
- package/dist/migrations/create.js.map +1 -0
- package/dist/migrations/directory.d.ts +119 -0
- package/dist/migrations/directory.js +229 -0
- package/dist/migrations/directory.js.map +1 -0
- package/dist/migrations/document.d.ts +106 -0
- package/dist/migrations/document.js +83 -0
- package/dist/migrations/document.js.map +1 -0
- package/dist/migrations/index.d.ts +21 -0
- package/dist/migrations/index.js +22 -0
- package/dist/migrations/index.js.map +1 -0
- package/dist/pr/comment.d.ts +32 -0
- package/dist/pr/comment.js +99 -0
- package/dist/pr/comment.js.map +1 -0
- package/dist/pr/forge.d.ts +54 -0
- package/dist/pr/forge.js +119 -0
- package/dist/pr/forge.js.map +1 -0
- package/dist/pr/issue.d.ts +68 -0
- package/dist/pr/issue.js +107 -0
- package/dist/pr/issue.js.map +1 -0
- package/dist/pr/model.d.ts +99 -0
- package/dist/pr/model.js +137 -0
- package/dist/pr/model.js.map +1 -0
- package/dist/pr/render.d.ts +29 -0
- package/dist/pr/render.js +279 -0
- package/dist/pr/render.js.map +1 -0
- package/dist/project.d.ts +62 -0
- package/dist/project.js +92 -0
- package/dist/project.js.map +1 -0
- package/dist/rebase/index.d.ts +121 -0
- package/dist/rebase/index.js +390 -0
- package/dist/rebase/index.js.map +1 -0
- package/dist/rebase/repair.d.ts +41 -0
- package/dist/rebase/repair.js +68 -0
- package/dist/rebase/repair.js.map +1 -0
- package/dist/replay/clickhouse.d.ts +33 -0
- package/dist/replay/clickhouse.js +106 -0
- package/dist/replay/clickhouse.js.map +1 -0
- package/dist/replay/index.d.ts +39 -0
- package/dist/replay/index.js +57 -0
- package/dist/replay/index.js.map +1 -0
- package/dist/replay/postgres.d.ts +31 -0
- package/dist/replay/postgres.js +140 -0
- package/dist/replay/postgres.js.map +1 -0
- package/dist/replay/replay.d.ts +114 -0
- package/dist/replay/replay.js +98 -0
- package/dist/replay/replay.js.map +1 -0
- package/dist/replay/report.d.ts +18 -0
- package/dist/replay/report.js +72 -0
- package/dist/replay/report.js.map +1 -0
- package/dist/replay/shared.d.ts +28 -0
- package/dist/replay/shared.js +77 -0
- package/dist/replay/shared.js.map +1 -0
- package/dist/schemas.d.ts +34 -0
- package/dist/schemas.js +38 -0
- package/dist/schemas.js.map +1 -0
- package/dist/server.d.ts +40 -0
- package/dist/server.js +87 -0
- package/dist/server.js.map +1 -0
- package/dist/steps/backfill.d.ts +132 -0
- package/dist/steps/backfill.js +307 -0
- package/dist/steps/backfill.js.map +1 -0
- package/dist/steps/index.d.ts +52 -0
- package/dist/steps/index.js +52 -0
- package/dist/steps/index.js.map +1 -0
- package/dist/steps/kinds.d.ts +101 -0
- package/dist/steps/kinds.js +143 -0
- package/dist/steps/kinds.js.map +1 -0
- package/dist/steps/postgres-migration.d.ts +95 -0
- package/dist/steps/postgres-migration.js +149 -0
- package/dist/steps/postgres-migration.js.map +1 -0
- package/dist/steps/rebuild.d.ts +97 -0
- package/dist/steps/rebuild.js +155 -0
- package/dist/steps/rebuild.js.map +1 -0
- package/dist/steps/run.d.ts +45 -0
- package/dist/steps/run.js +72 -0
- package/dist/steps/run.js.map +1 -0
- package/dist/topology/render.d.ts +17 -0
- package/dist/topology/render.js +17 -0
- package/dist/topology/render.js.map +1 -0
- package/dist/version.d.ts +2 -0
- package/dist/version.js +11 -0
- package/dist/version.js.map +1 -0
- package/dist/versioned/approval.d.ts +35 -0
- package/dist/versioned/approval.js +53 -0
- package/dist/versioned/approval.js.map +1 -0
- package/dist/versioned/checks.d.ts +29 -0
- package/dist/versioned/checks.js +54 -0
- package/dist/versioned/checks.js.map +1 -0
- package/dist/versioned/clickhouse.d.ts +24 -0
- package/dist/versioned/clickhouse.js +42 -0
- package/dist/versioned/clickhouse.js.map +1 -0
- package/dist/versioned/digest.d.ts +77 -0
- package/dist/versioned/digest.js +69 -0
- package/dist/versioned/digest.js.map +1 -0
- package/dist/versioned/driver.d.ts +51 -0
- package/dist/versioned/driver.js +13 -0
- package/dist/versioned/driver.js.map +1 -0
- package/dist/versioned/engine.d.ts +206 -0
- package/dist/versioned/engine.js +370 -0
- package/dist/versioned/engine.js.map +1 -0
- package/dist/versioned/explain.d.ts +123 -0
- package/dist/versioned/explain.js +294 -0
- package/dist/versioned/explain.js.map +1 -0
- package/dist/versioned/index.d.ts +46 -0
- package/dist/versioned/index.js +77 -0
- package/dist/versioned/index.js.map +1 -0
- package/dist/versioned/op.d.ts +72 -0
- package/dist/versioned/op.js +185 -0
- package/dist/versioned/op.js.map +1 -0
- package/dist/versioned/policy.d.ts +67 -0
- package/dist/versioned/policy.js +161 -0
- package/dist/versioned/policy.js.map +1 -0
- package/dist/versioned/postgres/catalog.d.ts +33 -0
- package/dist/versioned/postgres/catalog.js +34 -0
- package/dist/versioned/postgres/catalog.js.map +1 -0
- package/dist/versioned/postgres/driver.d.ts +71 -0
- package/dist/versioned/postgres/driver.js +140 -0
- package/dist/versioned/postgres/driver.js.map +1 -0
- package/dist/versioned/postgres/executor.d.ts +104 -0
- package/dist/versioned/postgres/executor.js +172 -0
- package/dist/versioned/postgres/executor.js.map +1 -0
- package/dist/versioned/postgres/index.d.ts +11 -0
- package/dist/versioned/postgres/index.js +12 -0
- package/dist/versioned/postgres/index.js.map +1 -0
- package/dist/versioned/postgres/waits.d.ts +46 -0
- package/dist/versioned/postgres/waits.js +78 -0
- package/dist/versioned/postgres/waits.js.map +1 -0
- package/dist/versioned/report.d.ts +25 -0
- package/dist/versioned/report.js +84 -0
- package/dist/versioned/report.js.map +1 -0
- package/dist/versioned/stand-down.d.ts +30 -0
- package/dist/versioned/stand-down.js +56 -0
- package/dist/versioned/stand-down.js.map +1 -0
- package/dist/versioned/state.d.ts +108 -0
- package/dist/versioned/state.js +153 -0
- package/dist/versioned/state.js.map +1 -0
- package/package.json +61 -4
- package/schemas/apply.schema.json +269 -0
- package/schemas/common.schema.json +213 -0
- package/schemas/drift.schema.json +36 -0
- package/schemas/init.schema.json +31 -0
- package/schemas/lint-rules.schema.json +54 -0
- package/schemas/lint.schema.json +290 -0
- package/schemas/mcp.schema.json +70 -0
- package/schemas/new.schema.json +71 -0
- package/schemas/plan.schema.json +371 -0
- package/schemas/rebase.schema.json +54 -0
- package/schemas/repair.schema.json +20 -0
- package/schemas/status.schema.json +310 -0
|
@@ -0,0 +1,229 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Reading and writing a project's `migrations/` directory.
|
|
3
|
+
*
|
|
4
|
+
* Order is the chain of parent ids, as in Alembic and pgroll: the first
|
|
5
|
+
* migration has `parent: null`, every other names the one it follows, and
|
|
6
|
+
* the list runs from the first to the newest. The ids' timestamps never
|
|
7
|
+
* decide the order. A directory is well formed when the parent links make
|
|
8
|
+
* one unbroken chain:
|
|
9
|
+
*
|
|
10
|
+
* - every directory under `migrations/` is named like an id and holds a
|
|
11
|
+
* readable `migration.sql` and `migration.json` whose `id` is that name
|
|
12
|
+
* (plain files directly under `migrations/`, such as a README, are
|
|
13
|
+
* ignored; files inside a migration's directory are its own and count in
|
|
14
|
+
* its checksum);
|
|
15
|
+
* - every parent names a migration in the directory;
|
|
16
|
+
* - no two migrations have the same parent, `null` included. Two branches
|
|
17
|
+
* that each added a migration give one parent two children: a fork. It is
|
|
18
|
+
* reported naming both, never ordered by timestamp or by name;
|
|
19
|
+
* - every migration is reachable from the first (no cycles).
|
|
20
|
+
*
|
|
21
|
+
* {@link scanMigrations} reports every problem and never throws on one, for
|
|
22
|
+
* `yodel lint`. {@link readMigrations} returns the ordered chain or throws a
|
|
23
|
+
* {@link MigrationDirectoryError} listing the problems, for the verbs that
|
|
24
|
+
* act on the chain (`new`, `apply`, `rebase`).
|
|
25
|
+
*/
|
|
26
|
+
import { existsSync, mkdirSync, readdirSync, readFileSync, renameSync, rmSync, statSync, writeFileSync } from "node:fs";
|
|
27
|
+
import { dirname, join } from "node:path";
|
|
28
|
+
import { YodelError } from "../project.js";
|
|
29
|
+
import { verifyChecksum } from "./checksum.js";
|
|
30
|
+
import { checksProblem } from "./checks.js";
|
|
31
|
+
import { MIGRATION_JSON, MIGRATION_SQL } from "./document.js";
|
|
32
|
+
/** Where a project keeps its migrations, relative to the project directory. */
|
|
33
|
+
export const MIGRATIONS_DIR = "migrations";
|
|
34
|
+
/** `<YYYYMMDDTHHMM>-<name>`; a name is lowercase letters and digits, words joined by `-` or `_`. */
|
|
35
|
+
export const MIGRATION_ID = /^(\d{8}T\d{4})-([a-z0-9]+(?:[-_][a-z0-9]+)*)$/;
|
|
36
|
+
export const MIGRATION_NAME = /^[a-z0-9]+(?:[-_][a-z0-9]+)*$/;
|
|
37
|
+
/** A migrations directory that is not one unbroken chain, or (when asked) whose checksums do not match. */
|
|
38
|
+
export class MigrationDirectoryError extends YodelError {
|
|
39
|
+
dir;
|
|
40
|
+
problems;
|
|
41
|
+
constructor(dir, problems) {
|
|
42
|
+
super(`${dir}:\n${problems.map((p) => ` ${p.message}`).join("\n")}`);
|
|
43
|
+
this.dir = dir;
|
|
44
|
+
this.problems = problems;
|
|
45
|
+
this.name = "MigrationDirectoryError";
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
const isRecord = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
|
|
49
|
+
/**
|
|
50
|
+
* A migration's files besides migration.sql and migration.json, by path
|
|
51
|
+
* relative to its directory (`/` separators): every regular file at any
|
|
52
|
+
* depth, except those two and anything whose name starts with `.`.
|
|
53
|
+
*/
|
|
54
|
+
export function readExtraFiles(at) {
|
|
55
|
+
const out = {};
|
|
56
|
+
const walk = (rel) => {
|
|
57
|
+
for (const entry of readdirSync(rel ? join(at, rel) : at).sort()) {
|
|
58
|
+
if (entry.startsWith("."))
|
|
59
|
+
continue;
|
|
60
|
+
const path = rel ? `${rel}/${entry}` : entry;
|
|
61
|
+
if (!rel && (entry === MIGRATION_SQL || entry === MIGRATION_JSON))
|
|
62
|
+
continue;
|
|
63
|
+
const st = statSync(join(at, path));
|
|
64
|
+
if (st.isDirectory())
|
|
65
|
+
walk(path);
|
|
66
|
+
else if (st.isFile())
|
|
67
|
+
out[path] = readFileSync(join(at, path), "utf8");
|
|
68
|
+
}
|
|
69
|
+
};
|
|
70
|
+
walk("");
|
|
71
|
+
return out;
|
|
72
|
+
}
|
|
73
|
+
function readOne(dir, id) {
|
|
74
|
+
const at = join(dir, id);
|
|
75
|
+
let sql;
|
|
76
|
+
let json;
|
|
77
|
+
let extra;
|
|
78
|
+
try {
|
|
79
|
+
sql = readFileSync(join(at, MIGRATION_SQL), "utf8");
|
|
80
|
+
json = readFileSync(join(at, MIGRATION_JSON), "utf8");
|
|
81
|
+
extra = readExtraFiles(at);
|
|
82
|
+
}
|
|
83
|
+
catch (e) {
|
|
84
|
+
return { kind: "unreadable", path: at, message: `${id}: ${e instanceof Error ? e.message : String(e)}` };
|
|
85
|
+
}
|
|
86
|
+
let doc;
|
|
87
|
+
try {
|
|
88
|
+
doc = JSON.parse(json);
|
|
89
|
+
}
|
|
90
|
+
catch (e) {
|
|
91
|
+
return { kind: "unreadable", path: join(at, MIGRATION_JSON), message: `${id}/${MIGRATION_JSON} is not JSON: ${e instanceof Error ? e.message : String(e)}` };
|
|
92
|
+
}
|
|
93
|
+
const bad = (what) => ({ kind: "unreadable", path: join(at, MIGRATION_JSON), message: `${id}/${MIGRATION_JSON}: ${what}` });
|
|
94
|
+
if (!isRecord(doc))
|
|
95
|
+
return bad("not an object");
|
|
96
|
+
if (typeof doc.id !== "string")
|
|
97
|
+
return bad("no id");
|
|
98
|
+
if (doc.parent !== null && typeof doc.parent !== "string")
|
|
99
|
+
return bad("parent is neither an id nor null");
|
|
100
|
+
if (typeof doc.checksum !== "string")
|
|
101
|
+
return bad("no checksum");
|
|
102
|
+
if (!Array.isArray(doc.steps))
|
|
103
|
+
return bad("no steps");
|
|
104
|
+
if (!isRecord(doc.schema))
|
|
105
|
+
return bad("no recorded schema");
|
|
106
|
+
const checks = checksProblem(doc.checks);
|
|
107
|
+
if (checks)
|
|
108
|
+
return bad(checks);
|
|
109
|
+
if (doc.id !== id) {
|
|
110
|
+
return { kind: "id-mismatch", id, recorded: doc.id, message: `${id}/${MIGRATION_JSON} records id ${doc.id}; a migration's id is its directory's name` };
|
|
111
|
+
}
|
|
112
|
+
return { id, dir: at, parent: doc.parent, checksum: doc.checksum, document: doc, files: { sql, json, ...(Object.keys(extra).length > 0 ? { extra } : {}) } };
|
|
113
|
+
}
|
|
114
|
+
/** Every migration in `dir` and every problem with the directory. A missing `dir` is an empty directory. */
|
|
115
|
+
export function scanMigrations(dir) {
|
|
116
|
+
const problems = [];
|
|
117
|
+
const migrations = [];
|
|
118
|
+
if (!existsSync(dir))
|
|
119
|
+
return { dir, migrations, ordered: [], problems };
|
|
120
|
+
for (const entry of readdirSync(dir).sort()) {
|
|
121
|
+
if (entry.startsWith("."))
|
|
122
|
+
continue; // a write in progress, editor and OS files
|
|
123
|
+
if (!statSync(join(dir, entry)).isDirectory())
|
|
124
|
+
continue;
|
|
125
|
+
if (!MIGRATION_ID.test(entry)) {
|
|
126
|
+
problems.push({ kind: "unreadable", path: join(dir, entry), message: `${entry}: not a migration directory name (expected <YYYYMMDDTHHMM>-<name>)` });
|
|
127
|
+
continue;
|
|
128
|
+
}
|
|
129
|
+
const read = readOne(dir, entry);
|
|
130
|
+
if ("kind" in read)
|
|
131
|
+
problems.push(read);
|
|
132
|
+
else
|
|
133
|
+
migrations.push(read);
|
|
134
|
+
}
|
|
135
|
+
const byId = new Map(migrations.map((m) => [m.id, m]));
|
|
136
|
+
const children = new Map();
|
|
137
|
+
for (const m of migrations) {
|
|
138
|
+
if (m.parent !== null && !byId.has(m.parent)) {
|
|
139
|
+
problems.push({ kind: "missing-parent", id: m.id, parent: m.parent, message: `${m.id} follows ${m.parent}, which is not in the directory` });
|
|
140
|
+
}
|
|
141
|
+
const list = children.get(m.parent) ?? [];
|
|
142
|
+
list.push(m);
|
|
143
|
+
children.set(m.parent, list);
|
|
144
|
+
}
|
|
145
|
+
for (const [parent, kids] of children) {
|
|
146
|
+
if (kids.length < 2)
|
|
147
|
+
continue;
|
|
148
|
+
const ids = kids.map((k) => k.id);
|
|
149
|
+
problems.push({
|
|
150
|
+
kind: "fork",
|
|
151
|
+
parent,
|
|
152
|
+
children: ids,
|
|
153
|
+
message: parent === null
|
|
154
|
+
? `fork: ${ids.join(" and ")} are each a first migration (parent null); rebase all but one onto the chain`
|
|
155
|
+
: `fork: ${ids.join(" and ")} both follow ${parent}; rebase one onto the other`,
|
|
156
|
+
});
|
|
157
|
+
}
|
|
158
|
+
let ordered;
|
|
159
|
+
const structural = problems.length > 0;
|
|
160
|
+
if (!structural) {
|
|
161
|
+
ordered = [];
|
|
162
|
+
for (let next = children.get(null)?.[0]; next; next = children.get(next.id)?.[0])
|
|
163
|
+
ordered.push(next);
|
|
164
|
+
if (ordered.length !== migrations.length) {
|
|
165
|
+
const reached = new Set(ordered.map((m) => m.id));
|
|
166
|
+
const ids = migrations.filter((m) => !reached.has(m.id)).map((m) => m.id);
|
|
167
|
+
problems.push({ kind: "unreachable", ids, message: `${ids.join(", ")} cannot be reached from the first migration (their parents form a cycle)` });
|
|
168
|
+
ordered = undefined;
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
for (const m of migrations) {
|
|
172
|
+
const check = verifyChecksum(m);
|
|
173
|
+
if (!check.ok) {
|
|
174
|
+
problems.push({ kind: "checksum", id: m.id, check, message: `${m.id}: its files hash to ${check.actual}, but ${MIGRATION_JSON} records ${check.recorded || "no checksum"}` });
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
return { dir, migrations, ordered, problems };
|
|
178
|
+
}
|
|
179
|
+
/**
|
|
180
|
+
* The migrations in `dir`, from the first to the newest. Throws a
|
|
181
|
+
* {@link MigrationDirectoryError} on any structural problem (unreadable,
|
|
182
|
+
* id mismatch, missing parent, fork, cycle), and, with `verify`, on a
|
|
183
|
+
* checksum mismatch too. A missing `dir` has no migrations.
|
|
184
|
+
*/
|
|
185
|
+
export function readMigrations(dir, options = {}) {
|
|
186
|
+
const scan = scanMigrations(dir);
|
|
187
|
+
const fatal = scan.problems.filter((p) => options.verify === true || p.kind !== "checksum");
|
|
188
|
+
if (fatal.length > 0 || !scan.ordered)
|
|
189
|
+
throw new MigrationDirectoryError(dir, fatal);
|
|
190
|
+
return scan.ordered;
|
|
191
|
+
}
|
|
192
|
+
/** The newest migration of an ordered list (the end of the chain), or `undefined` for none. */
|
|
193
|
+
export const newestMigration = (ordered) => ordered[ordered.length - 1];
|
|
194
|
+
/**
|
|
195
|
+
* Writes a migration's directory, `<dir>/<id>/`, creating `dir` as needed.
|
|
196
|
+
* The files go into a hidden directory first and are renamed into place, so
|
|
197
|
+
* a reader never sees half a migration. An existing `<id>` is an error.
|
|
198
|
+
* Returns the directory written.
|
|
199
|
+
*/
|
|
200
|
+
export function writeMigration(dir, migration) {
|
|
201
|
+
const target = join(dir, migration.id);
|
|
202
|
+
for (const path of Object.keys(migration.files.extra ?? {})) {
|
|
203
|
+
const parts = path.split("/");
|
|
204
|
+
if (parts.some((p) => p === "" || p.startsWith(".") || p.includes("\\")) || path === MIGRATION_SQL || path === MIGRATION_JSON) {
|
|
205
|
+
throw new YodelError(`${migration.id}: ${JSON.stringify(path)} is not a path a migration's own file can have (relative, "/"-separated, no part starting with ".")`);
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
if (existsSync(target))
|
|
209
|
+
throw new YodelError(`${target} already exists; a migration's directory is never overwritten`);
|
|
210
|
+
mkdirSync(dir, { recursive: true });
|
|
211
|
+
const staging = join(dir, `.${migration.id}.writing`);
|
|
212
|
+
rmSync(staging, { recursive: true, force: true });
|
|
213
|
+
mkdirSync(staging);
|
|
214
|
+
try {
|
|
215
|
+
writeFileSync(join(staging, MIGRATION_SQL), migration.files.sql);
|
|
216
|
+
writeFileSync(join(staging, MIGRATION_JSON), migration.files.json);
|
|
217
|
+
for (const [path, text] of Object.entries(migration.files.extra ?? {})) {
|
|
218
|
+
mkdirSync(dirname(join(staging, path)), { recursive: true });
|
|
219
|
+
writeFileSync(join(staging, path), text);
|
|
220
|
+
}
|
|
221
|
+
renameSync(staging, target);
|
|
222
|
+
}
|
|
223
|
+
catch (e) {
|
|
224
|
+
rmSync(staging, { recursive: true, force: true });
|
|
225
|
+
throw e;
|
|
226
|
+
}
|
|
227
|
+
return target;
|
|
228
|
+
}
|
|
229
|
+
//# sourceMappingURL=directory.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"directory.js","sourceRoot":"","sources":["../../src/migrations/directory.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What one migration directory holds, and how its `migration.json` is
|
|
3
|
+
* written so that the same migration always serializes to the same bytes.
|
|
4
|
+
*
|
|
5
|
+
* migrations/<id>/migration.sql the statements, each after a comment naming
|
|
6
|
+
* its object, rule and class (chant's
|
|
7
|
+
* `renderStatements`); an Op or manual step
|
|
8
|
+
* is a comment only
|
|
9
|
+
* migrations/<id>/migration.json a {@link MigrationDocument}
|
|
10
|
+
* migrations/<id>/<other files> only when a step names one, such as a
|
|
11
|
+
* backfill step's `backfill.ts`
|
|
12
|
+
*
|
|
13
|
+
* `<id>` is `<UTC timestamp YYYYMMDDTHHMM>-<name>`, e.g.
|
|
14
|
+
* `20261009T1412-add-country`. The timestamp is for people reading a
|
|
15
|
+
* directory listing; the order migrations run in is the chain of parent ids,
|
|
16
|
+
* never the ids' sort order (see `./directory.ts`).
|
|
17
|
+
*/
|
|
18
|
+
import type { MigrationStep, SchemaStatements } from "@intentius/chant-lexicon-sql/migration-statements";
|
|
19
|
+
import type { BuildOutput } from "../chant/build.js";
|
|
20
|
+
export type { MigrationStep, SchemaStatements };
|
|
21
|
+
/**
|
|
22
|
+
* One pre-migration check: a read-only query and what its result must be.
|
|
23
|
+
*
|
|
24
|
+
* expect "empty" (the default) the query returns no rows
|
|
25
|
+
* expect "true" one row, whose first column is true (or 1)
|
|
26
|
+
* expect { min?, max? } one row, whose first column is a number within the bounds (inclusive)
|
|
27
|
+
*/
|
|
28
|
+
export interface MigrationCheck {
|
|
29
|
+
/** Names the check in output, in a refusal and in the history. Unique within the migration. */
|
|
30
|
+
name: string;
|
|
31
|
+
sql: string;
|
|
32
|
+
expect?: "empty" | "true" | {
|
|
33
|
+
min?: number;
|
|
34
|
+
max?: number;
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
export declare const MIGRATION_SQL = "migration.sql";
|
|
38
|
+
export declare const MIGRATION_JSON = "migration.json";
|
|
39
|
+
/** The version of the `migration.json` layout. Bumped only on a change older readers cannot read. */
|
|
40
|
+
export declare const MIGRATION_FORMAT = 1;
|
|
41
|
+
/**
|
|
42
|
+
* `migration.json`. Every value is plain JSON. Keys are written in the
|
|
43
|
+
* order listed here, and every nested object (a step, the schema) with its
|
|
44
|
+
* keys sorted, except that a step starts with `kind, object, type, name`.
|
|
45
|
+
*/
|
|
46
|
+
export interface MigrationDocument {
|
|
47
|
+
/** {@link MIGRATION_FORMAT}. */
|
|
48
|
+
format: number;
|
|
49
|
+
/** The directory's name: `<YYYYMMDDTHHMM>-<name>`. */
|
|
50
|
+
id: string;
|
|
51
|
+
/** The id of the migration this one follows, or `null` for the first. */
|
|
52
|
+
parent: string | null;
|
|
53
|
+
/** `sha256:<hex>` over this migration's own files; see `./checksum.ts`. */
|
|
54
|
+
checksum: string;
|
|
55
|
+
/** When `yodel new` wrote it, ISO 8601 in UTC, to the second. */
|
|
56
|
+
createdAt?: string;
|
|
57
|
+
dialect: SchemaStatements["dialect"];
|
|
58
|
+
/** ClickHouse: the database a bare name in the statements means. */
|
|
59
|
+
defaultDatabase?: string;
|
|
60
|
+
/** Postgres: the schema a bare name means; an applier sets `search_path` to it. */
|
|
61
|
+
defaultSchema?: string;
|
|
62
|
+
/** Postgres: the major the changes were classified for. */
|
|
63
|
+
major?: number;
|
|
64
|
+
/** ClickHouse: unset (or `single`): yodel writes migrations topology-neutral and renders them per environment at apply time. */
|
|
65
|
+
topology?: string;
|
|
66
|
+
/** True when a step is an Op or a manual step: the migration is more than its SQL. */
|
|
67
|
+
refused: boolean;
|
|
68
|
+
/** What the diff pointed out besides the changes, e.g. a likely rename written as a drop and an add. */
|
|
69
|
+
hints: string[];
|
|
70
|
+
/**
|
|
71
|
+
* Pre-migration checks (#52): read-only queries `yodel apply` runs against
|
|
72
|
+
* the environment before the migration's first statement; one that fails
|
|
73
|
+
* refuses the run (exit 4). Absent when there are none. In the checksum,
|
|
74
|
+
* so in the plan digest. See `./checks.ts`.
|
|
75
|
+
*/
|
|
76
|
+
checks?: MigrationCheck[];
|
|
77
|
+
/** In the order they run. An Op step carries the Op's declaration; it has no SQL. */
|
|
78
|
+
steps: MigrationStep[];
|
|
79
|
+
/** The recorded schema: the `chant build` output after this migration. The next `yodel new` diffs from it. */
|
|
80
|
+
schema: BuildOutput;
|
|
81
|
+
}
|
|
82
|
+
/** A migration's files as text, before or after they are on disk. */
|
|
83
|
+
export interface MigrationFiles {
|
|
84
|
+
/** `migration.sql`. */
|
|
85
|
+
sql: string;
|
|
86
|
+
/** `migration.json`. */
|
|
87
|
+
json: string;
|
|
88
|
+
/**
|
|
89
|
+
* The migration's other files, by path relative to its directory with `/`
|
|
90
|
+
* separators: a backfill step's module, for one. Absent or empty for a
|
|
91
|
+
* migration that has only the two. They count in the checksum.
|
|
92
|
+
*/
|
|
93
|
+
extra?: Record<string, string>;
|
|
94
|
+
}
|
|
95
|
+
/** `value` with every object's keys sorted, `head` first in that order; `undefined` members dropped. */
|
|
96
|
+
export declare function sortKeys(value: unknown, head?: readonly string[]): unknown;
|
|
97
|
+
/**
|
|
98
|
+
* The document in its written key order. Keys outside
|
|
99
|
+
* {@link MigrationDocument} (a newer yodel's) are kept, sorted, after `hints`
|
|
100
|
+
* and before `steps`, so a rewrite never drops them.
|
|
101
|
+
*/
|
|
102
|
+
export declare function orderDocument(doc: Record<string, unknown>): Record<string, unknown>;
|
|
103
|
+
/** `migration.json` as written: two-space indent, the key order above, `\n` line endings and a trailing newline. */
|
|
104
|
+
export declare function serializeDocument(doc: MigrationDocument | Record<string, unknown>): string;
|
|
105
|
+
/** Line endings as `\n`: CRLF and lone CR both become LF. */
|
|
106
|
+
export declare const normalizeLineEndings: (text: string) => string;
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What one migration directory holds, and how its `migration.json` is
|
|
3
|
+
* written so that the same migration always serializes to the same bytes.
|
|
4
|
+
*
|
|
5
|
+
* migrations/<id>/migration.sql the statements, each after a comment naming
|
|
6
|
+
* its object, rule and class (chant's
|
|
7
|
+
* `renderStatements`); an Op or manual step
|
|
8
|
+
* is a comment only
|
|
9
|
+
* migrations/<id>/migration.json a {@link MigrationDocument}
|
|
10
|
+
* migrations/<id>/<other files> only when a step names one, such as a
|
|
11
|
+
* backfill step's `backfill.ts`
|
|
12
|
+
*
|
|
13
|
+
* `<id>` is `<UTC timestamp YYYYMMDDTHHMM>-<name>`, e.g.
|
|
14
|
+
* `20261009T1412-add-country`. The timestamp is for people reading a
|
|
15
|
+
* directory listing; the order migrations run in is the chain of parent ids,
|
|
16
|
+
* never the ids' sort order (see `./directory.ts`).
|
|
17
|
+
*/
|
|
18
|
+
export const MIGRATION_SQL = "migration.sql";
|
|
19
|
+
export const MIGRATION_JSON = "migration.json";
|
|
20
|
+
/** The version of the `migration.json` layout. Bumped only on a change older readers cannot read. */
|
|
21
|
+
export const MIGRATION_FORMAT = 1;
|
|
22
|
+
const STEP_HEAD = ["kind", "object", "type", "name"];
|
|
23
|
+
const DOCUMENT_ORDER = [
|
|
24
|
+
"format",
|
|
25
|
+
"id",
|
|
26
|
+
"parent",
|
|
27
|
+
"checksum",
|
|
28
|
+
"createdAt",
|
|
29
|
+
"dialect",
|
|
30
|
+
"defaultDatabase",
|
|
31
|
+
"defaultSchema",
|
|
32
|
+
"major",
|
|
33
|
+
"topology",
|
|
34
|
+
"refused",
|
|
35
|
+
"hints",
|
|
36
|
+
"checks",
|
|
37
|
+
"steps",
|
|
38
|
+
"schema",
|
|
39
|
+
];
|
|
40
|
+
const isRecord = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
|
|
41
|
+
/** `value` with every object's keys sorted, `head` first in that order; `undefined` members dropped. */
|
|
42
|
+
export function sortKeys(value, head = []) {
|
|
43
|
+
if (Array.isArray(value))
|
|
44
|
+
return value.map((v) => sortKeys(v));
|
|
45
|
+
if (!isRecord(value))
|
|
46
|
+
return value;
|
|
47
|
+
const keys = Object.keys(value).filter((k) => value[k] !== undefined);
|
|
48
|
+
const first = head.filter((k) => keys.includes(k));
|
|
49
|
+
const rest = keys.filter((k) => !head.includes(k)).sort();
|
|
50
|
+
return Object.fromEntries([...first, ...rest].map((k) => [k, sortKeys(value[k])]));
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* The document in its written key order. Keys outside
|
|
54
|
+
* {@link MigrationDocument} (a newer yodel's) are kept, sorted, after `hints`
|
|
55
|
+
* and before `steps`, so a rewrite never drops them.
|
|
56
|
+
*/
|
|
57
|
+
export function orderDocument(doc) {
|
|
58
|
+
const known = new Set(DOCUMENT_ORDER);
|
|
59
|
+
const out = {};
|
|
60
|
+
const put = (k) => {
|
|
61
|
+
const v = doc[k];
|
|
62
|
+
if (v === undefined)
|
|
63
|
+
return;
|
|
64
|
+
if (k === "steps" && Array.isArray(v))
|
|
65
|
+
out[k] = v.map((s) => sortKeys(s, STEP_HEAD));
|
|
66
|
+
else
|
|
67
|
+
out[k] = sortKeys(v);
|
|
68
|
+
};
|
|
69
|
+
for (const k of DOCUMENT_ORDER) {
|
|
70
|
+
if (k === "steps")
|
|
71
|
+
for (const extra of Object.keys(doc).filter((x) => !known.has(x)).sort())
|
|
72
|
+
put(extra);
|
|
73
|
+
put(k);
|
|
74
|
+
}
|
|
75
|
+
return out;
|
|
76
|
+
}
|
|
77
|
+
/** `migration.json` as written: two-space indent, the key order above, `\n` line endings and a trailing newline. */
|
|
78
|
+
export function serializeDocument(doc) {
|
|
79
|
+
return `${JSON.stringify(orderDocument(doc), null, 2)}\n`;
|
|
80
|
+
}
|
|
81
|
+
/** Line endings as `\n`: CRLF and lone CR both become LF. */
|
|
82
|
+
export const normalizeLineEndings = (text) => text.replace(/\r\n?/g, "\n");
|
|
83
|
+
//# sourceMappingURL=document.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"document.js","sourceRoot":"","sources":["../../src/migrations/document.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The migration directory (#7): what `yodel new` writes and what `apply`
|
|
3
|
+
* (#8), the replay check (#11), `lint` (#14) and `rebase` / `repair` (#15)
|
|
4
|
+
* read.
|
|
5
|
+
*
|
|
6
|
+
* readMigrations(dir, { verify? }) the chain, first to newest; throws on a fork, a missing parent, a cycle
|
|
7
|
+
* scanMigrations(dir) every migration and every problem, never throws on one (for lint)
|
|
8
|
+
* newestMigration(ordered) the end of the chain
|
|
9
|
+
* computeChecksum(files) the checksum of one migration's files
|
|
10
|
+
* verifyChecksum(m) recorded against recomputed
|
|
11
|
+
* createMigration(options) the next migration from a build, offline (pure)
|
|
12
|
+
* writeMigration(dir, migration) puts it on disk
|
|
13
|
+
*
|
|
14
|
+
* The file layout is in `./document.ts`, the checksum's exact definition in
|
|
15
|
+
* `./checksum.ts`, the ordering rules in `./directory.ts`.
|
|
16
|
+
*/
|
|
17
|
+
export * from "./checks.js";
|
|
18
|
+
export * from "./checksum.js";
|
|
19
|
+
export * from "./create.js";
|
|
20
|
+
export * from "./directory.js";
|
|
21
|
+
export * from "./document.js";
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The migration directory (#7): what `yodel new` writes and what `apply`
|
|
3
|
+
* (#8), the replay check (#11), `lint` (#14) and `rebase` / `repair` (#15)
|
|
4
|
+
* read.
|
|
5
|
+
*
|
|
6
|
+
* readMigrations(dir, { verify? }) the chain, first to newest; throws on a fork, a missing parent, a cycle
|
|
7
|
+
* scanMigrations(dir) every migration and every problem, never throws on one (for lint)
|
|
8
|
+
* newestMigration(ordered) the end of the chain
|
|
9
|
+
* computeChecksum(files) the checksum of one migration's files
|
|
10
|
+
* verifyChecksum(m) recorded against recomputed
|
|
11
|
+
* createMigration(options) the next migration from a build, offline (pure)
|
|
12
|
+
* writeMigration(dir, migration) puts it on disk
|
|
13
|
+
*
|
|
14
|
+
* The file layout is in `./document.ts`, the checksum's exact definition in
|
|
15
|
+
* `./checksum.ts`, the ordering rules in `./directory.ts`.
|
|
16
|
+
*/
|
|
17
|
+
export * from "./checks.js";
|
|
18
|
+
export * from "./checksum.js";
|
|
19
|
+
export * from "./create.js";
|
|
20
|
+
export * from "./directory.js";
|
|
21
|
+
export * from "./document.js";
|
|
22
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/migrations/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,cAAc,aAAa,CAAC;AAC5B,cAAc,eAAe,CAAC;AAC9B,cAAc,aAAa,CAAC;AAC5B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,eAAe,CAAC"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* One comment per environment on the pull request: found by its marker and
|
|
3
|
+
* updated, else created. The three forges' REST APIs:
|
|
4
|
+
*
|
|
5
|
+
* GitHub GET/POST {api}/repos/{owner}/{repo}/issues/{n}/comments
|
|
6
|
+
* PATCH {api}/repos/{owner}/{repo}/issues/comments/{id}
|
|
7
|
+
* GitLab GET/POST {api}/projects/{id}/merge_requests/{iid}/notes
|
|
8
|
+
* PUT {api}/projects/{id}/merge_requests/{iid}/notes/{note_id}
|
|
9
|
+
* Forgejo GET/POST {api}/repos/{owner}/{repo}/issues/{n}/comments (Gitea's API, /api/v1)
|
|
10
|
+
* PATCH {api}/repos/{owner}/{repo}/issues/comments/{id}
|
|
11
|
+
*/
|
|
12
|
+
import type { ForgeTarget } from "./forge.js";
|
|
13
|
+
export interface ForgeComment {
|
|
14
|
+
id: number;
|
|
15
|
+
body: string;
|
|
16
|
+
url?: string | undefined;
|
|
17
|
+
}
|
|
18
|
+
export interface CommentClient {
|
|
19
|
+
list(): Promise<ForgeComment[]>;
|
|
20
|
+
create(body: string): Promise<ForgeComment>;
|
|
21
|
+
update(id: number, body: string): Promise<ForgeComment>;
|
|
22
|
+
}
|
|
23
|
+
type Fetch = typeof fetch;
|
|
24
|
+
export declare function commentClient(t: ForgeTarget, f?: Fetch): CommentClient;
|
|
25
|
+
/** The comment carrying `marker`, the newest when several do. */
|
|
26
|
+
export declare function findComment(c: CommentClient, marker: string): Promise<ForgeComment | undefined>;
|
|
27
|
+
/** Updates the comment carrying `marker`, or creates one. */
|
|
28
|
+
export declare function upsertComment(c: CommentClient, marker: string, body: string, existing?: ForgeComment): Promise<{
|
|
29
|
+
action: "created" | "updated" | "unchanged";
|
|
30
|
+
comment: ForgeComment;
|
|
31
|
+
}>;
|
|
32
|
+
export {};
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* One comment per environment on the pull request: found by its marker and
|
|
3
|
+
* updated, else created. The three forges' REST APIs:
|
|
4
|
+
*
|
|
5
|
+
* GitHub GET/POST {api}/repos/{owner}/{repo}/issues/{n}/comments
|
|
6
|
+
* PATCH {api}/repos/{owner}/{repo}/issues/comments/{id}
|
|
7
|
+
* GitLab GET/POST {api}/projects/{id}/merge_requests/{iid}/notes
|
|
8
|
+
* PUT {api}/projects/{id}/merge_requests/{iid}/notes/{note_id}
|
|
9
|
+
* Forgejo GET/POST {api}/repos/{owner}/{repo}/issues/{n}/comments (Gitea's API, /api/v1)
|
|
10
|
+
* PATCH {api}/repos/{owner}/{repo}/issues/comments/{id}
|
|
11
|
+
*/
|
|
12
|
+
import { YodelError } from "../project.js";
|
|
13
|
+
const PAGE = 100;
|
|
14
|
+
const MAX_PAGES = 50;
|
|
15
|
+
function client(t, f, shape) {
|
|
16
|
+
const call = async (method, url, body) => {
|
|
17
|
+
let res;
|
|
18
|
+
try {
|
|
19
|
+
res = await f(url, { method, headers: { ...shape.headers, ...(body ? { "Content-Type": "application/json" } : {}) }, ...(body ? { body: JSON.stringify(body) } : {}) });
|
|
20
|
+
}
|
|
21
|
+
catch (e) {
|
|
22
|
+
throw new YodelError(`${t.forge}: ${method} ${url} failed: ${e instanceof Error ? e.message : String(e)}`);
|
|
23
|
+
}
|
|
24
|
+
const text = await res.text();
|
|
25
|
+
if (!res.ok)
|
|
26
|
+
throw new YodelError(`${t.forge}: ${method} ${url} answered ${String(res.status)}: ${text.slice(0, 300).replaceAll(t.token, "[token]")}`);
|
|
27
|
+
return text ? JSON.parse(text) : {};
|
|
28
|
+
};
|
|
29
|
+
const toComment = (c) => ({ id: Number(c.id), body: typeof c.body === "string" ? c.body : "", url: shape.webUrl?.(c) ?? (typeof c.html_url === "string" ? c.html_url : undefined) });
|
|
30
|
+
return {
|
|
31
|
+
async list() {
|
|
32
|
+
const all = [];
|
|
33
|
+
for (let page = 1; page <= MAX_PAGES; page++) {
|
|
34
|
+
const got = (await call("GET", shape.list(page)));
|
|
35
|
+
if (!Array.isArray(got))
|
|
36
|
+
throw new YodelError(`${t.forge}: listing comments did not return a list`);
|
|
37
|
+
all.push(...got.filter((c) => shape.keep?.(c) ?? true).map(toComment));
|
|
38
|
+
if (got.length < PAGE)
|
|
39
|
+
break;
|
|
40
|
+
}
|
|
41
|
+
return all;
|
|
42
|
+
},
|
|
43
|
+
async create(body) {
|
|
44
|
+
return toComment((await call("POST", shape.create, { body })));
|
|
45
|
+
},
|
|
46
|
+
async update(id, body) {
|
|
47
|
+
return toComment((await call(shape.updateMethod, shape.update(id), { body })));
|
|
48
|
+
},
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
const repoPath = (repo) => repo.split("/").map(encodeURIComponent).join("/");
|
|
52
|
+
export function commentClient(t, f = fetch) {
|
|
53
|
+
if (t.forge === "gitlab") {
|
|
54
|
+
const base = `${t.apiUrl}/projects/${encodeURIComponent(t.repo)}/merge_requests/${String(t.pr)}/notes`;
|
|
55
|
+
return client(t, f, {
|
|
56
|
+
headers: { "PRIVATE-TOKEN": t.token, Accept: "application/json" },
|
|
57
|
+
list: (page) => `${base}?per_page=${String(PAGE)}&page=${String(page)}&sort=asc&order_by=created_at`,
|
|
58
|
+
create: base,
|
|
59
|
+
update: (id) => `${base}/${String(id)}`,
|
|
60
|
+
updateMethod: "PUT",
|
|
61
|
+
// System notes ("added 1 commit") are the forge's own, never ours.
|
|
62
|
+
keep: (c) => c.system !== true,
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
if (t.forge === "forgejo") {
|
|
66
|
+
const base = `${t.apiUrl}/repos/${repoPath(t.repo)}/issues`;
|
|
67
|
+
return client(t, f, {
|
|
68
|
+
headers: { Authorization: `token ${t.token}`, Accept: "application/json" },
|
|
69
|
+
list: (page) => `${base}/${String(t.pr)}/comments?limit=${String(PAGE)}&page=${String(page)}`,
|
|
70
|
+
create: `${base}/${String(t.pr)}/comments`,
|
|
71
|
+
update: (id) => `${base}/comments/${String(id)}`,
|
|
72
|
+
updateMethod: "PATCH",
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
const base = `${t.apiUrl}/repos/${repoPath(t.repo)}/issues`;
|
|
76
|
+
return client(t, f, {
|
|
77
|
+
headers: { Authorization: `Bearer ${t.token}`, Accept: "application/vnd.github+json", "X-GitHub-Api-Version": "2022-11-28" },
|
|
78
|
+
list: (page) => `${base}/${String(t.pr)}/comments?per_page=${String(PAGE)}&page=${String(page)}`,
|
|
79
|
+
create: `${base}/${String(t.pr)}/comments`,
|
|
80
|
+
update: (id) => `${base}/comments/${String(id)}`,
|
|
81
|
+
updateMethod: "PATCH",
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
/** The comment carrying `marker`, the newest when several do. */
|
|
85
|
+
export async function findComment(c, marker) {
|
|
86
|
+
return (await c.list()).filter((x) => x.body.includes(marker)).at(-1);
|
|
87
|
+
}
|
|
88
|
+
/** Updates the comment carrying `marker`, or creates one. */
|
|
89
|
+
export async function upsertComment(c, marker, body, existing) {
|
|
90
|
+
if (!body.includes(marker))
|
|
91
|
+
throw new YodelError("the comment body has no marker; it could never be found again to update");
|
|
92
|
+
const found = existing ?? (await findComment(c, marker));
|
|
93
|
+
if (!found)
|
|
94
|
+
return { action: "created", comment: await c.create(body) };
|
|
95
|
+
if (found.body === body)
|
|
96
|
+
return { action: "unchanged", comment: found };
|
|
97
|
+
return { action: "updated", comment: await c.update(found.id, body) };
|
|
98
|
+
}
|
|
99
|
+
//# sourceMappingURL=comment.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"comment.js","sourceRoot":"","sources":["../../src/pr/comment.ts"],"names":[],"mappings":"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"}
|