@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,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Rule levels from `yodel.config.ts`:
|
|
3
|
+
*
|
|
4
|
+
* ```ts
|
|
5
|
+
* export default defineConfig({
|
|
6
|
+
* lint: { rules: { "ch-mutation": "warning", "pg-lock": "off" } },
|
|
7
|
+
* });
|
|
8
|
+
* ```
|
|
9
|
+
*
|
|
10
|
+
* A level is `error` (fails the run), `warning` (reported, does not fail)
|
|
11
|
+
* or `off`. The integrity rules (`fork`, `directory`, `checksum`,
|
|
12
|
+
* `unreadable-object`) are always errors and cannot be set.
|
|
13
|
+
*/
|
|
14
|
+
import { YodelError } from "../project.js";
|
|
15
|
+
const isRecord = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
|
|
16
|
+
const LEVELS = ["error", "warning", "off"];
|
|
17
|
+
/** Checks `lint` from `yodel.config.ts` against the rules there are. `where` names the file in errors. */
|
|
18
|
+
export function lintSettings(raw, rules, where = "yodel.config.ts") {
|
|
19
|
+
const levels = {};
|
|
20
|
+
if (raw === undefined)
|
|
21
|
+
return { levels };
|
|
22
|
+
for (const k of Object.keys(raw))
|
|
23
|
+
if (k !== "rules")
|
|
24
|
+
throw new YodelError(`${where}: lint.${k}: unknown setting (expected rules)`);
|
|
25
|
+
if (raw.rules === undefined)
|
|
26
|
+
return { levels };
|
|
27
|
+
if (!isRecord(raw.rules))
|
|
28
|
+
throw new YodelError(`${where}: lint.rules: expected an object of rule: "error" | "warning" | "off"`);
|
|
29
|
+
for (const [id, level] of Object.entries(raw.rules)) {
|
|
30
|
+
const rule = rules[id];
|
|
31
|
+
if (!rule)
|
|
32
|
+
throw new YodelError(`${where}: lint.rules.${id}: no such rule (the rules: ${Object.keys(rules).join(", ")})`);
|
|
33
|
+
if (typeof level !== "string" || !LEVELS.includes(level))
|
|
34
|
+
throw new YodelError(`${where}: lint.rules.${id}: expected "error", "warning" or "off"`);
|
|
35
|
+
if (!rule.configurable && level !== "error")
|
|
36
|
+
throw new YodelError(`${where}: lint.rules.${id}: ${id} is an integrity rule and is always an error`);
|
|
37
|
+
levels[id] = level;
|
|
38
|
+
}
|
|
39
|
+
return { levels };
|
|
40
|
+
}
|
|
41
|
+
/** The level a rule runs at. */
|
|
42
|
+
export const levelOf = (rule, settings) => settings?.levels[rule.id] ?? rule.level;
|
|
43
|
+
//# sourceMappingURL=config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/lint/config.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAO3C,MAAM,QAAQ,GAAG,CAAC,CAAU,EAAgC,EAAE,CAAC,OAAO,CAAC,KAAK,QAAQ,IAAI,CAAC,KAAK,IAAI,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;AACxH,MAAM,MAAM,GAAsB,CAAC,OAAO,EAAE,SAAS,EAAE,KAAK,CAAC,CAAC;AAE9D,0GAA0G;AAC1G,MAAM,UAAU,YAAY,CAAC,GAAwC,EAAE,KAAyC,EAAE,KAAK,GAAG,iBAAiB;IACzI,MAAM,MAAM,GAAiC,EAAE,CAAC;IAChD,IAAI,GAAG,KAAK,SAAS;QAAE,OAAO,EAAE,MAAM,EAAE,CAAC;IACzC,KAAK,MAAM,CAAC,IAAI,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC;QAAE,IAAI,CAAC,KAAK,OAAO;YAAE,MAAM,IAAI,UAAU,CAAC,GAAG,KAAK,UAAU,CAAC,oCAAoC,CAAC,CAAC;IACnI,IAAI,GAAG,CAAC,KAAK,KAAK,SAAS;QAAE,OAAO,EAAE,MAAM,EAAE,CAAC;IAC/C,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,KAAK,CAAC;QAAE,MAAM,IAAI,UAAU,CAAC,GAAG,KAAK,uEAAuE,CAAC,CAAC;IAChI,KAAK,MAAM,CAAC,EAAE,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC;QACpD,MAAM,IAAI,GAAG,KAAK,CAAC,EAAE,CAAC,CAAC;QACvB,IAAI,CAAC,IAAI;YAAE,MAAM,IAAI,UAAU,CAAC,GAAG,KAAK,gBAAgB,EAAE,8BAA8B,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QAC1H,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC;YAAE,MAAM,IAAI,UAAU,CAAC,GAAG,KAAK,gBAAgB,EAAE,wCAAwC,CAAC,CAAC;QACnJ,IAAI,CAAC,IAAI,CAAC,YAAY,IAAI,KAAK,KAAK,OAAO;YAAE,MAAM,IAAI,UAAU,CAAC,GAAG,KAAK,gBAAgB,EAAE,KAAK,EAAE,8CAA8C,CAAC,CAAC;QACnJ,MAAM,CAAC,EAAE,CAAC,GAAG,KAAqB,CAAC;IACrC,CAAC;IACD,OAAO,EAAE,MAAM,EAAE,CAAC;AACpB,CAAC;AAED,gCAAgC;AAChC,MAAM,CAAC,MAAM,OAAO,GAAG,CAAC,IAAc,EAAE,QAAkC,EAAgB,EAAE,CAAC,QAAQ,EAAE,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,IAAI,CAAC,KAAK,CAAC"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `yodel lint` (#14): rules over a migrations directory, free, for CI.
|
|
3
|
+
*
|
|
4
|
+
* lintMigrations(dir, options) every finding, with levels and silences applied
|
|
5
|
+
* migrationSilences(m) a migration's `-- yodel:allow` lines, as the history records them
|
|
6
|
+
* updateChecksum(m) writes migration.json's checksum to match its files
|
|
7
|
+
* renderLint / lintJson the output; lintExitCode the exit code
|
|
8
|
+
*
|
|
9
|
+
* The rules are in `./rules.ts`, how a silence is read in `./silences.ts`.
|
|
10
|
+
*/
|
|
11
|
+
import { type Migration } from "../migrations/index.js";
|
|
12
|
+
import { type Silence } from "./silences.js";
|
|
13
|
+
export * from "./check.js";
|
|
14
|
+
export * from "./config.js";
|
|
15
|
+
export * from "./report.js";
|
|
16
|
+
export * from "./rules.js";
|
|
17
|
+
export * from "./silences.js";
|
|
18
|
+
export * from "./statements.js";
|
|
19
|
+
/** The well-formed `-- yodel:allow` lines of a migration, each with the step it is above. */
|
|
20
|
+
export declare function silencesOf(m: Pick<Migration, "files" | "document">): Silence[];
|
|
21
|
+
/** {@link silencesOf} as the history's `silences` field holds it: a JSON array of `{ step, rule, reason }`. */
|
|
22
|
+
export declare const migrationSilences: (m: Pick<Migration, "files" | "document">) => string;
|
|
23
|
+
/**
|
|
24
|
+
* Writes `migration.json` again with its `checksum` matching the files, for
|
|
25
|
+
* a migration edited before it was applied anywhere (a silence added, say).
|
|
26
|
+
* Returns the old and new checksum, or `undefined` when they already match.
|
|
27
|
+
*/
|
|
28
|
+
export declare function updateChecksum(m: Migration): {
|
|
29
|
+
from: string;
|
|
30
|
+
to: string;
|
|
31
|
+
} | undefined;
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `yodel lint` (#14): rules over a migrations directory, free, for CI.
|
|
3
|
+
*
|
|
4
|
+
* lintMigrations(dir, options) every finding, with levels and silences applied
|
|
5
|
+
* migrationSilences(m) a migration's `-- yodel:allow` lines, as the history records them
|
|
6
|
+
* updateChecksum(m) writes migration.json's checksum to match its files
|
|
7
|
+
* renderLint / lintJson the output; lintExitCode the exit code
|
|
8
|
+
*
|
|
9
|
+
* The rules are in `./rules.ts`, how a silence is read in `./silences.ts`.
|
|
10
|
+
*/
|
|
11
|
+
import { renameSync, writeFileSync } from "node:fs";
|
|
12
|
+
import { join } from "node:path";
|
|
13
|
+
import { MIGRATION_JSON, serializeDocument, verifyChecksum } from "../migrations/index.js";
|
|
14
|
+
import { silenceableRules } from "./rules.js";
|
|
15
|
+
import { mapMigrationSql, silencesJson } from "./silences.js";
|
|
16
|
+
export * from "./check.js";
|
|
17
|
+
export * from "./config.js";
|
|
18
|
+
export * from "./report.js";
|
|
19
|
+
export * from "./rules.js";
|
|
20
|
+
export * from "./silences.js";
|
|
21
|
+
export * from "./statements.js";
|
|
22
|
+
/** The well-formed `-- yodel:allow` lines of a migration, each with the step it is above. */
|
|
23
|
+
export function silencesOf(m) {
|
|
24
|
+
return mapMigrationSql(m.files.sql, m.document.steps, new Set(silenceableRules())).silences;
|
|
25
|
+
}
|
|
26
|
+
/** {@link silencesOf} as the history's `silences` field holds it: a JSON array of `{ step, rule, reason }`. */
|
|
27
|
+
export const migrationSilences = (m) => silencesJson(silencesOf(m));
|
|
28
|
+
/**
|
|
29
|
+
* Writes `migration.json` again with its `checksum` matching the files, for
|
|
30
|
+
* a migration edited before it was applied anywhere (a silence added, say).
|
|
31
|
+
* Returns the old and new checksum, or `undefined` when they already match.
|
|
32
|
+
*/
|
|
33
|
+
export function updateChecksum(m) {
|
|
34
|
+
const check = verifyChecksum(m);
|
|
35
|
+
if (check.ok)
|
|
36
|
+
return undefined;
|
|
37
|
+
const path = join(m.dir, MIGRATION_JSON);
|
|
38
|
+
const tmp = join(m.dir, `.${MIGRATION_JSON}.updating`);
|
|
39
|
+
writeFileSync(tmp, serializeDocument({ ...JSON.parse(m.files.json), checksum: check.actual }));
|
|
40
|
+
renameSync(tmp, path);
|
|
41
|
+
return { from: check.recorded, to: check.actual };
|
|
42
|
+
}
|
|
43
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/lint/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AACpD,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,cAAc,EAAE,iBAAiB,EAAE,cAAc,EAAkB,MAAM,wBAAwB,CAAC;AAC3G,OAAO,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAC9C,OAAO,EAAE,eAAe,EAAE,YAAY,EAAgB,MAAM,eAAe,CAAC;AAE5E,cAAc,YAAY,CAAC;AAC3B,cAAc,aAAa,CAAC;AAC5B,cAAc,aAAa,CAAC;AAC5B,cAAc,YAAY,CAAC;AAC3B,cAAc,eAAe,CAAC;AAC9B,cAAc,iBAAiB,CAAC;AAEhC,6FAA6F;AAC7F,MAAM,UAAU,UAAU,CAAC,CAAwC;IACjE,OAAO,eAAe,CAAC,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,CAAC,QAAQ,CAAC,KAAK,EAAE,IAAI,GAAG,CAAS,gBAAgB,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC;AACtG,CAAC;AAED,+GAA+G;AAC/G,MAAM,CAAC,MAAM,iBAAiB,GAAG,CAAC,CAAwC,EAAU,EAAE,CAAC,YAAY,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC;AAEnH;;;;GAIG;AACH,MAAM,UAAU,cAAc,CAAC,CAAY;IACzC,MAAM,KAAK,GAAG,cAAc,CAAC,CAAC,CAAC,CAAC;IAChC,IAAI,KAAK,CAAC,EAAE;QAAE,OAAO,SAAS,CAAC;IAC/B,MAAM,IAAI,GAAG,IAAI,CAAC,CAAC,CAAC,GAAG,EAAE,cAAc,CAAC,CAAC;IACzC,MAAM,GAAG,GAAG,IAAI,CAAC,CAAC,CAAC,GAAG,EAAE,IAAI,cAAc,WAAW,CAAC,CAAC;IACvD,aAAa,CAAC,GAAG,EAAE,iBAAiB,CAAC,EAAE,GAAI,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAA6B,EAAE,QAAQ,EAAE,KAAK,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC;IAC5H,UAAU,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;IACtB,OAAO,EAAE,IAAI,EAAE,KAAK,CAAC,QAAQ,EAAE,EAAE,EAAE,KAAK,CAAC,MAAM,EAAE,CAAC;AACpD,CAAC"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The project-wide rules `yodel lint` runs after its own: rules that need
|
|
3
|
+
* more than the migrations directory, such as a server or the emulator.
|
|
4
|
+
*
|
|
5
|
+
* {@link projectRules} run on every lint. The replay check (#11,
|
|
6
|
+
* `../replay/`) runs only with `yodel lint --replay <env>`, because it needs
|
|
7
|
+
* a throwaway server: the command replays the history first (so a target
|
|
8
|
+
* that is not fresh refuses with the replay's own exit code, 4) and hands
|
|
9
|
+
* the result to {@link replayRule}, which turns a failure into a finding of
|
|
10
|
+
* rule `replay`. Its level comes from `yodel.config.ts` (`lint.rules.replay`)
|
|
11
|
+
* like a built-in rule's.
|
|
12
|
+
*/
|
|
13
|
+
import { type ReplayResult } from "../replay/index.js";
|
|
14
|
+
import type { ProjectRule } from "./check.js";
|
|
15
|
+
import type { LintRule } from "./rules.js";
|
|
16
|
+
export declare const projectRules: readonly ProjectRule[];
|
|
17
|
+
export declare const REPLAY_RULE: LintRule;
|
|
18
|
+
/** The replay's result as a rule: one finding naming the first migration that fails, or none. */
|
|
19
|
+
export declare function replayRule(result: ReplayResult): ProjectRule;
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The project-wide rules `yodel lint` runs after its own: rules that need
|
|
3
|
+
* more than the migrations directory, such as a server or the emulator.
|
|
4
|
+
*
|
|
5
|
+
* {@link projectRules} run on every lint. The replay check (#11,
|
|
6
|
+
* `../replay/`) runs only with `yodel lint --replay <env>`, because it needs
|
|
7
|
+
* a throwaway server: the command replays the history first (so a target
|
|
8
|
+
* that is not fresh refuses with the replay's own exit code, 4) and hands
|
|
9
|
+
* the result to {@link replayRule}, which turns a failure into a finding of
|
|
10
|
+
* rule `replay`. Its level comes from `yodel.config.ts` (`lint.rules.replay`)
|
|
11
|
+
* like a built-in rule's.
|
|
12
|
+
*/
|
|
13
|
+
import { replayFailure } from "../replay/index.js";
|
|
14
|
+
export const projectRules = [];
|
|
15
|
+
export const REPLAY_RULE = {
|
|
16
|
+
id: "replay",
|
|
17
|
+
summary: "with --replay <env>: the history, replayed into a fresh database, does not give each migration's recorded schema",
|
|
18
|
+
level: "error",
|
|
19
|
+
silenceable: false,
|
|
20
|
+
configurable: true,
|
|
21
|
+
};
|
|
22
|
+
/** The replay's result as a rule: one finding naming the first migration that fails, or none. */
|
|
23
|
+
export function replayRule(result) {
|
|
24
|
+
return {
|
|
25
|
+
rule: REPLAY_RULE,
|
|
26
|
+
check: () => {
|
|
27
|
+
const failure = replayFailure(result);
|
|
28
|
+
return Promise.resolve(failure ? [{ rule: REPLAY_RULE.id, migration: failure.migration, message: failure.message }] : []);
|
|
29
|
+
},
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
//# sourceMappingURL=project-rules.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"project-rules.js","sourceRoot":"","sources":["../../src/lint/project-rules.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,EAAE,aAAa,EAAqB,MAAM,oBAAoB,CAAC;AAItE,MAAM,CAAC,MAAM,YAAY,GAA2B,EAAE,CAAC;AAEvD,MAAM,CAAC,MAAM,WAAW,GAAa;IACnC,EAAE,EAAE,QAAQ;IACZ,OAAO,EAAE,kHAAkH;IAC3H,KAAK,EAAE,OAAO;IACd,WAAW,EAAE,KAAK;IAClB,YAAY,EAAE,IAAI;CACnB,CAAC;AAEF,iGAAiG;AACjG,MAAM,UAAU,UAAU,CAAC,MAAoB;IAC7C,OAAO;QACL,IAAI,EAAE,WAAW;QACjB,KAAK,EAAE,GAAG,EAAE;YACV,MAAM,OAAO,GAAG,aAAa,CAAC,MAAM,CAAC,CAAC;YACtC,OAAO,OAAO,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,WAAW,CAAC,EAAE,EAAE,SAAS,EAAE,OAAO,CAAC,SAAS,EAAE,OAAO,EAAE,OAAO,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;QAC5H,CAAC;KACF,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/** `yodel lint`'s output: text for people, JSON for a CI job, and the exit code. */
|
|
2
|
+
import type { LintReport } from "./check.js";
|
|
3
|
+
/** Exit code: at least one finding at level error that no silence covers. */
|
|
4
|
+
export declare const LINT_FAILED = 3;
|
|
5
|
+
export declare const lintExitCode: (report: LintReport) => number;
|
|
6
|
+
export declare function renderLint(report: LintReport): string;
|
|
7
|
+
export declare function lintJson(report: LintReport): Record<string, unknown>;
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/** `yodel lint`'s output: text for people, JSON for a CI job, and the exit code. */
|
|
2
|
+
/** Exit code: at least one finding at level error that no silence covers. */
|
|
3
|
+
export const LINT_FAILED = 3;
|
|
4
|
+
export const lintExitCode = (report) => (report.errors > 0 ? LINT_FAILED : 0);
|
|
5
|
+
const plural = (n, one, many = `${one}s`) => `${String(n)} ${n === 1 ? one : many}`;
|
|
6
|
+
function line(f) {
|
|
7
|
+
const head = f.silenced !== undefined ? "silenced" : f.level;
|
|
8
|
+
const out = [` ${head.padEnd(8)} ${f.rule}: ${f.message}`];
|
|
9
|
+
if (f.silenced !== undefined)
|
|
10
|
+
out.push(` reason: ${f.silenced}`);
|
|
11
|
+
return out;
|
|
12
|
+
}
|
|
13
|
+
export function renderLint(report) {
|
|
14
|
+
const where = report.environment ? `, against ${report.environment}'s history` : "";
|
|
15
|
+
const out = [];
|
|
16
|
+
const groups = new Map();
|
|
17
|
+
for (const f of report.findings) {
|
|
18
|
+
const key = f.migration ?? "";
|
|
19
|
+
groups.set(key, [...(groups.get(key) ?? []), f]);
|
|
20
|
+
}
|
|
21
|
+
for (const [id, list] of groups) {
|
|
22
|
+
out.push(id === "" ? "migrations/" : id);
|
|
23
|
+
for (const f of list)
|
|
24
|
+
out.push(...line(f));
|
|
25
|
+
out.push("");
|
|
26
|
+
}
|
|
27
|
+
const silenceHint = report.findings.some((f) => f.silenced === undefined && f.level === "error" && ["destructive", "pg-rewrite", "pg-lock", "ch-mutation", "ch-rebuild"].includes(f.rule));
|
|
28
|
+
if (silenceHint) {
|
|
29
|
+
out.push("A statement rule can be silenced for one statement, with a reason the history records when it applies:", " a line `-- yodel:allow <rule> <reason>` above the statement in migration.sql", "");
|
|
30
|
+
}
|
|
31
|
+
const counts = [plural(report.errors, "error"), plural(report.warnings, "warning"), `${String(report.silenced)} silenced`].join(", ");
|
|
32
|
+
out.push(`${plural(report.migrations.length, "migration")}${where}: ${counts}.`);
|
|
33
|
+
return `${out.join("\n")}\n`;
|
|
34
|
+
}
|
|
35
|
+
export function lintJson(report) {
|
|
36
|
+
return {
|
|
37
|
+
status: report.errors > 0 ? "failed" : "passed",
|
|
38
|
+
...(report.environment ? { environment: report.environment } : {}),
|
|
39
|
+
dialect: report.dialect ?? null,
|
|
40
|
+
migrations: report.migrations,
|
|
41
|
+
errors: report.errors,
|
|
42
|
+
warnings: report.warnings,
|
|
43
|
+
silenced: report.silenced,
|
|
44
|
+
findings: report.findings,
|
|
45
|
+
silences: report.silences,
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
//# sourceMappingURL=report.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"report.js","sourceRoot":"","sources":["../../src/lint/report.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `yodel lint`'s rules: what each checks, its level unless `yodel.config.ts`
|
|
3
|
+
* sets another, and whether a statement can silence it.
|
|
4
|
+
*
|
|
5
|
+
* Two kinds:
|
|
6
|
+
*
|
|
7
|
+
* - Integrity rules, over the directory and each migration as a whole:
|
|
8
|
+
* `fork`, `directory`, `checksum`, `unreadable-object`. Always errors; no
|
|
9
|
+
* config lowers them and nothing silences them, because a migration that
|
|
10
|
+
* breaks one cannot be applied safely or cannot be checked at all.
|
|
11
|
+
* - Statement rules, from the classes chant's classifier gives each step:
|
|
12
|
+
* `destructive`, `pg-rewrite`, `pg-lock`, `ch-mutation`, `ch-rebuild`.
|
|
13
|
+
* Errors by default; a line `-- yodel:allow <rule> <reason>` above the
|
|
14
|
+
* statement in `migration.sql` silences one for that statement, and the
|
|
15
|
+
* reason is recorded in the history when the migration applies.
|
|
16
|
+
*
|
|
17
|
+
* The rest (`refused-step`, `migration-sql`, `silence`) say a migration
|
|
18
|
+
* will not apply as written or a silence is wrong. Their level can be
|
|
19
|
+
* changed in `yodel.config.ts`, but they cannot be silenced.
|
|
20
|
+
*/
|
|
21
|
+
export type LintLevel = "error" | "warning";
|
|
22
|
+
export type LevelSetting = LintLevel | "off";
|
|
23
|
+
export interface LintRule {
|
|
24
|
+
id: string;
|
|
25
|
+
/** One line, for `yodel lint --rules` and the docs. */
|
|
26
|
+
summary: string;
|
|
27
|
+
level: LintLevel;
|
|
28
|
+
/** A `-- yodel:allow <id> <reason>` line can silence it for one statement. */
|
|
29
|
+
silenceable: boolean;
|
|
30
|
+
/** `yodel.config.ts` `lint.rules` may change its level. */
|
|
31
|
+
configurable: boolean;
|
|
32
|
+
/** The dialect it applies to; both when absent. */
|
|
33
|
+
dialect?: "clickhouse" | "postgres";
|
|
34
|
+
}
|
|
35
|
+
export declare const LINT_RULES: {
|
|
36
|
+
readonly fork: LintRule;
|
|
37
|
+
readonly directory: LintRule;
|
|
38
|
+
readonly checksum: LintRule;
|
|
39
|
+
readonly "unreadable-object": LintRule;
|
|
40
|
+
readonly destructive: LintRule;
|
|
41
|
+
readonly "pg-rewrite": LintRule;
|
|
42
|
+
readonly "pg-lock": LintRule;
|
|
43
|
+
readonly "ch-mutation": LintRule;
|
|
44
|
+
readonly "ch-rebuild": LintRule;
|
|
45
|
+
readonly "refused-step": LintRule;
|
|
46
|
+
readonly "migration-sql": LintRule;
|
|
47
|
+
readonly silence: LintRule;
|
|
48
|
+
};
|
|
49
|
+
export type LintRuleId = keyof typeof LINT_RULES;
|
|
50
|
+
export declare const ruleIds: LintRuleId[];
|
|
51
|
+
export declare const isRuleId: (id: string) => id is LintRuleId;
|
|
52
|
+
/** The rules a `-- yodel:allow` line can name. */
|
|
53
|
+
export declare const silenceableRules: () => LintRuleId[];
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `yodel lint`'s rules: what each checks, its level unless `yodel.config.ts`
|
|
3
|
+
* sets another, and whether a statement can silence it.
|
|
4
|
+
*
|
|
5
|
+
* Two kinds:
|
|
6
|
+
*
|
|
7
|
+
* - Integrity rules, over the directory and each migration as a whole:
|
|
8
|
+
* `fork`, `directory`, `checksum`, `unreadable-object`. Always errors; no
|
|
9
|
+
* config lowers them and nothing silences them, because a migration that
|
|
10
|
+
* breaks one cannot be applied safely or cannot be checked at all.
|
|
11
|
+
* - Statement rules, from the classes chant's classifier gives each step:
|
|
12
|
+
* `destructive`, `pg-rewrite`, `pg-lock`, `ch-mutation`, `ch-rebuild`.
|
|
13
|
+
* Errors by default; a line `-- yodel:allow <rule> <reason>` above the
|
|
14
|
+
* statement in `migration.sql` silences one for that statement, and the
|
|
15
|
+
* reason is recorded in the history when the migration applies.
|
|
16
|
+
*
|
|
17
|
+
* The rest (`refused-step`, `migration-sql`, `silence`) say a migration
|
|
18
|
+
* will not apply as written or a silence is wrong. Their level can be
|
|
19
|
+
* changed in `yodel.config.ts`, but they cannot be silenced.
|
|
20
|
+
*/
|
|
21
|
+
const integrity = (id, summary) => ({ id, summary, level: "error", silenceable: false, configurable: false });
|
|
22
|
+
const statement = (id, summary, dialect) => ({ id, summary, level: "error", silenceable: true, configurable: true, ...(dialect ? { dialect } : {}) });
|
|
23
|
+
const shape = (id, summary, level = "error") => ({ id, summary, level, silenceable: false, configurable: true });
|
|
24
|
+
export const LINT_RULES = {
|
|
25
|
+
fork: integrity("fork", "two migrations follow the same parent; rebase one onto the other (yodel rebase)"),
|
|
26
|
+
directory: integrity("directory", "the migrations directory is not one readable chain: a bad name or file, a missing parent, a cycle, another dialect"),
|
|
27
|
+
checksum: integrity("checksum", "a migration's files no longer hash to its checksum: an edit after it was written, or (with --env) after it was applied"),
|
|
28
|
+
"unreadable-object": integrity("unreadable-object", "an object in a recorded schema, or a step, the sql lexicon cannot read or classify; never skipped"),
|
|
29
|
+
destructive: statement("destructive", "a statement or Op step removes data that cannot be recovered (a table or column drop)"),
|
|
30
|
+
"pg-rewrite": statement("pg-rewrite", "Postgres: rewrites or scans the whole table under ACCESS EXCLUSIVE, blocking reads and writes while it runs", "postgres"),
|
|
31
|
+
"pg-lock": statement("pg-lock", "Postgres: holds a lock that blocks writes for a whole scan or index build (an index without CONCURRENTLY, a constraint validated in place)", "postgres"),
|
|
32
|
+
"ch-mutation": statement("ch-mutation", "ClickHouse: starts a mutation that rewrites existing parts in the background, with no rollback", "clickhouse"),
|
|
33
|
+
"ch-rebuild": statement("ch-rebuild", "ClickHouse: a rebuild, every row copied into a new table and swapped in (ClickHouseRebuildOp)", "clickhouse"),
|
|
34
|
+
"refused-step": shape("refused-step", "a step yodel apply refuses to run: a manual step, or an Op step it does not run"),
|
|
35
|
+
"migration-sql": shape("migration-sql", "migration.sql does not hold a statement as migration.json records it; apply runs migration.json's"),
|
|
36
|
+
silence: shape("silence", "a yodel:allow line that is malformed, names a rule no statement can silence, has no reason, or silences nothing"),
|
|
37
|
+
};
|
|
38
|
+
export const ruleIds = Object.keys(LINT_RULES);
|
|
39
|
+
export const isRuleId = (id) => Object.hasOwn(LINT_RULES, id);
|
|
40
|
+
/** The rules a `-- yodel:allow` line can name. */
|
|
41
|
+
export const silenceableRules = () => ruleIds.filter((id) => LINT_RULES[id].silenceable);
|
|
42
|
+
//# sourceMappingURL=rules.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rules.js","sourceRoot":"","sources":["../../src/lint/rules.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAkBH,MAAM,SAAS,GAAG,CAAC,EAAU,EAAE,OAAe,EAAY,EAAE,CAAC,CAAC,EAAE,EAAE,EAAE,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,WAAW,EAAE,KAAK,EAAE,YAAY,EAAE,KAAK,EAAE,CAAC,CAAC;AACxI,MAAM,SAAS,GAAG,CAAC,EAAU,EAAE,OAAe,EAAE,OAA6B,EAAY,EAAE,CAAC,CAAC,EAAE,EAAE,EAAE,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,WAAW,EAAE,IAAI,EAAE,YAAY,EAAE,IAAI,EAAE,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC;AACtM,MAAM,KAAK,GAAG,CAAC,EAAU,EAAE,OAAe,EAAE,QAAmB,OAAO,EAAY,EAAE,CAAC,CAAC,EAAE,EAAE,EAAE,OAAO,EAAE,KAAK,EAAE,WAAW,EAAE,KAAK,EAAE,YAAY,EAAE,IAAI,EAAE,CAAC,CAAC;AAEtJ,MAAM,CAAC,MAAM,UAAU,GAAG;IACxB,IAAI,EAAE,SAAS,CAAC,MAAM,EAAE,iFAAiF,CAAC;IAC1G,SAAS,EAAE,SAAS,CAAC,WAAW,EAAE,oHAAoH,CAAC;IACvJ,QAAQ,EAAE,SAAS,CAAC,UAAU,EAAE,wHAAwH,CAAC;IACzJ,mBAAmB,EAAE,SAAS,CAAC,mBAAmB,EAAE,mGAAmG,CAAC;IACxJ,WAAW,EAAE,SAAS,CAAC,aAAa,EAAE,uFAAuF,CAAC;IAC9H,YAAY,EAAE,SAAS,CAAC,YAAY,EAAE,6GAA6G,EAAE,UAAU,CAAC;IAChK,SAAS,EAAE,SAAS,CAAC,SAAS,EAAE,4IAA4I,EAAE,UAAU,CAAC;IACzL,aAAa,EAAE,SAAS,CAAC,aAAa,EAAE,gGAAgG,EAAE,YAAY,CAAC;IACvJ,YAAY,EAAE,SAAS,CAAC,YAAY,EAAE,+FAA+F,EAAE,YAAY,CAAC;IACpJ,cAAc,EAAE,KAAK,CAAC,cAAc,EAAE,iFAAiF,CAAC;IACxH,eAAe,EAAE,KAAK,CAAC,eAAe,EAAE,mGAAmG,CAAC;IAC5I,OAAO,EAAE,KAAK,CAAC,SAAS,EAAE,iHAAiH,CAAC;CACjG,CAAC;AAI9C,MAAM,CAAC,MAAM,OAAO,GAAG,MAAM,CAAC,IAAI,CAAC,UAAU,CAAiB,CAAC;AAE/D,MAAM,CAAC,MAAM,QAAQ,GAAG,CAAC,EAAU,EAAoB,EAAE,CAAC,MAAM,CAAC,MAAM,CAAC,UAAU,EAAE,EAAE,CAAC,CAAC;AAExF,kDAAkD;AAClD,MAAM,CAAC,MAAM,gBAAgB,GAAG,GAAiB,EAAE,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,UAAU,CAAC,EAAE,CAAC,CAAC,WAAW,CAAC,CAAC"}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Silences: a line in `migration.sql`, above the statement it is for,
|
|
3
|
+
*
|
|
4
|
+
* -- yodel:allow <rule> <reason>
|
|
5
|
+
*
|
|
6
|
+
* silences that statement rule for that one step. The reason is required
|
|
7
|
+
* and is recorded, with the rule and the step, in every history row the
|
|
8
|
+
* migration writes when it applies (the `silences` column), so the history
|
|
9
|
+
* says who accepted what and why. Because `migration.sql` is in the
|
|
10
|
+
* migration's checksum, a silence is part of what an approval covers.
|
|
11
|
+
*
|
|
12
|
+
* Which step a line is "above": `migration.sql` holds each step in the
|
|
13
|
+
* order `migration.json` lists them, a statement as its SQL followed by
|
|
14
|
+
* `;`, an Op or manual step as a comment starting `-- <object> (<name>):`
|
|
15
|
+
* (`renderStatements`). Each step is found in that order; a silence belongs
|
|
16
|
+
* to the first step found after it. A statement `migration.sql` does not
|
|
17
|
+
* hold as `migration.json` records it is reported (`migration-sql`): apply
|
|
18
|
+
* runs `migration.json`'s, so an edit to the SQL alone changes nothing.
|
|
19
|
+
*/
|
|
20
|
+
import type { MigrationStep } from "@intentius/chant-lexicon-sql/migration-statements";
|
|
21
|
+
/** One `-- yodel:allow` line, read. */
|
|
22
|
+
export interface Silence {
|
|
23
|
+
/** The step it is above, an index into `migration.json`'s `steps`. */
|
|
24
|
+
step: number;
|
|
25
|
+
rule: string;
|
|
26
|
+
reason: string;
|
|
27
|
+
/** Its line in migration.sql, from 1. */
|
|
28
|
+
line: number;
|
|
29
|
+
}
|
|
30
|
+
/** What was wrong with a migration.sql. */
|
|
31
|
+
export interface SqlProblem {
|
|
32
|
+
rule: "migration-sql" | "silence";
|
|
33
|
+
message: string;
|
|
34
|
+
step?: number;
|
|
35
|
+
line?: number;
|
|
36
|
+
}
|
|
37
|
+
export interface SqlMap {
|
|
38
|
+
silences: Silence[];
|
|
39
|
+
problems: SqlProblem[];
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Reads the silences in `sql` and assigns each to its step. `known` is the
|
|
43
|
+
* set of rule ids a silence may name. Problems: a silence that is
|
|
44
|
+
* malformed, names an unknown rule or one that cannot be silenced, has no
|
|
45
|
+
* reason, or follows the last step; a step migration.sql does not hold.
|
|
46
|
+
*/
|
|
47
|
+
export declare function mapMigrationSql(sql: string, steps: readonly MigrationStep[], silenceable: ReadonlySet<string>): SqlMap;
|
|
48
|
+
/** The silences as the history's `silences` field holds them: JSON, `[{"step":0,"rule":"...","reason":"..."}]`. */
|
|
49
|
+
export declare const silencesJson: (silences: readonly Silence[]) => string;
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Silences: a line in `migration.sql`, above the statement it is for,
|
|
3
|
+
*
|
|
4
|
+
* -- yodel:allow <rule> <reason>
|
|
5
|
+
*
|
|
6
|
+
* silences that statement rule for that one step. The reason is required
|
|
7
|
+
* and is recorded, with the rule and the step, in every history row the
|
|
8
|
+
* migration writes when it applies (the `silences` column), so the history
|
|
9
|
+
* says who accepted what and why. Because `migration.sql` is in the
|
|
10
|
+
* migration's checksum, a silence is part of what an approval covers.
|
|
11
|
+
*
|
|
12
|
+
* Which step a line is "above": `migration.sql` holds each step in the
|
|
13
|
+
* order `migration.json` lists them, a statement as its SQL followed by
|
|
14
|
+
* `;`, an Op or manual step as a comment starting `-- <object> (<name>):`
|
|
15
|
+
* (`renderStatements`). Each step is found in that order; a silence belongs
|
|
16
|
+
* to the first step found after it. A statement `migration.sql` does not
|
|
17
|
+
* hold as `migration.json` records it is reported (`migration-sql`): apply
|
|
18
|
+
* runs `migration.json`'s, so an edit to the SQL alone changes nothing.
|
|
19
|
+
*/
|
|
20
|
+
import { normalizeLineEndings } from "../migrations/document.js";
|
|
21
|
+
const DIRECTIVE = /yodel:\s*allow\b/i;
|
|
22
|
+
const SILENCE = /^\s*--\s*yodel:allow(?:\s+(\S+))?(?:\s+(.*?))?\s*$/;
|
|
23
|
+
/** The text that marks a step in migration.sql. */
|
|
24
|
+
function anchorOf(step) {
|
|
25
|
+
return step.kind === "statement" ? `${step.sql};` : `-- ${step.object} (${step.name}):`;
|
|
26
|
+
}
|
|
27
|
+
const describe = (step, i) => `step ${String(i)} (${step.kind === "statement" ? `${step.rule} ${step.class}` : step.kind}, ${step.name})`;
|
|
28
|
+
/**
|
|
29
|
+
* Reads the silences in `sql` and assigns each to its step. `known` is the
|
|
30
|
+
* set of rule ids a silence may name. Problems: a silence that is
|
|
31
|
+
* malformed, names an unknown rule or one that cannot be silenced, has no
|
|
32
|
+
* reason, or follows the last step; a step migration.sql does not hold.
|
|
33
|
+
*/
|
|
34
|
+
export function mapMigrationSql(sql, steps, silenceable) {
|
|
35
|
+
const text = normalizeLineEndings(sql);
|
|
36
|
+
const lineAt = (offset) => text.slice(0, offset).split("\n").length;
|
|
37
|
+
const out = { silences: [], problems: [] };
|
|
38
|
+
const readRegion = (from, to, step) => {
|
|
39
|
+
const region = text.slice(from, to);
|
|
40
|
+
let offset = from;
|
|
41
|
+
for (const raw of region.split("\n")) {
|
|
42
|
+
const line = lineAt(offset);
|
|
43
|
+
offset += raw.length + 1;
|
|
44
|
+
if (!DIRECTIVE.test(raw) || !raw.trimStart().startsWith("--"))
|
|
45
|
+
continue;
|
|
46
|
+
const m = SILENCE.exec(raw);
|
|
47
|
+
const rule = m?.[1];
|
|
48
|
+
const reason = m?.[2]?.trim() ?? "";
|
|
49
|
+
if (!m || rule === undefined) {
|
|
50
|
+
out.problems.push({ rule: "silence", line, message: `migration.sql line ${String(line)}: expected -- yodel:allow <rule> <reason>` });
|
|
51
|
+
continue;
|
|
52
|
+
}
|
|
53
|
+
if (!silenceable.has(rule)) {
|
|
54
|
+
out.problems.push({ rule: "silence", line, message: `migration.sql line ${String(line)}: ${rule} is not a rule a statement can silence (${[...silenceable].join(", ")})` });
|
|
55
|
+
continue;
|
|
56
|
+
}
|
|
57
|
+
if (reason === "") {
|
|
58
|
+
out.problems.push({ rule: "silence", line, message: `migration.sql line ${String(line)}: silencing ${rule} needs a reason: -- yodel:allow ${rule} <why this is safe>` });
|
|
59
|
+
continue;
|
|
60
|
+
}
|
|
61
|
+
if (step === undefined) {
|
|
62
|
+
out.problems.push({ rule: "silence", line, message: `migration.sql line ${String(line)}: yodel:allow ${rule} comes after the last step, so it silences nothing; put it above the statement` });
|
|
63
|
+
continue;
|
|
64
|
+
}
|
|
65
|
+
out.silences.push({ step, rule, reason, line });
|
|
66
|
+
}
|
|
67
|
+
};
|
|
68
|
+
let cursor = 0;
|
|
69
|
+
steps.forEach((step, i) => {
|
|
70
|
+
const anchor = anchorOf(step);
|
|
71
|
+
const at = text.indexOf(anchor, cursor);
|
|
72
|
+
if (at === -1) {
|
|
73
|
+
if (step.kind === "statement") {
|
|
74
|
+
out.problems.push({
|
|
75
|
+
rule: "migration-sql",
|
|
76
|
+
step: i,
|
|
77
|
+
message: `${describe(step, i)}: migration.sql does not hold its statement as migration.json records it (${step.sql.split("\n")[0] ?? ""}); yodel apply runs migration.json's`,
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
// An Op or manual step's comment is for people; a missing one moves nothing.
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
readRegion(cursor, at, i);
|
|
84
|
+
cursor = at + anchor.length;
|
|
85
|
+
});
|
|
86
|
+
readRegion(cursor, text.length, undefined);
|
|
87
|
+
return out;
|
|
88
|
+
}
|
|
89
|
+
/** The silences as the history's `silences` field holds them: JSON, `[{"step":0,"rule":"...","reason":"..."}]`. */
|
|
90
|
+
export const silencesJson = (silences) => JSON.stringify(silences.map((s) => ({ step: s.step, rule: s.rule, reason: s.reason })));
|
|
91
|
+
//# sourceMappingURL=silences.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"silences.js","sourceRoot":"","sources":["../../src/lint/silences.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The statement rules: each step of a migration against the class chant's
|
|
3
|
+
* classifier gave it (`migration.json` records the rule and class of every
|
|
4
|
+
* statement, and the changes an Op or manual step makes). Pure.
|
|
5
|
+
*
|
|
6
|
+
* - `destructive`: a statement marked destructive (a table or column drop),
|
|
7
|
+
* or an Op step one of whose changes is.
|
|
8
|
+
* - `ch-mutation`: a ClickHouse statement of class `rewrite`, or one that
|
|
9
|
+
* waits for its mutation (a column type or TTL change).
|
|
10
|
+
* - `ch-rebuild`: a `ClickHouseRebuildOp` step.
|
|
11
|
+
* - `pg-rewrite`: a Postgres statement of class `rewrite` (ACCESS EXCLUSIVE
|
|
12
|
+
* for a rewrite or a full scan).
|
|
13
|
+
* - `pg-lock`: a Postgres statement that blocks writes for a whole build or
|
|
14
|
+
* scan: an index created or dropped on an existing table without
|
|
15
|
+
* `CONCURRENTLY` (SQLPG240 to SQLPG243), a primary key or unique
|
|
16
|
+
* constraint built in place rather than `USING INDEX` (SQLPG221), a
|
|
17
|
+
* foreign key or domain constraint validated in place rather than added
|
|
18
|
+
* `NOT VALID` (SQLPG219, SQLPG262).
|
|
19
|
+
*
|
|
20
|
+
* `COMMENT ON` statements are skipped: chant writes one after a `CREATE`
|
|
21
|
+
* with the same rule, and a comment is catalog only.
|
|
22
|
+
*
|
|
23
|
+
* A statement whose rule is not one of the lexicon's, or a step of a kind
|
|
24
|
+
* it does not know, is `unreadable-object`: lint cannot tell what it does,
|
|
25
|
+
* and never passes what it cannot read.
|
|
26
|
+
*/
|
|
27
|
+
import type { MigrationStep } from "@intentius/chant-lexicon-sql/migration-statements";
|
|
28
|
+
export type Dialect = "clickhouse" | "postgres";
|
|
29
|
+
export interface StepFinding {
|
|
30
|
+
rule: string;
|
|
31
|
+
message: string;
|
|
32
|
+
}
|
|
33
|
+
/** What lint cannot read in a step, or `undefined`. */
|
|
34
|
+
export declare function unreadableStep(dialect: Dialect, step: MigrationStep): string | undefined;
|
|
35
|
+
/** The statement-rule findings for one step, before levels and silences. */
|
|
36
|
+
export declare function stepFindings(dialect: Dialect, step: MigrationStep): StepFinding[];
|
|
37
|
+
/** Why yodel apply refuses a step, or `undefined` for one it runs. */
|
|
38
|
+
export declare function refusedStep(step: MigrationStep): string | undefined;
|