@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,193 @@
|
|
|
1
|
+
import { parseArgs } from "node:util";
|
|
2
|
+
import { guarded } from "../env-args.js";
|
|
3
|
+
import { loadProject, resolveEnvironment, YodelError } from "../project.js";
|
|
4
|
+
import { scanMigrations } from "../migrations/index.js";
|
|
5
|
+
import { inspect, migrationsDir, openVersioned } from "../versioned/index.js";
|
|
6
|
+
import { LINT_RULES, lintExitCode, lintJson, lintMigrations, lintSettings, renderLint, updateChecksum } from "../lint/index.js";
|
|
7
|
+
import { projectRules, REPLAY_RULE, replayRule } from "../lint/project-rules.js";
|
|
8
|
+
import { renderReplay, replayHistory, replayJson } from "../replay/index.js";
|
|
9
|
+
const usage = "usage: yodel lint [--env <env>] [--replay <env> [--reset] [--keep]] [--json] [--dir <project>] [--update-checksum <id>]... [--rules]";
|
|
10
|
+
const help = `${usage}
|
|
11
|
+
|
|
12
|
+
Checks the migrations directory against every lint rule, offline: no server
|
|
13
|
+
and no dev database. Every rule is free and runs in CI on every pull request.
|
|
14
|
+
|
|
15
|
+
Rules (yodel lint --rules lists them with their levels):
|
|
16
|
+
fork two migrations follow the same parent (yodel rebase)
|
|
17
|
+
directory not one readable chain: a bad name or file, a missing
|
|
18
|
+
parent, a cycle, a migration of another dialect
|
|
19
|
+
checksum a migration's files no longer hash to its checksum
|
|
20
|
+
unreadable-object an object in a recorded schema, or a step, the sql
|
|
21
|
+
lexicon cannot read or classify; an error, never skipped
|
|
22
|
+
destructive a drop that removes data for good
|
|
23
|
+
pg-rewrite Postgres: a rewrite or full scan under ACCESS EXCLUSIVE
|
|
24
|
+
pg-lock Postgres: writes blocked for a whole index build or scan
|
|
25
|
+
ch-mutation ClickHouse: a mutation rewriting parts, no rollback
|
|
26
|
+
ch-rebuild ClickHouse: a rebuild (ClickHouseRebuildOp)
|
|
27
|
+
refused-step a step yodel apply refuses (a manual step, an Op it
|
|
28
|
+
does not run)
|
|
29
|
+
migration-sql migration.sql does not hold a statement migration.json
|
|
30
|
+
records (apply runs migration.json's)
|
|
31
|
+
silence a yodel:allow line that is wrong or silences nothing
|
|
32
|
+
replay with --replay <env>: the history, replayed into a fresh
|
|
33
|
+
database on <env>'s server, does not give a migration's
|
|
34
|
+
recorded schema (names the first that differs)
|
|
35
|
+
|
|
36
|
+
Silencing: a line above a statement in its migration.sql,
|
|
37
|
+
-- yodel:allow <rule> <reason>
|
|
38
|
+
silences that statement rule (destructive, pg-rewrite, pg-lock,
|
|
39
|
+
ch-mutation, ch-rebuild) for that one statement. The reason is required,
|
|
40
|
+
and yodel apply records the rule, the statement and the reason in every
|
|
41
|
+
history row the migration writes (the silences column). Adding the line
|
|
42
|
+
changes the migration's checksum: for a migration not yet applied anywhere,
|
|
43
|
+
record the new one with --update-checksum <id>.
|
|
44
|
+
|
|
45
|
+
Levels: yodel.config.ts lint.rules sets a rule to "error", "warning" or
|
|
46
|
+
"off", e.g. lint: { rules: { "ch-mutation": "warning" } }. fork, directory,
|
|
47
|
+
checksum and unreadable-object are always errors.
|
|
48
|
+
|
|
49
|
+
Options:
|
|
50
|
+
--env <env> also compare with <env>'s history: a
|
|
51
|
+
checksum error is then an applied migration whose
|
|
52
|
+
files changed since it ran; a pending migration's
|
|
53
|
+
edit is a warning
|
|
54
|
+
--update-checksum <id> first write <id>'s migration.json checksum to match
|
|
55
|
+
its files (repeatable); refused with --env when <id>
|
|
56
|
+
is applied there
|
|
57
|
+
--replay <env> also replay every migration in order into <env>'s
|
|
58
|
+
server (a throwaway emulator: chant emulator up
|
|
59
|
+
--lexicon sql) and compare each with its recorded
|
|
60
|
+
schema; the databases or schemas the history names
|
|
61
|
+
must not exist there yet
|
|
62
|
+
--reset with --replay: drop those databases or schemas
|
|
63
|
+
first when they exist. Destructive: never against a
|
|
64
|
+
server whose data matters
|
|
65
|
+
--keep with --replay: leave the replayed database in place
|
|
66
|
+
--rules list the rules and their levels, and exit
|
|
67
|
+
--json print the findings as JSON
|
|
68
|
+
-C, --dir <dir> the project directory (default: the working directory)
|
|
69
|
+
|
|
70
|
+
Exit codes:
|
|
71
|
+
0 no errors (warnings and silenced findings do not fail)
|
|
72
|
+
3 at least one error (a replay that differs counts)
|
|
73
|
+
4 --replay: the target already holds what the history names, and --reset
|
|
74
|
+
was not given; nothing was run
|
|
75
|
+
1 lint could not run: a bad config, an unknown migration for
|
|
76
|
+
--update-checksum, a server that does not answer (--env)
|
|
77
|
+
`;
|
|
78
|
+
function parseLintArgs(args) {
|
|
79
|
+
let parsed;
|
|
80
|
+
try {
|
|
81
|
+
parsed = parseArgs({
|
|
82
|
+
args,
|
|
83
|
+
allowPositionals: false,
|
|
84
|
+
strict: true,
|
|
85
|
+
options: {
|
|
86
|
+
env: { type: "string" },
|
|
87
|
+
replay: { type: "string" },
|
|
88
|
+
reset: { type: "boolean" },
|
|
89
|
+
keep: { type: "boolean" },
|
|
90
|
+
json: { type: "boolean" },
|
|
91
|
+
dir: { type: "string", short: "C" },
|
|
92
|
+
"update-checksum": { type: "string", multiple: true },
|
|
93
|
+
rules: { type: "boolean" },
|
|
94
|
+
},
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
catch (e) {
|
|
98
|
+
throw new YodelError(`${e instanceof Error ? e.message : String(e)}\n${usage}`);
|
|
99
|
+
}
|
|
100
|
+
const v = parsed.values;
|
|
101
|
+
const update = v["update-checksum"];
|
|
102
|
+
const replay = typeof v.replay === "string" ? v.replay : undefined;
|
|
103
|
+
if (replay === undefined && (v.reset === true || v.keep === true))
|
|
104
|
+
throw new YodelError(`--reset and --keep go with --replay <env>\n${usage}`);
|
|
105
|
+
return {
|
|
106
|
+
env: typeof v.env === "string" ? v.env : undefined,
|
|
107
|
+
replay,
|
|
108
|
+
reset: v.reset === true,
|
|
109
|
+
keep: v.keep === true,
|
|
110
|
+
json: v.json === true,
|
|
111
|
+
dir: typeof v.dir === "string" ? v.dir : process.cwd(),
|
|
112
|
+
update: Array.isArray(update) ? update.filter((u) => typeof u === "string") : [],
|
|
113
|
+
rules: v.rules === true,
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
function allRules() {
|
|
117
|
+
return { ...LINT_RULES, ...Object.fromEntries(projectRules.map((r) => [r.rule.id, r.rule])), [REPLAY_RULE.id]: REPLAY_RULE };
|
|
118
|
+
}
|
|
119
|
+
function listRules(io, json, project) {
|
|
120
|
+
const rules = allRules();
|
|
121
|
+
const settings = lintSettings(project.yodel?.config.lint, rules, project.yodel?.path);
|
|
122
|
+
const rows = Object.values(rules).map((r) => ({ ...r, configured: settings.levels[r.id] ?? r.level }));
|
|
123
|
+
if (json) {
|
|
124
|
+
io.out(`${JSON.stringify(rows, null, 2)}\n`);
|
|
125
|
+
return 0;
|
|
126
|
+
}
|
|
127
|
+
const width = Math.max(...rows.map((r) => r.id.length));
|
|
128
|
+
for (const r of rows) {
|
|
129
|
+
const marks = [r.configured, r.silenceable ? "silenceable" : "", r.dialect ?? ""].filter(Boolean).join(", ");
|
|
130
|
+
io.out(`${r.id.padEnd(width)} ${marks}\n${" ".repeat(width + 2)}${r.summary}\n`);
|
|
131
|
+
}
|
|
132
|
+
return 0;
|
|
133
|
+
}
|
|
134
|
+
/** `yodel lint`; the exit codes are in `help`. */
|
|
135
|
+
export const lint = {
|
|
136
|
+
name: "lint",
|
|
137
|
+
summary: "check the migrations against the lint rules",
|
|
138
|
+
implemented: true,
|
|
139
|
+
run: (args, io) => guarded("lint", io, args, help, async () => {
|
|
140
|
+
const a = parseLintArgs(args);
|
|
141
|
+
const project = await loadProject(a.dir);
|
|
142
|
+
if (a.rules)
|
|
143
|
+
return listRules(io, a.json, project);
|
|
144
|
+
const settings = lintSettings(project.yodel?.config.lint, allRules(), project.yodel?.path);
|
|
145
|
+
const dir = migrationsDir(project);
|
|
146
|
+
const options = { dialect: project.dialect, project, settings, rules: projectRules };
|
|
147
|
+
if (a.env !== undefined) {
|
|
148
|
+
const env = resolveEnvironment(project, a.env);
|
|
149
|
+
const ordered = scanMigrations(dir).ordered;
|
|
150
|
+
if (ordered) {
|
|
151
|
+
const venv = await openVersioned(project, env);
|
|
152
|
+
const { status } = await inspect(venv, ordered, { sync: false });
|
|
153
|
+
options.history = { environment: env.name, status };
|
|
154
|
+
}
|
|
155
|
+
else if (!a.json) {
|
|
156
|
+
io.err(`yodel lint: the migrations directory is not one chain, so it is not compared with ${env.name}'s history; fix the findings below first\n`);
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
if (a.update.length > 0) {
|
|
160
|
+
const byId = new Map(scanMigrations(dir).migrations.map((m) => [m.id, m]));
|
|
161
|
+
for (const id of a.update) {
|
|
162
|
+
const m = byId.get(id);
|
|
163
|
+
if (!m)
|
|
164
|
+
throw new YodelError(`--update-checksum ${id}: no such migration in ${dir}`);
|
|
165
|
+
const applied = options.history?.status.applied.some((x) => x.id === id);
|
|
166
|
+
if (applied)
|
|
167
|
+
throw new YodelError(`--update-checksum ${id}: it is applied in ${String(options.history?.environment)}; record an intended edit there with yodel repair ${id} --reason ... ${String(options.history?.environment)}`);
|
|
168
|
+
const changed = updateChecksum(m);
|
|
169
|
+
if (!a.json)
|
|
170
|
+
io.err(changed ? `${id}: checksum ${changed.from} is now ${changed.to}\n` : `${id}: its checksum already matches its files\n`);
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
let replay;
|
|
174
|
+
if (a.replay !== undefined) {
|
|
175
|
+
const env = resolveEnvironment(project, a.replay);
|
|
176
|
+
if (scanMigrations(dir).ordered) {
|
|
177
|
+
// A target that is not fresh throws here, exit 4, before anything is linted or run.
|
|
178
|
+
replay = await replayHistory(project, env, { reset: a.reset, keep: a.keep, log: (l) => { io.err(`replay: ${l}\n`); } });
|
|
179
|
+
options.rules = [...projectRules, replayRule(replay)];
|
|
180
|
+
}
|
|
181
|
+
else {
|
|
182
|
+
io.err(`yodel lint: the migrations directory is not one chain, so it is not replayed; fix the findings below first\n`);
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
const report = await lintMigrations(dir, options);
|
|
186
|
+
if (a.json)
|
|
187
|
+
io.out(`${JSON.stringify({ ...lintJson(report), ...(replay ? { replay: replayJson(replay) } : {}) }, null, 2)}\n`);
|
|
188
|
+
else
|
|
189
|
+
io.out(`${replay ? `${renderReplay(replay)}\n` : ""}${renderLint(report)}`);
|
|
190
|
+
return lintExitCode(report);
|
|
191
|
+
}),
|
|
192
|
+
};
|
|
193
|
+
//# sourceMappingURL=lint.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"lint.js","sourceRoot":"","sources":["../../src/commands/lint.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
2
|
+
import { dirname, join, relative, resolve } from "node:path";
|
|
3
|
+
import { parseArgs } from "node:util";
|
|
4
|
+
import { guarded } from "../env-args.js";
|
|
5
|
+
import { loadProject, YodelError } from "../project.js";
|
|
6
|
+
import { buildSchema } from "../chant/build.js";
|
|
7
|
+
import { createMigration, migrationId, MIGRATIONS_DIR, newestMigration, readMigrations, refusedSteps, verifyChecksum, writeMigration, } from "../migrations/index.js";
|
|
8
|
+
import { asBackfill, BACKFILL_FILE, backfillTemplate, loadBackfill, TEMPLATE_MARK } from "../steps/index.js";
|
|
9
|
+
const usage = "usage: yodel new <name> [--backfill <file>] [--json] [--dir <project>]";
|
|
10
|
+
const help = `${usage}
|
|
11
|
+
|
|
12
|
+
Writes the next migration, migrations/<YYYYMMDDTHHMM>-<name>/, by diffing
|
|
13
|
+
the newest migration's recorded schema (an empty schema for the first)
|
|
14
|
+
against the current build of the declared schema. Offline: no database,
|
|
15
|
+
dev or otherwise, is read. The name is lowercase letters and digits, words
|
|
16
|
+
joined by - or _. The timestamp is UTC.
|
|
17
|
+
|
|
18
|
+
The migration holds migration.sql (each statement after a comment naming its
|
|
19
|
+
object, rule and class) and migration.json (id, parent, checksum, the
|
|
20
|
+
recorded schema, the steps). A change no statement makes in place, such as a
|
|
21
|
+
ClickHouse sort-key change or a Postgres column rename, is an Op step:
|
|
22
|
+
written to migration.json with its declaration, and to migration.sql as a
|
|
23
|
+
comment only.
|
|
24
|
+
|
|
25
|
+
A data backfill is a step too. --backfill <file> ends the migration with a
|
|
26
|
+
backfill step: <file> (relative to the project directory) is a module
|
|
27
|
+
exporting a chant Op whose every step is an effect() batch with its own
|
|
28
|
+
receipt, running SQL with activity("yodelSql", { sql }). It is copied into
|
|
29
|
+
the migration's directory as ${BACKFILL_FILE} and counts in its checksum.
|
|
30
|
+
yodel apply runs it after the statements before it, and a run that fails or
|
|
31
|
+
is stopped part way resumes at the first batch without a receipt. When
|
|
32
|
+
<file> does not exist yet, a template is written there and no migration:
|
|
33
|
+
write the batches, then run the same command again. With --backfill a
|
|
34
|
+
migration is written even when the declared schema has not changed.
|
|
35
|
+
|
|
36
|
+
Options:
|
|
37
|
+
--backfill <file> end the migration with a backfill step running <file>
|
|
38
|
+
--json print what was written as JSON
|
|
39
|
+
-C, --dir <dir> the project directory (default: the working directory)
|
|
40
|
+
|
|
41
|
+
Exit codes:
|
|
42
|
+
0 a migration was written, or the declared schema has no change from the
|
|
43
|
+
newest recorded schema (then nothing is written)
|
|
44
|
+
1 no migration could be made: a bad config, a declaration chant cannot
|
|
45
|
+
build, a migrations directory that is not one chain (a fork, a missing
|
|
46
|
+
parent), a migration of that id already there
|
|
47
|
+
`;
|
|
48
|
+
function parseNewArgs(args) {
|
|
49
|
+
let parsed;
|
|
50
|
+
try {
|
|
51
|
+
parsed = parseArgs({
|
|
52
|
+
args,
|
|
53
|
+
allowPositionals: true,
|
|
54
|
+
strict: true,
|
|
55
|
+
options: { json: { type: "boolean" }, dir: { type: "string", short: "C" }, backfill: { type: "string" } },
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
catch (e) {
|
|
59
|
+
throw new YodelError(`${e instanceof Error ? e.message : String(e)}\n${usage}`);
|
|
60
|
+
}
|
|
61
|
+
const [name, ...rest] = parsed.positionals;
|
|
62
|
+
if (name === undefined || rest.length > 0)
|
|
63
|
+
throw new YodelError(usage);
|
|
64
|
+
migrationId(name, new Date()); // a bad name fails before anything is built
|
|
65
|
+
return {
|
|
66
|
+
name,
|
|
67
|
+
json: parsed.values.json === true,
|
|
68
|
+
dir: typeof parsed.values.dir === "string" ? parsed.values.dir : process.cwd(),
|
|
69
|
+
backfill: typeof parsed.values.backfill === "string" ? parsed.values.backfill : undefined,
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
function report(io, json, made, path, projectDir) {
|
|
73
|
+
const steps = made.statements.steps;
|
|
74
|
+
const refused = refusedSteps(steps);
|
|
75
|
+
const statementCount = steps.length - refused.length;
|
|
76
|
+
if (json) {
|
|
77
|
+
io.out(`${JSON.stringify({
|
|
78
|
+
written: true,
|
|
79
|
+
id: made.id,
|
|
80
|
+
path,
|
|
81
|
+
parent: made.document.parent,
|
|
82
|
+
checksum: made.document.checksum,
|
|
83
|
+
dialect: made.document.dialect,
|
|
84
|
+
statements: statementCount,
|
|
85
|
+
ops: refused.filter((s) => s.kind === "op").map((s) => ({ op: s.op, object: s.object, name: s.name, rules: s.changes.map((c) => c.rule) })),
|
|
86
|
+
manual: refused.filter((s) => s.kind === "manual").map((s) => ({ object: s.object, name: s.name, rules: s.changes.map((c) => c.rule), detail: s.detail })),
|
|
87
|
+
hints: made.document.hints,
|
|
88
|
+
}, null, 2)}\n`);
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
const where = relative(projectDir, path) || path;
|
|
92
|
+
const parent = made.document.parent ? `follows ${made.document.parent}` : "the first migration";
|
|
93
|
+
const lines = [`Wrote ${where}/ (${String(statementCount)} statement${statementCount === 1 ? "" : "s"}; ${parent})`];
|
|
94
|
+
for (const s of refused) {
|
|
95
|
+
const rules = s.changes.map((c) => c.rule).join(", ");
|
|
96
|
+
const backfill = asBackfill(s);
|
|
97
|
+
if (backfill) {
|
|
98
|
+
lines.push(` This migration ends with a backfill step: the Op ${backfill.options.file} exports, copied into the migration's directory.`);
|
|
99
|
+
lines.push(` yodel apply runs its effect() batches after the statements before it, resuming from their receipts after a failure.`);
|
|
100
|
+
}
|
|
101
|
+
else if (s.kind === "op") {
|
|
102
|
+
lines.push(` This migration contains an Op step: ${s.op} for ${s.object} (${s.name}), ${rules}.`);
|
|
103
|
+
lines.push(` It is not SQL. migration.json holds the Op's declaration:`);
|
|
104
|
+
lines.push(` ${s.declaration}`);
|
|
105
|
+
}
|
|
106
|
+
else {
|
|
107
|
+
lines.push(` This migration contains a manual step: ${s.object} (${s.name}), ${rules}. No statement and no Op makes it:`);
|
|
108
|
+
lines.push(` ${s.detail}`);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
for (const h of made.document.hints)
|
|
112
|
+
lines.push(` hint: ${h}`);
|
|
113
|
+
io.out(`${lines.join("\n")}\n`);
|
|
114
|
+
}
|
|
115
|
+
/** `yodel new <name>`; the exit codes are in `help`. */
|
|
116
|
+
export const newCommand = {
|
|
117
|
+
name: "new",
|
|
118
|
+
summary: "write the next migration from the declared schema",
|
|
119
|
+
implemented: true,
|
|
120
|
+
run: (args, io) => guarded("new", io, args, help, async () => {
|
|
121
|
+
const { name, json, dir, backfill: backfillArg } = parseNewArgs(args);
|
|
122
|
+
const project = await loadProject(dir);
|
|
123
|
+
let backfill;
|
|
124
|
+
if (backfillArg !== undefined) {
|
|
125
|
+
const path = resolve(project.dir, backfillArg);
|
|
126
|
+
if (!existsSync(path)) {
|
|
127
|
+
mkdirSync(dirname(path), { recursive: true });
|
|
128
|
+
writeFileSync(path, backfillTemplate(name, project.dialect));
|
|
129
|
+
const where = relative(project.dir, path) || path;
|
|
130
|
+
if (json)
|
|
131
|
+
io.out(`${JSON.stringify({ written: false, template: path }, null, 2)}\n`);
|
|
132
|
+
else
|
|
133
|
+
io.out(`Wrote a backfill template at ${where}. Write its batches, then run yodel new ${name} --backfill ${backfillArg} again. No migration written.\n`);
|
|
134
|
+
return 0;
|
|
135
|
+
}
|
|
136
|
+
const text = readFileSync(path, "utf8");
|
|
137
|
+
if (text.includes(TEMPLATE_MARK))
|
|
138
|
+
throw new YodelError(`${path} is still the template; write its batches' SQL first`);
|
|
139
|
+
await loadBackfill(path, text);
|
|
140
|
+
backfill = { text };
|
|
141
|
+
}
|
|
142
|
+
const migrationsDir = join(project.dir, MIGRATIONS_DIR);
|
|
143
|
+
const previous = newestMigration(readMigrations(migrationsDir));
|
|
144
|
+
if (previous && !verifyChecksum(previous).ok) {
|
|
145
|
+
io.err(`yodel new: warning: ${previous.id}'s files no longer match its recorded checksum; the new migration follows it as recorded\n`);
|
|
146
|
+
}
|
|
147
|
+
const build = await buildSchema(project);
|
|
148
|
+
let made;
|
|
149
|
+
try {
|
|
150
|
+
made = createMigration({ name, build: build.output, previous, ...(backfill ? { backfill } : {}) });
|
|
151
|
+
}
|
|
152
|
+
finally {
|
|
153
|
+
build.dispose();
|
|
154
|
+
}
|
|
155
|
+
if (made.id === undefined) {
|
|
156
|
+
const from = previous ? `the newest migration's recorded schema (${previous.id})` : "an empty schema (the declared schema declares nothing)";
|
|
157
|
+
if (json)
|
|
158
|
+
io.out(`${JSON.stringify({ written: false, parent: previous?.id ?? null, dialect: project.dialect, hints: made.statements.hints }, null, 2)}\n`);
|
|
159
|
+
else
|
|
160
|
+
io.out(`No changes: the declared schema matches ${from}. Nothing written.\n`);
|
|
161
|
+
return 0;
|
|
162
|
+
}
|
|
163
|
+
const path = writeMigration(migrationsDir, made);
|
|
164
|
+
report(io, json, made, path, project.dir);
|
|
165
|
+
return 0;
|
|
166
|
+
}),
|
|
167
|
+
};
|
|
168
|
+
//# sourceMappingURL=new.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"new.js","sourceRoot":"","sources":["../../src/commands/new.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
import { parseArgs } from "node:util";
|
|
2
|
+
import { guarded } from "../env-args.js";
|
|
3
|
+
import { loadProject, resolveEnvironment, YodelError } from "../project.js";
|
|
4
|
+
import { declarativeTopologyRefusal, describeSettingsTopology, environmentSettings } from "../config/settings.js";
|
|
5
|
+
import { buildSchema } from "../chant/build.js";
|
|
6
|
+
import { planEnvironment, planExitCode, planJson, renderPlan } from "../chant/plan.js";
|
|
7
|
+
import { hasMigrations } from "../versioned/index.js";
|
|
8
|
+
import { decodeSubject } from "../versioned/explain.js";
|
|
9
|
+
import { collectVersionedPlan } from "../pr/model.js";
|
|
10
|
+
import { commentMarker, planJsonVersioned, renderPlanMarkdown, renderPlanText, subjectIn } from "../pr/render.js";
|
|
11
|
+
import { resolveForgeTarget } from "../pr/forge.js";
|
|
12
|
+
import { commentClient, findComment, upsertComment } from "../pr/comment.js";
|
|
13
|
+
const usage = "usage: yodel plan <env> [--json] [--dir <project>] [--format text|markdown|json] [--allow-out-of-order] [--op <name>]\n" +
|
|
14
|
+
" [--comment [--forge github|gitlab|forgejo] [--repo <repo>] [--pr <n>] [--api-url <url>] [--token-env <VAR>]]";
|
|
15
|
+
const help = `${usage}
|
|
16
|
+
|
|
17
|
+
Declarative project (no migrations/ directory): shows the classified change
|
|
18
|
+
from the environment's live database (sql.profiles.<env>) to the declared
|
|
19
|
+
schema, as chant sql plan reports it.
|
|
20
|
+
|
|
21
|
+
Versioned project (a migrations/ directory): shows the pending migrations,
|
|
22
|
+
each step with its rule and class (an Op step with the Op that makes it, a
|
|
23
|
+
manual step with what it needs), integrity problems (directory problems,
|
|
24
|
+
checksum mismatches, out-of-order migrations), and the plan digest yodel
|
|
25
|
+
apply asks approval for, with the chant approve command that approves it.
|
|
26
|
+
When chant's gate ledger holds an approval of the migrations Op's gate for
|
|
27
|
+
another digest, it says what moved since. Reads only.
|
|
28
|
+
|
|
29
|
+
Options:
|
|
30
|
+
--json print the plan as JSON (the same as --format json)
|
|
31
|
+
--format <f> text (default), markdown (the pull request comment), json
|
|
32
|
+
--allow-out-of-order versioned: the plan yodel apply --allow-out-of-order would run
|
|
33
|
+
--op <name> versioned: the migrations Op, when more than one applies <env>
|
|
34
|
+
--comment versioned: post the markdown as one comment on the pull
|
|
35
|
+
(merge) request, updating the one posted before; prints
|
|
36
|
+
the text plan. The forge, repository, request and API
|
|
37
|
+
come from the CI job's environment (GitHub Actions,
|
|
38
|
+
GitLab CI, Forgejo Actions) unless given:
|
|
39
|
+
--forge <f> github, gitlab or forgejo
|
|
40
|
+
--repo <repo> owner/name (GitHub, Forgejo) or the project id (GitLab)
|
|
41
|
+
--pr <n> the pull request number or merge request iid
|
|
42
|
+
--api-url <url> the REST API base (https://api.github.com,
|
|
43
|
+
https://gitlab.example/api/v4, https://forgejo.example/api/v1)
|
|
44
|
+
--token-env <VAR> the variable holding the token (default GITHUB_TOKEN;
|
|
45
|
+
GITLAB_TOKEN; FORGEJO_TOKEN, else GITHUB_TOKEN)
|
|
46
|
+
-C, --dir <dir> the project directory (default: the working directory)
|
|
47
|
+
|
|
48
|
+
Exit codes:
|
|
49
|
+
0 every change can be made in place, or there is none; versioned: apply
|
|
50
|
+
would run (or has nothing to do)
|
|
51
|
+
2 a change needs its own Op: a ClickHouse rebuild, a Postgres expand and
|
|
52
|
+
contract (declarative); versioned: a pending migration has an Op or
|
|
53
|
+
manual step apply does not run yet
|
|
54
|
+
4 versioned: apply would refuse: a checksum mismatch, an applied migration
|
|
55
|
+
gone from the directory, an out-of-order migration
|
|
56
|
+
1 no plan could be made: a bad config, an unknown environment, a declaration
|
|
57
|
+
chant cannot build, a malformed migrations directory, a server it cannot
|
|
58
|
+
read, a comment the forge refused
|
|
59
|
+
`;
|
|
60
|
+
function parsePlanArgs(args) {
|
|
61
|
+
let parsed;
|
|
62
|
+
try {
|
|
63
|
+
parsed = parseArgs({
|
|
64
|
+
args,
|
|
65
|
+
allowPositionals: true,
|
|
66
|
+
strict: true,
|
|
67
|
+
options: {
|
|
68
|
+
json: { type: "boolean" },
|
|
69
|
+
dir: { type: "string", short: "C" },
|
|
70
|
+
format: { type: "string" },
|
|
71
|
+
comment: { type: "boolean" },
|
|
72
|
+
"allow-out-of-order": { type: "boolean" },
|
|
73
|
+
op: { type: "string" },
|
|
74
|
+
forge: { type: "string" },
|
|
75
|
+
repo: { type: "string" },
|
|
76
|
+
pr: { type: "string" },
|
|
77
|
+
"api-url": { type: "string" },
|
|
78
|
+
"token-env": { type: "string" },
|
|
79
|
+
},
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
catch (e) {
|
|
83
|
+
throw new YodelError(`${e instanceof Error ? e.message : String(e)}\n${usage}`);
|
|
84
|
+
}
|
|
85
|
+
const { values: v, positionals } = parsed;
|
|
86
|
+
const [env, ...rest] = positionals;
|
|
87
|
+
if (env === undefined || rest.length > 0)
|
|
88
|
+
throw new YodelError(usage);
|
|
89
|
+
const str = (k) => (typeof v[k] === "string" ? v[k] : undefined);
|
|
90
|
+
const format = str("format") ?? (v.json === true ? "json" : "text");
|
|
91
|
+
if (format !== "text" && format !== "markdown" && format !== "json")
|
|
92
|
+
throw new YodelError(`--format must be text, markdown or json, got ${JSON.stringify(format)}\n${usage}`);
|
|
93
|
+
if (v.json === true && format !== "json")
|
|
94
|
+
throw new YodelError(`--json and --format ${format} cannot be combined\n${usage}`);
|
|
95
|
+
const forgeFlags = ["forge", "repo", "pr", "api-url", "token-env"].filter((k) => str(k) !== undefined);
|
|
96
|
+
if (forgeFlags.length > 0 && v.comment !== true)
|
|
97
|
+
throw new YodelError(`--${forgeFlags.join(", --")} ${forgeFlags.length === 1 ? "goes" : "go"} with --comment\n${usage}`);
|
|
98
|
+
const forge = { forge: str("forge"), repo: str("repo"), pr: str("pr"), apiUrl: str("api-url"), tokenEnv: str("token-env") };
|
|
99
|
+
return {
|
|
100
|
+
env,
|
|
101
|
+
dir: str("dir") ?? process.cwd(),
|
|
102
|
+
format,
|
|
103
|
+
comment: v.comment === true,
|
|
104
|
+
allowOutOfOrder: v["allow-out-of-order"] === true,
|
|
105
|
+
op: str("op"),
|
|
106
|
+
forge,
|
|
107
|
+
versionedOnly: [
|
|
108
|
+
...(format === "markdown" ? ["--format markdown"] : []),
|
|
109
|
+
...(v.comment === true ? ["--comment"] : []),
|
|
110
|
+
...(v["allow-out-of-order"] === true ? ["--allow-out-of-order"] : []),
|
|
111
|
+
...(str("op") !== undefined ? ["--op"] : []),
|
|
112
|
+
],
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
/** `yodel plan <env>`; the exit codes are in `help`. */
|
|
116
|
+
export const plan = {
|
|
117
|
+
name: "plan",
|
|
118
|
+
summary: "show what apply would change in an environment",
|
|
119
|
+
implemented: true,
|
|
120
|
+
run: (args, io) => guarded("plan", io, args, help, async () => {
|
|
121
|
+
const a = parsePlanArgs(args);
|
|
122
|
+
const project = await loadProject(a.dir);
|
|
123
|
+
const env = resolveEnvironment(project, a.env);
|
|
124
|
+
if (hasMigrations(project))
|
|
125
|
+
return versionedPlan(project, env, a, io);
|
|
126
|
+
if (a.versionedOnly.length > 0)
|
|
127
|
+
throw new YodelError(`${a.versionedOnly.join(", ")} ${a.versionedOnly.length === 1 ? "is" : "are"} for versioned migrations, and ${project.dir} has no migrations/ directory with a migration`);
|
|
128
|
+
return declarativePlan(project, env, a.format === "json", io);
|
|
129
|
+
}),
|
|
130
|
+
};
|
|
131
|
+
async function declarativePlan(project, env, json, io) {
|
|
132
|
+
const refusal = project.dialect === "clickhouse" ? declarativeTopologyRefusal(project, env.name) : undefined;
|
|
133
|
+
if (refusal)
|
|
134
|
+
throw refusal;
|
|
135
|
+
const topology = project.dialect === "clickhouse" ? `, topology ${describeSettingsTopology(environmentSettings(project, env.name))}` : "";
|
|
136
|
+
const build = await buildSchema(project);
|
|
137
|
+
try {
|
|
138
|
+
const result = await planEnvironment(project, env, build);
|
|
139
|
+
if (json)
|
|
140
|
+
io.out(`${JSON.stringify(planJson(result), null, 2)}\n`);
|
|
141
|
+
else
|
|
142
|
+
io.out(`${await renderPlan(result, `Plan for ${env.name} (${project.dialect}, sql.profiles.${env.name}${topology})`)}\n`);
|
|
143
|
+
return planExitCode(result);
|
|
144
|
+
}
|
|
145
|
+
finally {
|
|
146
|
+
build.dispose();
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
async function versionedPlan(project, env, a, io) {
|
|
150
|
+
const marker = commentMarker(env.name);
|
|
151
|
+
// Resolve the forge first: a job missing its token fails before it reads the server.
|
|
152
|
+
const target = a.comment ? resolveForgeTarget(a.forge, process.env) : undefined;
|
|
153
|
+
const client = target ? commentClient(target) : undefined;
|
|
154
|
+
const existing = client ? await findComment(client, marker) : undefined;
|
|
155
|
+
const carried = existing ? subjectIn(existing.body) : undefined;
|
|
156
|
+
const previous = carried ? decodeSubject(carried) : undefined;
|
|
157
|
+
const result = await collectVersionedPlan(project, env, { allowOutOfOrder: a.allowOutOfOrder, opName: a.op, previous });
|
|
158
|
+
const code = result.refusal?.exitCode ?? 0;
|
|
159
|
+
if (client && target) {
|
|
160
|
+
const posted = await upsertComment(client, marker, renderPlanMarkdown(result), existing);
|
|
161
|
+
io.err(`yodel plan: ${posted.action} the plan comment on ${target.forge} ${target.repo}#${String(target.pr)}${posted.comment.url ? ` (${posted.comment.url})` : ` (comment ${String(posted.comment.id)})`}\n`);
|
|
162
|
+
}
|
|
163
|
+
if (a.format === "json")
|
|
164
|
+
io.out(`${JSON.stringify(planJsonVersioned(result), null, 2)}\n`);
|
|
165
|
+
else if (a.format === "markdown")
|
|
166
|
+
io.out(renderPlanMarkdown(result));
|
|
167
|
+
else
|
|
168
|
+
io.out(renderPlanText(result));
|
|
169
|
+
return code;
|
|
170
|
+
}
|
|
171
|
+
//# sourceMappingURL=plan.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"plan.js","sourceRoot":"","sources":["../../src/commands/plan.ts"],"names":[],"mappings":"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"}
|