@catladder/cli 0.0.0-next-aea5d3ad → 0.0.0-next-2b60440e

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 (56) hide show
  1. package/dist/bundles/cli/index.js +705 -229
  2. package/dist/bundles/runner-images/semantic-release/Dockerfile +15 -12
  3. package/dist/bundles/runner-images/semantic-release/scripts/semanticRelease +10 -1
  4. package/dist/bundles/skills/catladder-cli/references/commands.md +37 -2
  5. package/dist/bundles/skills/catladder-secrets/SKILL.md +42 -9
  6. package/dist/cli/src/commands/index.d.ts +4 -0
  7. package/dist/cli/src/commands/index.js +9 -1
  8. package/dist/cli/src/commands/index.js.map +1 -1
  9. package/dist/cli/src/commands/project/commandConfigSecrets.d.ts +6 -1
  10. package/dist/cli/src/commands/project/commandConfigSecrets.js +56 -15
  11. package/dist/cli/src/commands/project/commandConfigSecrets.js.map +1 -1
  12. package/dist/cli/src/commands/project/secrets/commandSecretsList.d.ts +24 -0
  13. package/dist/cli/src/commands/project/secrets/commandSecretsList.js +64 -0
  14. package/dist/cli/src/commands/project/secrets/commandSecretsList.js.map +1 -0
  15. package/dist/cli/src/commands/project/secrets/commandSecretsPull.d.ts +19 -0
  16. package/dist/cli/src/commands/project/secrets/commandSecretsPull.js +48 -0
  17. package/dist/cli/src/commands/project/secrets/commandSecretsPull.js.map +1 -0
  18. package/dist/cli/src/commands/project/secrets/commandSecretsPush.d.ts +19 -0
  19. package/dist/cli/src/commands/project/secrets/commandSecretsPush.js +74 -0
  20. package/dist/cli/src/commands/project/secrets/commandSecretsPush.js.map +1 -0
  21. package/dist/cli/src/commands/project/secrets/commandSecretsSet.d.ts +24 -0
  22. package/dist/cli/src/commands/project/secrets/commandSecretsSet.js +82 -0
  23. package/dist/cli/src/commands/project/secrets/commandSecretsSet.js.map +1 -0
  24. package/dist/cli/src/commands/project/secrets/document.d.ts +47 -0
  25. package/dist/cli/src/commands/project/secrets/document.js +180 -0
  26. package/dist/cli/src/commands/project/secrets/document.js.map +1 -0
  27. package/dist/cli/src/commands/project/secrets/parseKeysInput.d.ts +2 -0
  28. package/dist/cli/src/commands/project/secrets/parseKeysInput.js +10 -0
  29. package/dist/cli/src/commands/project/secrets/parseKeysInput.js.map +1 -0
  30. package/dist/cli/src/commands/project/secrets/scope.d.ts +35 -0
  31. package/dist/cli/src/commands/project/secrets/scope.js +113 -0
  32. package/dist/cli/src/commands/project/secrets/scope.js.map +1 -0
  33. package/dist/cli/src/utils/readStdin.d.ts +2 -0
  34. package/dist/cli/src/utils/readStdin.js +13 -0
  35. package/dist/cli/src/utils/readStdin.js.map +1 -0
  36. package/dist/runner-images/semantic-release/Dockerfile +15 -12
  37. package/dist/runner-images/semantic-release/scripts/semanticRelease +10 -1
  38. package/dist/skills/catladder-cli/references/commands.md +37 -2
  39. package/dist/skills/catladder-secrets/SKILL.md +42 -9
  40. package/dist/tsconfig.tsbuildinfo +1 -1
  41. package/package.json +1 -1
  42. package/src/commands/index.ts +4 -0
  43. package/src/commands/project/commandConfigSecrets.ts +76 -15
  44. package/src/commands/project/secrets/__tests__/document.test.ts +122 -0
  45. package/src/commands/project/secrets/commandSecretsList.ts +75 -0
  46. package/src/commands/project/secrets/commandSecretsPull.ts +55 -0
  47. package/src/commands/project/secrets/commandSecretsPush.ts +100 -0
  48. package/src/commands/project/secrets/commandSecretsSet.ts +102 -0
  49. package/src/commands/project/secrets/document.ts +269 -0
  50. package/src/commands/project/secrets/parseKeysInput.ts +8 -0
  51. package/src/commands/project/secrets/scope.ts +151 -0
  52. package/src/utils/readStdin.ts +8 -0
  53. package/dist/cli/src/apps/cli/commands/project/commandConfigSecrets.d.ts +0 -2
  54. package/dist/cli/src/apps/cli/commands/project/commandConfigSecrets.js +0 -206
  55. package/dist/cli/src/apps/cli/commands/project/commandConfigSecrets.js.map +0 -1
  56. package/src/apps/cli/commands/project/commandConfigSecrets.ts +0 -322
