@intentius/sql-yodeler 0.0.0-stage → 0.1.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 +110 -2
- package/dist/bin.d.ts +2 -0
- package/dist/bin.js +14 -0
- package/dist/bin.js.map +1 -0
- package/dist/chant/apply.d.ts +51 -0
- package/dist/chant/apply.js +104 -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 +44 -0
- package/dist/chant/drift.js +91 -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/cli.d.ts +4 -0
- package/dist/cli.js +59 -0
- package/dist/cli.js.map +1 -0
- package/dist/command.d.ts +17 -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 +306 -0
- package/dist/commands/apply.js.map +1 -0
- package/dist/commands/drift.d.ts +3 -0
- package/dist/commands/drift.js +38 -0
- package/dist/commands/drift.js.map +1 -0
- package/dist/commands/index.d.ts +3 -0
- package/dist/commands/index.js +22 -0
- package/dist/commands/index.js.map +1 -0
- package/dist/commands/init.d.ts +3 -0
- package/dist/commands/init.js +135 -0
- package/dist/commands/init.js.map +1 -0
- package/dist/commands/lint.d.ts +3 -0
- package/dist/commands/lint.js +193 -0
- package/dist/commands/lint.js.map +1 -0
- package/dist/commands/new.d.ts +3 -0
- package/dist/commands/new.js +168 -0
- package/dist/commands/new.js.map +1 -0
- package/dist/commands/plan.d.ts +3 -0
- package/dist/commands/plan.js +171 -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 +3 -0
- package/dist/commands/repair.js +117 -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 +50 -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 +102 -0
- package/dist/config/load.js.map +1 -0
- package/dist/config/settings.d.ts +40 -0
- package/dist/config/settings.js +88 -0
- package/dist/config/settings.js.map +1 -0
- package/dist/env-args.d.ts +18 -0
- package/dist/env-args.js +62 -0
- package/dist/env-args.js.map +1 -0
- package/dist/history/client.d.ts +17 -0
- package/dist/history/client.js +19 -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 +100 -0
- package/dist/history/lock.js +224 -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 +138 -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 +28 -0
- package/dist/init/live.js +45 -0
- package/dist/init/live.js.map +1 -0
- package/dist/lint/check.d.ts +81 -0
- package/dist/lint/check.js +208 -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/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 +73 -0
- package/dist/migrations/create.js +119 -0
- package/dist/migrations/create.js.map +1 -0
- package/dist/migrations/directory.d.ts +119 -0
- package/dist/migrations/directory.js +225 -0
- package/dist/migrations/directory.js.map +1 -0
- package/dist/migrations/document.d.ts +83 -0
- package/dist/migrations/document.js +82 -0
- package/dist/migrations/document.js.map +1 -0
- package/dist/migrations/index.d.ts +20 -0
- package/dist/migrations/index.js +21 -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 +50 -0
- package/dist/pr/forge.js +105 -0
- package/dist/pr/forge.js.map +1 -0
- package/dist/pr/model.d.ts +91 -0
- package/dist/pr/model.js +113 -0
- package/dist/pr/model.js.map +1 -0
- package/dist/pr/render.d.ts +27 -0
- package/dist/pr/render.js +246 -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 +36 -0
- package/dist/replay/index.js +54 -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/steps/backfill.d.ts +128 -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 +162 -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/clickhouse.d.ts +24 -0
- package/dist/versioned/clickhouse.js +34 -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 +41 -0
- package/dist/versioned/driver.js +13 -0
- package/dist/versioned/driver.js.map +1 -0
- package/dist/versioned/engine.d.ts +152 -0
- package/dist/versioned/engine.js +277 -0
- package/dist/versioned/engine.js.map +1 -0
- package/dist/versioned/explain.d.ts +123 -0
- package/dist/versioned/explain.js +289 -0
- package/dist/versioned/explain.js.map +1 -0
- package/dist/versioned/index.d.ts +37 -0
- package/dist/versioned/index.js +54 -0
- package/dist/versioned/index.js.map +1 -0
- package/dist/versioned/op.d.ts +62 -0
- package/dist/versioned/op.js +153 -0
- package/dist/versioned/op.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 +69 -0
- package/dist/versioned/postgres/driver.js +113 -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/report.d.ts +22 -0
- package/dist/versioned/report.js +81 -0
- package/dist/versioned/report.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 +51 -4
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `yodel init --from <env>`: adopt a live database in one go.
|
|
3
|
+
*
|
|
4
|
+
* 1. Refuse, before anything is written, when the project already declares
|
|
5
|
+
* a schema (unless `force`), already has migrations, or the
|
|
6
|
+
* environment's history already records any; and when the environment
|
|
7
|
+
* names no `databases` (ClickHouse) or `schemas` (Postgres), or names
|
|
8
|
+
* the history's own.
|
|
9
|
+
* 2. Read the live database with chant's import (`chant import --from`'s
|
|
10
|
+
* library functions) and write the declarations into the source
|
|
11
|
+
* directory.
|
|
12
|
+
* 3. Build them and plan the build against the same database. The plan
|
|
13
|
+
* must be empty: the declarations are the database. If it is not, the
|
|
14
|
+
* import does not round-trip, and everything written is taken back.
|
|
15
|
+
* 4. Write the baseline migration: the first migration (parent `null`),
|
|
16
|
+
* whose statements create everything, from an empty schema to the build.
|
|
17
|
+
* 5. Record it applied in the history without running it (`./baseline.ts`),
|
|
18
|
+
* on ClickHouse in `<history database>.history`, on Postgres in
|
|
19
|
+
* `<history schema>.history` (#13), under the apply lock.
|
|
20
|
+
*
|
|
21
|
+
* Any failure in 2 to 5 takes back every file written. The live database
|
|
22
|
+
* is only read; the one write to the server is the history row, and the
|
|
23
|
+
* history database (schema) and table when they were not there.
|
|
24
|
+
*/
|
|
25
|
+
import { join, relative } from "node:path";
|
|
26
|
+
import { loadProject, YodelError } from "../project.js";
|
|
27
|
+
import { buildSchema } from "../chant/build.js";
|
|
28
|
+
import { planEnvironment, renderPlan } from "../chant/plan.js";
|
|
29
|
+
import { createMigration, MIGRATIONS_DIR, writeMigration } from "../migrations/index.js";
|
|
30
|
+
import { environmentSettings } from "../config/settings.js";
|
|
31
|
+
import { closeVersioned, hasMigrations, openVersioned, REFUSED } from "../versioned/index.js";
|
|
32
|
+
import { BASELINE_NAME, checkHistoryEmpty, recordBaseline } from "./baseline.js";
|
|
33
|
+
import { declarationFiles, Journal } from "./files.js";
|
|
34
|
+
import { describeImported, importLive } from "./live.js";
|
|
35
|
+
/** Where the declarations go: `sourceDir` when the config names one, else `src`. */
|
|
36
|
+
export const schemaDir = (project) => join(project.dir, project.config.sourceDir ?? "src");
|
|
37
|
+
/** The refusals that need no server: a schema already declared, migrations already there, a ClickHouse scope that would take in the history. */
|
|
38
|
+
export function preflight(project, env, options = {}) {
|
|
39
|
+
if (hasMigrations(project)) {
|
|
40
|
+
throw new YodelError(`refused: ${join(project.dir, MIGRATIONS_DIR)} already holds migrations; init writes the first one (the baseline), so it adopts a database only in a project with none. Nothing was written.`, REFUSED);
|
|
41
|
+
}
|
|
42
|
+
const dir = schemaDir(project);
|
|
43
|
+
const existing = declarationFiles(dir);
|
|
44
|
+
if (existing.length > 0 && !options.force) {
|
|
45
|
+
throw new YodelError(`refused: ${relative(project.dir, dir) || "."} already declares a schema (${existing.slice(0, 3).join(", ")}${existing.length > 3 ? ", ..." : ""}); ` +
|
|
46
|
+
`init writes the declarations from the live database there. Pass --force to overwrite (files the import does not write are kept, and are built with it). Nothing was written.`, REFUSED);
|
|
47
|
+
}
|
|
48
|
+
const { historyDatabase } = environmentSettings(project, env.name, options.processEnv);
|
|
49
|
+
const [key, what] = project.dialect === "postgres" ? ["schemas", "schema"] : ["databases", "database"];
|
|
50
|
+
const scope = project.dialect === "postgres" ? env.profile.schemas : env.profile.databases;
|
|
51
|
+
if (!scope || scope.length === 0) {
|
|
52
|
+
throw new YodelError(`refused: sql.profiles.${env.name} names no ${key}, so the import would read every ${what} on the server, the history's (${historyDatabase}) included. ` +
|
|
53
|
+
`List the ${key} this project owns in sql.profiles.${env.name}.${key}. Nothing was written.`, REFUSED);
|
|
54
|
+
}
|
|
55
|
+
if (scope.includes(historyDatabase)) {
|
|
56
|
+
throw new YodelError(`refused: sql.profiles.${env.name}.${key} includes ${historyDatabase}, the history's ${what} (yodel.config.ts environments.${env.name}.history.database, else YODEL_HISTORY_DATABASE); the history is yodel's, never part of the declared schema. Nothing was written.`, REFUSED);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
/** Whether a plan has nothing to do: no change to a declared object, and no undeclared object it would drop. */
|
|
60
|
+
const planIsEmpty = (plan) => plan.diff.changes.length === 0 && plan.refused.length === 0;
|
|
61
|
+
/** Adopts `env`'s live database into the project; see the module comment. */
|
|
62
|
+
export async function adopt(project, env, options = {}) {
|
|
63
|
+
const processEnv = options.processEnv ?? process.env;
|
|
64
|
+
const log = options.log ?? (() => undefined);
|
|
65
|
+
preflight(project, env, { ...(options.force !== undefined ? { force: options.force } : {}), processEnv });
|
|
66
|
+
const venv = await openVersioned(project, env, processEnv);
|
|
67
|
+
try {
|
|
68
|
+
await checkHistoryEmpty(venv);
|
|
69
|
+
return await adoptWith(project, env, venv, options, processEnv, log);
|
|
70
|
+
}
|
|
71
|
+
finally {
|
|
72
|
+
await closeVersioned(venv);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
async function adoptWith(project, env, venv, options, processEnv, log) {
|
|
76
|
+
const live = await importLive(project, env, processEnv);
|
|
77
|
+
if (live.ir.resources.length === 0) {
|
|
78
|
+
throw new YodelError(`refused: ${env.name} holds no objects in its scope (sql.profiles.${env.name}); there is nothing to adopt. Declare a schema and run yodel new instead. Nothing was written.`, REFUSED);
|
|
79
|
+
}
|
|
80
|
+
const objects = describeImported(live.ir);
|
|
81
|
+
log(`read ${String(objects.length)} object(s) from ${env.name}`);
|
|
82
|
+
const journal = new Journal();
|
|
83
|
+
try {
|
|
84
|
+
const dir = schemaDir(project);
|
|
85
|
+
journal.mkdir(dir);
|
|
86
|
+
const schemaFiles = [];
|
|
87
|
+
for (const f of live.files) {
|
|
88
|
+
const path = join(dir, f.path);
|
|
89
|
+
journal.write(path, f.content);
|
|
90
|
+
schemaFiles.push(relative(project.dir, path));
|
|
91
|
+
}
|
|
92
|
+
log(`wrote ${schemaFiles.join(", ")}`);
|
|
93
|
+
// The source directory may have been made just now; load the project again so it is built from there.
|
|
94
|
+
const adopted = await loadProject(project.dir);
|
|
95
|
+
const build = await buildSchema(adopted, { env: processEnv });
|
|
96
|
+
let made;
|
|
97
|
+
try {
|
|
98
|
+
const plan = await planEnvironment(adopted, env, build, { env: processEnv });
|
|
99
|
+
if (!planIsEmpty(plan)) {
|
|
100
|
+
throw new YodelError(`the declarations chant's import wrote do not plan clean against ${env.name}, so they are not yet the database; nothing was kept. ` +
|
|
101
|
+
`This is an object chant cannot round-trip; please report it with the plan:\n${await renderPlan(plan, `Plan for ${env.name} after import`)}`);
|
|
102
|
+
}
|
|
103
|
+
log(`planned against ${env.name}: no change`);
|
|
104
|
+
made = createMigration({ name: options.name ?? BASELINE_NAME, build: build.output, ...(options.now ? { now: options.now } : {}) });
|
|
105
|
+
}
|
|
106
|
+
finally {
|
|
107
|
+
build.dispose();
|
|
108
|
+
}
|
|
109
|
+
if (made.id === undefined)
|
|
110
|
+
throw new YodelError("the build declares nothing, so there is no baseline to write");
|
|
111
|
+
const migrations = join(project.dir, MIGRATIONS_DIR);
|
|
112
|
+
journal.mkdir(migrations);
|
|
113
|
+
const path = writeMigration(migrations, made);
|
|
114
|
+
journal.created(path);
|
|
115
|
+
const migration = { id: made.id, dir: path, parent: null, checksum: made.document.checksum, document: made.document, files: made.files };
|
|
116
|
+
const statements = made.statements.steps.length;
|
|
117
|
+
log(`wrote ${relative(project.dir, path)} (${String(statements)} statement${statements === 1 ? "" : "s"})`);
|
|
118
|
+
const base = {
|
|
119
|
+
environment: env.name,
|
|
120
|
+
dialect: project.dialect,
|
|
121
|
+
objects,
|
|
122
|
+
schemaFiles,
|
|
123
|
+
migrationPath: relative(project.dir, path),
|
|
124
|
+
baseline: { id: made.id, checksum: made.document.checksum, statements },
|
|
125
|
+
warnings: live.warnings,
|
|
126
|
+
};
|
|
127
|
+
const { row, lock } = await recordBaseline(venv, migration, processEnv);
|
|
128
|
+
if (lock.notice)
|
|
129
|
+
log(`lock: ${lock.notice}`);
|
|
130
|
+
log(`recorded ${made.id} applied in ${venv.location.database}.history as a baseline; none of its statements was run`);
|
|
131
|
+
return { ...base, recorded: { database: venv.location.database, row, lock } };
|
|
132
|
+
}
|
|
133
|
+
catch (e) {
|
|
134
|
+
journal.undo();
|
|
135
|
+
throw e;
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
//# sourceMappingURL=adopt.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"adopt.js","sourceRoot":"","sources":["../../src/init/adopt.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The baseline: the first migration of an adopted database, holding the
|
|
3
|
+
* schema the database already had, recorded applied without running it.
|
|
4
|
+
*
|
|
5
|
+
* ## How the history records it
|
|
6
|
+
*
|
|
7
|
+
* One migration row, `status = 'succeeded'`, `note` starting `baseline`
|
|
8
|
+
* ({@link BASELINE_NOTE}), the migration's checksum, `parent = ''`, and no
|
|
9
|
+
* statement rows: none of its statements was sent, since the objects they
|
|
10
|
+
* create are the ones it was read from. `plan_digest` is empty, as nothing
|
|
11
|
+
* was approved.
|
|
12
|
+
*
|
|
13
|
+
* The rest of yodel needs nothing new to read it. `deriveState` makes the
|
|
14
|
+
* migration's state its latest migration row, `succeeded`, so `isApplied`
|
|
15
|
+
* holds; `compareHistory` lists it applied, checks its checksum like any
|
|
16
|
+
* other's, and never puts it among the pending, so `yodel apply` never sends
|
|
17
|
+
* its statements; and statement rows are only read for a migration that is
|
|
18
|
+
* part way. A status other than `succeeded` (say `adopted`) would read as
|
|
19
|
+
* part way, and apply would run it, so the status stays `succeeded` and the
|
|
20
|
+
* note is what says it was adopted.
|
|
21
|
+
*/
|
|
22
|
+
import { YodelError } from "../project.js";
|
|
23
|
+
import type { Migration } from "../migrations/index.js";
|
|
24
|
+
import { type HistoryRow } from "../history/index.js";
|
|
25
|
+
import { type VersionedEnv } from "../versioned/index.js";
|
|
26
|
+
/** The baseline migration's name: its id is `<YYYYMMDDTHHMM>-baseline`. */
|
|
27
|
+
export declare const BASELINE_NAME = "baseline";
|
|
28
|
+
/** The start of a baseline row's `note`. */
|
|
29
|
+
export declare const BASELINE_NOTE = "baseline";
|
|
30
|
+
/** The note a baseline row carries. */
|
|
31
|
+
export declare const baselineNote: (environment: string) => string;
|
|
32
|
+
/** Whether a history row records a baseline. */
|
|
33
|
+
export declare const isBaselineRow: (row: Pick<HistoryRow, "kind" | "note">) => boolean;
|
|
34
|
+
/** The one row that records `m` applied as a baseline. */
|
|
35
|
+
export declare function baselineRow(m: Pick<Migration, "id" | "parent" | "checksum">, input: {
|
|
36
|
+
seq: number;
|
|
37
|
+
runId: string;
|
|
38
|
+
appliedBy: string;
|
|
39
|
+
environment: string;
|
|
40
|
+
}): HistoryRow;
|
|
41
|
+
/** Why the history cannot take a baseline, or `undefined`: a baseline is the first thing a history records. */
|
|
42
|
+
export declare function historyRefusal(state: {
|
|
43
|
+
migrations: readonly {
|
|
44
|
+
id: string;
|
|
45
|
+
}[];
|
|
46
|
+
}, location: string): YodelError | undefined;
|
|
47
|
+
/** Reads the history without the lock, for a refusal before anything is written. */
|
|
48
|
+
export declare function checkHistoryEmpty(venv: VersionedEnv): Promise<void>;
|
|
49
|
+
/**
|
|
50
|
+
* Records `m` applied as the baseline, under the apply lock: makes the
|
|
51
|
+
* history when it is not there, checks again that it records nothing, and
|
|
52
|
+
* appends the one row.
|
|
53
|
+
*/
|
|
54
|
+
export declare function recordBaseline(venv: VersionedEnv, m: Migration, processEnv?: NodeJS.ProcessEnv): Promise<{
|
|
55
|
+
row: HistoryRow;
|
|
56
|
+
lock: {
|
|
57
|
+
kind: string;
|
|
58
|
+
where: string;
|
|
59
|
+
notice?: string;
|
|
60
|
+
};
|
|
61
|
+
}>;
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The baseline: the first migration of an adopted database, holding the
|
|
3
|
+
* schema the database already had, recorded applied without running it.
|
|
4
|
+
*
|
|
5
|
+
* ## How the history records it
|
|
6
|
+
*
|
|
7
|
+
* One migration row, `status = 'succeeded'`, `note` starting `baseline`
|
|
8
|
+
* ({@link BASELINE_NOTE}), the migration's checksum, `parent = ''`, and no
|
|
9
|
+
* statement rows: none of its statements was sent, since the objects they
|
|
10
|
+
* create are the ones it was read from. `plan_digest` is empty, as nothing
|
|
11
|
+
* was approved.
|
|
12
|
+
*
|
|
13
|
+
* The rest of yodel needs nothing new to read it. `deriveState` makes the
|
|
14
|
+
* migration's state its latest migration row, `succeeded`, so `isApplied`
|
|
15
|
+
* holds; `compareHistory` lists it applied, checks its checksum like any
|
|
16
|
+
* other's, and never puts it among the pending, so `yodel apply` never sends
|
|
17
|
+
* its statements; and statement rows are only read for a migration that is
|
|
18
|
+
* part way. A status other than `succeeded` (say `adopted`) would read as
|
|
19
|
+
* part way, and apply would run it, so the status stays `succeeded` and the
|
|
20
|
+
* note is what says it was adopted.
|
|
21
|
+
*/
|
|
22
|
+
import { randomUUID } from "node:crypto";
|
|
23
|
+
import { YodelError } from "../project.js";
|
|
24
|
+
import { appliedBy, MIGRATION_ROW, withLock } from "../history/index.js";
|
|
25
|
+
import { REFUSED } from "../versioned/index.js";
|
|
26
|
+
/** The baseline migration's name: its id is `<YYYYMMDDTHHMM>-baseline`. */
|
|
27
|
+
export const BASELINE_NAME = "baseline";
|
|
28
|
+
/** The start of a baseline row's `note`. */
|
|
29
|
+
export const BASELINE_NOTE = "baseline";
|
|
30
|
+
/** The note a baseline row carries. */
|
|
31
|
+
export const baselineNote = (environment) => `${BASELINE_NOTE}: adopted from the live database by yodel init --from ${environment}; its statements were not run`;
|
|
32
|
+
/** Whether a history row records a baseline. */
|
|
33
|
+
export const isBaselineRow = (row) => row.kind === "migration" && (row.note === BASELINE_NOTE || row.note.startsWith(`${BASELINE_NOTE}:`));
|
|
34
|
+
/** The one row that records `m` applied as a baseline. */
|
|
35
|
+
export function baselineRow(m, input) {
|
|
36
|
+
if (m.parent !== null)
|
|
37
|
+
throw new YodelError(`${m.id} follows ${m.parent}; only a first migration can be a baseline`);
|
|
38
|
+
return {
|
|
39
|
+
seq: input.seq,
|
|
40
|
+
run_id: input.runId,
|
|
41
|
+
kind: "migration",
|
|
42
|
+
migration_id: m.id,
|
|
43
|
+
parent: "",
|
|
44
|
+
checksum: m.checksum,
|
|
45
|
+
statement_index: MIGRATION_ROW,
|
|
46
|
+
statement_sha: "",
|
|
47
|
+
status: "succeeded",
|
|
48
|
+
error: "",
|
|
49
|
+
applied_by: input.appliedBy,
|
|
50
|
+
plan_digest: "",
|
|
51
|
+
silences: "[]",
|
|
52
|
+
note: baselineNote(input.environment),
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
/** Why the history cannot take a baseline, or `undefined`: a baseline is the first thing a history records. */
|
|
56
|
+
export function historyRefusal(state, location) {
|
|
57
|
+
if (state.migrations.length === 0)
|
|
58
|
+
return undefined;
|
|
59
|
+
const ids = state.migrations.map((m) => m.id);
|
|
60
|
+
return new YodelError(`refused: the history (${location}) already records ${ids.length === 1 ? "a migration" : `${String(ids.length)} migrations`} (${ids.slice(0, 3).join(", ")}${ids.length > 3 ? ", ..." : ""}); ` +
|
|
61
|
+
`this environment is already under versioned migrations, so there is nothing to adopt. Nothing was written.`, REFUSED);
|
|
62
|
+
}
|
|
63
|
+
/** Reads the history without the lock, for a refusal before anything is written. */
|
|
64
|
+
export async function checkHistoryEmpty(venv) {
|
|
65
|
+
const refusal = historyRefusal(await venv.store.state(), `${venv.location.database}.history`);
|
|
66
|
+
if (refusal)
|
|
67
|
+
throw refusal;
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Records `m` applied as the baseline, under the apply lock: makes the
|
|
71
|
+
* history when it is not there, checks again that it records nothing, and
|
|
72
|
+
* appends the one row.
|
|
73
|
+
*/
|
|
74
|
+
export async function recordBaseline(venv, m, processEnv = process.env) {
|
|
75
|
+
const by = appliedBy(processEnv);
|
|
76
|
+
const lock = await venv.driver.acquireLock(by);
|
|
77
|
+
return withLock(lock, venv.lockTtlMs, async () => {
|
|
78
|
+
await venv.store.ensure();
|
|
79
|
+
const state = await venv.store.state();
|
|
80
|
+
const refusal = historyRefusal(state, `${venv.location.database}.history`);
|
|
81
|
+
if (refusal)
|
|
82
|
+
throw refusal;
|
|
83
|
+
const row = baselineRow(m, { seq: state.maxSeq + 1, runId: randomUUID(), appliedBy: by, environment: venv.environment });
|
|
84
|
+
await venv.store.append([row]);
|
|
85
|
+
return { row, lock: { kind: lock.kind, where: lock.where, ...(lock.notice ? { notice: lock.notice } : {}) } };
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
//# sourceMappingURL=baseline.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"baseline.js","sourceRoot":"","sources":["../../src/init/baseline.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The files `yodel init` writes, and taking them back when a later step
|
|
3
|
+
* fails, so an init that does not finish leaves the project as it found it.
|
|
4
|
+
*/
|
|
5
|
+
/** Whether a file is a declaration chant builds: TypeScript (not a test or a `.d.ts`) or SQL. */
|
|
6
|
+
export declare const isDeclarationFile: (name: string) => boolean;
|
|
7
|
+
/** The declaration files under `dir`, relative to it, sorted. None when `dir` does not exist. */
|
|
8
|
+
export declare function declarationFiles(dir: string): string[];
|
|
9
|
+
/**
|
|
10
|
+
* Records every file and directory written, with what a file held before,
|
|
11
|
+
* so {@link Journal.undo} puts things back.
|
|
12
|
+
*/
|
|
13
|
+
export declare class Journal {
|
|
14
|
+
private readonly steps;
|
|
15
|
+
/** Makes `dir` and any parents missing, remembering the ones it made. */
|
|
16
|
+
mkdir(dir: string): void;
|
|
17
|
+
/** Writes a file, remembering what was there. */
|
|
18
|
+
write(path: string, content: string): void;
|
|
19
|
+
/** Remembers a directory something else made (a migration's), to remove on undo. */
|
|
20
|
+
created(path: string): void;
|
|
21
|
+
/** Puts back everything, newest first. */
|
|
22
|
+
undo(): void;
|
|
23
|
+
}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The files `yodel init` writes, and taking them back when a later step
|
|
3
|
+
* fails, so an init that does not finish leaves the project as it found it.
|
|
4
|
+
*/
|
|
5
|
+
import { existsSync, mkdirSync, readdirSync, readFileSync, rmSync, statSync, writeFileSync } from "node:fs";
|
|
6
|
+
import { dirname, join, relative } from "node:path";
|
|
7
|
+
/** Directories never searched for declarations. */
|
|
8
|
+
const SKIP = new Set(["node_modules", "dist", "coverage"]);
|
|
9
|
+
/** Whether a file is a declaration chant builds: TypeScript (not a test or a `.d.ts`) or SQL. */
|
|
10
|
+
export const isDeclarationFile = (name) => (/\.(ts|mts|sql)$/.test(name) && !/\.d\.ts$/.test(name) && !/\.(test|spec)\.ts$/.test(name));
|
|
11
|
+
/** The declaration files under `dir`, relative to it, sorted. None when `dir` does not exist. */
|
|
12
|
+
export function declarationFiles(dir) {
|
|
13
|
+
if (!existsSync(dir))
|
|
14
|
+
return [];
|
|
15
|
+
const out = [];
|
|
16
|
+
const walk = (at) => {
|
|
17
|
+
for (const e of readdirSync(at, { withFileTypes: true })) {
|
|
18
|
+
if (e.name.startsWith(".") || SKIP.has(e.name))
|
|
19
|
+
continue;
|
|
20
|
+
const path = join(at, e.name);
|
|
21
|
+
if (e.isDirectory())
|
|
22
|
+
walk(path);
|
|
23
|
+
else if (e.isFile() && isDeclarationFile(e.name))
|
|
24
|
+
out.push(relative(dir, path));
|
|
25
|
+
}
|
|
26
|
+
};
|
|
27
|
+
walk(dir);
|
|
28
|
+
return out.sort();
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Records every file and directory written, with what a file held before,
|
|
32
|
+
* so {@link Journal.undo} puts things back.
|
|
33
|
+
*/
|
|
34
|
+
export class Journal {
|
|
35
|
+
steps = [];
|
|
36
|
+
/** Makes `dir` and any parents missing, remembering the ones it made. */
|
|
37
|
+
mkdir(dir) {
|
|
38
|
+
const missing = [];
|
|
39
|
+
for (let d = dir; !existsSync(d); d = dirname(d))
|
|
40
|
+
missing.push(d);
|
|
41
|
+
if (missing.length === 0)
|
|
42
|
+
return;
|
|
43
|
+
mkdirSync(dir, { recursive: true });
|
|
44
|
+
const top = missing[missing.length - 1];
|
|
45
|
+
if (top !== undefined)
|
|
46
|
+
this.steps.push(() => { rmSync(top, { recursive: true, force: true }); });
|
|
47
|
+
}
|
|
48
|
+
/** Writes a file, remembering what was there. */
|
|
49
|
+
write(path, content) {
|
|
50
|
+
this.mkdir(dirname(path));
|
|
51
|
+
const before = existsSync(path) && statSync(path).isFile() ? readFileSync(path, "utf8") : undefined;
|
|
52
|
+
writeFileSync(path, content);
|
|
53
|
+
this.steps.push(() => {
|
|
54
|
+
if (before === undefined)
|
|
55
|
+
rmSync(path, { force: true });
|
|
56
|
+
else
|
|
57
|
+
writeFileSync(path, before);
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
/** Remembers a directory something else made (a migration's), to remove on undo. */
|
|
61
|
+
created(path) {
|
|
62
|
+
this.steps.push(() => { rmSync(path, { recursive: true, force: true }); });
|
|
63
|
+
}
|
|
64
|
+
/** Puts back everything, newest first. */
|
|
65
|
+
undo() {
|
|
66
|
+
for (const step of this.steps.reverse()) {
|
|
67
|
+
try {
|
|
68
|
+
step();
|
|
69
|
+
}
|
|
70
|
+
catch {
|
|
71
|
+
// Best effort: the rest still runs.
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
this.steps.length = 0;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
//# sourceMappingURL=files.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"files.js","sourceRoot":"","sources":["../../src/init/files.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Adoption (#10): `yodel init --from <env>` reads a live database with
|
|
3
|
+
* chant's import, writes its declarations, writes the baseline migration
|
|
4
|
+
* holding them, and records the baseline applied without running it.
|
|
5
|
+
*
|
|
6
|
+
* adopt(project, env, options) the whole of it; takes back what it wrote on a failure
|
|
7
|
+
* preflight(project, env) the refusals that need no server
|
|
8
|
+
* importLive(project, env) chant import --from, in process: the IR and the files
|
|
9
|
+
* baselineRow(migration, input) the history row that records a baseline
|
|
10
|
+
* isBaselineRow(row) whether a row is one
|
|
11
|
+
* recordBaseline(venv, migration) appends it under the apply lock
|
|
12
|
+
*/
|
|
13
|
+
export * from "./adopt.js";
|
|
14
|
+
export * from "./baseline.js";
|
|
15
|
+
export * from "./files.js";
|
|
16
|
+
export * from "./live.js";
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Adoption (#10): `yodel init --from <env>` reads a live database with
|
|
3
|
+
* chant's import, writes its declarations, writes the baseline migration
|
|
4
|
+
* holding them, and records the baseline applied without running it.
|
|
5
|
+
*
|
|
6
|
+
* adopt(project, env, options) the whole of it; takes back what it wrote on a failure
|
|
7
|
+
* preflight(project, env) the refusals that need no server
|
|
8
|
+
* importLive(project, env) chant import --from, in process: the IR and the files
|
|
9
|
+
* baselineRow(migration, input) the history row that records a baseline
|
|
10
|
+
* isBaselineRow(row) whether a row is one
|
|
11
|
+
* recordBaseline(venv, migration) appends it under the apply lock
|
|
12
|
+
*/
|
|
13
|
+
export * from "./adopt.js";
|
|
14
|
+
export * from "./baseline.js";
|
|
15
|
+
export * from "./files.js";
|
|
16
|
+
export * from "./live.js";
|
|
17
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/init/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,cAAc,YAAY,CAAC;AAC3B,cAAc,eAAe,CAAC;AAC9B,cAAc,YAAY,CAAC;AAC3B,cAAc,WAAW,CAAC"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Reading a live database as declarations: the library functions behind
|
|
3
|
+
* `chant import --from <env>`, called in process with the project's own
|
|
4
|
+
* config, so the server read is the one `sql.profiles.<env>` binds and the
|
|
5
|
+
* files written are the ones `chant import --from` writes.
|
|
6
|
+
*
|
|
7
|
+
* chant's import already strips its ownership marker from every comment it
|
|
8
|
+
* reads, so the declarations say what an author would, and an object that
|
|
9
|
+
* carries no marker (one made by hand) reads the same as one chant made.
|
|
10
|
+
*/
|
|
11
|
+
import type { GeneratedFile } from "@intentius/chant/import/generator";
|
|
12
|
+
import type { TemplateIR } from "@intentius/chant/import/parser";
|
|
13
|
+
import { type Environment, type Project } from "../project.js";
|
|
14
|
+
export interface LiveImport {
|
|
15
|
+
/** What the server holds, as chant's import IR: one resource per object. */
|
|
16
|
+
ir: TemplateIR;
|
|
17
|
+
/** The declaration files, paths relative to the source directory (chant's sql generator writes one, `schema.ts`). */
|
|
18
|
+
files: GeneratedFile[];
|
|
19
|
+
/** What the import left out or could not carry, as chant reports it. */
|
|
20
|
+
warnings: string[];
|
|
21
|
+
}
|
|
22
|
+
/** The objects an import read, as `<type> <name>` lines for people. */
|
|
23
|
+
export declare function describeImported(ir: TemplateIR): string[];
|
|
24
|
+
/**
|
|
25
|
+
* Reads the environment's live database and generates its declarations,
|
|
26
|
+
* exactly as `chant import --from <env>` does. Nothing is written.
|
|
27
|
+
*/
|
|
28
|
+
export declare function importLive(project: Project, env: Environment, processEnv?: NodeJS.ProcessEnv): Promise<LiveImport>;
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Reading a live database as declarations: the library functions behind
|
|
3
|
+
* `chant import --from <env>`, called in process with the project's own
|
|
4
|
+
* config, so the server read is the one `sql.profiles.<env>` binds and the
|
|
5
|
+
* files written are the ones `chant import --from` writes.
|
|
6
|
+
*
|
|
7
|
+
* chant's import already strips its ownership marker from every comment it
|
|
8
|
+
* reads, so the declarations say what an author would, and an object that
|
|
9
|
+
* carries no marker (one made by hand) reads the same as one chant made.
|
|
10
|
+
*/
|
|
11
|
+
import { YodelError } from "../project.js";
|
|
12
|
+
/** The objects an import read, as `<type> <name>` lines for people. */
|
|
13
|
+
export function describeImported(ir) {
|
|
14
|
+
return ir.resources.map((r) => {
|
|
15
|
+
const p = r.properties;
|
|
16
|
+
const where = p.database ?? p.schema;
|
|
17
|
+
return `${r.type.replace(/^(ClickHouse|Postgres)::/, "")} ${where ? `${where}.` : ""}${p.name ?? r.logicalId}`;
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Reads the environment's live database and generates its declarations,
|
|
22
|
+
* exactly as `chant import --from <env>` does. Nothing is written.
|
|
23
|
+
*/
|
|
24
|
+
export async function importLive(project, env, processEnv = process.env) {
|
|
25
|
+
const options = { environment: env.name, config: project.config, cwd: project.dir, env: processEnv };
|
|
26
|
+
let exported;
|
|
27
|
+
try {
|
|
28
|
+
if (project.dialect === "postgres") {
|
|
29
|
+
const { exportResources } = await import("@intentius/chant-lexicon-sql/postgres/import/live-export");
|
|
30
|
+
exported = await exportResources(options);
|
|
31
|
+
}
|
|
32
|
+
else {
|
|
33
|
+
const { exportResources } = await import("@intentius/chant-lexicon-sql/clickhouse/import/live-export");
|
|
34
|
+
exported = await exportResources(options);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
catch (e) {
|
|
38
|
+
throw new YodelError(`reading ${env.name}'s live schema failed: ${e instanceof Error ? e.message : String(e)}`);
|
|
39
|
+
}
|
|
40
|
+
const ir = { resources: exported.resources, parameters: exported.parameters ?? [] };
|
|
41
|
+
const { sqlTemplateGenerator } = await import("@intentius/chant-lexicon-sql/import-generator");
|
|
42
|
+
const files = ir.resources.length === 0 ? [] : sqlTemplateGenerator.generate(ir);
|
|
43
|
+
return { ir, files, warnings: exported.warnings ?? [] };
|
|
44
|
+
}
|
|
45
|
+
//# sourceMappingURL=live.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"live.js","sourceRoot":"","sources":["../../src/init/live.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAIH,OAAO,EAAE,UAAU,EAAkC,MAAM,eAAe,CAAC;AAW3E,uEAAuE;AACvE,MAAM,UAAU,gBAAgB,CAAC,EAAc;IAC7C,OAAO,EAAE,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE;QAC5B,MAAM,CAAC,GAAG,CAAC,CAAC,UAAmE,CAAC;QAChF,MAAM,KAAK,GAAG,CAAC,CAAC,QAAQ,IAAI,CAAC,CAAC,MAAM,CAAC;QACrC,OAAO,GAAG,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,0BAA0B,EAAE,EAAE,CAAC,IAAI,KAAK,CAAC,CAAC,CAAC,GAAG,KAAK,GAAG,CAAC,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC,SAAS,EAAE,CAAC;IACjH,CAAC,CAAC,CAAC;AACL,CAAC;AAED;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,UAAU,CAAC,OAAgB,EAAE,GAAgB,EAAE,aAAgC,OAAO,CAAC,GAAG;IAC9G,MAAM,OAAO,GAAG,EAAE,WAAW,EAAE,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,MAAe,EAAE,GAAG,EAAE,OAAO,CAAC,GAAG,EAAE,GAAG,EAAE,UAAU,EAAE,CAAC;IAC9G,IAAI,QAA4G,CAAC;IACjH,IAAI,CAAC;QACH,IAAI,OAAO,CAAC,OAAO,KAAK,UAAU,EAAE,CAAC;YACnC,MAAM,EAAE,eAAe,EAAE,GAAG,MAAM,MAAM,CAAC,0DAA0D,CAAC,CAAC;YACrG,QAAQ,GAAG,MAAM,eAAe,CAAC,OAAO,CAAC,CAAC;QAC5C,CAAC;aAAM,CAAC;YACN,MAAM,EAAE,eAAe,EAAE,GAAG,MAAM,MAAM,CAAC,4DAA4D,CAAC,CAAC;YACvG,QAAQ,GAAG,MAAM,eAAe,CAAC,OAAO,CAAC,CAAC;QAC5C,CAAC;IACH,CAAC;IAAC,OAAO,CAAC,EAAE,CAAC;QACX,MAAM,IAAI,UAAU,CAAC,WAAW,GAAG,CAAC,IAAI,0BAA0B,CAAC,YAAY,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;IAClH,CAAC;IACD,MAAM,EAAE,GAAe,EAAE,SAAS,EAAE,QAAQ,CAAC,SAAS,EAAE,UAAU,EAAE,QAAQ,CAAC,UAAU,IAAI,EAAE,EAAE,CAAC;IAChG,MAAM,EAAE,oBAAoB,EAAE,GAAG,MAAM,MAAM,CAAC,+CAA+C,CAAC,CAAC;IAC/F,MAAM,KAAK,GAAG,EAAE,CAAC,SAAS,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,oBAAoB,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC;IACjF,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,QAAQ,EAAE,QAAQ,CAAC,QAAQ,IAAI,EAAE,EAAE,CAAC;AAC1D,CAAC"}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `yodel lint` over a migrations directory: every rule in `./rules.ts`,
|
|
3
|
+
* offline, with no server. Optionally against one environment's history
|
|
4
|
+
* (`--env`), which turns the checksum rule from "the files changed since
|
|
5
|
+
* yodel wrote them" into "an applied migration changed since it ran".
|
|
6
|
+
*
|
|
7
|
+
* Rules that need more than the directory, such as the replay check (#11),
|
|
8
|
+
* plug in as {@link ProjectRule}s through {@link LintOptions.rules}.
|
|
9
|
+
*/
|
|
10
|
+
import type { Project } from "../project.js";
|
|
11
|
+
import { type MigrationScan } from "../migrations/index.js";
|
|
12
|
+
import type { VersionedStatus } from "../versioned/state.js";
|
|
13
|
+
import { type LintSettings } from "./config.js";
|
|
14
|
+
import { type LintLevel, type LintRule } from "./rules.js";
|
|
15
|
+
import { type Silence } from "./silences.js";
|
|
16
|
+
import { type Dialect } from "./statements.js";
|
|
17
|
+
/** One thing a rule found. */
|
|
18
|
+
export interface Finding {
|
|
19
|
+
rule: string;
|
|
20
|
+
level: LintLevel;
|
|
21
|
+
message: string;
|
|
22
|
+
/** The migration it is in, when it is in one. */
|
|
23
|
+
migration?: string;
|
|
24
|
+
/** The step, an index into the migration's `steps`. */
|
|
25
|
+
step?: number;
|
|
26
|
+
/** The step's object on the server (`database.table`, `schema.table`). */
|
|
27
|
+
object?: string;
|
|
28
|
+
/** A line in migration.sql. */
|
|
29
|
+
line?: number;
|
|
30
|
+
/** Set when a `-- yodel:allow` line silenced it: the reason given. A silenced finding fails nothing. */
|
|
31
|
+
silenced?: string;
|
|
32
|
+
}
|
|
33
|
+
/** What a rule reports, before its level and any silence are applied. */
|
|
34
|
+
export type RawFinding = Omit<Finding, "level" | "silenced"> & {
|
|
35
|
+
/** Overrides the rule's level for this one finding (a pending migration edited, with --env, is a warning). */
|
|
36
|
+
level?: LintLevel;
|
|
37
|
+
};
|
|
38
|
+
export interface LintContext {
|
|
39
|
+
/** The migrations directory, absolute. */
|
|
40
|
+
dir: string;
|
|
41
|
+
scan: MigrationScan;
|
|
42
|
+
dialect: Dialect | undefined;
|
|
43
|
+
project: Project | undefined;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* A rule that checks the project as a whole and may reach a server or the
|
|
47
|
+
* emulator: the hook for the replay check (#11). Its findings get levels
|
|
48
|
+
* from `yodel.config.ts` like any rule's; `yodel lint --rules` lists it.
|
|
49
|
+
*/
|
|
50
|
+
export interface ProjectRule {
|
|
51
|
+
rule: LintRule;
|
|
52
|
+
check: (ctx: LintContext) => Promise<RawFinding[]>;
|
|
53
|
+
}
|
|
54
|
+
export interface LintOptions {
|
|
55
|
+
/** The project's dialect. Every migration must be this dialect. */
|
|
56
|
+
dialect?: Dialect;
|
|
57
|
+
project?: Project;
|
|
58
|
+
settings?: LintSettings;
|
|
59
|
+
/** An environment's history compared with the directory (`compareHistory`), for --env. */
|
|
60
|
+
history?: {
|
|
61
|
+
environment: string;
|
|
62
|
+
status: VersionedStatus;
|
|
63
|
+
};
|
|
64
|
+
/** More rules: the replay check (#11) plugs in here. */
|
|
65
|
+
rules?: readonly ProjectRule[];
|
|
66
|
+
}
|
|
67
|
+
export interface LintReport {
|
|
68
|
+
dir: string;
|
|
69
|
+
dialect: Dialect | undefined;
|
|
70
|
+
environment: string | undefined;
|
|
71
|
+
/** Every migration read, sorted by id. */
|
|
72
|
+
migrations: string[];
|
|
73
|
+
findings: Finding[];
|
|
74
|
+
/** The silences each migration's migration.sql holds, by migration id. */
|
|
75
|
+
silences: Record<string, Silence[]>;
|
|
76
|
+
errors: number;
|
|
77
|
+
warnings: number;
|
|
78
|
+
silenced: number;
|
|
79
|
+
}
|
|
80
|
+
/** Lints the migrations in `dir`. Never throws on what it finds; a finding is a result. */
|
|
81
|
+
export declare function lintMigrations(dir: string, options?: LintOptions): Promise<LintReport>;
|