@intentius/sql-yodeler 0.2.0 → 0.3.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/chant/build.js +7 -0
- package/dist/chant/build.js.map +1 -1
- package/dist/ci/render.d.ts +289 -0
- package/dist/ci/render.js +688 -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/commands/apply.d.ts +113 -0
- package/dist/commands/apply.js +228 -21
- package/dist/commands/apply.js.map +1 -1
- package/dist/commands/approve.js +44 -0
- 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 +118 -0
- package/dist/commands/ci.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 +18 -2
- package/dist/commands/drift.js.map +1 -1
- package/dist/commands/index.js +10 -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 +11 -3
- package/dist/commands/lint.js.map +1 -1
- package/dist/commands/override.js +48 -16
- package/dist/commands/override.js.map +1 -1
- package/dist/commands/plan.js +5 -1
- 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/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 +123 -1
- package/dist/config/define.js +2 -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 +90 -5
- package/dist/config/load.js.map +1 -1
- package/dist/config/settings.d.ts +4 -1
- package/dist/config/settings.js +8 -2
- package/dist/config/settings.js.map +1 -1
- 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 +4 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/lint/check.js +17 -2
- package/dist/lint/check.js.map +1 -1
- 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 +16 -4
- package/dist/lint/rules.js +9 -4
- package/dist/lint/rules.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 +110 -0
- package/dist/migrations/checkpoint.js.map +1 -0
- package/dist/migrations/create.js +2 -0
- 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 +31 -0
- package/dist/migrations/routines.js +55 -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 +3 -0
- package/dist/pr/model.js +16 -2
- package/dist/pr/model.js.map +1 -1
- package/dist/pr/render.js +27 -1
- package/dist/pr/render.js.map +1 -1
- package/dist/project.d.ts +19 -2
- package/dist/project.js +33 -1
- package/dist/project.js.map +1 -1
- package/dist/replay/replay.d.ts +9 -1
- 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 +202 -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 +5 -2
- package/dist/schemas.js +5 -2
- package/dist/schemas.js.map +1 -1
- package/dist/sources/ddl.d.ts +53 -0
- package/dist/sources/ddl.js +347 -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/postgres-migration.d.ts +35 -25
- package/dist/steps/postgres-migration.js +42 -29
- package/dist/steps/postgres-migration.js.map +1 -1
- package/dist/tenants/index.d.ts +40 -0
- package/dist/tenants/index.js +122 -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/versioned/engine.d.ts +6 -0
- package/dist/versioned/engine.js +12 -2
- package/dist/versioned/engine.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 +36 -3
- package/dist/versioned/state.js.map +1 -1
- package/dist/waves/index.d.ts +179 -0
- package/dist/waves/index.js +268 -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 +24 -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/drift.schema.json +26 -1
- package/schemas/event.schema.json +36 -0
- package/schemas/lint.schema.json +4 -0
- package/schemas/plan.schema.json +118 -0
- package/schemas/report.schema.json +114 -0
- package/schemas/revert.schema.json +69 -0
- package/schemas/status.schema.json +64 -3
|
@@ -0,0 +1,179 @@
|
|
|
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 { 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
|
+
export * from "./promotion.js";
|
|
34
|
+
export * from "./record.js";
|
|
35
|
+
/** The waves spec the CI script writes and every wave job reads. */
|
|
36
|
+
export declare const WAVES_SPEC_FILE = "yodel-waves.json";
|
|
37
|
+
export declare const WAVE_PLAN_KIND = "yodel-wave";
|
|
38
|
+
/** The gate policy an environment no wave names gets. */
|
|
39
|
+
export declare const DEFAULT_WAVE_GATE: WaveGate;
|
|
40
|
+
/** `env`'s wave gate in `config`; a tenant's is its environment's. */
|
|
41
|
+
export declare function waveGate(config: YodelConfig | undefined, env: string): {
|
|
42
|
+
gate: WaveGate;
|
|
43
|
+
declared: boolean;
|
|
44
|
+
};
|
|
45
|
+
/** A pending step `on-destructive` waits on, and why. */
|
|
46
|
+
export interface DestructiveStep {
|
|
47
|
+
migration: string;
|
|
48
|
+
step: number;
|
|
49
|
+
what: string;
|
|
50
|
+
/** The lint rule that classes it, or `op-step` / `manual-step`. */
|
|
51
|
+
why: string;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* The steps of `pending` that `on-destructive` waits on: a destructive
|
|
55
|
+
* statement or Op step, a Postgres rewrite or lock-heavy statement, a
|
|
56
|
+
* ClickHouse mutation or rebuild (lint's statement rules, whatever their
|
|
57
|
+
* level or silences), and any Op or manual step.
|
|
58
|
+
*/
|
|
59
|
+
export declare function destructiveSteps(dialect: "clickhouse" | "postgres", pending: ReadonlyArray<Pick<PendingMigration, "id"> & {
|
|
60
|
+
migration: Pick<Migration, "document">;
|
|
61
|
+
}>): DestructiveStep[];
|
|
62
|
+
/** What `--wave-plan` writes: chant's plan file, and what yodel needs to apply exactly it. */
|
|
63
|
+
export interface WavePlanFile {
|
|
64
|
+
/** chant's: what the wave's set digest is taken over. */
|
|
65
|
+
planDigest: string;
|
|
66
|
+
destructive: boolean;
|
|
67
|
+
empty: boolean;
|
|
68
|
+
environment: string;
|
|
69
|
+
/** The migrations' plan digest, as `yodel apply --digest` prints it; the history records it. */
|
|
70
|
+
migrations: string;
|
|
71
|
+
/** The gate policy at the base commit, and that commit. */
|
|
72
|
+
gate: WaveGate;
|
|
73
|
+
base: string;
|
|
74
|
+
pending: string[];
|
|
75
|
+
destructiveSteps: DestructiveStep[];
|
|
76
|
+
/** Each environment this one requires, and the pending migrations its history records applied. */
|
|
77
|
+
requires: Array<{
|
|
78
|
+
env: string;
|
|
79
|
+
applied: Array<{
|
|
80
|
+
id: string;
|
|
81
|
+
checksum: string;
|
|
82
|
+
}>;
|
|
83
|
+
}>;
|
|
84
|
+
}
|
|
85
|
+
/** The wave plan's digest: over what the run would do and the policy it is gated under. */
|
|
86
|
+
export declare function wavePlanDigest(subject: {
|
|
87
|
+
environment: string;
|
|
88
|
+
migrations: string;
|
|
89
|
+
gate: WaveGate;
|
|
90
|
+
destructive: boolean;
|
|
91
|
+
requires?: ReadonlyArray<{
|
|
92
|
+
env: string;
|
|
93
|
+
applied: ReadonlyArray<{
|
|
94
|
+
id: string;
|
|
95
|
+
checksum: string;
|
|
96
|
+
}>;
|
|
97
|
+
}>;
|
|
98
|
+
}): Promise<string>;
|
|
99
|
+
/** The gate policy for `env` at the base commit. */
|
|
100
|
+
export declare function gateAtBase(project: Project, env: string): Promise<{
|
|
101
|
+
gate: WaveGate;
|
|
102
|
+
at: ConfigAtBase;
|
|
103
|
+
declared: boolean;
|
|
104
|
+
}>;
|
|
105
|
+
/** The wave plan for one environment, now. A refusal (out of order, a checksum) throws, as `--digest` does. */
|
|
106
|
+
export declare function planWave(project: Project, venv: VersionedEnv, ordered: readonly Migration[], options: {
|
|
107
|
+
allowOutOfOrder: boolean;
|
|
108
|
+
}): Promise<WavePlanFile>;
|
|
109
|
+
/** `--wave-plan <file>`: writes the plan file. */
|
|
110
|
+
export declare function writeWavePlan(project: Project, venv: VersionedEnv, ordered: readonly Migration[], file: string, options: {
|
|
111
|
+
allowOutOfOrder: boolean;
|
|
112
|
+
}): Promise<WavePlanFile>;
|
|
113
|
+
/**
|
|
114
|
+
* Throws unless chant's decision for `env`'s wave lets `planDigest` apply:
|
|
115
|
+
* the decision names this plan for `env`, and either the gate ledger holds
|
|
116
|
+
* an approval of the wave's set digest, or the wave's policy at the base
|
|
117
|
+
* commit needs no approval for these plans.
|
|
118
|
+
*/
|
|
119
|
+
export declare function verifyWaveDecision(cwd: string, specFile: string, env: string, planDigest: string): Promise<{
|
|
120
|
+
line: string;
|
|
121
|
+
wave: WaveRecord;
|
|
122
|
+
}>;
|
|
123
|
+
/** chant's decision for a wave, as `chant run wave` will make it once this plan is written. */
|
|
124
|
+
export interface WaveForecast {
|
|
125
|
+
wave: number;
|
|
126
|
+
name: string;
|
|
127
|
+
gate: string;
|
|
128
|
+
digest: string;
|
|
129
|
+
policy: string;
|
|
130
|
+
base: string;
|
|
131
|
+
/** No approval of `digest` stands and the policy asks for one: chant exits 3. */
|
|
132
|
+
waits: boolean;
|
|
133
|
+
/** `chant approve <op> <gate> --plan <digest>`. */
|
|
134
|
+
approve: string;
|
|
135
|
+
}
|
|
136
|
+
/** Where chant run wave writes a run's plan file (chant's opWavePlanPath). */
|
|
137
|
+
export declare const wavePlanPath: (op: string, wave: number, target: string) => string;
|
|
138
|
+
/**
|
|
139
|
+
* The decision chant makes for `plan`'s wave right after `--wave-plan`,
|
|
140
|
+
* made the same way and from the same sources: the policy from the spec at
|
|
141
|
+
* yodel's base, chant's own rule, and the gate ledger for an approval of
|
|
142
|
+
* the wave's set digest. A tenant set's wave (#56) has a run per tenant,
|
|
143
|
+
* which chant plans in order: the forecast is made at the last run, from
|
|
144
|
+
* the plan files the runs before it wrote; at any other run it is
|
|
145
|
+
* undefined.
|
|
146
|
+
*/
|
|
147
|
+
export declare function forecastWave(cwd: string, specFile: string, plan: WavePlanFile): Promise<WaveForecast | undefined>;
|
|
148
|
+
/** Reads a plan file `--wave-plan` wrote. */
|
|
149
|
+
export declare function readWavePlan(file: string): WavePlanFile;
|
|
150
|
+
/** What `yodel plan` and the pull request comment show of an environment's wave. */
|
|
151
|
+
export interface WaveView {
|
|
152
|
+
/** 1-based, in yodel.config.ts `waves`. */
|
|
153
|
+
wave: number;
|
|
154
|
+
gate: WaveGate;
|
|
155
|
+
/** The base commit the gate was read at, and how it was chosen. */
|
|
156
|
+
base: string;
|
|
157
|
+
baseSource: string;
|
|
158
|
+
destructiveSteps: DestructiveStep[];
|
|
159
|
+
/** Under the gate at the base, the wave waits for an approval of this plan. */
|
|
160
|
+
waits: boolean;
|
|
161
|
+
/** The wave plan digest and the set digest the wave's gate binds. */
|
|
162
|
+
planDigest: string;
|
|
163
|
+
digest: string;
|
|
164
|
+
/** `chant approve yodel-apply yodel-apply-wave-<k> --plan <digest>`. */
|
|
165
|
+
approve: string;
|
|
166
|
+
/** What each required environment's history records of the pending migrations. */
|
|
167
|
+
promotions: Promotion[];
|
|
168
|
+
/** A pending migration has not run where it must first: the wave refuses until it has, so there is nothing to approve yet. */
|
|
169
|
+
blocked: boolean;
|
|
170
|
+
}
|
|
171
|
+
/**
|
|
172
|
+
* `env`'s wave, as the apply pipeline will plan it after the merge, when
|
|
173
|
+
* yodel.config.ts declares waves and names `env`; undefined otherwise. The
|
|
174
|
+
* gate is the base commit's: on a pull request, the merge base with the
|
|
175
|
+
* target branch.
|
|
176
|
+
*/
|
|
177
|
+
export declare function waveView(project: Project, env: string, ordered: readonly Migration[], pending: readonly PendingMigration[], migrations: string): Promise<WaveView | undefined>;
|
|
178
|
+
/** One line on the wave, for text and markdown. */
|
|
179
|
+
export declare function describeWave(w: WaveView, code?: (s: string) => string): string;
|
|
@@ -0,0 +1,268 @@
|
|
|
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 { 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 } from "./promotion.js";
|
|
36
|
+
import { baseEnvironment, splitTarget } from "../tenants/target.js";
|
|
37
|
+
export * from "./promotion.js";
|
|
38
|
+
export * from "./record.js";
|
|
39
|
+
/** The waves spec the CI script writes and every wave job reads. */
|
|
40
|
+
export const WAVES_SPEC_FILE = "yodel-waves.json";
|
|
41
|
+
export const WAVE_PLAN_KIND = "yodel-wave";
|
|
42
|
+
/** The gate policy an environment no wave names gets. */
|
|
43
|
+
export const DEFAULT_WAVE_GATE = "always";
|
|
44
|
+
/** `env`'s wave gate in `config`; a tenant's is its environment's. */
|
|
45
|
+
export function waveGate(config, env) {
|
|
46
|
+
const wave = config?.waves?.find((w) => w.env === baseEnvironment(env));
|
|
47
|
+
return wave ? { gate: wave.gate ?? DEFAULT_WAVE_GATE, declared: true } : { gate: DEFAULT_WAVE_GATE, declared: false };
|
|
48
|
+
}
|
|
49
|
+
const GATED_RULES = new Set(["destructive", "pg-rewrite", "pg-lock", "ch-mutation", "ch-rebuild"]);
|
|
50
|
+
/**
|
|
51
|
+
* The steps of `pending` that `on-destructive` waits on: a destructive
|
|
52
|
+
* statement or Op step, a Postgres rewrite or lock-heavy statement, a
|
|
53
|
+
* ClickHouse mutation or rebuild (lint's statement rules, whatever their
|
|
54
|
+
* level or silences), and any Op or manual step.
|
|
55
|
+
*/
|
|
56
|
+
export function destructiveSteps(dialect, pending) {
|
|
57
|
+
const out = [];
|
|
58
|
+
for (const p of pending) {
|
|
59
|
+
p.migration.document.steps.forEach((step, i) => {
|
|
60
|
+
const rules = stepFindings(dialect, step).map((f) => f.rule).filter((r) => GATED_RULES.has(r));
|
|
61
|
+
const why = rules[0] ?? (step.kind === "op" ? "op-step" : step.kind === "manual" ? "manual-step" : undefined);
|
|
62
|
+
if (why)
|
|
63
|
+
out.push({ migration: p.id, step: i, what: describeStep(step), why: [...new Set([why, ...rules])].join(", ") });
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
return out;
|
|
67
|
+
}
|
|
68
|
+
/** The wave plan's digest: over what the run would do and the policy it is gated under. */
|
|
69
|
+
export async function wavePlanDigest(subject) {
|
|
70
|
+
const { computePlanDigest } = await import("@intentius/chant/lifecycle/plan-digest");
|
|
71
|
+
return computePlanDigest(WAVE_PLAN_KIND, subject);
|
|
72
|
+
}
|
|
73
|
+
/** The gate policy for `env` at the base commit. */
|
|
74
|
+
export async function gateAtBase(project, env) {
|
|
75
|
+
const at = await loadYodelConfigAtBase(project.dir);
|
|
76
|
+
return { ...waveGate(at.config, env), at };
|
|
77
|
+
}
|
|
78
|
+
/** The wave plan for one environment, now. A refusal (out of order, a checksum) throws, as `--digest` does. */
|
|
79
|
+
export async function planWave(project, venv, ordered, options) {
|
|
80
|
+
const { status } = await inspect(venv, ordered);
|
|
81
|
+
const toApply = selectToApply(status, options.allowOutOfOrder);
|
|
82
|
+
// Before the gate: a pending migration the required environment has not applied refuses here (exit 4).
|
|
83
|
+
const requires = promotionEvidence(await enforcePromotion(project, venv.environment, ordered, toApply));
|
|
84
|
+
const migrations = await currentDigest(venv, status, toApply);
|
|
85
|
+
const { gate, at } = await gateAtBase(project, venv.environment);
|
|
86
|
+
const steps = destructiveSteps(project.dialect === "postgres" ? "postgres" : "clickhouse", toApply);
|
|
87
|
+
const destructive = steps.length > 0;
|
|
88
|
+
return {
|
|
89
|
+
planDigest: await wavePlanDigest({ environment: venv.environment, migrations, gate, destructive, requires }),
|
|
90
|
+
destructive,
|
|
91
|
+
empty: toApply.length === 0,
|
|
92
|
+
environment: venv.environment,
|
|
93
|
+
migrations,
|
|
94
|
+
gate,
|
|
95
|
+
base: at.sha,
|
|
96
|
+
pending: toApply.map((p) => p.id),
|
|
97
|
+
destructiveSteps: steps,
|
|
98
|
+
requires,
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
/** `--wave-plan <file>`: writes the plan file. */
|
|
102
|
+
export async function writeWavePlan(project, venv, ordered, file, options) {
|
|
103
|
+
const plan = await planWave(project, venv, ordered, options);
|
|
104
|
+
const path = resolve(file);
|
|
105
|
+
mkdirSync(dirname(path), { recursive: true });
|
|
106
|
+
writeFileSync(path, `${JSON.stringify(plan, null, 2)}\n`);
|
|
107
|
+
return plan;
|
|
108
|
+
}
|
|
109
|
+
// eslint-disable-next-line @typescript-eslint/no-unnecessary-type-parameters -- the caller names the document's shape
|
|
110
|
+
function readJson(path, what) {
|
|
111
|
+
if (!existsSync(path))
|
|
112
|
+
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);
|
|
113
|
+
try {
|
|
114
|
+
return JSON.parse(readFileSync(path, "utf8"));
|
|
115
|
+
}
|
|
116
|
+
catch (e) {
|
|
117
|
+
throw new YodelError(`${path}: ${e instanceof Error ? e.message : String(e)}`);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
/** The wave `env` is in, by the spec. */
|
|
121
|
+
function waveOf(spec, env, specFile) {
|
|
122
|
+
const i = spec.waves.findIndex((w) => w.runs.some((r) => r.target === env));
|
|
123
|
+
const wave = spec.waves[i];
|
|
124
|
+
if (!wave)
|
|
125
|
+
throw new YodelError(`refused: ${specFile} has no wave for ${env}; nothing was applied`, EXIT_REFUSED);
|
|
126
|
+
return { k: i + 1, name: wave.name };
|
|
127
|
+
}
|
|
128
|
+
/**
|
|
129
|
+
* Throws unless chant's decision for `env`'s wave lets `planDigest` apply:
|
|
130
|
+
* the decision names this plan for `env`, and either the gate ledger holds
|
|
131
|
+
* an approval of the wave's set digest, or the wave's policy at the base
|
|
132
|
+
* commit needs no approval for these plans.
|
|
133
|
+
*/
|
|
134
|
+
export async function verifyWaveDecision(cwd, specFile, env, planDigest) {
|
|
135
|
+
const specPath = resolve(cwd, specFile);
|
|
136
|
+
const spec = readJson(specPath, "the waves spec");
|
|
137
|
+
const { k, name } = waveOf(spec, env, specFile);
|
|
138
|
+
const decision = readJson(join(cwd, ".chant", "op-waves", spec.name, `wave-${String(k)}.json`), `chant's decision for wave ${String(k)} (${name})`);
|
|
139
|
+
const member = decision.members.find((m) => m.member === env);
|
|
140
|
+
if (decision.wave !== k || !member || member.planDigest !== planDigest) {
|
|
141
|
+
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);
|
|
142
|
+
}
|
|
143
|
+
const { waveSetDigest } = await import("@intentius/chant/gated-waves");
|
|
144
|
+
if (waveSetDigest(decision.members) !== decision.digest)
|
|
145
|
+
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);
|
|
146
|
+
if (decision.status === "approved") {
|
|
147
|
+
const by = await verifyApproval(await gitLedger(cwd), { name: spec.name, filePath: specFile, gate: decision.gate, quorum: false }, decision.digest);
|
|
148
|
+
return {
|
|
149
|
+
line: `wave ${String(k)} (${name}): approved by ${by.resolvedBy} at ${by.timestamp}`,
|
|
150
|
+
wave: { wave: k, op: spec.name, gate: decision.gate, digest: decision.digest, status: "approved" },
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
// Not required: ask chant's rule again rather than trust the record, at yodel's base
|
|
154
|
+
// (../config/base.ts), never at a base the spec in the change names.
|
|
155
|
+
const { opWaveNeedsApproval, readOpWavePolicy } = await import("@intentius/chant/op");
|
|
156
|
+
const policy = readOpWavePolicy(specFile, name, baseRef(cwd).ref, cwd);
|
|
157
|
+
if (decision.status !== "not-required" || opWaveNeedsApproval(policy.policy, decision.members)) {
|
|
158
|
+
throw new YodelError(`refused: wave ${String(k)} (${name}) needs an approval of ${decision.digest} under gate ${policy.policy} (read at ${policy.base.slice(0, 12)}), and chant's gate ledger has none. Nothing was applied.`, NOT_APPROVED);
|
|
159
|
+
}
|
|
160
|
+
return {
|
|
161
|
+
line: `wave ${String(k)} (${name}): gate ${policy.policy} at ${policy.base.slice(0, 12)} needs no approval for this plan`,
|
|
162
|
+
wave: { wave: k, op: spec.name, gate: decision.gate, digest: decision.digest, status: "not-required", policy: policy.policy, base: policy.base },
|
|
163
|
+
};
|
|
164
|
+
}
|
|
165
|
+
/** Where chant run wave writes a run's plan file (chant's opWavePlanPath). */
|
|
166
|
+
export const wavePlanPath = (op, wave, target) => `.chant/op-waves/${op}/wave-${String(wave)}/${target.replace(/[^A-Za-z0-9._-]/g, "_")}.plan.json`;
|
|
167
|
+
/**
|
|
168
|
+
* The decision chant makes for `plan`'s wave right after `--wave-plan`,
|
|
169
|
+
* made the same way and from the same sources: the policy from the spec at
|
|
170
|
+
* yodel's base, chant's own rule, and the gate ledger for an approval of
|
|
171
|
+
* the wave's set digest. A tenant set's wave (#56) has a run per tenant,
|
|
172
|
+
* which chant plans in order: the forecast is made at the last run, from
|
|
173
|
+
* the plan files the runs before it wrote; at any other run it is
|
|
174
|
+
* undefined.
|
|
175
|
+
*/
|
|
176
|
+
export async function forecastWave(cwd, specFile, plan) {
|
|
177
|
+
const spec = readJson(resolve(cwd, specFile), "the waves spec");
|
|
178
|
+
const { k, name } = waveOf(spec, plan.environment, specFile);
|
|
179
|
+
const { waveSetDigest, waveGateName } = await import("@intentius/chant/gated-waves");
|
|
180
|
+
const { opWaveNeedsApproval, readOpWavePolicy } = await import("@intentius/chant/op");
|
|
181
|
+
const member = (target, p) => ({ member: target, planDigest: p.planDigest, op: "", ...(p.destructive ? { destructive: true } : {}), ...(p.empty ? { empty: true } : {}) });
|
|
182
|
+
const runs = spec.waves[k - 1]?.runs ?? [];
|
|
183
|
+
if (runs.at(-1)?.target !== plan.environment)
|
|
184
|
+
return undefined;
|
|
185
|
+
const members = [];
|
|
186
|
+
for (const run of runs) {
|
|
187
|
+
if (run.target === plan.environment) {
|
|
188
|
+
members.push(member(run.target, plan));
|
|
189
|
+
continue;
|
|
190
|
+
}
|
|
191
|
+
const path = join(cwd, wavePlanPath(spec.name, k, run.target));
|
|
192
|
+
if (!existsSync(path))
|
|
193
|
+
return undefined;
|
|
194
|
+
members.push(member(run.target, JSON.parse(readFileSync(path, "utf8"))));
|
|
195
|
+
}
|
|
196
|
+
const digest = waveSetDigest(members);
|
|
197
|
+
const gate = waveGateName(spec.gate ?? spec.name, k);
|
|
198
|
+
const policy = readOpWavePolicy(specFile, name, baseRef(cwd).ref, cwd);
|
|
199
|
+
let waits = opWaveNeedsApproval(policy.policy, members);
|
|
200
|
+
if (waits) {
|
|
201
|
+
try {
|
|
202
|
+
await verifyApproval(await gitLedger(cwd), { name: spec.name, filePath: specFile, gate, quorum: false }, digest);
|
|
203
|
+
waits = false;
|
|
204
|
+
}
|
|
205
|
+
catch {
|
|
206
|
+
// No approval of this digest: chant waits.
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
return { wave: k, name, gate, digest, policy: policy.policy, base: policy.base, waits, approve: `chant approve ${spec.name} ${gate} --plan ${digest}` };
|
|
210
|
+
}
|
|
211
|
+
/** Reads a plan file `--wave-plan` wrote. */
|
|
212
|
+
export function readWavePlan(file) {
|
|
213
|
+
const plan = readJson(resolve(file), "the wave's plan file");
|
|
214
|
+
if (typeof plan.planDigest !== "string" || typeof plan.migrations !== "string")
|
|
215
|
+
throw new YodelError(`${file} is not a plan yodel apply --wave-plan wrote`);
|
|
216
|
+
return plan;
|
|
217
|
+
}
|
|
218
|
+
/**
|
|
219
|
+
* `env`'s wave, as the apply pipeline will plan it after the merge, when
|
|
220
|
+
* yodel.config.ts declares waves and names `env`; undefined otherwise. The
|
|
221
|
+
* gate is the base commit's: on a pull request, the merge base with the
|
|
222
|
+
* target branch.
|
|
223
|
+
*/
|
|
224
|
+
export async function waveView(project, env, ordered, pending, migrations) {
|
|
225
|
+
// A tenant's wave is its whole set's, which one tenant's plan does not make (#56).
|
|
226
|
+
if (splitTarget(env).tenant !== undefined)
|
|
227
|
+
return undefined;
|
|
228
|
+
const k = (project.yodel?.config.waves ?? []).findIndex((w) => w.env === env) + 1;
|
|
229
|
+
if (k === 0)
|
|
230
|
+
return undefined;
|
|
231
|
+
const { gate, at } = await gateAtBase(project, env);
|
|
232
|
+
const steps = destructiveSteps(project.dialect === "postgres" ? "postgres" : "clickhouse", pending);
|
|
233
|
+
const destructive = steps.length > 0;
|
|
234
|
+
const promotions = await readPromotions(project, env, ordered, pending);
|
|
235
|
+
const blocked = promotions.some((p) => p.migrations.some((m) => m.ran !== "applied"));
|
|
236
|
+
const planDigest = await wavePlanDigest({ environment: env, migrations, gate, destructive, requires: promotionEvidence(promotions) });
|
|
237
|
+
const { waveSetDigest, waveGateName } = await import("@intentius/chant/gated-waves");
|
|
238
|
+
const digest = waveSetDigest([{ member: env, planDigest }]);
|
|
239
|
+
const waits = !blocked && pending.length > 0 && (gate === "always" || (gate === "on-destructive" && destructive));
|
|
240
|
+
return {
|
|
241
|
+
wave: k,
|
|
242
|
+
gate,
|
|
243
|
+
base: at.sha,
|
|
244
|
+
baseSource: at.base.source,
|
|
245
|
+
destructiveSteps: steps,
|
|
246
|
+
waits,
|
|
247
|
+
planDigest,
|
|
248
|
+
digest,
|
|
249
|
+
approve: `chant approve ${WAVES_PIPELINE} ${waveGateName(WAVES_PIPELINE, k)} --plan ${digest}`,
|
|
250
|
+
promotions,
|
|
251
|
+
blocked,
|
|
252
|
+
};
|
|
253
|
+
}
|
|
254
|
+
/** One line on the wave, for text and markdown. */
|
|
255
|
+
export function describeWave(w, code = (s) => s) {
|
|
256
|
+
const at = `gate ${code(w.gate)} at ${code(w.base.slice(0, 12))}`;
|
|
257
|
+
if (w.blocked) {
|
|
258
|
+
const envs = [...new Set(w.promotions.filter((p) => p.migrations.some((m) => m.ran !== "applied")).map((p) => code(p.env)))];
|
|
259
|
+
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}).`;
|
|
260
|
+
}
|
|
261
|
+
if (!w.waits) {
|
|
262
|
+
const why = w.gate === "never" ? "" : `: nothing pending is destructive`;
|
|
263
|
+
return `The apply pipeline applies this in wave ${String(w.wave)} with no approval (${at}${why}).`;
|
|
264
|
+
}
|
|
265
|
+
const why = w.gate === "on-destructive" ? `: ${w.destructiveSteps.map((d) => `${d.migration} step ${String(d.step)} (${d.why})`).join(", ")}` : "";
|
|
266
|
+
return `The apply pipeline waits in wave ${String(w.wave)} for an approval of ${code(w.digest)} (${at}${why}).`;
|
|
267
|
+
}
|
|
268
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/waves/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAEH,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAC7E,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAEnD,OAAO,EAAE,OAAO,EAAE,qBAAqB,EAAqB,MAAM,mBAAmB,CAAC;AAEtF,OAAO,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AAErD,OAAO,EAAE,UAAU,EAAgB,MAAM,eAAe,CAAC;AACzD,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AACjD,OAAO,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAC1C,OAAO,EAAE,aAAa,EAAE,OAAO,EAAE,aAAa,EAAqB,MAAM,wBAAwB,CAAC;AAClG,OAAO,EAAE,SAAS,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAC;AAEnF,OAAO,EAAE,cAAc,EAAmB,MAAM,aAAa,CAAC;AAC9D,OAAO,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,cAAc,EAAkB,MAAM,gBAAgB,CAAC;AACrG,OAAO,EAAE,eAAe,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAEpE,cAAc,gBAAgB,CAAC;AAC/B,cAAc,aAAa,CAAC;AAE5B,oEAAoE;AACpE,MAAM,CAAC,MAAM,eAAe,GAAG,kBAAkB,CAAC;AAClD,MAAM,CAAC,MAAM,cAAc,GAAG,YAAY,CAAC;AAE3C,yDAAyD;AACzD,MAAM,CAAC,MAAM,iBAAiB,GAAa,QAAQ,CAAC;AAEpD,sEAAsE;AACtE,MAAM,UAAU,QAAQ,CAAC,MAA+B,EAAE,GAAW;IACnE,MAAM,IAAI,GAAG,MAAM,EAAE,KAAK,EAAE,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,GAAG,KAAK,eAAe,CAAC,GAAG,CAAC,CAAC,CAAC;IACxE,OAAO,IAAI,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,IAAI,iBAAiB,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,iBAAiB,EAAE,QAAQ,EAAE,KAAK,EAAE,CAAC;AACxH,CAAC;AAWD,MAAM,WAAW,GAAG,IAAI,GAAG,CAAC,CAAC,aAAa,EAAE,YAAY,EAAE,SAAS,EAAE,aAAa,EAAE,YAAY,CAAC,CAAC,CAAC;AAEnG;;;;;GAKG;AACH,MAAM,UAAU,gBAAgB,CAAC,OAAkC,EAAE,OAAiG;IACpK,MAAM,GAAG,GAAsB,EAAE,CAAC;IAClC,KAAK,MAAM,CAAC,IAAI,OAAO,EAAE,CAAC;QACxB,CAAC,CAAC,SAAS,CAAC,QAAQ,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,IAAmB,EAAE,CAAS,EAAE,EAAE;YACpE,MAAM,KAAK,GAAG,YAAY,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;YAC/F,MAAM,GAAG,GAAG,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,KAAK,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC;YAC9G,IAAI,GAAG;gBAAE,GAAG,CAAC,IAAI,CAAC,EAAE,SAAS,EAAE,CAAC,CAAC,EAAE,EAAE,IAAI,EAAE,CAAC,EAAE,IAAI,EAAE,YAAY,CAAC,IAAI,CAAC,EAAE,GAAG,EAAE,CAAC,GAAG,IAAI,GAAG,CAAC,CAAC,GAAG,EAAE,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QAC3H,CAAC,CAAC,CAAC;IACL,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC;AAoBD,2FAA2F;AAC3F,MAAM,CAAC,KAAK,UAAU,cAAc,CAAC,OAMpC;IACC,MAAM,EAAE,iBAAiB,EAAE,GAAG,MAAM,MAAM,CAAC,wCAAwC,CAAC,CAAC;IACrF,OAAO,iBAAiB,CAAC,cAAc,EAAE,OAAO,CAAC,CAAC;AACpD,CAAC;AAED,oDAAoD;AACpD,MAAM,CAAC,KAAK,UAAU,UAAU,CAAC,OAAgB,EAAE,GAAW;IAC5D,MAAM,EAAE,GAAG,MAAM,qBAAqB,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;IACpD,OAAO,EAAE,GAAG,QAAQ,CAAC,EAAE,CAAC,MAAM,EAAE,GAAG,CAAC,EAAE,EAAE,EAAE,CAAC;AAC7C,CAAC;AAED,+GAA+G;AAC/G,MAAM,CAAC,KAAK,UAAU,QAAQ,CAAC,OAAgB,EAAE,IAAkB,EAAE,OAA6B,EAAE,OAAqC;IACvI,MAAM,EAAE,MAAM,EAAE,GAAG,MAAM,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;IAChD,MAAM,OAAO,GAAG,aAAa,CAAC,MAAM,EAAE,OAAO,CAAC,eAAe,CAAC,CAAC;IAC/D,uGAAuG;IACvG,MAAM,QAAQ,GAAG,iBAAiB,CAAC,MAAM,gBAAgB,CAAC,OAAO,EAAE,IAAI,CAAC,WAAW,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC;IACxG,MAAM,UAAU,GAAG,MAAM,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,CAAC;IAC9D,MAAM,EAAE,IAAI,EAAE,EAAE,EAAE,GAAG,MAAM,UAAU,CAAC,OAAO,EAAE,IAAI,CAAC,WAAW,CAAC,CAAC;IACjE,MAAM,KAAK,GAAG,gBAAgB,CAAC,OAAO,CAAC,OAAO,KAAK,UAAU,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,YAAY,EAAE,OAAO,CAAC,CAAC;IACpG,MAAM,WAAW,GAAG,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC;IACrC,OAAO;QACL,UAAU,EAAE,MAAM,cAAc,CAAC,EAAE,WAAW,EAAE,IAAI,CAAC,WAAW,EAAE,UAAU,EAAE,IAAI,EAAE,WAAW,EAAE,QAAQ,EAAE,CAAC;QAC5G,WAAW;QACX,KAAK,EAAE,OAAO,CAAC,MAAM,KAAK,CAAC;QAC3B,WAAW,EAAE,IAAI,CAAC,WAAW;QAC7B,UAAU;QACV,IAAI;QACJ,IAAI,EAAE,EAAE,CAAC,GAAG;QACZ,OAAO,EAAE,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QACjC,gBAAgB,EAAE,KAAK;QACvB,QAAQ;KACT,CAAC;AACJ,CAAC;AAED,kDAAkD;AAClD,MAAM,CAAC,KAAK,UAAU,aAAa,CAAC,OAAgB,EAAE,IAAkB,EAAE,OAA6B,EAAE,IAAY,EAAE,OAAqC;IAC1J,MAAM,IAAI,GAAG,MAAM,QAAQ,CAAC,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC;IAC7D,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC3B,SAAS,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAC9C,aAAa,CAAC,IAAI,EAAE,GAAG,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC;IAC1D,OAAO,IAAI,CAAC;AACd,CAAC;AAkBD,sHAAsH;AACtH,SAAS,QAAQ,CAAI,IAAY,EAAE,IAAY;IAC7C,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC;QAAE,MAAM,IAAI,UAAU,CAAC,YAAY,IAAI,cAAc,IAAI,2FAA2F,EAAE,YAAY,CAAC,CAAC;IACzL,IAAI,CAAC;QACH,OAAO,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,IAAI,EAAE,MAAM,CAAC,CAAM,CAAC;IACrD,CAAC;IAAC,OAAO,CAAC,EAAE,CAAC;QACX,MAAM,IAAI,UAAU,CAAC,GAAG,IAAI,KAAK,CAAC,YAAY,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;IACjF,CAAC;AACH,CAAC;AAED,yCAAyC;AACzC,SAAS,MAAM,CAAC,IAAe,EAAE,GAAW,EAAE,QAAgB;IAC5D,MAAM,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,KAAK,GAAG,CAAC,CAAC,CAAC;IAC5E,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;IAC3B,IAAI,CAAC,IAAI;QAAE,MAAM,IAAI,UAAU,CAAC,YAAY,QAAQ,oBAAoB,GAAG,uBAAuB,EAAE,YAAY,CAAC,CAAC;IAClH,OAAO,EAAE,CAAC,EAAE,CAAC,GAAG,CAAC,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,CAAC;AACvC,CAAC;AAED;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,kBAAkB,CAAC,GAAW,EAAE,QAAgB,EAAE,GAAW,EAAE,UAAkB;IACrG,MAAM,QAAQ,GAAG,OAAO,CAAC,GAAG,EAAE,QAAQ,CAAC,CAAC;IACxC,MAAM,IAAI,GAAG,QAAQ,CAAY,QAAQ,EAAE,gBAAgB,CAAC,CAAC;IAC7D,MAAM,EAAE,CAAC,EAAE,IAAI,EAAE,GAAG,MAAM,CAAC,IAAI,EAAE,GAAG,EAAE,QAAQ,CAAC,CAAC;IAChD,MAAM,QAAQ,GAAG,QAAQ,CAAe,IAAI,CAAC,GAAG,EAAE,QAAQ,EAAE,UAAU,EAAE,IAAI,CAAC,IAAI,EAAE,QAAQ,MAAM,CAAC,CAAC,CAAC,OAAO,CAAC,EAAE,6BAA6B,MAAM,CAAC,CAAC,CAAC,KAAK,IAAI,GAAG,CAAC,CAAC;IAClK,MAAM,MAAM,GAAG,QAAQ,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,KAAK,GAAG,CAAC,CAAC;IAC9D,IAAI,QAAQ,CAAC,IAAI,KAAK,CAAC,IAAI,CAAC,MAAM,IAAI,MAAM,CAAC,UAAU,KAAK,UAAU,EAAE,CAAC;QACvE,MAAM,IAAI,UAAU,CAClB,+BAA+B,MAAM,CAAC,CAAC,CAAC,KAAK,IAAI,QAAQ,MAAM,CAAC,CAAC,CAAC,QAAQ,MAAM,CAAC,UAAU,EAAE,CAAC,CAAC,CAAC,eAAe,GAAG,EAAE,SAAS,GAAG,kBAAkB,UAAU,4CAA4C,EACxM,YAAY,CACb,CAAC;IACJ,CAAC;IACD,MAAM,EAAE,aAAa,EAAE,GAAG,MAAM,MAAM,CAAC,8BAA8B,CAAC,CAAC;IACvE,IAAI,aAAa,CAAC,QAAQ,CAAC,OAAO,CAAC,KAAK,QAAQ,CAAC,MAAM;QAAE,MAAM,IAAI,UAAU,CAAC,sCAAsC,MAAM,CAAC,CAAC,CAAC,mEAAmE,EAAE,YAAY,CAAC,CAAC;IAChN,IAAI,QAAQ,CAAC,MAAM,KAAK,UAAU,EAAE,CAAC;QACnC,MAAM,EAAE,GAAG,MAAM,cAAc,CAAC,MAAM,SAAS,CAAC,GAAG,CAAC,EAAE,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,QAAQ,EAAE,QAAQ,EAAE,IAAI,EAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,EAAE,QAAQ,CAAC,MAAM,CAAC,CAAC;QACpJ,OAAO;YACL,IAAI,EAAE,QAAQ,MAAM,CAAC,CAAC,CAAC,KAAK,IAAI,kBAAkB,EAAE,CAAC,UAAU,OAAO,EAAE,CAAC,SAAS,EAAE;YACpF,IAAI,EAAE,EAAE,IAAI,EAAE,CAAC,EAAE,EAAE,EAAE,IAAI,CAAC,IAAI,EAAE,IAAI,EAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,CAAC,MAAM,EAAE,MAAM,EAAE,UAAU,EAAE;SACnG,CAAC;IACJ,CAAC;IACD,qFAAqF;IACrF,qEAAqE;IACrE,MAAM,EAAE,mBAAmB,EAAE,gBAAgB,EAAE,GAAG,MAAM,MAAM,CAAC,qBAAqB,CAAC,CAAC;IACtF,MAAM,MAAM,GAAG,gBAAgB,CAAC,QAAQ,EAAE,IAAI,EAAE,OAAO,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC;IACvE,IAAI,QAAQ,CAAC,MAAM,KAAK,cAAc,IAAI,mBAAmB,CAAC,MAAM,CAAC,MAAM,EAAE,QAAQ,CAAC,OAAgB,CAAC,EAAE,CAAC;QACxG,MAAM,IAAI,UAAU,CAClB,iBAAiB,MAAM,CAAC,CAAC,CAAC,KAAK,IAAI,0BAA0B,QAAQ,CAAC,MAAM,eAAe,MAAM,CAAC,MAAM,aAAa,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,2DAA2D,EACxM,YAAY,CACb,CAAC;IACJ,CAAC;IACD,OAAO;QACL,IAAI,EAAE,QAAQ,MAAM,CAAC,CAAC,CAAC,KAAK,IAAI,WAAW,MAAM,CAAC,MAAM,OAAO,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,kCAAkC;QACzH,IAAI,EAAE,EAAE,IAAI,EAAE,CAAC,EAAE,EAAE,EAAE,IAAI,CAAC,IAAI,EAAE,IAAI,EAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,CAAC,MAAM,EAAE,MAAM,EAAE,cAAc,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,IAAI,EAAE,MAAM,CAAC,IAAI,EAAE;KACjJ,CAAC;AACJ,CAAC;AAgBD,8EAA8E;AAC9E,MAAM,CAAC,MAAM,YAAY,GAAG,CAAC,EAAU,EAAE,IAAY,EAAE,MAAc,EAAU,EAAE,CAAC,mBAAmB,EAAE,SAAS,MAAM,CAAC,IAAI,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,kBAAkB,EAAE,GAAG,CAAC,YAAY,CAAC;AAEpL;;;;;;;;GAQG;AACH,MAAM,CAAC,KAAK,UAAU,YAAY,CAAC,GAAW,EAAE,QAAgB,EAAE,IAAkB;IAClF,MAAM,IAAI,GAAG,QAAQ,CAAgC,OAAO,CAAC,GAAG,EAAE,QAAQ,CAAC,EAAE,gBAAgB,CAAC,CAAC;IAC/F,MAAM,EAAE,CAAC,EAAE,IAAI,EAAE,GAAG,MAAM,CAAC,IAAI,EAAE,IAAI,CAAC,WAAW,EAAE,QAAQ,CAAC,CAAC;IAC7D,MAAM,EAAE,aAAa,EAAE,YAAY,EAAE,GAAG,MAAM,MAAM,CAAC,8BAA8B,CAAC,CAAC;IACrF,MAAM,EAAE,mBAAmB,EAAE,gBAAgB,EAAE,GAAG,MAAM,MAAM,CAAC,qBAAqB,CAAC,CAAC;IACtF,MAAM,MAAM,GAAG,CAAC,MAAc,EAAE,CAA6D,EAAE,EAAE,CAAC,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,UAAU,EAAE,CAAC,CAAC,UAAU,EAAE,EAAE,EAAE,EAAE,EAAE,GAAG,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,EAAE,WAAW,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,GAAG,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC;IAC/O,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,IAAI,EAAE,CAAC;IAC3C,IAAI,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,MAAM,KAAK,IAAI,CAAC,WAAW;QAAE,OAAO,SAAS,CAAC;IAC/D,MAAM,OAAO,GAAG,EAAE,CAAC;IACnB,KAAK,MAAM,GAAG,IAAI,IAAI,EAAE,CAAC;QACvB,IAAI,GAAG,CAAC,MAAM,KAAK,IAAI,CAAC,WAAW,EAAE,CAAC;YACpC,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC,CAAC;YACvC,SAAS;QACX,CAAC;QACD,MAAM,IAAI,GAAG,IAAI,CAAC,GAAG,EAAE,YAAY,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC;QAC/D,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC;YAAE,OAAO,SAAS,CAAC;QACxC,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,MAAM,EAAE,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,IAAI,EAAE,MAAM,CAAC,CAAiB,CAAC,CAAC,CAAC;IAC3F,CAAC;IACD,MAAM,MAAM,GAAG,aAAa,CAAC,OAAO,CAAC,CAAC;IACtC,MAAM,IAAI,GAAG,YAAY,CAAC,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC;IACrD,MAAM,MAAM,GAAG,gBAAgB,CAAC,QAAQ,EAAE,IAAI,EAAE,OAAO,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC;IACvE,IAAI,KAAK,GAAG,mBAAmB,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACxD,IAAI,KAAK,EAAE,CAAC;QACV,IAAI,CAAC;YACH,MAAM,cAAc,CAAC,MAAM,SAAS,CAAC,GAAG,CAAC,EAAE,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,QAAQ,EAAE,QAAQ,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,EAAE,MAAM,CAAC,CAAC;YACjH,KAAK,GAAG,KAAK,CAAC;QAChB,CAAC;QAAC,MAAM,CAAC;YACP,2CAA2C;QAC7C,CAAC;IACH,CAAC;IACD,OAAO,EAAE,IAAI,EAAE,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,IAAI,EAAE,MAAM,CAAC,IAAI,EAAE,KAAK,EAAE,OAAO,EAAE,iBAAiB,IAAI,CAAC,IAAI,IAAI,IAAI,WAAW,MAAM,EAAE,EAAE,CAAC;AAC1J,CAAC;AAED,6CAA6C;AAC7C,MAAM,UAAU,YAAY,CAAC,IAAY;IACvC,MAAM,IAAI,GAAG,QAAQ,CAAe,OAAO,CAAC,IAAI,CAAC,EAAE,sBAAsB,CAAC,CAAC;IAC3E,IAAI,OAAO,IAAI,CAAC,UAAU,KAAK,QAAQ,IAAI,OAAO,IAAI,CAAC,UAAU,KAAK,QAAQ;QAAE,MAAM,IAAI,UAAU,CAAC,GAAG,IAAI,8CAA8C,CAAC,CAAC;IAC5J,OAAO,IAAI,CAAC;AACd,CAAC;AAwBD;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,QAAQ,CAAC,OAAgB,EAAE,GAAW,EAAE,OAA6B,EAAE,OAAoC,EAAE,UAAkB;IACnJ,mFAAmF;IACnF,IAAI,WAAW,CAAC,GAAG,CAAC,CAAC,MAAM,KAAK,SAAS;QAAE,OAAO,SAAS,CAAC;IAC5D,MAAM,CAAC,GAAG,CAAC,OAAO,CAAC,KAAK,EAAE,MAAM,CAAC,KAAK,IAAI,EAAE,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,GAAG,KAAK,GAAG,CAAC,GAAG,CAAC,CAAC;IAClF,IAAI,CAAC,KAAK,CAAC;QAAE,OAAO,SAAS,CAAC;IAC9B,MAAM,EAAE,IAAI,EAAE,EAAE,EAAE,GAAG,MAAM,UAAU,CAAC,OAAO,EAAE,GAAG,CAAC,CAAC;IACpD,MAAM,KAAK,GAAG,gBAAgB,CAAC,OAAO,CAAC,OAAO,KAAK,UAAU,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,YAAY,EAAE,OAAO,CAAC,CAAC;IACpG,MAAM,WAAW,GAAG,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC;IACrC,MAAM,UAAU,GAAG,MAAM,cAAc,CAAC,OAAO,EAAE,GAAG,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC;IACxE,MAAM,OAAO,GAAG,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,GAAG,KAAK,SAAS,CAAC,CAAC,CAAC;IACtF,MAAM,UAAU,GAAG,MAAM,cAAc,CAAC,EAAE,WAAW,EAAE,GAAG,EAAE,UAAU,EAAE,IAAI,EAAE,WAAW,EAAE,QAAQ,EAAE,iBAAiB,CAAC,UAAU,CAAC,EAAE,CAAC,CAAC;IACtI,MAAM,EAAE,aAAa,EAAE,YAAY,EAAE,GAAG,MAAM,MAAM,CAAC,8BAA8B,CAAC,CAAC;IACrF,MAAM,MAAM,GAAG,aAAa,CAAC,CAAC,EAAE,MAAM,EAAE,GAAG,EAAE,UAAU,EAAE,CAAC,CAAC,CAAC;IAC5D,MAAM,KAAK,GAAG,CAAC,OAAO,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC,IAAI,CAAC,IAAI,KAAK,QAAQ,IAAI,CAAC,IAAI,KAAK,gBAAgB,IAAI,WAAW,CAAC,CAAC,CAAC;IAClH,OAAO;QACL,IAAI,EAAE,CAAC;QACP,IAAI;QACJ,IAAI,EAAE,EAAE,CAAC,GAAG;QACZ,UAAU,EAAE,EAAE,CAAC,IAAI,CAAC,MAAM;QAC1B,gBAAgB,EAAE,KAAK;QACvB,KAAK;QACL,UAAU;QACV,MAAM;QACN,OAAO,EAAE,iBAAiB,cAAc,IAAI,YAAY,CAAC,cAAc,EAAE,CAAC,CAAC,WAAW,MAAM,EAAE;QAC9F,UAAU;QACV,OAAO;KACR,CAAC;AACJ,CAAC;AAED,mDAAmD;AACnD,MAAM,UAAU,YAAY,CAAC,CAAW,EAAE,OAA8B,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;IAC9E,MAAM,EAAE,GAAG,QAAQ,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,CAAC;IAClE,IAAI,CAAC,CAAC,OAAO,EAAE,CAAC;QACd,MAAM,IAAI,GAAG,CAAC,GAAG,IAAI,GAAG,CAAC,CAAC,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,GAAG,KAAK,SAAS,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;QAC7H,OAAO,2CAA2C,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,UAAU,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,iCAAiC,EAAE,IAAI,CAAC;IAC5K,CAAC;IACD,IAAI,CAAC,CAAC,CAAC,KAAK,EAAE,CAAC;QACb,MAAM,GAAG,GAAG,CAAC,CAAC,IAAI,KAAK,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,kCAAkC,CAAC;QACzE,OAAO,2CAA2C,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,sBAAsB,EAAE,GAAG,GAAG,IAAI,CAAC;IACrG,CAAC;IACD,MAAM,GAAG,GAAG,CAAC,CAAC,IAAI,KAAK,gBAAgB,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,gBAAgB,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,CAAC,CAAC,SAAS,SAAS,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,GAAG,GAAG,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;IACnJ,OAAO,oCAAoC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,uBAAuB,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,EAAE,GAAG,GAAG,IAAI,CAAC;AAClH,CAAC"}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Promotion (#43): an environment applies only migrations the environment
|
|
3
|
+
* before it recorded as applied.
|
|
4
|
+
*
|
|
5
|
+
* `waves[].requires` names that environment; it defaults to the wave
|
|
6
|
+
* before, and `false` turns it off. Before anything is applied (and before
|
|
7
|
+
* the gate), `yodel apply <env>` reads each required environment's history
|
|
8
|
+
* with that environment's own profile, which outside an apply job is its
|
|
9
|
+
* reader, and refuses (exit 4) any pending migration the history does not
|
|
10
|
+
* record as applied with the checksum its files have now. The requirement
|
|
11
|
+
* is read from the working tree's yodel.config.ts and from the base
|
|
12
|
+
* commit's, and both are enforced, so a change that drops its own
|
|
13
|
+
* requirement is still held to the base's.
|
|
14
|
+
*/
|
|
15
|
+
import type { YodelConfig } from "../config/define.js";
|
|
16
|
+
import type { Migration } from "../migrations/index.js";
|
|
17
|
+
import { YodelError, type Project } from "../project.js";
|
|
18
|
+
import type { PendingMigration } from "../versioned/state.js";
|
|
19
|
+
/** The environment `env` requires in `config`: `requires`, else the wave before; undefined for none. */
|
|
20
|
+
export declare function requiredIn(config: YodelConfig | undefined, env: string): string | undefined;
|
|
21
|
+
/**
|
|
22
|
+
* Each required environment and where the requirement comes from: the
|
|
23
|
+
* working tree, the base commit, or both. A tenant (#56) requires what its
|
|
24
|
+
* environment requires: the same tenant when that environment has a tenant
|
|
25
|
+
* set too, else the environment.
|
|
26
|
+
*/
|
|
27
|
+
export declare function requiredEnvironments(project: Project, env: string): Promise<Array<{
|
|
28
|
+
env: string;
|
|
29
|
+
from: string;
|
|
30
|
+
}>>;
|
|
31
|
+
/** What one required environment's history says of each pending migration. */
|
|
32
|
+
export interface Promotion {
|
|
33
|
+
/** The required environment. */
|
|
34
|
+
env: string;
|
|
35
|
+
from: string;
|
|
36
|
+
migrations: Array<{
|
|
37
|
+
id: string;
|
|
38
|
+
checksum: string;
|
|
39
|
+
ran: "applied" | "not applied" | "applied with another checksum";
|
|
40
|
+
recorded?: string;
|
|
41
|
+
}>;
|
|
42
|
+
}
|
|
43
|
+
/** Reads each required environment's history for `pending`. */
|
|
44
|
+
export declare function readPromotions(project: Project, env: string, ordered: readonly Migration[], pending: ReadonlyArray<Pick<PendingMigration, "id" | "checksum">>): Promise<Promotion[]>;
|
|
45
|
+
/** The refusal for the promotions, or undefined when every pending migration ran where it had to. */
|
|
46
|
+
export declare function promotionRefusal(env: string, promotions: readonly Promotion[]): YodelError | undefined;
|
|
47
|
+
/** Reads the required environments and throws the refusal when a pending migration has not run there. */
|
|
48
|
+
export declare function enforcePromotion(project: Project, env: string, ordered: readonly Migration[], pending: ReadonlyArray<Pick<PendingMigration, "id" | "checksum">>): Promise<Promotion[]>;
|
|
49
|
+
/** What the wave plan digest takes of the promotions: each required environment and the pending migrations' checksums it recorded. */
|
|
50
|
+
export declare const promotionEvidence: (promotions: readonly Promotion[]) => {
|
|
51
|
+
env: string;
|
|
52
|
+
applied: {
|
|
53
|
+
id: string;
|
|
54
|
+
checksum: string;
|
|
55
|
+
}[];
|
|
56
|
+
}[];
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Promotion (#43): an environment applies only migrations the environment
|
|
3
|
+
* before it recorded as applied.
|
|
4
|
+
*
|
|
5
|
+
* `waves[].requires` names that environment; it defaults to the wave
|
|
6
|
+
* before, and `false` turns it off. Before anything is applied (and before
|
|
7
|
+
* the gate), `yodel apply <env>` reads each required environment's history
|
|
8
|
+
* with that environment's own profile, which outside an apply job is its
|
|
9
|
+
* reader, and refuses (exit 4) any pending migration the history does not
|
|
10
|
+
* record as applied with the checksum its files have now. The requirement
|
|
11
|
+
* is read from the working tree's yodel.config.ts and from the base
|
|
12
|
+
* commit's, and both are enforced, so a change that drops its own
|
|
13
|
+
* requirement is still held to the base's.
|
|
14
|
+
*/
|
|
15
|
+
import { loadYodelConfigAtBase } from "../config/base.js";
|
|
16
|
+
import { EXIT_REFUSED } from "../exit.js";
|
|
17
|
+
import { resolveEnvironment, YodelError } from "../project.js";
|
|
18
|
+
import { closeVersioned, inspect, openVersioned } from "../versioned/engine.js";
|
|
19
|
+
import { splitTarget, tenantTarget } from "../tenants/target.js";
|
|
20
|
+
/** The environment `env` requires in `config`: `requires`, else the wave before; undefined for none. */
|
|
21
|
+
export function requiredIn(config, env) {
|
|
22
|
+
const waves = config?.waves ?? [];
|
|
23
|
+
const i = waves.findIndex((w) => w.env === env);
|
|
24
|
+
const wave = waves[i];
|
|
25
|
+
if (!wave || wave.requires === false)
|
|
26
|
+
return undefined;
|
|
27
|
+
return wave.requires ?? waves[i - 1]?.env;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Each required environment and where the requirement comes from: the
|
|
31
|
+
* working tree, the base commit, or both. A tenant (#56) requires what its
|
|
32
|
+
* environment requires: the same tenant when that environment has a tenant
|
|
33
|
+
* set too, else the environment.
|
|
34
|
+
*/
|
|
35
|
+
export async function requiredEnvironments(project, env) {
|
|
36
|
+
const out = new Map();
|
|
37
|
+
const { env: base, tenant } = splitTarget(env);
|
|
38
|
+
const add = (e, from) => {
|
|
39
|
+
const target = e && tenant !== undefined && project.yodel?.config.environments?.[e]?.tenants ? tenantTarget(e, tenant) : e;
|
|
40
|
+
if (target && target !== env)
|
|
41
|
+
out.set(target, [...(out.get(target) ?? []), from]);
|
|
42
|
+
};
|
|
43
|
+
add(requiredIn(project.yodel?.config, base), "yodel.config.ts");
|
|
44
|
+
try {
|
|
45
|
+
const at = await loadYodelConfigAtBase(project.dir);
|
|
46
|
+
add(requiredIn(at.config, base), `yodel.config.ts at ${at.sha.slice(0, 12)}`);
|
|
47
|
+
}
|
|
48
|
+
catch {
|
|
49
|
+
// No git, or no base commit (a repository's first commit): the working tree's requirement alone.
|
|
50
|
+
}
|
|
51
|
+
return [...out].map(([e, from]) => ({ env: e, from: from.join(" and ") }));
|
|
52
|
+
}
|
|
53
|
+
/** Reads each required environment's history for `pending`. */
|
|
54
|
+
export async function readPromotions(project, env, ordered, pending) {
|
|
55
|
+
const out = [];
|
|
56
|
+
for (const req of await requiredEnvironments(project, env)) {
|
|
57
|
+
let environment;
|
|
58
|
+
try {
|
|
59
|
+
environment = resolveEnvironment(project, req.env);
|
|
60
|
+
}
|
|
61
|
+
catch (e) {
|
|
62
|
+
throw new YodelError(`refused: ${env} requires ${req.env} (${req.from}), and ${e instanceof Error ? e.message : String(e)}; nothing was applied`, EXIT_REFUSED);
|
|
63
|
+
}
|
|
64
|
+
const venv = await openVersioned(project, environment);
|
|
65
|
+
try {
|
|
66
|
+
const { status } = await inspect(venv, ordered, { sync: false });
|
|
67
|
+
const applied = new Map(status.applied.map((a) => [a.id, a.checksum]));
|
|
68
|
+
out.push({
|
|
69
|
+
env: req.env,
|
|
70
|
+
from: req.from,
|
|
71
|
+
migrations: pending.map((p) => {
|
|
72
|
+
const recorded = applied.get(p.id);
|
|
73
|
+
if (recorded === undefined)
|
|
74
|
+
return { id: p.id, checksum: p.checksum, ran: "not applied" };
|
|
75
|
+
return recorded === p.checksum ? { id: p.id, checksum: p.checksum, ran: "applied" } : { id: p.id, checksum: p.checksum, ran: "applied with another checksum", recorded };
|
|
76
|
+
}),
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
finally {
|
|
80
|
+
await closeVersioned(venv);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
return out;
|
|
84
|
+
}
|
|
85
|
+
/** The refusal for the promotions, or undefined when every pending migration ran where it had to. */
|
|
86
|
+
export function promotionRefusal(env, promotions) {
|
|
87
|
+
const lines = promotions.flatMap((p) => p.migrations
|
|
88
|
+
.filter((m) => m.ran !== "applied")
|
|
89
|
+
.map((m) => ` ${m.id}: ${m.ran === "not applied" ? `${p.env}'s history does not record it applied` : `${p.env} applied it with checksum ${m.recorded ?? "?"}, and its files now hash to ${m.checksum}`} (required by ${p.from})`));
|
|
90
|
+
if (lines.length === 0)
|
|
91
|
+
return undefined;
|
|
92
|
+
const envs = [...new Set(promotions.filter((p) => p.migrations.some((m) => m.ran !== "applied")).map((p) => p.env))];
|
|
93
|
+
return new YodelError(`refused: ${env} applies only migrations ${envs.join(" and ")} applied first, and ${String(lines.length)} pending ${lines.length === 1 ? "one has" : "ones have"} not run there:\n${lines.join("\n")}\n` +
|
|
94
|
+
`Nothing was applied. Apply ${lines.length === 1 ? "it" : "them"} to ${envs.join(" and ")} first.`, EXIT_REFUSED);
|
|
95
|
+
}
|
|
96
|
+
/** Reads the required environments and throws the refusal when a pending migration has not run there. */
|
|
97
|
+
export async function enforcePromotion(project, env, ordered, pending) {
|
|
98
|
+
if (pending.length === 0)
|
|
99
|
+
return [];
|
|
100
|
+
const promotions = await readPromotions(project, env, ordered, pending);
|
|
101
|
+
const refusal = promotionRefusal(env, promotions);
|
|
102
|
+
if (refusal)
|
|
103
|
+
throw refusal;
|
|
104
|
+
return promotions;
|
|
105
|
+
}
|
|
106
|
+
/** What the wave plan digest takes of the promotions: each required environment and the pending migrations' checksums it recorded. */
|
|
107
|
+
export const promotionEvidence = (promotions) => promotions.map((p) => ({ env: p.env, applied: p.migrations.filter((m) => m.ran === "applied").map((m) => ({ id: m.id, checksum: m.checksum })) }));
|
|
108
|
+
//# sourceMappingURL=promotion.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"promotion.js","sourceRoot":"","sources":["../../src/waves/promotion.ts"],"names":[],"mappings":"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"}
|