@intentius/sql-yodeler 0.3.0 → 0.5.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.
Files changed (220) hide show
  1. package/README.md +2 -2
  2. package/dist/apply-steps/index.d.ts +65 -0
  3. package/dist/apply-steps/index.js +106 -0
  4. package/dist/apply-steps/index.js.map +1 -0
  5. package/dist/approval-mode/index.d.ts +78 -0
  6. package/dist/approval-mode/index.js +105 -0
  7. package/dist/approval-mode/index.js.map +1 -0
  8. package/dist/chant/access.d.ts +84 -0
  9. package/dist/chant/access.js +155 -0
  10. package/dist/chant/access.js.map +1 -0
  11. package/dist/chant/drift.d.ts +17 -1
  12. package/dist/chant/drift.js +55 -2
  13. package/dist/chant/drift.js.map +1 -1
  14. package/dist/chant/plan.d.ts +18 -2
  15. package/dist/chant/plan.js +21 -3
  16. package/dist/chant/plan.js.map +1 -1
  17. package/dist/ci/login.d.ts +78 -0
  18. package/dist/ci/login.js +199 -0
  19. package/dist/ci/login.js.map +1 -0
  20. package/dist/ci/render.d.ts +64 -10
  21. package/dist/ci/render.js +385 -88
  22. package/dist/ci/render.js.map +1 -1
  23. package/dist/cleanup/index.d.ts +92 -0
  24. package/dist/cleanup/index.js +213 -0
  25. package/dist/cleanup/index.js.map +1 -0
  26. package/dist/commands/apply.js +178 -20
  27. package/dist/commands/apply.js.map +1 -1
  28. package/dist/commands/approve.d.ts +13 -0
  29. package/dist/commands/approve.js +206 -24
  30. package/dist/commands/approve.js.map +1 -1
  31. package/dist/commands/ci.js +12 -0
  32. package/dist/commands/ci.js.map +1 -1
  33. package/dist/commands/cleanup.d.ts +13 -0
  34. package/dist/commands/cleanup.js +158 -0
  35. package/dist/commands/cleanup.js.map +1 -0
  36. package/dist/commands/config.d.ts +3 -0
  37. package/dist/commands/config.js +136 -0
  38. package/dist/commands/config.js.map +1 -0
  39. package/dist/commands/drift.js +33 -3
  40. package/dist/commands/drift.js.map +1 -1
  41. package/dist/commands/index.js +6 -0
  42. package/dist/commands/index.js.map +1 -1
  43. package/dist/commands/init.d.ts +11 -0
  44. package/dist/commands/init.js +98 -10
  45. package/dist/commands/init.js.map +1 -1
  46. package/dist/commands/lint.js +67 -10
  47. package/dist/commands/lint.js.map +1 -1
  48. package/dist/commands/new.js +135 -54
  49. package/dist/commands/new.js.map +1 -1
  50. package/dist/commands/plan.js +39 -5
  51. package/dist/commands/plan.js.map +1 -1
  52. package/dist/commands/test.d.ts +3 -0
  53. package/dist/commands/test.js +164 -0
  54. package/dist/commands/test.js.map +1 -0
  55. package/dist/config/define.d.ts +175 -2
  56. package/dist/config/define.js +2 -0
  57. package/dist/config/define.js.map +1 -1
  58. package/dist/config/load.js +78 -10
  59. package/dist/config/load.js.map +1 -1
  60. package/dist/config/settings.d.ts +20 -0
  61. package/dist/config/settings.js +28 -0
  62. package/dist/config/settings.js.map +1 -1
  63. package/dist/credentials.d.ts +119 -0
  64. package/dist/credentials.js +119 -0
  65. package/dist/credentials.js.map +1 -0
  66. package/dist/exit.js +1 -1
  67. package/dist/exit.js.map +1 -1
  68. package/dist/index.d.ts +5 -1
  69. package/dist/index.js +2 -0
  70. package/dist/index.js.map +1 -1
  71. package/dist/init/adopt.js +1 -1
  72. package/dist/init/adopt.js.map +1 -1
  73. package/dist/init/clickhouse-access.d.ts +64 -0
  74. package/dist/init/clickhouse-access.js +211 -0
  75. package/dist/init/clickhouse-access.js.map +1 -0
  76. package/dist/init/index.d.ts +2 -0
  77. package/dist/init/index.js +2 -0
  78. package/dist/init/index.js.map +1 -1
  79. package/dist/init/live.d.ts +4 -0
  80. package/dist/init/live.js +16 -0
  81. package/dist/init/live.js.map +1 -1
  82. package/dist/init/second.d.ts +70 -0
  83. package/dist/init/second.js +149 -0
  84. package/dist/init/second.js.map +1 -0
  85. package/dist/lint/access.d.ts +18 -0
  86. package/dist/lint/access.js +34 -0
  87. package/dist/lint/access.js.map +1 -0
  88. package/dist/lint/annotations.d.ts +49 -0
  89. package/dist/lint/annotations.js +82 -0
  90. package/dist/lint/annotations.js.map +1 -0
  91. package/dist/lint/check.js +25 -7
  92. package/dist/lint/check.js.map +1 -1
  93. package/dist/lint/config.d.ts +7 -1
  94. package/dist/lint/config.js +11 -5
  95. package/dist/lint/config.js.map +1 -1
  96. package/dist/lint/index.d.ts +4 -0
  97. package/dist/lint/index.js +4 -0
  98. package/dist/lint/index.js.map +1 -1
  99. package/dist/lint/naming.d.ts +62 -0
  100. package/dist/lint/naming.js +138 -0
  101. package/dist/lint/naming.js.map +1 -0
  102. package/dist/lint/rules.d.ts +37 -4
  103. package/dist/lint/rules.js +16 -4
  104. package/dist/lint/rules.js.map +1 -1
  105. package/dist/lint/schema-checks.d.ts +63 -0
  106. package/dist/lint/schema-checks.js +128 -0
  107. package/dist/lint/schema-checks.js.map +1 -0
  108. package/dist/lint/statements.d.ts +22 -1
  109. package/dist/lint/statements.js +40 -1
  110. package/dist/lint/statements.js.map +1 -1
  111. package/dist/mcp/tools.d.ts +1 -1
  112. package/dist/mcp/tools.js +1 -1
  113. package/dist/mcp/tools.js.map +1 -1
  114. package/dist/migrations/checkpoint.js +0 -2
  115. package/dist/migrations/checkpoint.js.map +1 -1
  116. package/dist/migrations/checks.d.ts +19 -1
  117. package/dist/migrations/checks.js +15 -0
  118. package/dist/migrations/checks.js.map +1 -1
  119. package/dist/migrations/create.d.ts +13 -4
  120. package/dist/migrations/create.js +24 -9
  121. package/dist/migrations/create.js.map +1 -1
  122. package/dist/migrations/routines.d.ts +5 -15
  123. package/dist/migrations/routines.js +5 -30
  124. package/dist/migrations/routines.js.map +1 -1
  125. package/dist/pr/model.d.ts +4 -1
  126. package/dist/pr/model.js +13 -5
  127. package/dist/pr/model.js.map +1 -1
  128. package/dist/pr/render.d.ts +4 -1
  129. package/dist/pr/render.js +22 -8
  130. package/dist/pr/render.js.map +1 -1
  131. package/dist/pr/tenants.d.ts +37 -0
  132. package/dist/pr/tenants.js +138 -0
  133. package/dist/pr/tenants.js.map +1 -0
  134. package/dist/probe.d.ts +27 -0
  135. package/dist/probe.js +106 -0
  136. package/dist/probe.js.map +1 -0
  137. package/dist/project.d.ts +16 -0
  138. package/dist/project.js +29 -1
  139. package/dist/project.js.map +1 -1
  140. package/dist/replay/clickhouse.d.ts +31 -0
  141. package/dist/replay/clickhouse.js +103 -6
  142. package/dist/replay/clickhouse.js.map +1 -1
  143. package/dist/replay/replay.d.ts +5 -2
  144. package/dist/replay/replay.js.map +1 -1
  145. package/dist/report/audit.d.ts +1 -1
  146. package/dist/report/audit.js +20 -3
  147. package/dist/report/audit.js.map +1 -1
  148. package/dist/schemas.d.ts +3 -1
  149. package/dist/schemas.js +2 -0
  150. package/dist/schemas.js.map +1 -1
  151. package/dist/sources/ddl.d.ts +15 -20
  152. package/dist/sources/ddl.js +34 -328
  153. package/dist/sources/ddl.js.map +1 -1
  154. package/dist/sources/index.js +2 -2
  155. package/dist/sources/index.js.map +1 -1
  156. package/dist/steps/backfill.d.ts +40 -5
  157. package/dist/steps/backfill.js +128 -8
  158. package/dist/steps/backfill.js.map +1 -1
  159. package/dist/steps/index.d.ts +19 -1
  160. package/dist/steps/index.js +34 -7
  161. package/dist/steps/index.js.map +1 -1
  162. package/dist/steps/kinds.d.ts +42 -4
  163. package/dist/steps/kinds.js +88 -14
  164. package/dist/steps/kinds.js.map +1 -1
  165. package/dist/steps/postgres-migration.d.ts +3 -0
  166. package/dist/steps/postgres-migration.js +7 -3
  167. package/dist/steps/postgres-migration.js.map +1 -1
  168. package/dist/steps/rebuild.d.ts +15 -6
  169. package/dist/steps/rebuild.js +12 -9
  170. package/dist/steps/rebuild.js.map +1 -1
  171. package/dist/tenants/index.d.ts +2 -1
  172. package/dist/tenants/index.js +3 -4
  173. package/dist/tenants/index.js.map +1 -1
  174. package/dist/tenants/wave.d.ts +128 -0
  175. package/dist/tenants/wave.js +254 -0
  176. package/dist/tenants/wave.js.map +1 -0
  177. package/dist/testing/define.d.ts +62 -0
  178. package/dist/testing/define.js +32 -0
  179. package/dist/testing/define.js.map +1 -0
  180. package/dist/testing/files.d.ts +5 -0
  181. package/dist/testing/files.js +18 -0
  182. package/dist/testing/files.js.map +1 -0
  183. package/dist/testing/report.d.ts +14 -0
  184. package/dist/testing/report.js +83 -0
  185. package/dist/testing/report.js.map +1 -0
  186. package/dist/testing/run.d.ts +104 -0
  187. package/dist/testing/run.js +328 -0
  188. package/dist/testing/run.js.map +1 -0
  189. package/dist/versioned/approval.d.ts +6 -0
  190. package/dist/versioned/approval.js +11 -5
  191. package/dist/versioned/approval.js.map +1 -1
  192. package/dist/versioned/checks.d.ts +8 -2
  193. package/dist/versioned/checks.js +34 -13
  194. package/dist/versioned/checks.js.map +1 -1
  195. package/dist/versioned/driver.d.ts +2 -2
  196. package/dist/versioned/engine.d.ts +10 -0
  197. package/dist/versioned/engine.js +8 -4
  198. package/dist/versioned/engine.js.map +1 -1
  199. package/dist/versioned/explain.d.ts +2 -0
  200. package/dist/versioned/explain.js +3 -1
  201. package/dist/versioned/explain.js.map +1 -1
  202. package/dist/versioned/op.d.ts +4 -0
  203. package/dist/versioned/op.js +5 -2
  204. package/dist/versioned/op.js.map +1 -1
  205. package/dist/versioned/state.js +1 -1
  206. package/dist/versioned/state.js.map +1 -1
  207. package/dist/waves/index.d.ts +58 -13
  208. package/dist/waves/index.js +109 -24
  209. package/dist/waves/index.js.map +1 -1
  210. package/dist/waves/record.d.ts +10 -0
  211. package/dist/waves/record.js.map +1 -1
  212. package/package.json +8 -7
  213. package/schemas/apply.schema.json +1 -1
  214. package/schemas/cleanup.schema.json +40 -0
  215. package/schemas/init.schema.json +51 -22
  216. package/schemas/lint-rules.schema.json +4 -0
  217. package/schemas/new.schema.json +41 -3
  218. package/schemas/plan.schema.json +39 -0
  219. package/schemas/report.schema.json +1 -1
  220. package/schemas/test.schema.json +41 -0
