@intentius/sql-yodeler 0.2.0 → 0.4.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/README.md +3 -3
- package/dist/apply-steps/index.d.ts +65 -0
- package/dist/apply-steps/index.js +106 -0
- package/dist/apply-steps/index.js.map +1 -0
- package/dist/approval-mode/index.d.ts +78 -0
- package/dist/approval-mode/index.js +105 -0
- package/dist/approval-mode/index.js.map +1 -0
- package/dist/chant/access.d.ts +87 -0
- package/dist/chant/access.js +169 -0
- package/dist/chant/access.js.map +1 -0
- package/dist/chant/build.js +7 -0
- package/dist/chant/build.js.map +1 -1
- package/dist/chant/drift.d.ts +17 -1
- package/dist/chant/drift.js +55 -2
- package/dist/chant/drift.js.map +1 -1
- package/dist/chant/plan.d.ts +18 -2
- package/dist/chant/plan.js +21 -3
- package/dist/chant/plan.js.map +1 -1
- package/dist/ci/render.d.ts +333 -0
- package/dist/ci/render.js +885 -0
- package/dist/ci/render.js.map +1 -0
- package/dist/ci/replay.d.ts +17 -0
- package/dist/ci/replay.js +69 -0
- package/dist/ci/replay.js.map +1 -0
- package/dist/cleanup/index.d.ts +86 -0
- package/dist/cleanup/index.js +207 -0
- package/dist/cleanup/index.js.map +1 -0
- package/dist/commands/apply.d.ts +113 -0
- package/dist/commands/apply.js +370 -30
- package/dist/commands/apply.js.map +1 -1
- package/dist/commands/approve.d.ts +13 -0
- package/dist/commands/approve.js +239 -13
- package/dist/commands/approve.js.map +1 -1
- package/dist/commands/checkpoint.d.ts +3 -0
- package/dist/commands/checkpoint.js +78 -0
- package/dist/commands/checkpoint.js.map +1 -0
- package/dist/commands/ci.d.ts +3 -0
- package/dist/commands/ci.js +130 -0
- package/dist/commands/ci.js.map +1 -0
- package/dist/commands/cleanup.d.ts +13 -0
- package/dist/commands/cleanup.js +138 -0
- package/dist/commands/cleanup.js.map +1 -0
- package/dist/commands/config.d.ts +3 -0
- package/dist/commands/config.js +136 -0
- package/dist/commands/config.js.map +1 -0
- package/dist/commands/docs.d.ts +11 -0
- package/dist/commands/docs.js +108 -0
- package/dist/commands/docs.js.map +1 -0
- package/dist/commands/drift.d.ts +13 -1
- package/dist/commands/drift.js +52 -5
- package/dist/commands/drift.js.map +1 -1
- package/dist/commands/index.js +16 -0
- package/dist/commands/index.js.map +1 -1
- package/dist/commands/init.js +1 -1
- package/dist/commands/init.js.map +1 -1
- package/dist/commands/lint.js +39 -7
- package/dist/commands/lint.js.map +1 -1
- package/dist/commands/new.js +133 -34
- package/dist/commands/new.js.map +1 -1
- package/dist/commands/override.js +48 -16
- package/dist/commands/override.js.map +1 -1
- package/dist/commands/plan.js +47 -6
- package/dist/commands/plan.js.map +1 -1
- package/dist/commands/report.d.ts +13 -0
- package/dist/commands/report.js +121 -0
- package/dist/commands/report.js.map +1 -0
- package/dist/commands/revert.d.ts +20 -0
- package/dist/commands/revert.js +246 -0
- package/dist/commands/revert.js.map +1 -0
- package/dist/commands/status.js +32 -5
- package/dist/commands/status.js.map +1 -1
- package/dist/commands/test.d.ts +3 -0
- package/dist/commands/test.js +164 -0
- package/dist/commands/test.js.map +1 -0
- package/dist/config/base.d.ts +69 -0
- package/dist/config/base.js +121 -0
- package/dist/config/base.js.map +1 -0
- package/dist/config/define.d.ts +240 -3
- package/dist/config/define.js +4 -0
- package/dist/config/define.js.map +1 -1
- package/dist/config/index.d.ts +2 -0
- package/dist/config/index.js +2 -0
- package/dist/config/index.js.map +1 -1
- package/dist/config/load.d.ts +1 -1
- package/dist/config/load.js +156 -7
- package/dist/config/load.js.map +1 -1
- package/dist/config/settings.d.ts +22 -1
- package/dist/config/settings.js +34 -2
- package/dist/config/settings.js.map +1 -1
- package/dist/credentials.d.ts +119 -0
- package/dist/credentials.js +119 -0
- package/dist/credentials.js.map +1 -0
- package/dist/exit.js +1 -1
- package/dist/exit.js.map +1 -1
- package/dist/history/postgres/ddl.d.ts +18 -0
- package/dist/history/postgres/ddl.js +27 -2
- package/dist/history/postgres/ddl.js.map +1 -1
- package/dist/history/postgres/store.d.ts +2 -0
- package/dist/history/postgres/store.js +7 -1
- package/dist/history/postgres/store.js.map +1 -1
- package/dist/history/rows.d.ts +16 -3
- package/dist/history/rows.js +30 -1
- package/dist/history/rows.js.map +1 -1
- package/dist/history/store.d.ts +2 -0
- package/dist/history/store.js +2 -2
- package/dist/history/store.js.map +1 -1
- package/dist/index.d.ts +8 -1
- package/dist/index.js +3 -0
- package/dist/index.js.map +1 -1
- package/dist/init/adopt.js +1 -1
- package/dist/init/adopt.js.map +1 -1
- package/dist/init/clickhouse-access.d.ts +64 -0
- package/dist/init/clickhouse-access.js +211 -0
- package/dist/init/clickhouse-access.js.map +1 -0
- package/dist/init/live.d.ts +4 -0
- package/dist/init/live.js +16 -0
- package/dist/init/live.js.map +1 -1
- package/dist/lint/access.d.ts +18 -0
- package/dist/lint/access.js +34 -0
- package/dist/lint/access.js.map +1 -0
- package/dist/lint/check.js +38 -6
- package/dist/lint/check.js.map +1 -1
- package/dist/lint/config.d.ts +7 -1
- package/dist/lint/config.js +11 -5
- package/dist/lint/config.js.map +1 -1
- package/dist/lint/index.d.ts +2 -0
- package/dist/lint/index.js +2 -0
- package/dist/lint/index.js.map +1 -1
- package/dist/lint/naming.d.ts +62 -0
- package/dist/lint/naming.js +138 -0
- package/dist/lint/naming.js.map +1 -0
- package/dist/lint/report.js +1 -1
- package/dist/lint/report.js.map +1 -1
- package/dist/lint/routines.d.ts +26 -0
- package/dist/lint/routines.js +65 -0
- package/dist/lint/routines.js.map +1 -0
- package/dist/lint/rules.d.ts +51 -6
- package/dist/lint/rules.js +23 -6
- package/dist/lint/rules.js.map +1 -1
- package/dist/lint/schema-checks.d.ts +63 -0
- package/dist/lint/schema-checks.js +128 -0
- package/dist/lint/schema-checks.js.map +1 -0
- package/dist/lint/statements.d.ts +22 -1
- package/dist/lint/statements.js +40 -1
- package/dist/lint/statements.js.map +1 -1
- package/dist/mcp/tools.d.ts +1 -1
- package/dist/mcp/tools.js +1 -1
- package/dist/mcp/tools.js.map +1 -1
- package/dist/migrations/checkpoint.d.ts +45 -0
- package/dist/migrations/checkpoint.js +108 -0
- package/dist/migrations/checkpoint.js.map +1 -0
- package/dist/migrations/checks.d.ts +19 -1
- package/dist/migrations/checks.js +15 -0
- package/dist/migrations/checks.js.map +1 -1
- package/dist/migrations/create.d.ts +13 -4
- package/dist/migrations/create.js +24 -7
- package/dist/migrations/create.js.map +1 -1
- package/dist/migrations/document.d.ts +10 -0
- package/dist/migrations/document.js +1 -0
- package/dist/migrations/document.js.map +1 -1
- package/dist/migrations/index.d.ts +2 -0
- package/dist/migrations/index.js +2 -0
- package/dist/migrations/index.js.map +1 -1
- package/dist/migrations/routines.d.ts +21 -0
- package/dist/migrations/routines.js +30 -0
- package/dist/migrations/routines.js.map +1 -0
- package/dist/notify/index.d.ts +124 -0
- package/dist/notify/index.js +210 -0
- package/dist/notify/index.js.map +1 -0
- package/dist/notify/verify.d.ts +53 -0
- package/dist/notify/verify.js +71 -0
- package/dist/notify/verify.js.map +1 -0
- package/dist/pr/model.d.ts +7 -1
- package/dist/pr/model.js +29 -7
- package/dist/pr/model.js.map +1 -1
- package/dist/pr/render.d.ts +4 -1
- package/dist/pr/render.js +49 -9
- package/dist/pr/render.js.map +1 -1
- package/dist/pr/tenants.d.ts +37 -0
- package/dist/pr/tenants.js +138 -0
- package/dist/pr/tenants.js.map +1 -0
- package/dist/probe.d.ts +27 -0
- package/dist/probe.js +106 -0
- package/dist/probe.js.map +1 -0
- package/dist/project.d.ts +35 -2
- package/dist/project.js +62 -2
- package/dist/project.js.map +1 -1
- package/dist/replay/clickhouse.d.ts +31 -0
- package/dist/replay/clickhouse.js +103 -6
- package/dist/replay/clickhouse.js.map +1 -1
- package/dist/replay/replay.d.ts +14 -3
- package/dist/replay/replay.js +62 -31
- package/dist/replay/replay.js.map +1 -1
- package/dist/replay/report.js +1 -1
- package/dist/replay/report.js.map +1 -1
- package/dist/report/audit.d.ts +138 -0
- package/dist/report/audit.js +219 -0
- package/dist/report/audit.js.map +1 -0
- package/dist/report/jsonl.d.ts +21 -0
- package/dist/report/jsonl.js +52 -0
- package/dist/report/jsonl.js.map +1 -0
- package/dist/report/model.d.ts +68 -0
- package/dist/report/model.js +127 -0
- package/dist/report/model.js.map +1 -0
- package/dist/report/render.d.ts +18 -0
- package/dist/report/render.js +127 -0
- package/dist/report/render.js.map +1 -0
- package/dist/schema-docs/render.d.ts +49 -0
- package/dist/schema-docs/render.js +359 -0
- package/dist/schema-docs/render.js.map +1 -0
- package/dist/schemas.d.ts +7 -2
- package/dist/schemas.js +7 -2
- package/dist/schemas.js.map +1 -1
- package/dist/sources/ddl.d.ts +48 -0
- package/dist/sources/ddl.js +53 -0
- package/dist/sources/ddl.js.map +1 -0
- package/dist/sources/index.d.ts +69 -0
- package/dist/sources/index.js +170 -0
- package/dist/sources/index.js.map +1 -0
- package/dist/steps/backfill.d.ts +40 -5
- package/dist/steps/backfill.js +128 -8
- package/dist/steps/backfill.js.map +1 -1
- package/dist/steps/index.d.ts +19 -1
- package/dist/steps/index.js +34 -7
- package/dist/steps/index.js.map +1 -1
- package/dist/steps/kinds.d.ts +42 -4
- package/dist/steps/kinds.js +88 -14
- package/dist/steps/kinds.js.map +1 -1
- package/dist/steps/postgres-migration.d.ts +38 -25
- package/dist/steps/postgres-migration.js +49 -32
- package/dist/steps/postgres-migration.js.map +1 -1
- package/dist/steps/rebuild.d.ts +15 -6
- package/dist/steps/rebuild.js +12 -9
- package/dist/steps/rebuild.js.map +1 -1
- package/dist/tenants/index.d.ts +41 -0
- package/dist/tenants/index.js +121 -0
- package/dist/tenants/index.js.map +1 -0
- package/dist/tenants/target.d.ts +23 -0
- package/dist/tenants/target.js +29 -0
- package/dist/tenants/target.js.map +1 -0
- package/dist/tenants/wave.d.ts +128 -0
- package/dist/tenants/wave.js +254 -0
- package/dist/tenants/wave.js.map +1 -0
- package/dist/testing/define.d.ts +62 -0
- package/dist/testing/define.js +32 -0
- package/dist/testing/define.js.map +1 -0
- package/dist/testing/files.d.ts +5 -0
- package/dist/testing/files.js +18 -0
- package/dist/testing/files.js.map +1 -0
- package/dist/testing/report.d.ts +14 -0
- package/dist/testing/report.js +83 -0
- package/dist/testing/report.js.map +1 -0
- package/dist/testing/run.d.ts +104 -0
- package/dist/testing/run.js +328 -0
- package/dist/testing/run.js.map +1 -0
- package/dist/versioned/approval.d.ts +6 -0
- package/dist/versioned/approval.js +11 -5
- package/dist/versioned/approval.js.map +1 -1
- package/dist/versioned/checks.d.ts +8 -2
- package/dist/versioned/checks.js +34 -13
- package/dist/versioned/checks.js.map +1 -1
- package/dist/versioned/driver.d.ts +2 -2
- package/dist/versioned/engine.d.ts +16 -0
- package/dist/versioned/engine.js +20 -6
- package/dist/versioned/engine.js.map +1 -1
- package/dist/versioned/explain.d.ts +2 -0
- package/dist/versioned/explain.js +3 -1
- package/dist/versioned/explain.js.map +1 -1
- package/dist/versioned/index.d.ts +1 -0
- package/dist/versioned/index.js +1 -0
- package/dist/versioned/index.js.map +1 -1
- package/dist/versioned/op.d.ts +6 -1
- package/dist/versioned/op.js +7 -2
- package/dist/versioned/op.js.map +1 -1
- package/dist/versioned/policy.d.ts +33 -2
- package/dist/versioned/policy.js +59 -8
- package/dist/versioned/policy.js.map +1 -1
- package/dist/versioned/report.js +9 -0
- package/dist/versioned/report.js.map +1 -1
- package/dist/versioned/revert.d.ts +128 -0
- package/dist/versioned/revert.js +251 -0
- package/dist/versioned/revert.js.map +1 -0
- package/dist/versioned/state.d.ts +25 -0
- package/dist/versioned/state.js +37 -4
- package/dist/versioned/state.js.map +1 -1
- package/dist/waves/index.d.ts +224 -0
- package/dist/waves/index.js +353 -0
- package/dist/waves/index.js.map +1 -0
- package/dist/waves/promotion.d.ts +56 -0
- package/dist/waves/promotion.js +108 -0
- package/dist/waves/promotion.js.map +1 -0
- package/dist/waves/record.d.ts +34 -0
- package/dist/waves/record.js +20 -0
- package/dist/waves/record.js.map +1 -0
- package/package.json +11 -7
- package/schemas/apply.schema.json +162 -3
- package/schemas/checkpoint.schema.json +19 -0
- package/schemas/cleanup.schema.json +40 -0
- package/schemas/drift.schema.json +26 -1
- package/schemas/event.schema.json +36 -0
- package/schemas/lint-rules.schema.json +4 -0
- package/schemas/lint.schema.json +4 -0
- package/schemas/new.schema.json +41 -3
- package/schemas/plan.schema.json +157 -0
- package/schemas/report.schema.json +114 -0
- package/schemas/revert.schema.json +69 -0
- package/schemas/status.schema.json +64 -3
- package/schemas/test.schema.json +41 -0
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Environment waves (#42): the apply pipeline as chant's Op waves, one wave
|
|
3
|
+
* per environment, each behind a gate policy of its own.
|
|
4
|
+
*
|
|
5
|
+
* The project's CI script renders `yodel.config.ts` `waves` as a chant Op
|
|
6
|
+
* waves spec, {@link WAVES_SPEC_FILE}, and the pipeline from it (`chant run
|
|
7
|
+
* --generate <forge> --spec`, or `generateOpWavesPipeline`). Every wave's
|
|
8
|
+
* job runs `chant run wave`, which calls back into yodel twice per
|
|
9
|
+
* environment:
|
|
10
|
+
*
|
|
11
|
+
* - `yodel apply <env> --wave-plan <file>` ({@link writeWavePlan}): the
|
|
12
|
+
* plan file chant binds the wave's gate to. Its `planDigest` covers the
|
|
13
|
+
* migrations' plan digest (what `--digest` prints), the environment's gate
|
|
14
|
+
* policy as the base commit's `yodel.config.ts` has it, and whether a
|
|
15
|
+
* pending step is destructive, which is what `on-destructive` waits on.
|
|
16
|
+
* - `yodel apply <env> --wave-apply <file>` ({@link applyWave}): applies
|
|
17
|
+
* that plan, under the lock, only when chant's decision for the wave lets
|
|
18
|
+
* it through: an approval of the wave's set digest in the gate ledger, or
|
|
19
|
+
* a policy at the base commit that needs none. The plan is computed again
|
|
20
|
+
* under the lock and must be the one the decision was made on.
|
|
21
|
+
*
|
|
22
|
+
* chant reads the policy from the spec file at the first parent, so a pull
|
|
23
|
+
* request that loosens its own environment's gate merges under the old one.
|
|
24
|
+
*/
|
|
25
|
+
import { type ConfigAtBase } from "../config/base.js";
|
|
26
|
+
import type { WaveGate, YodelConfig } from "../config/define.js";
|
|
27
|
+
import type { Migration } from "../migrations/index.js";
|
|
28
|
+
import { YodelError, type Project } from "../project.js";
|
|
29
|
+
import { type VersionedEnv } from "../versioned/engine.js";
|
|
30
|
+
import type { PendingMigration } from "../versioned/state.js";
|
|
31
|
+
import { type WaveRecord } from "./record.js";
|
|
32
|
+
import { type Promotion } from "./promotion.js";
|
|
33
|
+
import { type StepOutcome } from "../apply-steps/index.js";
|
|
34
|
+
import { type ApprovalMode } from "../approval-mode/index.js";
|
|
35
|
+
export * from "./promotion.js";
|
|
36
|
+
export * from "./record.js";
|
|
37
|
+
/** The waves spec the CI script writes and every wave job reads. */
|
|
38
|
+
export declare const WAVES_SPEC_FILE = "yodel-waves.json";
|
|
39
|
+
export declare const WAVE_PLAN_KIND = "yodel-wave";
|
|
40
|
+
/** The gate policy an environment no wave names gets. */
|
|
41
|
+
export declare const DEFAULT_WAVE_GATE: WaveGate;
|
|
42
|
+
/** `env`'s wave gate in `config`; a tenant's is its environment's. */
|
|
43
|
+
export declare function waveGate(config: YodelConfig | undefined, env: string): {
|
|
44
|
+
gate: WaveGate;
|
|
45
|
+
declared: boolean;
|
|
46
|
+
};
|
|
47
|
+
/** A pending step `on-destructive` waits on, and why. */
|
|
48
|
+
export interface DestructiveStep {
|
|
49
|
+
migration: string;
|
|
50
|
+
step: number;
|
|
51
|
+
what: string;
|
|
52
|
+
/** The lint rule that classes it, or `op-step` / `manual-step`. */
|
|
53
|
+
why: string;
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* The steps of `pending` that `on-destructive` waits on: a destructive
|
|
57
|
+
* statement or Op step, a Postgres rewrite or lock-heavy statement, one
|
|
58
|
+
* that takes access away (a revoke, a policy dropped, row-level security
|
|
59
|
+
* turned off; on ClickHouse a revoke), a
|
|
60
|
+
* ClickHouse mutation or rebuild (lint's statement rules, whatever their
|
|
61
|
+
* level or silences), and any Op or manual step.
|
|
62
|
+
*/
|
|
63
|
+
export declare function destructiveSteps(dialect: "clickhouse" | "postgres", pending: ReadonlyArray<Pick<PendingMigration, "id"> & {
|
|
64
|
+
migration: Pick<Migration, "document">;
|
|
65
|
+
}>): DestructiveStep[];
|
|
66
|
+
/** What `--wave-plan` writes: chant's plan file, and what yodel needs to apply exactly it. */
|
|
67
|
+
export interface WavePlanFile {
|
|
68
|
+
/** chant's: what the wave's set digest is taken over. */
|
|
69
|
+
planDigest: string;
|
|
70
|
+
destructive: boolean;
|
|
71
|
+
empty: boolean;
|
|
72
|
+
environment: string;
|
|
73
|
+
/** The migrations' plan digest, as `yodel apply --digest` prints it; the history records it. */
|
|
74
|
+
migrations: string;
|
|
75
|
+
/** The gate policy at the base commit, and that commit. */
|
|
76
|
+
gate: WaveGate;
|
|
77
|
+
base: string;
|
|
78
|
+
pending: string[];
|
|
79
|
+
destructiveSteps: DestructiveStep[];
|
|
80
|
+
/** Each environment this one requires, and the pending migrations its history records applied. */
|
|
81
|
+
requires: Array<{
|
|
82
|
+
env: string;
|
|
83
|
+
applied: Array<{
|
|
84
|
+
id: string;
|
|
85
|
+
checksum: string;
|
|
86
|
+
}>;
|
|
87
|
+
}>;
|
|
88
|
+
/** The `before` steps' outcomes (#64), when the environment has steps and something is pending. */
|
|
89
|
+
steps?: StepOutcome[];
|
|
90
|
+
/** The `before` steps that failed with `onFailure: "approve"`: the wave waits for a person whatever its gate, and the digest covers them. */
|
|
91
|
+
held?: string[];
|
|
92
|
+
}
|
|
93
|
+
/** The wave plan's digest: over what the run would do and the policy it is gated under. */
|
|
94
|
+
export declare function wavePlanDigest(subject: {
|
|
95
|
+
environment: string;
|
|
96
|
+
migrations: string;
|
|
97
|
+
gate: WaveGate;
|
|
98
|
+
destructive: boolean;
|
|
99
|
+
requires?: ReadonlyArray<{
|
|
100
|
+
env: string;
|
|
101
|
+
applied: ReadonlyArray<{
|
|
102
|
+
id: string;
|
|
103
|
+
checksum: string;
|
|
104
|
+
}>;
|
|
105
|
+
}>;
|
|
106
|
+
held?: readonly string[];
|
|
107
|
+
}): Promise<string>;
|
|
108
|
+
/** The gate policy for `env` at the base commit. */
|
|
109
|
+
export declare function gateAtBase(project: Project, env: string): Promise<{
|
|
110
|
+
gate: WaveGate;
|
|
111
|
+
at: ConfigAtBase;
|
|
112
|
+
declared: boolean;
|
|
113
|
+
}>;
|
|
114
|
+
export { RECORD_PLANS_ENV } from "../approval-mode/index.js";
|
|
115
|
+
/** Options of {@link planWave}. */
|
|
116
|
+
export interface PlanWaveOptions {
|
|
117
|
+
allowOutOfOrder: boolean;
|
|
118
|
+
/**
|
|
119
|
+
* The plan a pull request's review approves (#48, `--record-plans`): what
|
|
120
|
+
* the wave will plan after the merge, once each environment it requires
|
|
121
|
+
* has applied the pending migrations (so the promotion is taken as met,
|
|
122
|
+
* and checked when the wave runs), with no `before` step run.
|
|
123
|
+
*/
|
|
124
|
+
forReview?: boolean;
|
|
125
|
+
}
|
|
126
|
+
/** The wave plan for one environment, now. A refusal (out of order, a checksum) throws, as `--digest` does. */
|
|
127
|
+
export declare function planWave(project: Project, venv: VersionedEnv, ordered: readonly Migration[], options: PlanWaveOptions): Promise<WavePlanFile>;
|
|
128
|
+
/**
|
|
129
|
+
* The wave plan digest `--wave-plan` would write for `venv` now, without
|
|
130
|
+
* running the `before` steps: what `yodel plan` and `yodel approve` show of
|
|
131
|
+
* a tenant set's wave (#56), where each tenant is a run. A plan with nothing
|
|
132
|
+
* pending requires nothing, as {@link planWave} has it; a pending migration
|
|
133
|
+
* the required environment has not applied makes it `blocked`, where
|
|
134
|
+
* `planWave` would refuse.
|
|
135
|
+
*/
|
|
136
|
+
export declare function viewWavePlan(project: Project, venv: VersionedEnv, ordered: readonly Migration[], options?: {
|
|
137
|
+
allowOutOfOrder?: boolean;
|
|
138
|
+
}): Promise<{
|
|
139
|
+
planDigest: string;
|
|
140
|
+
destructive: boolean;
|
|
141
|
+
empty: boolean;
|
|
142
|
+
pending: string[];
|
|
143
|
+
destructiveSteps: DestructiveStep[];
|
|
144
|
+
promotions: Promotion[];
|
|
145
|
+
blocked: boolean;
|
|
146
|
+
}>;
|
|
147
|
+
/** `--wave-apply` found the wave plan moved since the wave was decided: nothing was applied. */
|
|
148
|
+
export declare class WavePlanMoved extends YodelError {
|
|
149
|
+
constructor(message: string);
|
|
150
|
+
}
|
|
151
|
+
/** `--wave-plan <file>`: writes the plan file. */
|
|
152
|
+
export declare function writeWavePlan(project: Project, venv: VersionedEnv, ordered: readonly Migration[], file: string, options: PlanWaveOptions): Promise<WavePlanFile>;
|
|
153
|
+
/**
|
|
154
|
+
* Throws unless chant's decision for `env`'s wave lets `planDigest` apply:
|
|
155
|
+
* the decision names this plan for `env`, and either the gate ledger holds
|
|
156
|
+
* an approval of the wave's set digest, or the wave's policy at the base
|
|
157
|
+
* commit needs no approval for these plans. A plan with `held` steps (#64)
|
|
158
|
+
* needs that approval whatever the policy.
|
|
159
|
+
*/
|
|
160
|
+
export declare function verifyWaveDecision(cwd: string, specFile: string, env: string, planDigest: string, held?: readonly string[]): Promise<{
|
|
161
|
+
line: string;
|
|
162
|
+
wave: WaveRecord;
|
|
163
|
+
}>;
|
|
164
|
+
/** chant's decision for a wave, as `chant run wave` will make it once this plan is written. */
|
|
165
|
+
export interface WaveForecast {
|
|
166
|
+
wave: number;
|
|
167
|
+
name: string;
|
|
168
|
+
gate: string;
|
|
169
|
+
digest: string;
|
|
170
|
+
policy: string;
|
|
171
|
+
base: string;
|
|
172
|
+
/** No approval of `digest` stands and the policy asks for one: chant exits 3. */
|
|
173
|
+
waits: boolean;
|
|
174
|
+
/** `chant approve <op> <gate> --plan <digest>`, with `--sign` when the wave is sealed. */
|
|
175
|
+
approve: string;
|
|
176
|
+
/** Which approvals count (#48), read at the base with the policy. */
|
|
177
|
+
approval: ApprovalMode;
|
|
178
|
+
}
|
|
179
|
+
/** Where chant run wave writes a run's plan file (chant's opWavePlanPath). */
|
|
180
|
+
export declare const wavePlanPath: (op: string, wave: number, target: string) => string;
|
|
181
|
+
/**
|
|
182
|
+
* The decision chant makes for `plan`'s wave right after `--wave-plan`,
|
|
183
|
+
* made the same way and from the same sources: the policy from the spec at
|
|
184
|
+
* yodel's base, chant's own rule, and the gate ledger for an approval of
|
|
185
|
+
* the wave's set digest. A tenant set's wave (#56) has a run per tenant,
|
|
186
|
+
* which chant plans in order: the forecast is made at the last run, from
|
|
187
|
+
* the plan files the runs before it wrote; at any other run it is
|
|
188
|
+
* undefined.
|
|
189
|
+
*/
|
|
190
|
+
export declare function forecastWave(cwd: string, specFile: string, plan: Pick<WavePlanFile, "planDigest" | "destructive" | "empty" | "environment" | "held">): Promise<WaveForecast | undefined>;
|
|
191
|
+
/** Reads a plan file `--wave-plan` wrote. */
|
|
192
|
+
export declare function readWavePlan(file: string): WavePlanFile;
|
|
193
|
+
/** What `yodel plan` and the pull request comment show of an environment's wave. */
|
|
194
|
+
export interface WaveView {
|
|
195
|
+
/** 1-based, in yodel.config.ts `waves`. */
|
|
196
|
+
wave: number;
|
|
197
|
+
gate: WaveGate;
|
|
198
|
+
/** The base commit the gate was read at, and how it was chosen. */
|
|
199
|
+
base: string;
|
|
200
|
+
baseSource: string;
|
|
201
|
+
destructiveSteps: DestructiveStep[];
|
|
202
|
+
/** Under the gate at the base, the wave waits for an approval of this plan. */
|
|
203
|
+
waits: boolean;
|
|
204
|
+
/** The wave plan digest and the set digest the wave's gate binds. */
|
|
205
|
+
planDigest: string;
|
|
206
|
+
digest: string;
|
|
207
|
+
/** `chant approve yodel-apply yodel-apply-wave-<k> --plan <digest>`, with `--sign` when the wave is sealed. */
|
|
208
|
+
approve: string;
|
|
209
|
+
/** Which approvals of the digest count (#48), as the base commit's yodel.config.ts has the wave; absent, `ledger`. */
|
|
210
|
+
approval?: ApprovalMode | undefined;
|
|
211
|
+
/** What each required environment's history records of the pending migrations. */
|
|
212
|
+
promotions: Promotion[];
|
|
213
|
+
/** A pending migration has not run where it must first: the wave refuses until it has, so there is nothing to approve yet. */
|
|
214
|
+
blocked: boolean;
|
|
215
|
+
}
|
|
216
|
+
/**
|
|
217
|
+
* `env`'s wave, as the apply pipeline will plan it after the merge, when
|
|
218
|
+
* yodel.config.ts declares waves and names `env`; undefined otherwise. The
|
|
219
|
+
* gate is the base commit's: on a pull request, the merge base with the
|
|
220
|
+
* target branch.
|
|
221
|
+
*/
|
|
222
|
+
export declare function waveView(project: Project, env: string, ordered: readonly Migration[], pending: readonly PendingMigration[], migrations: string): Promise<WaveView | undefined>;
|
|
223
|
+
/** One line on the wave, for text and markdown. */
|
|
224
|
+
export declare function describeWave(w: WaveView, code?: (s: string) => string): string;
|
|
@@ -0,0 +1,353 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Environment waves (#42): the apply pipeline as chant's Op waves, one wave
|
|
3
|
+
* per environment, each behind a gate policy of its own.
|
|
4
|
+
*
|
|
5
|
+
* The project's CI script renders `yodel.config.ts` `waves` as a chant Op
|
|
6
|
+
* waves spec, {@link WAVES_SPEC_FILE}, and the pipeline from it (`chant run
|
|
7
|
+
* --generate <forge> --spec`, or `generateOpWavesPipeline`). Every wave's
|
|
8
|
+
* job runs `chant run wave`, which calls back into yodel twice per
|
|
9
|
+
* environment:
|
|
10
|
+
*
|
|
11
|
+
* - `yodel apply <env> --wave-plan <file>` ({@link writeWavePlan}): the
|
|
12
|
+
* plan file chant binds the wave's gate to. Its `planDigest` covers the
|
|
13
|
+
* migrations' plan digest (what `--digest` prints), the environment's gate
|
|
14
|
+
* policy as the base commit's `yodel.config.ts` has it, and whether a
|
|
15
|
+
* pending step is destructive, which is what `on-destructive` waits on.
|
|
16
|
+
* - `yodel apply <env> --wave-apply <file>` ({@link applyWave}): applies
|
|
17
|
+
* that plan, under the lock, only when chant's decision for the wave lets
|
|
18
|
+
* it through: an approval of the wave's set digest in the gate ledger, or
|
|
19
|
+
* a policy at the base commit that needs none. The plan is computed again
|
|
20
|
+
* under the lock and must be the one the decision was made on.
|
|
21
|
+
*
|
|
22
|
+
* chant reads the policy from the spec file at the first parent, so a pull
|
|
23
|
+
* request that loosens its own environment's gate merges under the old one.
|
|
24
|
+
*/
|
|
25
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
26
|
+
import { dirname, join, resolve } from "node:path";
|
|
27
|
+
import { baseRef, loadYodelConfigAtBase } from "../config/base.js";
|
|
28
|
+
import { createdObjects, stepFindings } from "../lint/statements.js";
|
|
29
|
+
import { YodelError } from "../project.js";
|
|
30
|
+
import { describeStep } from "../steps/kinds.js";
|
|
31
|
+
import { EXIT_REFUSED } from "../exit.js";
|
|
32
|
+
import { currentDigest, inspect, selectToApply } from "../versioned/engine.js";
|
|
33
|
+
import { gitLedger, NOT_APPROVED, verifyApproval } from "../versioned/approval.js";
|
|
34
|
+
import { WAVES_PIPELINE } from "./record.js";
|
|
35
|
+
import { enforcePromotion, promotionEvidence, readPromotions, requiredEnvironments } from "./promotion.js";
|
|
36
|
+
import { baseEnvironment, splitTarget } from "../tenants/target.js";
|
|
37
|
+
import { heldSteps, runApplySteps, stepsAtBase, stepsRefusal } from "../apply-steps/index.js";
|
|
38
|
+
import { describeApprovalMode, portFor, signFlag, waveApproval } from "../approval-mode/index.js";
|
|
39
|
+
export * from "./promotion.js";
|
|
40
|
+
export * from "./record.js";
|
|
41
|
+
/** The waves spec the CI script writes and every wave job reads. */
|
|
42
|
+
export const WAVES_SPEC_FILE = "yodel-waves.json";
|
|
43
|
+
export const WAVE_PLAN_KIND = "yodel-wave";
|
|
44
|
+
/** The gate policy an environment no wave names gets. */
|
|
45
|
+
export const DEFAULT_WAVE_GATE = "always";
|
|
46
|
+
/** `env`'s wave gate in `config`; a tenant's is its environment's. */
|
|
47
|
+
export function waveGate(config, env) {
|
|
48
|
+
const wave = config?.waves?.find((w) => w.env === baseEnvironment(env));
|
|
49
|
+
return wave ? { gate: wave.gate ?? DEFAULT_WAVE_GATE, declared: true } : { gate: DEFAULT_WAVE_GATE, declared: false };
|
|
50
|
+
}
|
|
51
|
+
const GATED_RULES = new Set(["destructive", "pg-rewrite", "pg-lock", "pg-access", "ch-mutation", "ch-rebuild", "ch-access"]);
|
|
52
|
+
/**
|
|
53
|
+
* The steps of `pending` that `on-destructive` waits on: a destructive
|
|
54
|
+
* statement or Op step, a Postgres rewrite or lock-heavy statement, one
|
|
55
|
+
* that takes access away (a revoke, a policy dropped, row-level security
|
|
56
|
+
* turned off; on ClickHouse a revoke), a
|
|
57
|
+
* ClickHouse mutation or rebuild (lint's statement rules, whatever their
|
|
58
|
+
* level or silences), and any Op or manual step.
|
|
59
|
+
*/
|
|
60
|
+
export function destructiveSteps(dialect, pending) {
|
|
61
|
+
const out = [];
|
|
62
|
+
for (const p of pending) {
|
|
63
|
+
const created = dialect === "postgres" ? createdObjects(p.migration.document.steps) : undefined;
|
|
64
|
+
p.migration.document.steps.forEach((step, i) => {
|
|
65
|
+
const rules = stepFindings(dialect, step, created ? { created } : {}).map((f) => f.rule).filter((r) => GATED_RULES.has(r));
|
|
66
|
+
const why = rules[0] ?? (step.kind === "op" ? "op-step" : step.kind === "manual" ? "manual-step" : undefined);
|
|
67
|
+
if (why)
|
|
68
|
+
out.push({ migration: p.id, step: i, what: describeStep(step), why: [...new Set([why, ...rules])].join(", ") });
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
return out;
|
|
72
|
+
}
|
|
73
|
+
/** The wave plan's digest: over what the run would do and the policy it is gated under. */
|
|
74
|
+
export async function wavePlanDigest(subject) {
|
|
75
|
+
const { computePlanDigest } = await import("@intentius/chant/lifecycle/plan-digest");
|
|
76
|
+
return computePlanDigest(WAVE_PLAN_KIND, subject);
|
|
77
|
+
}
|
|
78
|
+
/** The gate policy for `env` at the base commit. */
|
|
79
|
+
export async function gateAtBase(project, env) {
|
|
80
|
+
const at = await loadYodelConfigAtBase(project.dir);
|
|
81
|
+
return { ...waveGate(at.config, env), at };
|
|
82
|
+
}
|
|
83
|
+
export { RECORD_PLANS_ENV } from "../approval-mode/index.js";
|
|
84
|
+
/** The promotion evidence a wave plans with once each environment it requires has applied `pending`: what {@link enforcePromotion} lets through. */
|
|
85
|
+
async function evidenceAfterPromotion(project, env, pending) {
|
|
86
|
+
if (pending.length === 0)
|
|
87
|
+
return [];
|
|
88
|
+
return (await requiredEnvironments(project, env)).map((r) => ({ env: r.env, applied: pending.map((p) => ({ id: p.id, checksum: p.checksum })) }));
|
|
89
|
+
}
|
|
90
|
+
/** The wave plan for one environment, now. A refusal (out of order, a checksum) throws, as `--digest` does. */
|
|
91
|
+
export async function planWave(project, venv, ordered, options) {
|
|
92
|
+
const { status } = await inspect(venv, ordered);
|
|
93
|
+
const toApply = selectToApply(status, options.allowOutOfOrder);
|
|
94
|
+
// Before the gate: a pending migration the required environment has not applied refuses here (exit 4).
|
|
95
|
+
// Planned for a review (#48), the wave is planned as after the merge, when the environments before it have run.
|
|
96
|
+
const requires = options.forReview === true ? await evidenceAfterPromotion(project, venv.environment, toApply) : promotionEvidence(await enforcePromotion(project, venv.environment, ordered, toApply));
|
|
97
|
+
const migrations = await currentDigest(venv, status, toApply);
|
|
98
|
+
const { gate, at } = await gateAtBase(project, venv.environment);
|
|
99
|
+
const steps = destructiveSteps(project.dialect === "postgres" ? "postgres" : "clickhouse", toApply);
|
|
100
|
+
// The before steps (#64), as the base commit declares them: one that fails refuses; one that fails with onFailure approve holds the wave for a person.
|
|
101
|
+
// A command is not run here (#107): it may have side effects, so it runs once, under the lock (yodel apply --wave-apply), and the plan shows that it will.
|
|
102
|
+
const before = toApply.length > 0 && options.forReview !== true ? ((await stepsAtBase(project, venv.environment)).steps?.before ?? []) : [];
|
|
103
|
+
const outcomes = before.length > 0 ? await runApplySteps(venv.driver, before, "before", { dir: project.dir, env: venv.environment, commands: "plan" }) : [];
|
|
104
|
+
const refusal = stepsRefusal(venv.environment, outcomes, "as the wave was planned");
|
|
105
|
+
if (refusal)
|
|
106
|
+
throw refusal;
|
|
107
|
+
const held = heldSteps(outcomes);
|
|
108
|
+
// A held step is what on-destructive waits on too; the gate never is held by --wave-apply (verifyWaveDecision).
|
|
109
|
+
const destructive = steps.length > 0 || held.length > 0;
|
|
110
|
+
return {
|
|
111
|
+
planDigest: await wavePlanDigest({ environment: venv.environment, migrations, gate, destructive, requires, ...(held.length > 0 ? { held } : {}) }),
|
|
112
|
+
destructive,
|
|
113
|
+
empty: toApply.length === 0,
|
|
114
|
+
environment: venv.environment,
|
|
115
|
+
migrations,
|
|
116
|
+
gate,
|
|
117
|
+
base: at.sha,
|
|
118
|
+
pending: toApply.map((p) => p.id),
|
|
119
|
+
destructiveSteps: steps,
|
|
120
|
+
requires,
|
|
121
|
+
...(outcomes.length > 0 ? { steps: outcomes } : {}),
|
|
122
|
+
...(held.length > 0 ? { held } : {}),
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* The wave plan digest `--wave-plan` would write for `venv` now, without
|
|
127
|
+
* running the `before` steps: what `yodel plan` and `yodel approve` show of
|
|
128
|
+
* a tenant set's wave (#56), where each tenant is a run. A plan with nothing
|
|
129
|
+
* pending requires nothing, as {@link planWave} has it; a pending migration
|
|
130
|
+
* the required environment has not applied makes it `blocked`, where
|
|
131
|
+
* `planWave` would refuse.
|
|
132
|
+
*/
|
|
133
|
+
export async function viewWavePlan(project, venv, ordered, options = {}) {
|
|
134
|
+
const { status } = await inspect(venv, ordered, { sync: false });
|
|
135
|
+
const toApply = selectToApply(status, options.allowOutOfOrder === true);
|
|
136
|
+
const promotions = toApply.length > 0 ? await readPromotions(project, venv.environment, ordered, toApply) : [];
|
|
137
|
+
const blocked = promotions.some((p) => p.migrations.some((m) => m.ran !== "applied"));
|
|
138
|
+
const migrations = await currentDigest(venv, status, toApply);
|
|
139
|
+
const { gate } = await gateAtBase(project, venv.environment);
|
|
140
|
+
const steps = destructiveSteps(project.dialect === "postgres" ? "postgres" : "clickhouse", toApply);
|
|
141
|
+
const destructive = steps.length > 0;
|
|
142
|
+
return {
|
|
143
|
+
planDigest: await wavePlanDigest({ environment: venv.environment, migrations, gate, destructive, requires: promotionEvidence(promotions) }),
|
|
144
|
+
destructive,
|
|
145
|
+
empty: toApply.length === 0,
|
|
146
|
+
pending: toApply.map((p) => p.id),
|
|
147
|
+
destructiveSteps: steps,
|
|
148
|
+
promotions,
|
|
149
|
+
blocked,
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
/** `--wave-apply` found the wave plan moved since the wave was decided: nothing was applied. */
|
|
153
|
+
export class WavePlanMoved extends YodelError {
|
|
154
|
+
constructor(message) {
|
|
155
|
+
super(message, 1);
|
|
156
|
+
this.name = "WavePlanMoved";
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
/** `--wave-plan <file>`: writes the plan file. */
|
|
160
|
+
export async function writeWavePlan(project, venv, ordered, file, options) {
|
|
161
|
+
const plan = await planWave(project, venv, ordered, options);
|
|
162
|
+
const path = resolve(file);
|
|
163
|
+
mkdirSync(dirname(path), { recursive: true });
|
|
164
|
+
writeFileSync(path, `${JSON.stringify(plan, null, 2)}\n`);
|
|
165
|
+
return plan;
|
|
166
|
+
}
|
|
167
|
+
// eslint-disable-next-line @typescript-eslint/no-unnecessary-type-parameters -- the caller names the document's shape
|
|
168
|
+
function readJson(path, what) {
|
|
169
|
+
if (!existsSync(path))
|
|
170
|
+
throw new YodelError(`refused: ${what} is not at ${path}. --wave-apply runs inside chant run wave, after it decided the wave; nothing was applied`, NOT_APPROVED);
|
|
171
|
+
try {
|
|
172
|
+
return JSON.parse(readFileSync(path, "utf8"));
|
|
173
|
+
}
|
|
174
|
+
catch (e) {
|
|
175
|
+
throw new YodelError(`${path}: ${e instanceof Error ? e.message : String(e)}`);
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
/** The wave `env` is in, by the spec. */
|
|
179
|
+
function waveOf(spec, env, specFile) {
|
|
180
|
+
const i = spec.waves.findIndex((w) => w.runs.some((r) => r.target === env));
|
|
181
|
+
const wave = spec.waves[i];
|
|
182
|
+
if (!wave)
|
|
183
|
+
throw new YodelError(`refused: ${specFile} has no wave for ${env}; nothing was applied`, EXIT_REFUSED);
|
|
184
|
+
return { k: i + 1, name: wave.name };
|
|
185
|
+
}
|
|
186
|
+
/**
|
|
187
|
+
* Throws unless chant's decision for `env`'s wave lets `planDigest` apply:
|
|
188
|
+
* the decision names this plan for `env`, and either the gate ledger holds
|
|
189
|
+
* an approval of the wave's set digest, or the wave's policy at the base
|
|
190
|
+
* commit needs no approval for these plans. A plan with `held` steps (#64)
|
|
191
|
+
* needs that approval whatever the policy.
|
|
192
|
+
*/
|
|
193
|
+
export async function verifyWaveDecision(cwd, specFile, env, planDigest, held = []) {
|
|
194
|
+
const specPath = resolve(cwd, specFile);
|
|
195
|
+
const spec = readJson(specPath, "the waves spec");
|
|
196
|
+
const { k, name } = waveOf(spec, env, specFile);
|
|
197
|
+
const decision = readJson(join(cwd, ".chant", "op-waves", spec.name, `wave-${String(k)}.json`), `chant's decision for wave ${String(k)} (${name})`);
|
|
198
|
+
const member = decision.members.find((m) => m.member === env);
|
|
199
|
+
if (decision.wave !== k || !member || member.planDigest !== planDigest) {
|
|
200
|
+
throw new YodelError(`refused: chant decided wave ${String(k)} (${name}) on ${member ? `plan ${member.planDigest}` : `no plan for ${env}`}, and ${env}'s plan is now ${planDigest}. Nothing was applied; run the wave again.`, NOT_APPROVED);
|
|
201
|
+
}
|
|
202
|
+
const { waveSetDigest } = await import("@intentius/chant/gated-waves");
|
|
203
|
+
if (waveSetDigest(decision.members) !== decision.digest)
|
|
204
|
+
throw new YodelError(`refused: chant's decision for wave ${String(k)} does not hold the digest of its own members; nothing was applied`, NOT_APPROVED);
|
|
205
|
+
// The policy and the approval mode (#48), asked of chant's rule again rather than trusting the record,
|
|
206
|
+
// at yodel's base (../config/base.ts), never at a base the spec in the change names.
|
|
207
|
+
const { opWaveNeedsApproval, readOpWavePolicy } = await import("@intentius/chant/op");
|
|
208
|
+
const policy = readOpWavePolicy(specFile, name, baseRef(cwd).ref, cwd);
|
|
209
|
+
const ledger = async () => portFor(await gitLedger(cwd), policy.approval, cwd, policy.base);
|
|
210
|
+
const how = policy.approval === "ledger" ? "" : `, approval ${policy.approval}`;
|
|
211
|
+
if (decision.status === "approved") {
|
|
212
|
+
const by = await verifyApproval(await ledger(), { name: spec.name, filePath: specFile, gate: decision.gate, quorum: false }, decision.digest);
|
|
213
|
+
return {
|
|
214
|
+
line: `wave ${String(k)} (${name}): approved by ${by.resolvedBy} at ${by.timestamp}${how}${by.review ? ` (the review of #${String(by.review.pr)} at ${by.review.head.slice(0, 12)})` : ""}`,
|
|
215
|
+
wave: { wave: k, op: spec.name, gate: decision.gate, digest: decision.digest, status: "approved", ...evidence(policy.approval, by) },
|
|
216
|
+
};
|
|
217
|
+
}
|
|
218
|
+
if (held.length > 0) {
|
|
219
|
+
// A before step failed with onFailure approve: a person approves this plan, whatever the gate.
|
|
220
|
+
try {
|
|
221
|
+
const by = await verifyApproval(await ledger(), { name: spec.name, filePath: specFile, gate: decision.gate, quorum: false }, decision.digest);
|
|
222
|
+
return {
|
|
223
|
+
line: `wave ${String(k)} (${name}): held by ${held.join(", ")}, approved by ${by.resolvedBy} at ${by.timestamp}${how}`,
|
|
224
|
+
wave: { wave: k, op: spec.name, gate: decision.gate, digest: decision.digest, status: "approved", ...evidence(policy.approval, by) },
|
|
225
|
+
};
|
|
226
|
+
}
|
|
227
|
+
catch {
|
|
228
|
+
throw new YodelError(`refused: ${env}'s before step${held.length === 1 ? "" : "s"} ${held.join(", ")} failed with onFailure approve, so wave ${String(k)} (${name}) waits for a person under any gate. Nothing was applied. ` +
|
|
229
|
+
`Approve it with: chant approve ${spec.name} ${decision.gate} --plan ${decision.digest}${signFlag(policy.approval)}`, NOT_APPROVED);
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
if (decision.status !== "not-required" || opWaveNeedsApproval(policy.policy, decision.members)) {
|
|
233
|
+
throw new YodelError(`refused: wave ${String(k)} (${name}) needs an approval of ${decision.digest} under gate ${policy.policy}${how} (read at ${policy.base.slice(0, 12)}), and chant's gate ledger has none${policy.approval === "sealed" ? " that is sealed by a signer at base" : ""}. Nothing was applied.`, NOT_APPROVED);
|
|
234
|
+
}
|
|
235
|
+
return {
|
|
236
|
+
line: `wave ${String(k)} (${name}): gate ${policy.policy} at ${policy.base.slice(0, 12)} needs no approval for this plan`,
|
|
237
|
+
wave: { wave: k, op: spec.name, gate: decision.gate, digest: decision.digest, status: "not-required", policy: policy.policy, base: policy.base },
|
|
238
|
+
};
|
|
239
|
+
}
|
|
240
|
+
/** What the history records of how an approval counted (#48): the mode, the review or the signer. */
|
|
241
|
+
function evidence(approval, by) {
|
|
242
|
+
return {
|
|
243
|
+
...(approval !== "ledger" ? { approval } : {}),
|
|
244
|
+
...(by.review ? { review: by.review } : {}),
|
|
245
|
+
...(by.signer !== undefined ? { signer: by.signer } : {}),
|
|
246
|
+
};
|
|
247
|
+
}
|
|
248
|
+
/** Where chant run wave writes a run's plan file (chant's opWavePlanPath). */
|
|
249
|
+
export const wavePlanPath = (op, wave, target) => `.chant/op-waves/${op}/wave-${String(wave)}/${target.replace(/[^A-Za-z0-9._-]/g, "_")}.plan.json`;
|
|
250
|
+
/**
|
|
251
|
+
* The decision chant makes for `plan`'s wave right after `--wave-plan`,
|
|
252
|
+
* made the same way and from the same sources: the policy from the spec at
|
|
253
|
+
* yodel's base, chant's own rule, and the gate ledger for an approval of
|
|
254
|
+
* the wave's set digest. A tenant set's wave (#56) has a run per tenant,
|
|
255
|
+
* which chant plans in order: the forecast is made at the last run, from
|
|
256
|
+
* the plan files the runs before it wrote; at any other run it is
|
|
257
|
+
* undefined.
|
|
258
|
+
*/
|
|
259
|
+
export async function forecastWave(cwd, specFile, plan) {
|
|
260
|
+
const spec = readJson(resolve(cwd, specFile), "the waves spec");
|
|
261
|
+
const { k, name } = waveOf(spec, plan.environment, specFile);
|
|
262
|
+
const { waveSetDigest, waveGateName } = await import("@intentius/chant/gated-waves");
|
|
263
|
+
const { opWaveNeedsApproval, readOpWavePolicy } = await import("@intentius/chant/op");
|
|
264
|
+
const member = (target, p) => ({ member: target, planDigest: p.planDigest, op: "", ...(p.destructive ? { destructive: true } : {}), ...(p.empty ? { empty: true } : {}) });
|
|
265
|
+
const runs = spec.waves[k - 1]?.runs ?? [];
|
|
266
|
+
if (runs.at(-1)?.target !== plan.environment)
|
|
267
|
+
return undefined;
|
|
268
|
+
const members = [];
|
|
269
|
+
for (const run of runs) {
|
|
270
|
+
if (run.target === plan.environment) {
|
|
271
|
+
members.push(member(run.target, plan));
|
|
272
|
+
continue;
|
|
273
|
+
}
|
|
274
|
+
const path = join(cwd, wavePlanPath(spec.name, k, run.target));
|
|
275
|
+
if (!existsSync(path))
|
|
276
|
+
return undefined;
|
|
277
|
+
members.push(member(run.target, JSON.parse(readFileSync(path, "utf8"))));
|
|
278
|
+
}
|
|
279
|
+
const digest = waveSetDigest(members);
|
|
280
|
+
const gate = waveGateName(spec.gate ?? spec.name, k);
|
|
281
|
+
const policy = readOpWavePolicy(specFile, name, baseRef(cwd).ref, cwd);
|
|
282
|
+
let waits = opWaveNeedsApproval(policy.policy, members) || (plan.held?.length ?? 0) > 0;
|
|
283
|
+
if (waits) {
|
|
284
|
+
try {
|
|
285
|
+
await verifyApproval(await portFor(await gitLedger(cwd), policy.approval, cwd, policy.base), { name: spec.name, filePath: specFile, gate, quorum: false }, digest);
|
|
286
|
+
waits = false;
|
|
287
|
+
}
|
|
288
|
+
catch {
|
|
289
|
+
// No approval of this digest that counts: chant waits (under pr-review, unless the merged pull request's review approves it).
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
return { wave: k, name, gate, digest, policy: policy.policy, base: policy.base, waits, approve: `chant approve ${spec.name} ${gate} --plan ${digest}${signFlag(policy.approval)}`, approval: policy.approval };
|
|
293
|
+
}
|
|
294
|
+
/** Reads a plan file `--wave-plan` wrote. */
|
|
295
|
+
export function readWavePlan(file) {
|
|
296
|
+
const plan = readJson(resolve(file), "the wave's plan file");
|
|
297
|
+
if (typeof plan.planDigest !== "string" || typeof plan.migrations !== "string")
|
|
298
|
+
throw new YodelError(`${file} is not a plan yodel apply --wave-plan wrote`);
|
|
299
|
+
return plan;
|
|
300
|
+
}
|
|
301
|
+
/**
|
|
302
|
+
* `env`'s wave, as the apply pipeline will plan it after the merge, when
|
|
303
|
+
* yodel.config.ts declares waves and names `env`; undefined otherwise. The
|
|
304
|
+
* gate is the base commit's: on a pull request, the merge base with the
|
|
305
|
+
* target branch.
|
|
306
|
+
*/
|
|
307
|
+
export async function waveView(project, env, ordered, pending, migrations) {
|
|
308
|
+
// A tenant's wave is its whole set's, which one tenant's plan does not make (#56).
|
|
309
|
+
if (splitTarget(env).tenant !== undefined)
|
|
310
|
+
return undefined;
|
|
311
|
+
const k = (project.yodel?.config.waves ?? []).findIndex((w) => w.env === env) + 1;
|
|
312
|
+
if (k === 0)
|
|
313
|
+
return undefined;
|
|
314
|
+
const { gate, at } = await gateAtBase(project, env);
|
|
315
|
+
const steps = destructiveSteps(project.dialect === "postgres" ? "postgres" : "clickhouse", pending);
|
|
316
|
+
const destructive = steps.length > 0;
|
|
317
|
+
const promotions = await readPromotions(project, env, ordered, pending);
|
|
318
|
+
const blocked = promotions.some((p) => p.migrations.some((m) => m.ran !== "applied"));
|
|
319
|
+
const planDigest = await wavePlanDigest({ environment: env, migrations, gate, destructive, requires: promotionEvidence(promotions) });
|
|
320
|
+
const { waveSetDigest, waveGateName } = await import("@intentius/chant/gated-waves");
|
|
321
|
+
const digest = waveSetDigest([{ member: env, planDigest }]);
|
|
322
|
+
const waits = !blocked && pending.length > 0 && (gate === "always" || (gate === "on-destructive" && destructive));
|
|
323
|
+
const approval = waveApproval(at.config, env);
|
|
324
|
+
return {
|
|
325
|
+
wave: k,
|
|
326
|
+
gate,
|
|
327
|
+
base: at.sha,
|
|
328
|
+
baseSource: at.base.source,
|
|
329
|
+
destructiveSteps: steps,
|
|
330
|
+
waits,
|
|
331
|
+
planDigest,
|
|
332
|
+
digest,
|
|
333
|
+
approve: `chant approve ${WAVES_PIPELINE} ${waveGateName(WAVES_PIPELINE, k)} --plan ${digest}${signFlag(approval)}`,
|
|
334
|
+
...(approval !== "ledger" ? { approval } : {}),
|
|
335
|
+
promotions,
|
|
336
|
+
blocked,
|
|
337
|
+
};
|
|
338
|
+
}
|
|
339
|
+
/** One line on the wave, for text and markdown. */
|
|
340
|
+
export function describeWave(w, code = (s) => s) {
|
|
341
|
+
const at = `gate ${code(w.gate)} at ${code(w.base.slice(0, 12))}`;
|
|
342
|
+
if (w.blocked) {
|
|
343
|
+
const envs = [...new Set(w.promotions.filter((p) => p.migrations.some((m) => m.ran !== "applied")).map((p) => code(p.env)))];
|
|
344
|
+
return `The apply pipeline refuses this in wave ${String(w.wave)} until ${envs.join(" and ")} ${envs.length === 1 ? "has" : "have"} applied it; approve it then (${at}).`;
|
|
345
|
+
}
|
|
346
|
+
if (!w.waits) {
|
|
347
|
+
const why = w.gate === "never" ? "" : `: nothing pending is destructive`;
|
|
348
|
+
return `The apply pipeline applies this in wave ${String(w.wave)} with no approval (${at}${why}).`;
|
|
349
|
+
}
|
|
350
|
+
const why = w.gate === "on-destructive" ? `: ${w.destructiveSteps.map((d) => `${d.migration} step ${String(d.step)} (${d.why})`).join(", ")}` : "";
|
|
351
|
+
return `The apply pipeline waits in wave ${String(w.wave)} for an approval of ${code(w.digest)} (${at}${why}).${w.approval && w.approval !== "ledger" ? ` ${describeApprovalMode(w.approval, code)}` : ""}`;
|
|
352
|
+
}
|
|
353
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/waves/index.ts"],"names":[],"mappings":"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"}
|