@@ -1,322 +0,0 @@
1
- import {
2
- getEnabledPipelineTypes,
3
- getSecretVarName,
4
- getVaultConfig,
5
- } from "@catladder/pipeline";
6
- import type { Config } from "@catladder/pipeline";
7
- import { stripIndents } from "common-tags";
8
- import { difference } from "lodash-es";
9
- import type { IO } from "../../../../core/types";
10
- import {
11
- getAllComponentsWithAllEnvsHierarchical,
12
- getEnvironment,
13
- getEnvVarsResolved,
14
- getJobOnlyEnvVarsResolved,
15
- getProjectComponents,
16
- getProjectConfig,
17
- parseChoice,
18
- } from "../../../../config/getProjectConfig";
19
- import {
20
- collectSecretTargets,
21
- getConfiguredGithubRepo,
22
- pushSecretsToGithub,
23
- } from "../../../../commands/project/commandSecretsSyncGithub";
24
- import { editAsFile } from "../../../../utils/editAsFile";
25
- import { isGhAuthenticated } from "../../../../utils/github";
26
- import { upsertAllVariables } from "../../../../utils/gitlab";
27
- import { delay } from "../../../../utils/promise";
28
-
29
- type Vars = {
30
- [env: string]: {
31
- [componentName: string]: Record<string, string>;
32
- };
33
- };
34
- /* for convenience, parse json objects. that makes it easier to edit secrets that are object */
35
- const resolveJson = (v: Vars) =>
36
- Object.fromEntries(
37
- Object.entries(v).map(([componentName, envs]) => {
38
- return [
39
- componentName,
40
- Object.fromEntries(
41
- Object.entries(envs).map(([env, secrets]) => [
42
- env,
43
- Object.fromEntries(
44
- Object.entries(secrets).map(([key, value]) => {
45
- try {
46
- return [key, JSON.parse(value)];
47
- } catch (e) {
48
- return [key, value];
49
- }
50
- }),
51
- ),
52
- ]),
53
- ),
54
- ];
55
- }),
56
- );
57
-
58
- const getSecretEnvVarKeysToConfigure = async (
59
- env: string,
60
- componentName: string,
61
- ) => {
62
- const { secretEnvVarKeys, jobOnlyVars } = await getEnvironment(
63
- env,
64
- componentName,
65
- );
66
- return [
67
- ...jobOnlyVars.build.secretEnvVarKeys,
68
- ...jobOnlyVars.deploy.secretEnvVarKeys,
69
- ...secretEnvVarKeys,
70
- ]
71
- .filter((k) => !k.hidden)
72
- .map((k) => k.key);
73
- };
74
- const getEnvVarsToEdit = async (
75
- instance: IO,
76
- env: string,
77
- componentName: string,
78
- ) => {
79
- const secretEnvVarKeys = await getSecretEnvVarKeysToConfigure(
80
- env,
81
- componentName,
82
- );
83
-
84
- const normalEnvVars = await getEnvVarsResolved(instance, env, componentName);
85
- const jobOnlyEnvVars = await getJobOnlyEnvVarsResolved(
86
- instance,
87
- env,
88
- componentName,
89
- );
90
- const allEnvVars = {
91
- ...normalEnvVars,
92
- ...jobOnlyEnvVars,
93
- };
94
- return Object.fromEntries(
95
- secretEnvVarKeys.map((key) => {
96
- const value = allEnvVars[key];
97
- // due to some quirky way to resolve these variables, unset variables have the $CL_ prefix, so we remove thouse here
98
- const variableIsNotSet =
99
- value === "$" + getSecretVarName(env, componentName, key);
100
- return [key, variableIsNotSet ? "🚨 FILL ME" : value];
101
- }),
102
- );
103
- };
104
- const doItFor = async (
105
- instance: IO,
106
- envAndComponents: {
107
- [componentName: string]: string[];
108
- },
109
- ) => {
110
- let valuesToEdit: Vars = Object.fromEntries(
111
- await Promise.all(
112
- Object.entries(envAndComponents).map(async ([componentName, envs]) => [
113
- componentName,
114
- Object.fromEntries(
115
- await Promise.all(
116
- envs.map(async (env) => [
117
- env,
118
- await getEnvVarsToEdit(instance, env, componentName),
119
- ]),
120
- ),
121
- ),
122
- ]),
123
- ),
124
- );
125
- let hasErrors = true;
126
- while (hasErrors) {
127
- valuesToEdit = await editAsFile(
128
- resolveJson(valuesToEdit),
129
- stripIndents`
130
- Please fill in all secrets for:
131
-
132
- ${Object.entries(envAndComponents)
133
- .map(
134
- ([componentName, envs]) => `- ${componentName}: ${envs.join(", ")}`,
135
- )
136
- .join("\n")}
137
-
138
- `,
139
- );
140
- // check for errors
141
- hasErrors = false;
142
- for (const [componentName, envs] of Object.entries(envAndComponents)) {
143
- for (const env of envs) {
144
- const usedKeys = valuesToEdit[componentName][env]
145
- ? Object.keys(valuesToEdit[componentName][env])
146
- : [];
147
- // check whether newValues have the exact number of keys
148
- const secretEnvVarKeys = await getSecretEnvVarKeysToConfigure(
149
- env,
150
- componentName,
151
- );
152
-
153
- const extranous = difference(usedKeys, secretEnvVarKeys);
154
- const missing = difference(secretEnvVarKeys, usedKeys);
155
-
156
- if (extranous.length > 0 || missing.length > 0) {
157
- instance.log("");
158
- instance.log(
159
- `😿 Oh no! There is something wrong with "${componentName}"`,
160
- );
161
- instance.log("");
162
- if (extranous.length > 0) {
163
- instance.log("these secrets are not declared in the config");
164
- extranous.forEach((key) => instance.log(key));
165
- instance.log("");
166
- }
167
- if (missing.length > 0) {
168
- instance.log("these secrets have not been provided:");
169
- missing.forEach((key) => instance.log(key));
170
- instance.log("");
171
- }
172
-
173
- await delay(1000);
174
- const shouldContinue = await instance.confirm("Try again? 🤔");
175
-
176
- if (!shouldContinue) {
177
- throw new Error("abort");
178
- }
179
- hasErrors = true;
180
- }
181
- }
182
- }
183
- }
184
- const config = await getProjectConfig();
185
- if (!config) {
186
- throw new Error("no catladder config found");
187
- }
188
-
189
- instance.log("");
190
- instance.log("writing all secrets to the vault, please wait...");
191
- instance.log("");
192
- for (const [componentName, envs] of Object.entries(envAndComponents)) {
193
- for (const env of envs) {
194
- instance.log("writing " + env + ":" + componentName + "...\n");
195
- await (
196
- await instance.getVaultManager()
197
- ).writeSecrets(env, componentName, valuesToEdit[componentName][env]);
198
- instance.log("");
199
- instance.log("✅ " + env + ":" + componentName);
200
- instance.log("--------------------------------\n");
201
- }
202
- }
203
-
204
- await mirrorSecretsToCiBackends(
205
- instance,
206
- config,
207
- envAndComponents,
208
- valuesToEdit,
209
- );
210
-
211
- instance.log("done! 😻");
212
- instance.log("");
213
- };
214
-
215
- /**
216
- * the vault is the source of truth; every enabled CI backend receives
217
- * a mirrored copy of the edited secrets (gitlab variables, github
218
- * secrets), so all CI systems stay in sync
219
- */
220
- const mirrorSecretsToCiBackends = async (
221
- instance: IO,
222
- config: Config,
223
- envAndComponents: { [componentName: string]: string[] },
224
- valuesToEdit: Vars,
225
- ) => {
226
- const enabled = getEnabledPipelineTypes(config);
227
-
228
- // gitlab mirror (unless gitlab is the vault itself, then it is
229
- // already written)
230
- if (getVaultConfig(config).type !== "gitlab" && enabled.includes("gitlab")) {
231
- for (const [componentName, envs] of Object.entries(envAndComponents)) {
232
- for (const env of envs) {
233
- await upsertAllVariables(
234
- instance,
235
- valuesToEdit[componentName][env],
236
- env,
237
- componentName,
238
- );
239
- }
240
- }
241
- instance.log("✅ mirrored to gitlab");
242
- }
243
-
244
- if (!enabled.includes("github")) {
245
- return;
246
- }
247
- const kinds = new Map<string, "secret" | "variable">();
248
- for (const [componentName, envs] of Object.entries(envAndComponents)) {
249
- for (const env of envs) {
250
- const { secretEnvVarKeys, jobOnlyVars } = await getEnvironment(
251
- env,
252
- componentName,
253
- );
254
- [
255
- ...secretEnvVarKeys,
256
- ...jobOnlyVars.build.secretEnvVarKeys,
257
- ...jobOnlyVars.deploy.secretEnvVarKeys,
258
- ].forEach(({ key, kind }) =>
259
- kinds.set(getSecretVarName(env, componentName, key), kind ?? "secret"),
260
- );
261
- }
262
- }
263
- const secrets = Object.entries(envAndComponents).flatMap(
264
- ([componentName, envs]) =>
265
- envs.flatMap((env) =>
266
- Object.entries(valuesToEdit[componentName][env] ?? {}).map(
267
- ([key, value]) => {
268
- const name = getSecretVarName(env, componentName, key);
269
- return {
270
- name,
271
- value:
272
- typeof value === "string" ? value : JSON.stringify(value ?? ""),
273
- kind: kinds.get(name) ?? ("secret" as const),
274
- };
275
- },
276
- ),
277
- ),
278
- );
279
- if (secrets.length === 0) {
280
- return;
281
- }
282
- if (!(await isGhAuthenticated())) {
283
- instance.log(
284
- "⚠️ github pipeline is enabled, but the github cli (gh) is not authenticated — run `project secrets-sync-github` later to mirror the secrets",
285
- );
286
- return;
287
- }
288
- const repo = await getConfiguredGithubRepo();
289
- if (!repo) {
290
- instance.log(
291
- "⚠️ github pipeline is enabled, but no github repository was found — run `project secrets-sync-github` later to mirror the secrets",
292
- );
293
- return;
294
- }
295
- instance.log(`mirroring ${secrets.length} secrets to github ('${repo}')...`);
296
- await pushSecretsToGithub(
297
- instance,
298
- repo,
299
- secrets,
300
- await collectSecretTargets(config),
301
- );
302
- };
303
-
304
- export const projectConfigSecrets = async (io: IO, envComponent?: string) => {
305
- if (!envComponent) {
306
- const allEnvAndcomponents = await getAllComponentsWithAllEnvsHierarchical();
307
- await doItFor(io, allEnvAndcomponents);
308
- } else {
309
- const { env, componentName } = parseChoice(envComponent);
310
-
311
- // componentName can be null. in this case, iterate over all components
312
- if (!componentName) {
313
- const components = await getProjectComponents();
314
- await doItFor(io, Object.fromEntries(components.map((c) => [c, [env]])));
315
- }
316
- if (componentName) {
317
- await doItFor(io, {
318
- [componentName]: [env],
319
- });
320
- }
321
- }
322
- };