@@ -0,0 +1,199 @@
1
+ /**
2
+ * The cloud login a CI job runs before yodel (#132), when a role it holds
3
+ * has a password minted by a cloud token source: the job's OIDC token,
4
+ * exchanged by the cloud's own command line, which then mints the database
5
+ * token (`aws rds generate-db-auth-token`, `gcloud sql generate-login-token`,
6
+ * `az account get-access-token`). yodel.config.ts `ci.login` says what to
7
+ * log in as ({@link CiLogin}); this renders the steps per forge. Nothing
8
+ * here calls a cloud: the rendered job does, when it runs.
9
+ *
10
+ * The token, per forge:
11
+ * - GitHub: the job requests it for the cloud's audience
12
+ * (ACTIONS_ID_TOKEN_REQUEST_URL, granted by `id-token: write`) and writes
13
+ * it to the token file.
14
+ * - GitLab: the job's `id_tokens` variable (AWS_ID_TOKEN, GCP_ID_TOKEN,
15
+ * AZURE_ID_TOKEN) is written to the token file.
16
+ * - Forgejo gives a job no OIDC token: a login is rendered only when the
17
+ * config's `tokenFile` names a token the runner provides.
18
+ *
19
+ * The login, per cloud, from the token file:
20
+ * - aws: AWS_ROLE_ARN, AWS_WEB_IDENTITY_TOKEN_FILE and AWS_ROLE_SESSION_NAME,
21
+ * which the aws command line assumes the role with.
22
+ * - gcp: an external_account credential configuration over the token file
23
+ * (the workload identity provider, impersonating the service account),
24
+ * `gcloud auth login --cred-file` with it, and GOOGLE_APPLICATION_CREDENTIALS.
25
+ * - azure: `az login --service-principal --federated-token`, and
26
+ * AZURE_CLIENT_ID, AZURE_TENANT_ID and AZURE_FEDERATED_TOKEN_FILE.
27
+ *
28
+ * On GitHub and Forgejo the variables go to $GITHUB_ENV, so every later step
29
+ * has them (the report too); on GitLab they are the job's `variables`, so
30
+ * after_script has them too.
31
+ */
32
+ export const CLOUDS = ["aws", "gcp", "azure"];
33
+ /** The cloud whose login a token source mints from. */
34
+ export const CLOUD_OF = { "rds-iam": "aws", "cloud-sql-iam": "gcp", entra: "azure" };
35
+ /** The ID token's audience a cloud accepts. GCP's is the workload identity provider's own. */
36
+ export function audience(login) {
37
+ if (login.cloud === "aws")
38
+ return "sts.amazonaws.com";
39
+ if (login.cloud === "gcp")
40
+ return `https://iam.googleapis.com/${login.config.workloadIdentityProvider}`;
41
+ return "api://AzureADTokenExchange";
42
+ }
43
+ /** The GitLab ID token variable per cloud, as #50 named them. */
44
+ export const GITLAB_ID_TOKEN = { aws: "AWS_ID_TOKEN", gcp: "GCP_ID_TOKEN", azure: "AZURE_ID_TOKEN" };
45
+ /** The login config for `env`'s roles minted by `token`: the environment's own, else the project's. */
46
+ export function loginFor(login, env, token) {
47
+ const cloud = CLOUD_OF[token];
48
+ const config = login?.environments?.[env]?.[cloud] ?? login?.[cloud];
49
+ return config ? { cloud, config } : undefined;
50
+ }
51
+ /**
52
+ * The logins of a job holding `held` roles, each cloud once. A job logs in
53
+ * once per cloud, so two environments it holds that name different logins
54
+ * for one cloud are refused.
55
+ */
56
+ export function jobLogins(login, held, cloudOf) {
57
+ const out = new Map();
58
+ for (const [env, role] of held) {
59
+ const token = cloudOf(env, role);
60
+ if (!token)
61
+ continue;
62
+ const l = loginFor(login, env, token);
63
+ if (!l)
64
+ continue;
65
+ const seen = out.get(l.cloud);
66
+ if (seen && JSON.stringify(seen.login.config) !== JSON.stringify(l.config)) {
67
+ throw new Error(`yodel.config.ts ci.login: a job holds ${seen.env}'s and ${env}'s roles, which log in to ${l.cloud} differently; a job logs in once per cloud, so give them one ${l.cloud} login`);
68
+ }
69
+ if (!seen)
70
+ out.set(l.cloud, { login: l, env });
71
+ }
72
+ return CLOUDS.flatMap((c) => {
73
+ const l = out.get(c)?.login;
74
+ return l ? [l] : [];
75
+ });
76
+ }
77
+ /** Where a job keeps a cloud's token by default. */
78
+ const defaultTokenFile = (forge, cloud) => {
79
+ if (forge === "github")
80
+ return `$RUNNER_TEMP/yodel-${cloud}-token`;
81
+ if (forge === "gitlab")
82
+ return `/tmp/yodel-${cloud}-token`;
83
+ return undefined;
84
+ };
85
+ /**
86
+ * Requests the job's OIDC token for an audience and writes it to a file:
87
+ * `node -e '<this>' <audience> <file>`, so every image with node runs it.
88
+ */
89
+ const GITHUB_OIDC = 'const [aud, file] = process.argv.slice(1); const url = process.env.ACTIONS_ID_TOKEN_REQUEST_URL, auth = process.env.ACTIONS_ID_TOKEN_REQUEST_TOKEN; if (!url || !auth) { console.error("no OIDC token for this job: the workflow needs permissions id-token: write"); process.exit(1); } fetch(url + "&audience=" + encodeURIComponent(aud), { headers: { Authorization: "bearer " + auth } }).then(async (r) => { if (!r.ok) throw new Error("the OIDC token request answered " + r.status); require("fs").writeFileSync(file, (await r.json()).value, { mode: 0o600 }); }).catch((e) => { console.error(e.message); process.exit(1); })';
90
+ /** Writes the gcloud credential configuration: `node -e '<this>' <provider> <service account> <token file> <config file>`. */
91
+ const GCP_CREDENTIALS = 'const [provider, sa, token, file] = process.argv.slice(1); require("fs").writeFileSync(file, JSON.stringify({ type: "external_account", audience: "//iam.googleapis.com/" + provider, subject_token_type: "urn:ietf:params:oauth:token-type:jwt", token_url: "https://sts.googleapis.com/v1/token", service_account_impersonation_url: "https://iamcredentials.googleapis.com/v1/projects/-/serviceAccounts/" + sa + ":generateAccessToken", credential_source: { file: token } }, null, 2))';
92
+ const quote = (s) => `'${s.replace(/'/g, `'\\''`)}'`;
93
+ /** A path that may name a variable ($RUNNER_TEMP): double-quoted, so the shell expands it. */
94
+ const path = (s) => `"${s}"`;
95
+ /** The variables a cloud's login sets, from its token file (and gcp's credential configuration). */
96
+ function loginVariables(l, tokenFile) {
97
+ if (l.cloud === "aws")
98
+ return { AWS_ROLE_ARN: l.config.roleArn, AWS_WEB_IDENTITY_TOKEN_FILE: tokenFile, AWS_ROLE_SESSION_NAME: l.config.sessionName ?? "yodel" };
99
+ if (l.cloud === "gcp")
100
+ return { GOOGLE_APPLICATION_CREDENTIALS: `${tokenFile}.json` };
101
+ return { AZURE_CLIENT_ID: l.config.clientId, AZURE_TENANT_ID: l.config.tenantId, AZURE_FEDERATED_TOKEN_FILE: tokenFile };
102
+ }
103
+ const STEP_NAME = { aws: "Log in to AWS (web identity)", gcp: "Log in to Google Cloud (workload identity)", azure: "Log in to Azure (federated token)" };
104
+ /**
105
+ * The login steps of a job on `forge`, for `logins`. On Forgejo a cloud
106
+ * without a `tokenFile` is left out: the runner's own identity mints there.
107
+ */
108
+ export function loginSteps(forge, logins) {
109
+ const out = { steps: [], variables: {} };
110
+ for (const l of logins) {
111
+ const file = l.config.tokenFile ?? defaultTokenFile(forge, l.cloud);
112
+ if (file === undefined)
113
+ continue;
114
+ const lines = [];
115
+ if (forge === "github")
116
+ lines.push(`node -e ${quote(GITHUB_OIDC)} ${quote(audience(l))} ${path(file)}`);
117
+ if (forge === "gitlab")
118
+ lines.push(`(umask 077 && printf '%s' "$${GITLAB_ID_TOKEN[l.cloud]}" > ${path(file)})`);
119
+ if (l.cloud === "gcp") {
120
+ lines.push(`node -e ${quote(GCP_CREDENTIALS)} ${quote(l.config.workloadIdentityProvider)} ${quote(l.config.serviceAccount)} ${path(file)} ${path(`${file}.json`)}`);
121
+ lines.push(`gcloud auth login --cred-file=${path(`${file}.json`)} --quiet`);
122
+ }
123
+ if (l.cloud === "azure") {
124
+ lines.push(`az login --service-principal --username ${quote(l.config.clientId)} --tenant ${quote(l.config.tenantId)} --federated-token "$(cat ${path(file)})" --allow-no-subscriptions --output none`);
125
+ }
126
+ const vars = loginVariables(l, file);
127
+ if (forge === "gitlab")
128
+ Object.assign(out.variables, vars);
129
+ else
130
+ lines.push(...Object.entries(vars).map(([k, v]) => `echo "${k}=${v}" >> "$GITHUB_ENV"`));
131
+ out.steps.push({ name: STEP_NAME[l.cloud], lines });
132
+ }
133
+ return out;
134
+ }
135
+ // ── yodel.config.ts ci.login, checked ───────────────────────────────────────
136
+ const ROLE_ARN = /^arn:aws[a-z-]*:iam::\d{12}:role\/[\w+=,.@/-]+$/;
137
+ const PROVIDER = /^projects\/\d+\/locations\/global\/workloadIdentityPools\/[a-z0-9-]+\/providers\/[a-z0-9-]+$/;
138
+ const SERVICE_ACCOUNT = /^[a-z0-9-]+@[a-z0-9-]+\.iam\.gserviceaccount\.com$/;
139
+ const AZURE_ID = /^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$/;
140
+ /** A path, optionally under a variable ($RUNNER_TEMP/...): nothing a shell would read as more than that. */
141
+ const TOKEN_FILE = /^(\$[A-Z_][A-Z0-9_]*)?[\w./-]+$/;
142
+ const SESSION_NAME = /^[\w+=,.@-]{2,64}$/;
143
+ const isRecord = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
144
+ const FIELDS = {
145
+ aws: {
146
+ roleArn: { re: ROLE_ARN, what: "a role ARN, arn:aws:iam::<account>:role/<name>", required: true },
147
+ tokenFile: { re: TOKEN_FILE, what: "a file path", required: false },
148
+ sessionName: { re: SESSION_NAME, what: "2 to 64 of letters, digits and +=,.@_-", required: false },
149
+ },
150
+ gcp: {
151
+ workloadIdentityProvider: { re: PROVIDER, what: "projects/<number>/locations/global/workloadIdentityPools/<pool>/providers/<provider>", required: true },
152
+ serviceAccount: { re: SERVICE_ACCOUNT, what: "<name>@<project>.iam.gserviceaccount.com", required: true },
153
+ tokenFile: { re: TOKEN_FILE, what: "a file path", required: false },
154
+ },
155
+ azure: {
156
+ clientId: { re: AZURE_ID, what: "the application (client) id, a GUID", required: true },
157
+ tenantId: { re: AZURE_ID, what: "the directory (tenant) id, a GUID", required: true },
158
+ tokenFile: { re: TOKEN_FILE, what: "a file path", required: false },
159
+ },
160
+ };
161
+ function cloudsProblem(value, where, allowEnvironments) {
162
+ for (const [k, v] of Object.entries(value)) {
163
+ if (k === "environments" && allowEnvironments) {
164
+ if (!isRecord(v))
165
+ return `${where}.environments: an object keyed by environment, each { aws?, gcp?, azure? }`;
166
+ for (const [env, logins] of Object.entries(v)) {
167
+ if (!isRecord(logins))
168
+ return `${where}.environments.${env}: expected { aws?, gcp?, azure? }`;
169
+ const problem = cloudsProblem(logins, `${where}.environments.${env}`, false);
170
+ if (problem)
171
+ return problem;
172
+ }
173
+ continue;
174
+ }
175
+ if (!CLOUDS.includes(k))
176
+ return `${where}.${k}: unknown setting (expected ${[...CLOUDS, ...(allowEnvironments ? ["environments"] : [])].join(", ")})`;
177
+ const fields = FIELDS[k];
178
+ if (!isRecord(v))
179
+ return `${where}.${k}: expected { ${Object.keys(fields).join(", ")} }`;
180
+ for (const f of Object.keys(v))
181
+ if (!(f in fields))
182
+ return `${where}.${k}.${f}: unknown setting (expected ${Object.keys(fields).join(", ")})`;
183
+ for (const [f, spec] of Object.entries(fields)) {
184
+ const x = v[f];
185
+ if (x === undefined && !spec.required)
186
+ continue;
187
+ if (typeof x !== "string" || !spec.re.test(x))
188
+ return `${where}.${k}.${f}: ${spec.what}`;
189
+ }
190
+ }
191
+ return undefined;
192
+ }
193
+ /** What is wrong with yodel.config.ts `ci.login`, if anything. */
194
+ export function loginProblem(value, where) {
195
+ if (!isRecord(value))
196
+ return `${where}: expected { aws?, gcp?, azure?, environments? }`;
197
+ return cloudsProblem(value, where, true);
198
+ }
199
+ //# sourceMappingURL=login.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"login.js","sourceRoot":"","sources":["../../src/ci/login.ts"],"names":[],"mappings":"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"}
@@ -68,6 +68,8 @@
68
68
  * in that image instead, which carries node, yodel, chant and the lexicons
69
69
  * on PATH, and no job runs `npm ci` ({@link Toolchain}).
70
70
  */
71
+ import { type ApprovalMode, type CiLogin } from "../config/define.js";
72
+ import { type CloudToken, type Role } from "../credentials.js";
71
73
  import type { Declarable } from "@intentius/chant/declarable";
72
74
  export type Forge = "github" | "gitlab" | "forgejo";
73
75
  export declare const FORGES: readonly Forge[];
@@ -107,17 +109,44 @@ export interface Environments {
107
109
  jobs?: ProjectJobs;
108
110
  /** yodel.config.ts `ci.image`: the yodel image every job runs in, by digest. */
109
111
  image?: string;
112
+ /** yodel.config.ts `ci.resume`: the scheduled job that resumes a wave whose approval arrived. */
113
+ resume?: {
114
+ schedule: string;
115
+ };
110
116
  /** The apply pipeline's waves, in order. */
111
117
  waves: readonly Wave[];
112
118
  /** chant.config.ts declares the `replay` profile, so the lint job runs the replay check. */
113
119
  replay: boolean;
120
+ /** The project has tests/*.test.ts: with the replay profile, the lint job runs yodel test on its server too (#54). */
121
+ tests?: boolean;
122
+ /**
123
+ * The environments whose profile is made with credentials() (#50): the
124
+ * variables each role reads, and the cloud token source that mints its
125
+ * password, if any. An environment not here reads the default variables.
126
+ */
127
+ credentials?: Record<string, EnvCredentials>;
128
+ /** yodel.config.ts `ci.login` (#132): the cloud login a job runs before yodel when a role it holds is cloud-minted. */
129
+ login?: CiLogin;
130
+ }
131
+ /** What a job holding one role of an environment needs: the variables it reads, and the cloud identity its token source mints from. */
132
+ export interface RoleNeeds {
133
+ variables: string[];
134
+ cloud?: CloudToken;
114
135
  }
136
+ export type EnvCredentials = Record<Role, RoleNeeds>;
115
137
  /** DEV_CLICKHOUSE for dev on ClickHouse: what an environment's variables start with. */
116
138
  export declare const variablePrefix: (env: string, dialect: Dialect) => string;
117
- /** The variables a job reading `env` holds: the URL and the reader. */
118
- export declare const readerVariables: (env: string, dialect: Dialect) => string[];
139
+ /** The variables a job reading `env` holds: the URL and the reader (the variables credentials() names; none for a token-minted password). */
140
+ export declare const readerVariables: (env: string, dialect: Dialect, creds?: EnvCredentials) => string[];
119
141
  /** The variables the apply job for `env` holds: the URL and the writer. */
120
- export declare const writerVariables: (env: string, dialect: Dialect) => string[];
142
+ export declare const writerVariables: (env: string, dialect: Dialect, creds?: EnvCredentials) => string[];
143
+ /** The GitLab ID token per cloud: its variable and its audience. */
144
+ export declare const ID_TOKENS: Record<CloudToken, {
145
+ variable: string;
146
+ aud: string;
147
+ }>;
148
+ /** The write probe a pull request job runs before its plan: it holds no writer for `envs`. */
149
+ export declare const writeProbe: (envs: readonly string[]) => string;
121
150
  /**
122
151
  * The approvals: `chant approve` records them on the chant/lifecycle branch,
123
152
  * and approvers push it. A CI checkout fetches one ref, so fetch this one too.
@@ -161,7 +190,17 @@ export declare const REPLAY_SERVICE: Record<Dialect, ReplayService>;
161
190
  /** Waits up to 90 s for the replay server's port; a shell-free one-liner, so every forge runs it the same. */
162
191
  export declare const waitFor: (host: string, port: number) => string;
163
192
  export declare const LINT = "yodel lint";
193
+ /**
194
+ * The lint job's `yodel lint`, with its findings where the forge shows them
195
+ * (#129): workflow commands on GitHub and Forgejo, which annotate the
196
+ * migration files in the pull request; on GitLab the code quality report,
197
+ * which the job keeps as its `artifacts:reports:codequality`
198
+ * ({@link CODE_QUALITY_FILE}) and the merge request shows.
199
+ */
200
+ export declare const lintCommand: (forge: Forge) => string;
164
201
  export declare const REPLAY = "yodel lint --replay replay --reset";
202
+ /** yodel test (#54), after the replay check on the same throwaway server, its outcome as JUnit XML. */
203
+ export declare const TEST = "yodel test --env replay --reset --junit yodel-test.xml";
165
204
  /** Fails a pull request whose pipelines differ from what yodel ci renders. */
166
205
  export declare const CI_CHECK = "yodel ci --check";
167
206
  /**
@@ -200,12 +239,15 @@ export interface Wave {
200
239
  */
201
240
  requires?: string;
202
241
  /**
203
- * A tenant set's wave (#56): a run per tenant, `<env>/<tenant>`, in place
204
- * of the environment's one, all behind the wave's one gate.
242
+ * A tenant set's wave (#56): a run per tenant, `<env>/<tenant>`, or per
243
+ * slot of a set read at run time, `<env>/@<i>of<n>`, in place of the
244
+ * environment's one, all behind the wave's one gate.
205
245
  */
206
246
  targets?: readonly string[];
207
247
  /** Split the wave into a deciding job and this many share jobs (yodel.config.ts `waves[].shares`). */
208
248
  shares?: number;
249
+ /** Which approvals of the wave's digest count (yodel.config.ts `waves[].approval`, #48); absent, `ledger`. */
250
+ approval?: ApprovalMode;
209
251
  }
210
252
  /** The waves spec: committed, and read by every wave job (its gate policy at the commit before the push). */
211
253
  export declare const WAVES_SPEC = "yodel-waves.json";
@@ -224,25 +266,35 @@ interface WavesSpec {
224
266
  }>;
225
267
  gate: WaveGate;
226
268
  shares?: number;
269
+ approval?: ApprovalMode;
227
270
  environment?: {
228
271
  name: string;
229
272
  };
230
273
  variables?: Record<string, string>;
231
274
  }>;
275
+ /** The scheduled resume job (yodel.config.ts `ci.resume`); only the pipelines are rendered from it, the spec file leaves it out. */
276
+ resume?: {
277
+ schedule: string;
278
+ };
232
279
  }
233
280
  /**
234
281
  * The waves spec. What the file holds is the same for every forge; each
235
282
  * forge's jobs get their own variables on top (`variables`).
236
283
  */
237
- export declare function wavesSpec(waves: readonly Wave[], variables?: (env: string) => Record<string, string>): WavesSpec;
284
+ export declare function wavesSpec(waves: readonly Wave[], variables?: (env: string) => Record<string, string>, resume?: {
285
+ schedule: string;
286
+ }): WavesSpec;
238
287
  /** The waves spec file and the apply pipeline on each forge. */
239
288
  export declare function applyPipelines(dir: string, envs: Environments): Promise<RenderedFile[]>;
240
289
  /** yodel.config.ts `waves`, checked against the environments; one wave per environment, gate `always`, without it. */
241
290
  export declare function readWaves(dir: string, envs: readonly string[]): Promise<Wave[]>;
242
291
  /**
243
- * Each wave of an environment with a tenant set (#56), with a run per
244
- * tenant. A set given by a query is read from the server now, with the
245
- * environment's profile: run yodel ci again when the tenants change.
292
+ * Each wave of an environment with a tenant set (#56), with its runs: a
293
+ * run per tenant of a list or a file, or for a set given by a query,
294
+ * `shares` slots (one without) that read the tenants when the wave runs.
295
+ * The query is not run here, so the pipeline stays the same as tenants come
296
+ * and go; a list or a file is in the repository, and changing it is
297
+ * rendering again.
246
298
  */
247
299
  export declare function withTenants(dir: string, waves: readonly Wave[]): Promise<Wave[]>;
248
300
  /**
@@ -251,7 +303,7 @@ export declare function withTenants(dir: string, waves: readonly Wave[]): Promis
251
303
  * writer's only under its environment, which the watch does not declare), and
252
304
  * `NAME: $NAME` there would refer to itself.
253
305
  */
254
- export declare function watchSpec(env: string, dialect: Dialect, forge: Forge): {
306
+ export declare function watchSpec(env: string, dialect: Dialect, forge: Forge, creds?: EnvCredentials): {
255
307
  name: string;
256
308
  variables?: Record<string, string>;
257
309
  permissions?: Record<string, string>;
@@ -259,6 +311,8 @@ export declare function watchSpec(env: string, dialect: Dialect, forge: Forge):
259
311
  export declare function watchPipelines(dir: string, envs_: Environments): Promise<RenderedFile[]>;
260
312
  /** The dialect and the environments chant.config.ts declares, in order. */
261
313
  export declare function environments(dir: string): Promise<Environments>;
314
+ /** The roles of each environment whose profile is made with credentials(). */
315
+ export declare function credentialsIn(profiles: Record<string, unknown>, envs: readonly string[]): Record<string, EnvCredentials>;
262
316
  /**
263
317
  * The project's own jobs (#65): the module yodel.config.ts `ci.jobs` names,
264
318
  * relative to the project, declaring jobs with chant's lexicons. `actions`