@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
@@ -357,219 +357,6 @@ exports.getProjectMongodbAllPodsSortedWithLabel = getProjectMongodbAllPodsSorted
357
357
 
358
358
  /***/ }),
359
359
 
360
- /***/ 58002:
361
- /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => {
362
-
363
- "use strict";
364
-
365
- Object.defineProperty(exports, "__esModule", ({ value: true }));
366
- exports.projectConfigSecrets = void 0;
367
- const pipeline_1 = __nccwpck_require__(60241);
368
- const common_tags_1 = __nccwpck_require__(63779);
369
- const lodash_es_1 = __nccwpck_require__(64284);
370
- const getProjectConfig_1 = __nccwpck_require__(50001);
371
- const commandSecretsSyncGithub_1 = __nccwpck_require__(50962);
372
- const editAsFile_1 = __nccwpck_require__(82805);
373
- const github_1 = __nccwpck_require__(18036);
374
- const gitlab_1 = __nccwpck_require__(4508);
375
- const promise_1 = __nccwpck_require__(51762);
376
- /* for convenience, parse json objects. that makes it easier to edit secrets that are object */
377
- const resolveJson = (v) => Object.fromEntries(Object.entries(v).map(([componentName, envs]) => {
378
- return [
379
- componentName,
380
- Object.fromEntries(Object.entries(envs).map(([env, secrets]) => [
381
- env,
382
- Object.fromEntries(Object.entries(secrets).map(([key, value]) => {
383
- try {
384
- return [key, JSON.parse(value)];
385
- }
386
- catch (e) {
387
- return [key, value];
388
- }
389
- })),
390
- ])),
391
- ];
392
- }));
393
- const getSecretEnvVarKeysToConfigure = async (env, componentName) => {
394
- const { secretEnvVarKeys, jobOnlyVars } = await (0, getProjectConfig_1.getEnvironment)(env, componentName);
395
- return [
396
- ...jobOnlyVars.build.secretEnvVarKeys,
397
- ...jobOnlyVars.deploy.secretEnvVarKeys,
398
- ...secretEnvVarKeys,
399
- ]
400
- .filter((k) => !k.hidden)
401
- .map((k) => k.key);
402
- };
403
- const getEnvVarsToEdit = async (instance, env, componentName) => {
404
- const secretEnvVarKeys = await getSecretEnvVarKeysToConfigure(env, componentName);
405
- const normalEnvVars = await (0, getProjectConfig_1.getEnvVarsResolved)(instance, env, componentName);
406
- const jobOnlyEnvVars = await (0, getProjectConfig_1.getJobOnlyEnvVarsResolved)(instance, env, componentName);
407
- const allEnvVars = {
408
- ...normalEnvVars,
409
- ...jobOnlyEnvVars,
410
- };
411
- return Object.fromEntries(secretEnvVarKeys.map((key) => {
412
- const value = allEnvVars[key];
413
- // due to some quirky way to resolve these variables, unset variables have the $CL_ prefix, so we remove thouse here
414
- const variableIsNotSet = value === "$" + (0, pipeline_1.getSecretVarName)(env, componentName, key);
415
- return [key, variableIsNotSet ? "🚨 FILL ME" : value];
416
- }));
417
- };
418
- const doItFor = async (instance, envAndComponents) => {
419
- let valuesToEdit = Object.fromEntries(await Promise.all(Object.entries(envAndComponents).map(async ([componentName, envs]) => [
420
- componentName,
421
- Object.fromEntries(await Promise.all(envs.map(async (env) => [
422
- env,
423
- await getEnvVarsToEdit(instance, env, componentName),
424
- ]))),
425
- ])));
426
- let hasErrors = true;
427
- while (hasErrors) {
428
- valuesToEdit = await (0, editAsFile_1.editAsFile)(resolveJson(valuesToEdit), (0, common_tags_1.stripIndents) `
429
- Please fill in all secrets for:
430
-
431
- ${Object.entries(envAndComponents)
432
- .map(([componentName, envs]) => `- ${componentName}: ${envs.join(", ")}`)
433
- .join("\n")}
434
-
435
- `);
436
- // check for errors
437
- hasErrors = false;
438
- for (const [componentName, envs] of Object.entries(envAndComponents)) {
439
- for (const env of envs) {
440
- const usedKeys = valuesToEdit[componentName][env]
441
- ? Object.keys(valuesToEdit[componentName][env])
442
- : [];
443
- // check whether newValues have the exact number of keys
444
- const secretEnvVarKeys = await getSecretEnvVarKeysToConfigure(env, componentName);
445
- const extranous = (0, lodash_es_1.difference)(usedKeys, secretEnvVarKeys);
446
- const missing = (0, lodash_es_1.difference)(secretEnvVarKeys, usedKeys);
447
- if (extranous.length > 0 || missing.length > 0) {
448
- instance.log("");
449
- instance.log(`😿 Oh no! There is something wrong with "${componentName}"`);
450
- instance.log("");
451
- if (extranous.length > 0) {
452
- instance.log("these secrets are not declared in the config");
453
- extranous.forEach((key) => instance.log(key));
454
- instance.log("");
455
- }
456
- if (missing.length > 0) {
457
- instance.log("these secrets have not been provided:");
458
- missing.forEach((key) => instance.log(key));
459
- instance.log("");
460
- }
461
- await (0, promise_1.delay)(1000);
462
- const shouldContinue = await instance.confirm("Try again? 🤔");
463
- if (!shouldContinue) {
464
- throw new Error("abort");
465
- }
466
- hasErrors = true;
467
- }
468
- }
469
- }
470
- }
471
- const config = await (0, getProjectConfig_1.getProjectConfig)();
472
- if (!config) {
473
- throw new Error("no catladder config found");
474
- }
475
- instance.log("");
476
- instance.log("writing all secrets to the vault, please wait...");
477
- instance.log("");
478
- for (const [componentName, envs] of Object.entries(envAndComponents)) {
479
- for (const env of envs) {
480
- instance.log("writing " + env + ":" + componentName + "...\n");
481
- await (await instance.getVaultManager()).writeSecrets(env, componentName, valuesToEdit[componentName][env]);
482
- instance.log("");
483
- instance.log("✅ " + env + ":" + componentName);
484
- instance.log("--------------------------------\n");
485
- }
486
- }
487
- await mirrorSecretsToCiBackends(instance, config, envAndComponents, valuesToEdit);
488
- instance.log("done! 😻");
489
- instance.log("");
490
- };
491
- /**
492
- * the vault is the source of truth; every enabled CI backend receives
493
- * a mirrored copy of the edited secrets (gitlab variables, github
494
- * secrets), so all CI systems stay in sync
495
- */
496
- const mirrorSecretsToCiBackends = async (instance, config, envAndComponents, valuesToEdit) => {
497
- const enabled = (0, pipeline_1.getEnabledPipelineTypes)(config);
498
- // gitlab mirror (unless gitlab is the vault itself, then it is
499
- // already written)
500
- if ((0, pipeline_1.getVaultConfig)(config).type !== "gitlab" && enabled.includes("gitlab")) {
501
- for (const [componentName, envs] of Object.entries(envAndComponents)) {
502
- for (const env of envs) {
503
- await (0, gitlab_1.upsertAllVariables)(instance, valuesToEdit[componentName][env], env, componentName);
504
- }
505
- }
506
- instance.log("✅ mirrored to gitlab");
507
- }
508
- if (!enabled.includes("github")) {
509
- return;
510
- }
511
- const kinds = new Map();
512
- for (const [componentName, envs] of Object.entries(envAndComponents)) {
513
- for (const env of envs) {
514
- const { secretEnvVarKeys, jobOnlyVars } = await (0, getProjectConfig_1.getEnvironment)(env, componentName);
515
- [
516
- ...secretEnvVarKeys,
517
- ...jobOnlyVars.build.secretEnvVarKeys,
518
- ...jobOnlyVars.deploy.secretEnvVarKeys,
519
- ].forEach(({ key, kind }) => kinds.set((0, pipeline_1.getSecretVarName)(env, componentName, key), kind !== null && kind !== void 0 ? kind : "secret"));
520
- }
521
- }
522
- const secrets = Object.entries(envAndComponents).flatMap(([componentName, envs]) => envs.flatMap((env) => {
523
- var _a;
524
- return Object.entries((_a = valuesToEdit[componentName][env]) !== null && _a !== void 0 ? _a : {}).map(([key, value]) => {
525
- var _a;
526
- const name = (0, pipeline_1.getSecretVarName)(env, componentName, key);
527
- return {
528
- name,
529
- value: typeof value === "string" ? value : JSON.stringify(value !== null && value !== void 0 ? value : ""),
530
- kind: (_a = kinds.get(name)) !== null && _a !== void 0 ? _a : "secret",
531
- };
532
- });
533
- }));
534
- if (secrets.length === 0) {
535
- return;
536
- }
537
- if (!(await (0, github_1.isGhAuthenticated)())) {
538
- instance.log("⚠️ github pipeline is enabled, but the github cli (gh) is not authenticated — run `project secrets-sync-github` later to mirror the secrets");
539
- return;
540
- }
541
- const repo = await (0, commandSecretsSyncGithub_1.getConfiguredGithubRepo)();
542
- if (!repo) {
543
- instance.log("⚠️ github pipeline is enabled, but no github repository was found — run `project secrets-sync-github` later to mirror the secrets");
544
- return;
545
- }
546
- instance.log(`mirroring ${secrets.length} secrets to github ('${repo}')...`);
547
- await (0, commandSecretsSyncGithub_1.pushSecretsToGithub)(instance, repo, secrets, await (0, commandSecretsSyncGithub_1.collectSecretTargets)(config));
548
- };
549
- const projectConfigSecrets = async (io, envComponent) => {
550
- if (!envComponent) {
551
- const allEnvAndcomponents = await (0, getProjectConfig_1.getAllComponentsWithAllEnvsHierarchical)();
552
- await doItFor(io, allEnvAndcomponents);
553
- }
554
- else {
555
- const { env, componentName } = (0, getProjectConfig_1.parseChoice)(envComponent);
556
- // componentName can be null. in this case, iterate over all components
557
- if (!componentName) {
558
- const components = await (0, getProjectConfig_1.getProjectComponents)();
559
- await doItFor(io, Object.fromEntries(components.map((c) => [c, [env]])));
560
- }
561
- if (componentName) {
562
- await doItFor(io, {
563
- [componentName]: [env],
564
- });
565
- }
566
- }
567
- };
568
- exports.projectConfigSecrets = projectConfigSecrets;
569
- //# sourceMappingURL=commandConfigSecrets.js.map
570
-
571
- /***/ }),
572
-
573
360
  /***/ 88089:
574
361
  /***/ ((__unused_webpack_module, exports) => {
575
362
 
@@ -2533,7 +2320,7 @@ exports.commandKubeStopPortforward = (0, defineCommand_1.defineCommand)({
2533
2320
  "use strict";
2534
2321
 
2535
2322
  Object.defineProperty(exports, "__esModule", ({ value: true }));
2536
- exports.commandStarwars = exports.commandDadjoke = exports.commandSecurityAuditCreate = exports.commandSecurityAuditEvaluate = exports.commandSecurityAuditCiJob = exports.commandRestoreDb = exports.commandMongoDestroyMember = exports.commandMongoPortForward = exports.commandMongoGetShell = exports.commandTriggerCronjobProject = exports.commandDeleteProject = exports.commandPauseProject = exports.commandDeletePods = exports.commandGetShell = exports.commandListPods = exports.commandNamespace = exports.commandSecurityEvaluate = exports.commandProjectRestoreDb = exports.commandCiJobLog = exports.commandCiJobOpen = exports.commandDoctor = exports.commandSetup = exports.commandGetMyTotalWorktime = exports.commandRenewToken = exports.commandTriggerCronjobGeneral = exports.commandOpenLogs = exports.commandSecretsSyncGithub = exports.commandSecretsClearBackups = exports.commandConfigSecrets = exports.commandCloudSqlProxy = exports.commandOpenGit = exports.commandOpenEnv = exports.commandPortForward = exports.commandEnvVars = exports.commandKubePortForward = exports.commandKubeGetShell = exports.commandKubeStopPortforward = exports.commandKubeListPods = exports.commandKubeListSecrets = exports.commandKubeListNamespaces = exports.commandKubeCurrentContext = void 0;
2323
+ exports.commandStarwars = exports.commandDadjoke = exports.commandSecurityAuditCreate = exports.commandSecurityAuditEvaluate = exports.commandSecurityAuditCiJob = exports.commandRestoreDb = exports.commandMongoDestroyMember = exports.commandMongoPortForward = exports.commandMongoGetShell = exports.commandTriggerCronjobProject = exports.commandDeleteProject = exports.commandPauseProject = exports.commandDeletePods = exports.commandGetShell = exports.commandListPods = exports.commandNamespace = exports.commandSecurityEvaluate = exports.commandProjectRestoreDb = exports.commandCiJobLog = exports.commandCiJobOpen = exports.commandDoctor = exports.commandSetup = exports.commandGetMyTotalWorktime = exports.commandRenewToken = exports.commandTriggerCronjobGeneral = exports.commandOpenLogs = exports.commandSecretsSyncGithub = exports.commandSecretsClearBackups = exports.commandSecretsList = exports.commandSecretsSet = exports.commandSecretsPush = exports.commandSecretsPull = exports.commandConfigSecrets = exports.commandCloudSqlProxy = exports.commandOpenGit = exports.commandOpenEnv = exports.commandPortForward = exports.commandEnvVars = exports.commandKubePortForward = exports.commandKubeGetShell = exports.commandKubeStopPortforward = exports.commandKubeListPods = exports.commandKubeListSecrets = exports.commandKubeListNamespaces = exports.commandKubeCurrentContext = void 0;
2537
2324
  // General commands
2538
2325
  var commandKubeCurrentContext_1 = __nccwpck_require__(70243);
2539
2326
  Object.defineProperty(exports, "commandKubeCurrentContext", ({ enumerable: true, get: function () { return commandKubeCurrentContext_1.commandKubeCurrentContext; } }));
@@ -2562,6 +2349,14 @@ var commandCloudSqlProxy_1 = __nccwpck_require__(10878);
2562
2349
  Object.defineProperty(exports, "commandCloudSqlProxy", ({ enumerable: true, get: function () { return commandCloudSqlProxy_1.commandCloudSqlProxy; } }));
2563
2350
  var commandConfigSecrets_1 = __nccwpck_require__(40876);
2564
2351
  Object.defineProperty(exports, "commandConfigSecrets", ({ enumerable: true, get: function () { return commandConfigSecrets_1.commandConfigSecrets; } }));
2352
+ var commandSecretsPull_1 = __nccwpck_require__(2835);
2353
+ Object.defineProperty(exports, "commandSecretsPull", ({ enumerable: true, get: function () { return commandSecretsPull_1.commandSecretsPull; } }));
2354
+ var commandSecretsPush_1 = __nccwpck_require__(1460);
2355
+ Object.defineProperty(exports, "commandSecretsPush", ({ enumerable: true, get: function () { return commandSecretsPush_1.commandSecretsPush; } }));
2356
+ var commandSecretsSet_1 = __nccwpck_require__(65514);
2357
+ Object.defineProperty(exports, "commandSecretsSet", ({ enumerable: true, get: function () { return commandSecretsSet_1.commandSecretsSet; } }));
2358
+ var commandSecretsList_1 = __nccwpck_require__(97240);
2359
+ Object.defineProperty(exports, "commandSecretsList", ({ enumerable: true, get: function () { return commandSecretsList_1.commandSecretsList; } }));
2565
2360
  var commandSecretsClearBackups_1 = __nccwpck_require__(49558);
2566
2361
  Object.defineProperty(exports, "commandSecretsClearBackups", ({ enumerable: true, get: function () { return commandSecretsClearBackups_1.commandSecretsClearBackups; } }));
2567
2362
  var commandSecretsSyncGithub_1 = __nccwpck_require__(50962);
@@ -2977,34 +2772,75 @@ exports.commandCloudSqlProxy = (0, defineCommand_1.defineCommand)({
2977
2772
 
2978
2773
  Object.defineProperty(exports, "__esModule", ({ value: true }));
2979
2774
  exports.commandConfigSecrets = void 0;
2775
+ const common_tags_1 = __nccwpck_require__(63779);
2980
2776
  const defineCommand_1 = __nccwpck_require__(39489);
2981
- const commandConfigSecrets_1 = __nccwpck_require__(58002);
2982
- const autocompletions_1 = __nccwpck_require__(52468);
2983
- // This command delegates to the existing implementation which uses
2984
- // editAsFile (opens editor) and has retry loops with confirm prompts.
2985
- // Full migration to CommandIO would require reworking the editor flow,
2986
- // so for now we bridge it.
2777
+ const editAsFile_1 = __nccwpck_require__(82805);
2778
+ const promise_1 = __nccwpck_require__(51762);
2779
+ const document_1 = __nccwpck_require__(52283);
2780
+ const scope_1 = __nccwpck_require__(17712);
2781
+ const parseKeysInput_1 = __nccwpck_require__(55443);
2987
2782
  exports.commandConfigSecrets = (0, defineCommand_1.defineCommand)({
2988
2783
  name: "project config-secrets",
2989
- description: "setup/update secrets stored in pass",
2784
+ description: "setup/update secrets in the vault via your editor",
2990
2785
  group: "project",
2991
2786
  inputs: {
2992
2787
  envComponent: {
2993
2788
  type: "string",
2994
- message: "environment:component",
2789
+ message: "scope: env:component, env:, :component or empty for everything",
2995
2790
  positional: true,
2996
2791
  required: false,
2997
- choices: async () => (0, autocompletions_1.envAndComponents)(),
2792
+ choices: async () => (0, scope_1.secretsScopeChoices)(),
2793
+ },
2794
+ key: {
2795
+ type: "string",
2796
+ message: "only edit these secrets (comma-separated keys)",
2797
+ required: false,
2998
2798
  },
2999
2799
  },
3000
2800
  execute: async (ctx) => {
3001
2801
  const envComponent = await ctx.get("envComponent");
3002
- // Bridge: the existing implementation accepts CommandInstance-like
3003
- // objects for logging and prompting. Since it uses this.prompt()
3004
- // internally (not ctx.prompt), we pass ctx which satisfies the
3005
- // IO interface for logging. The interactive prompts inside
3006
- // projectConfigSecrets will need a deeper migration later.
3007
- await (0, commandConfigSecrets_1.projectConfigSecrets)(ctx, envComponent);
2802
+ const keys = (0, parseKeysInput_1.parseKeysInput)(await ctx.get("key"));
2803
+ if (!ctx.interactive) {
2804
+ throw new Error("project config-secrets opens an editor and needs an interactive terminal — " +
2805
+ "use `project secrets-pull`, `project secrets-push` or `project secrets-set` instead");
2806
+ }
2807
+ const scope = await (0, scope_1.resolveSecretsScope)(envComponent);
2808
+ const scoped = await (0, scope_1.getScopedSecretKeys)(scope, keys);
2809
+ let document = await (0, document_1.buildSecretsDocument)(ctx, scoped);
2810
+ let hasErrors = true;
2811
+ while (hasErrors) {
2812
+ document = await (0, editAsFile_1.editAsFile)(document, (0, common_tags_1.stripIndents) `
2813
+ Please fill in all secrets for:
2814
+
2815
+ ${(0, scope_1.formatScope)(scoped)}
2816
+
2817
+ `);
2818
+ const problems = (0, document_1.checkSecretsDocument)(document, scoped, {
2819
+ requireComplete: true,
2820
+ });
2821
+ hasErrors = problems.length > 0;
2822
+ if (hasErrors) {
2823
+ ctx.log("");
2824
+ ctx.log("😿 Oh no! There is something wrong:");
2825
+ ctx.log("");
2826
+ problems.forEach((problem) => ctx.log(problem));
2827
+ ctx.log("");
2828
+ await (0, promise_1.delay)(1000);
2829
+ const shouldContinue = await ctx.confirm("Try again? 🤔");
2830
+ if (!shouldContinue) {
2831
+ throw new Error("abort");
2832
+ }
2833
+ }
2834
+ }
2835
+ const { writes, skipped } = (0, document_1.collectSecretsWrites)(document, scoped);
2836
+ if (skipped.length > 0) {
2837
+ ctx.log("");
2838
+ ctx.log("⏭️ these secrets are still unset and will not be written:");
2839
+ skipped.forEach((entry) => ctx.log(` - ${entry}`));
2840
+ }
2841
+ await (0, document_1.writeSecretsAndMirror)(ctx, writes);
2842
+ ctx.log("done! 😻");
2843
+ ctx.log("");
3008
2844
  },
3009
2845
  });
3010
2846
  //# sourceMappingURL=commandConfigSecrets.js.map
@@ -4248,6 +4084,626 @@ exports.commandTriggerCronjobProject = (0, defineCommand_1.defineCommand)({
4248
4084
 
4249
4085
  /***/ }),
4250
4086
 
4087
+ /***/ 97240:
4088
+ /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => {
4089
+
4090
+ "use strict";
4091
+
4092
+ Object.defineProperty(exports, "__esModule", ({ value: true }));
4093
+ exports.commandSecretsList = void 0;
4094
+ const pipeline_1 = __nccwpck_require__(60241);
4095
+ const defineCommand_1 = __nccwpck_require__(39489);
4096
+ const scope_1 = __nccwpck_require__(17712);
4097
+ const parseKeysInput_1 = __nccwpck_require__(55443);
4098
+ exports.commandSecretsList = (0, defineCommand_1.defineCommand)({
4099
+ name: "project secrets-list",
4100
+ description: "lists the declared secrets of a scope with their set/unset status (values only with --reveal)",
4101
+ group: "project",
4102
+ inputs: {
4103
+ envComponent: {
4104
+ type: "string",
4105
+ message: "scope: env:component, env:, :component or empty for everything",
4106
+ positional: true,
4107
+ required: false,
4108
+ choices: async () => (0, scope_1.secretsScopeChoices)(),
4109
+ },
4110
+ key: {
4111
+ type: "string",
4112
+ message: "only these secrets (comma-separated keys)",
4113
+ required: false,
4114
+ },
4115
+ reveal: {
4116
+ type: "boolean",
4117
+ message: "also print the secret values",
4118
+ default: false,
4119
+ },
4120
+ check: {
4121
+ type: "boolean",
4122
+ message: "exit with an error when any secret is unset (for CI/agents)",
4123
+ default: false,
4124
+ },
4125
+ },
4126
+ execute: async (ctx) => {
4127
+ const scope = await (0, scope_1.resolveSecretsScope)(await ctx.get("envComponent"));
4128
+ const scoped = await (0, scope_1.getScopedSecretKeys)(scope, (0, parseKeysInput_1.parseKeysInput)(await ctx.get("key")));
4129
+ const reveal = await ctx.get("reveal");
4130
+ const names = scoped.flatMap(({ env, componentName, keys }) => keys.map((key) => (0, pipeline_1.getSecretVarName)(env, componentName, key)));
4131
+ const values = await (await ctx.getVaultManager()).readSecrets(names, ctx);
4132
+ let unsetCount = 0;
4133
+ let setCount = 0;
4134
+ for (const { env, componentName, keys } of scoped) {
4135
+ for (const key of keys) {
4136
+ const value = values[(0, pipeline_1.getSecretVarName)(env, componentName, key)];
4137
+ if (value === undefined) {
4138
+ unsetCount += 1;
4139
+ ctx.log(`🚨 ${env}:${componentName} ${key} (unset)`);
4140
+ }
4141
+ else {
4142
+ setCount += 1;
4143
+ ctx.log(`✅ ${env}:${componentName} ${key}` + (reveal ? ` = ${value}` : ""));
4144
+ }
4145
+ }
4146
+ }
4147
+ ctx.log("");
4148
+ ctx.log(`${setCount} set, ${unsetCount} unset`);
4149
+ if ((await ctx.get("check")) && unsetCount > 0) {
4150
+ throw new Error(`${unsetCount} secrets are unset`);
4151
+ }
4152
+ },
4153
+ });
4154
+ //# sourceMappingURL=commandSecretsList.js.map
4155
+
4156
+ /***/ }),
4157
+
4158
+ /***/ 2835:
4159
+ /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => {
4160
+
4161
+ "use strict";
4162
+
4163
+ Object.defineProperty(exports, "__esModule", ({ value: true }));
4164
+ exports.commandSecretsPull = void 0;
4165
+ const fs_extra_1 = __nccwpck_require__(96954);
4166
+ const yaml_1 = __nccwpck_require__(6637);
4167
+ const defineCommand_1 = __nccwpck_require__(39489);
4168
+ const document_1 = __nccwpck_require__(52283);
4169
+ const scope_1 = __nccwpck_require__(17712);
4170
+ const parseKeysInput_1 = __nccwpck_require__(55443);
4171
+ exports.commandSecretsPull = (0, defineCommand_1.defineCommand)({
4172
+ name: "project secrets-pull",
4173
+ description: "prints the current secrets of a scope as yaml (unset ones as a placeholder) — edit and feed to secrets-push",
4174
+ group: "project",
4175
+ inputs: {
4176
+ envComponent: {
4177
+ type: "string",
4178
+ message: "scope: env:component, env:, :component or empty for everything",
4179
+ positional: true,
4180
+ required: false,
4181
+ choices: async () => (0, scope_1.secretsScopeChoices)(),
4182
+ },
4183
+ key: {
4184
+ type: "string",
4185
+ message: "only these secrets (comma-separated keys)",
4186
+ required: false,
4187
+ },
4188
+ out: {
4189
+ type: "string",
4190
+ message: "write the yaml to this file instead of stdout",
4191
+ required: false,
4192
+ },
4193
+ },
4194
+ execute: async (ctx) => {
4195
+ const scope = await (0, scope_1.resolveSecretsScope)(await ctx.get("envComponent"));
4196
+ const scoped = await (0, scope_1.getScopedSecretKeys)(scope, (0, parseKeysInput_1.parseKeysInput)(await ctx.get("key")));
4197
+ const document = await (0, document_1.buildSecretsDocument)(ctx, scoped);
4198
+ const yaml = (0, yaml_1.stringify)(document, { aliasDuplicateObjects: false });
4199
+ const out = await ctx.get("out");
4200
+ if (out) {
4201
+ await (0, fs_extra_1.writeFile)(out, yaml);
4202
+ ctx.log(`wrote secrets to ${out} — ⚠️ contains secret values, don't commit it`);
4203
+ }
4204
+ else {
4205
+ ctx.log(yaml);
4206
+ }
4207
+ },
4208
+ });
4209
+ //# sourceMappingURL=commandSecretsPull.js.map
4210
+
4211
+ /***/ }),
4212
+
4213
+ /***/ 1460:
4214
+ /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => {
4215
+
4216
+ "use strict";
4217
+
4218
+ Object.defineProperty(exports, "__esModule", ({ value: true }));
4219
+ exports.commandSecretsPush = void 0;
4220
+ const fs_extra_1 = __nccwpck_require__(96954);
4221
+ const yaml_1 = __nccwpck_require__(6637);
4222
+ const defineCommand_1 = __nccwpck_require__(39489);
4223
+ const readStdin_1 = __nccwpck_require__(185);
4224
+ const document_1 = __nccwpck_require__(52283);
4225
+ const scope_1 = __nccwpck_require__(17712);
4226
+ const parseKeysInput_1 = __nccwpck_require__(55443);
4227
+ exports.commandSecretsPush = (0, defineCommand_1.defineCommand)({
4228
+ name: "project secrets-push",
4229
+ description: "writes secrets from a yaml document (secrets-pull format, --file or stdin) to the vault; may be partial",
4230
+ group: "project",
4231
+ inputs: {
4232
+ envComponent: {
4233
+ type: "string",
4234
+ message: "scope: env:component, env:, :component or empty for everything",
4235
+ positional: true,
4236
+ required: false,
4237
+ choices: async () => (0, scope_1.secretsScopeChoices)(),
4238
+ },
4239
+ key: {
4240
+ type: "string",
4241
+ message: "only write these secrets (comma-separated keys)",
4242
+ required: false,
4243
+ },
4244
+ file: {
4245
+ type: "string",
4246
+ message: "yaml file to push (component → env → key: value); stdin when omitted",
4247
+ required: false,
4248
+ },
4249
+ },
4250
+ execute: async (ctx) => {
4251
+ const scope = await (0, scope_1.resolveSecretsScope)(await ctx.get("envComponent"));
4252
+ const scoped = await (0, scope_1.getScopedSecretKeys)(scope, (0, parseKeysInput_1.parseKeysInput)(await ctx.get("key")));
4253
+ const file = await ctx.get("file");
4254
+ if (!file && process.stdin.isTTY) {
4255
+ throw new Error("nothing to push — pass --file <secrets.yml> or pipe yaml (secrets-pull format) to stdin");
4256
+ }
4257
+ const raw = file
4258
+ ? (await (0, fs_extra_1.readFile)(file)).toString("utf-8")
4259
+ : await (0, readStdin_1.readStdin)();
4260
+ const document = (0, yaml_1.parse)(raw);
4261
+ if (!document || typeof document !== "object") {
4262
+ throw new Error("the document must be a yaml object: component → env → key: value (see secrets-pull)");
4263
+ }
4264
+ const problems = (0, document_1.checkSecretsDocument)(document, scoped, {
4265
+ requireComplete: false,
4266
+ });
4267
+ if (problems.length > 0) {
4268
+ throw new Error("😿 the document does not match the config:\n" +
4269
+ problems.map((problem) => ` - ${problem}`).join("\n"));
4270
+ }
4271
+ const { writes, skipped } = (0, document_1.collectSecretsWrites)(document, scoped);
4272
+ if (skipped.length > 0) {
4273
+ ctx.log("⏭️ skipping placeholder/empty values:");
4274
+ skipped.forEach((entry) => ctx.log(` - ${entry}`));
4275
+ }
4276
+ if (writes.length === 0) {
4277
+ ctx.log("no secrets to write 🤷");
4278
+ return;
4279
+ }
4280
+ ctx.log("about to write:");
4281
+ writes.forEach(({ env, componentName, secrets }) => ctx.log(` - ${env}:${componentName}: ${Object.keys(secrets).join(", ")}`));
4282
+ const confirmed = await ctx.confirm("continue? 🤔");
4283
+ if (!confirmed) {
4284
+ throw new Error("abort");
4285
+ }
4286
+ await (0, document_1.writeSecretsAndMirror)(ctx, writes);
4287
+ ctx.log("done! 😻");
4288
+ },
4289
+ });
4290
+ //# sourceMappingURL=commandSecretsPush.js.map
4291
+
4292
+ /***/ }),
4293
+
4294
+ /***/ 65514:
4295
+ /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => {
4296
+
4297
+ "use strict";
4298
+
4299
+ Object.defineProperty(exports, "__esModule", ({ value: true }));
4300
+ exports.commandSecretsSet = void 0;
4301
+ const fs_extra_1 = __nccwpck_require__(96954);
4302
+ const defineCommand_1 = __nccwpck_require__(39489);
4303
+ const readStdin_1 = __nccwpck_require__(185);
4304
+ const document_1 = __nccwpck_require__(52283);
4305
+ const scope_1 = __nccwpck_require__(17712);
4306
+ const resolveValue = async (io, value, valueFile) => {
4307
+ if (valueFile) {
4308
+ // a trailing newline is almost never part of the secret
4309
+ return (await (0, fs_extra_1.readFile)(valueFile)).toString("utf-8").replace(/\n$/, "");
4310
+ }
4311
+ if (value !== undefined) {
4312
+ return value;
4313
+ }
4314
+ if (!process.stdin.isTTY) {
4315
+ return (await (0, readStdin_1.readStdin)()).replace(/\n$/, "");
4316
+ }
4317
+ return io.promptDirect({
4318
+ name: "value",
4319
+ type: "string",
4320
+ message: "value",
4321
+ });
4322
+ };
4323
+ exports.commandSecretsSet = (0, defineCommand_1.defineCommand)({
4324
+ name: "project secrets-set",
4325
+ description: "sets one secret — in one env (dev:web), one env everywhere (dev:), or all envs of a component (:web); value via --value, --value-file or stdin",
4326
+ group: "project",
4327
+ inputs: {
4328
+ envComponent: {
4329
+ type: "string",
4330
+ message: "scope: env:component, env:, or :component",
4331
+ positional: true,
4332
+ choices: async () => (0, scope_1.secretsScopeChoices)(),
4333
+ },
4334
+ key: {
4335
+ type: "string",
4336
+ message: "which secret?",
4337
+ positional: true,
4338
+ choices: async (ctx) => {
4339
+ const scope = await (0, scope_1.resolveSecretsScope)(await ctx.get("envComponent"));
4340
+ const scoped = await (0, scope_1.getScopedSecretKeys)(scope);
4341
+ return [...new Set(scoped.flatMap(({ keys }) => keys))];
4342
+ },
4343
+ },
4344
+ value: {
4345
+ type: "string",
4346
+ message: "the secret value (prefer --value-file or stdin over shell history)",
4347
+ required: false,
4348
+ },
4349
+ valueFile: {
4350
+ type: "string",
4351
+ message: "read the secret value from this file",
4352
+ required: false,
4353
+ },
4354
+ },
4355
+ execute: async (ctx) => {
4356
+ const scope = await (0, scope_1.resolveSecretsScope)(await ctx.get("envComponent"));
4357
+ const key = await ctx.get("key");
4358
+ // drops env/component pairs where the key is not declared and
4359
+ // throws when it is declared nowhere in the scope
4360
+ const scoped = await (0, scope_1.getScopedSecretKeys)(scope, [key]);
4361
+ const value = await resolveValue(ctx, await ctx.get("value"), await ctx.get("valueFile"));
4362
+ const targets = scoped.map(({ env, componentName }) => `${env}:${componentName}`);
4363
+ ctx.log(`setting ${key} in: ${targets.join(", ")}`);
4364
+ if (targets.length > 1) {
4365
+ const confirmed = await ctx.confirm(`write the same value to ${targets.length} environments? 🤔`);
4366
+ if (!confirmed) {
4367
+ throw new Error("abort");
4368
+ }
4369
+ }
4370
+ const writes = scoped.map(({ env, componentName }) => ({
4371
+ env,
4372
+ componentName,
4373
+ secrets: { [key]: value },
4374
+ }));
4375
+ await (0, document_1.writeSecretsAndMirror)(ctx, writes);
4376
+ ctx.log("done! 😻");
4377
+ },
4378
+ });
4379
+ //# sourceMappingURL=commandSecretsSet.js.map
4380
+
4381
+ /***/ }),
4382
+
4383
+ /***/ 52283:
4384
+ /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => {
4385
+
4386
+ "use strict";
4387
+
4388
+ Object.defineProperty(exports, "__esModule", ({ value: true }));
4389
+ exports.writeSecretsAndMirror = exports.collectSecretsWrites = exports.checkSecretsDocument = exports.buildSecretsDocument = exports.FILL_ME = void 0;
4390
+ const pipeline_1 = __nccwpck_require__(60241);
4391
+ const getProjectConfig_1 = __nccwpck_require__(50001);
4392
+ const github_1 = __nccwpck_require__(18036);
4393
+ const gitlab_1 = __nccwpck_require__(4508);
4394
+ const commandSecretsSyncGithub_1 = __nccwpck_require__(50962);
4395
+ /** placeholder for secrets that have no value in the vault yet */
4396
+ exports.FILL_ME = "🚨 FILL ME";
4397
+ /* for convenience, parse json values. that makes it easier to edit secrets that are objects */
4398
+ const parseJsonValue = (value) => {
4399
+ try {
4400
+ return JSON.parse(value);
4401
+ }
4402
+ catch (e) {
4403
+ return value;
4404
+ }
4405
+ };
4406
+ /**
4407
+ * builds the document for the given scope with the current values from
4408
+ * the vault; unset secrets appear as the FILL_ME placeholder
4409
+ */
4410
+ const buildSecretsDocument = async (io, scoped) => {
4411
+ var _a;
4412
+ const names = scoped.flatMap(({ env, componentName, keys }) => keys.map((key) => (0, pipeline_1.getSecretVarName)(env, componentName, key)));
4413
+ const values = await (await io.getVaultManager()).readSecrets(names, io);
4414
+ const document = {};
4415
+ for (const { env, componentName, keys } of scoped) {
4416
+ (_a = document[componentName]) !== null && _a !== void 0 ? _a : (document[componentName] = {});
4417
+ document[componentName][env] = Object.fromEntries(keys.map((key) => {
4418
+ const value = values[(0, pipeline_1.getSecretVarName)(env, componentName, key)];
4419
+ return [key, value === undefined ? exports.FILL_ME : parseJsonValue(value)];
4420
+ }));
4421
+ }
4422
+ return document;
4423
+ };
4424
+ exports.buildSecretsDocument = buildSecretsDocument;
4425
+ /**
4426
+ * validates a document against the scope. Undeclared components, envs
4427
+ * or keys are always problems; missing keys only when `requireComplete`
4428
+ * (the guided editor flow) — pushes may be partial.
4429
+ */
4430
+ const checkSecretsDocument = (document, scoped, { requireComplete }) => {
4431
+ var _a, _b;
4432
+ const problems = [];
4433
+ const scopedKeys = new Map();
4434
+ scoped.forEach(({ env, componentName, keys }) => scopedKeys.set(`${env}:${componentName}`, keys));
4435
+ const scopedComponents = new Set(scoped.map((s) => s.componentName));
4436
+ for (const [componentName, envs] of Object.entries(document !== null && document !== void 0 ? document : {})) {
4437
+ if (!scopedComponents.has(componentName)) {
4438
+ problems.push(`"${componentName}" is not a component in this scope — remove it or widen the scope`);
4439
+ continue;
4440
+ }
4441
+ for (const [env, secrets] of Object.entries(envs !== null && envs !== void 0 ? envs : {})) {
4442
+ const keys = scopedKeys.get(`${env}:${componentName}`);
4443
+ if (!keys) {
4444
+ problems.push(`"${env}:${componentName}" is not in this scope — remove it or widen the scope`);
4445
+ continue;
4446
+ }
4447
+ const extraneous = Object.keys(secrets !== null && secrets !== void 0 ? secrets : {}).filter((key) => !keys.includes(key));
4448
+ extraneous.forEach((key) => problems.push(`"${env}:${componentName}": "${key}" is not declared in the config (or filtered out by --key)`));
4449
+ }
4450
+ }
4451
+ if (requireComplete) {
4452
+ for (const { env, componentName, keys } of scoped) {
4453
+ const provided = Object.keys((_b = (_a = document === null || document === void 0 ? void 0 : document[componentName]) === null || _a === void 0 ? void 0 : _a[env]) !== null && _b !== void 0 ? _b : {});
4454
+ keys
4455
+ .filter((key) => !provided.includes(key))
4456
+ .forEach((key) => problems.push(`"${env}:${componentName}": "${key}" has not been provided`));
4457
+ }
4458
+ }
4459
+ return problems;
4460
+ };
4461
+ exports.checkSecretsDocument = checkSecretsDocument;
4462
+ /**
4463
+ * turns a (validated) document into writes. FILL_ME placeholders and
4464
+ * empty (null) values are skipped, so an untouched placeholder can
4465
+ * never end up as a live secret value.
4466
+ */
4467
+ const collectSecretsWrites = (document, scoped) => {
4468
+ var _a, _b;
4469
+ const writes = [];
4470
+ const skipped = [];
4471
+ for (const { env, componentName, keys } of scoped) {
4472
+ const provided = (_b = (_a = document === null || document === void 0 ? void 0 : document[componentName]) === null || _a === void 0 ? void 0 : _a[env]) !== null && _b !== void 0 ? _b : {};
4473
+ const secrets = {};
4474
+ for (const key of keys) {
4475
+ if (!(key in provided)) {
4476
+ continue;
4477
+ }
4478
+ const value = provided[key];
4479
+ if (value === exports.FILL_ME || value === null || value === undefined) {
4480
+ skipped.push(`${env}:${componentName} ${key}`);
4481
+ continue;
4482
+ }
4483
+ secrets[key] = value;
4484
+ }
4485
+ if (Object.keys(secrets).length > 0) {
4486
+ writes.push({ env, componentName, secrets });
4487
+ }
4488
+ }
4489
+ return { writes, skipped };
4490
+ };
4491
+ exports.collectSecretsWrites = collectSecretsWrites;
4492
+ /**
4493
+ * writes secrets to the vault (the source of truth) and mirrors them
4494
+ * to every enabled CI backend — which backends exist is invisible to
4495
+ * the caller
4496
+ */
4497
+ const writeSecretsAndMirror = async (io, writes) => {
4498
+ const config = await (0, getProjectConfig_1.getProjectConfig)();
4499
+ if (!config) {
4500
+ throw new Error("no catladder config found");
4501
+ }
4502
+ io.log("");
4503
+ io.log("writing secrets to the vault, please wait...");
4504
+ io.log("");
4505
+ for (const { env, componentName, secrets } of writes) {
4506
+ io.log("writing " + env + ":" + componentName + "...\n");
4507
+ await (await io.getVaultManager()).writeSecrets(env, componentName, secrets);
4508
+ io.log("");
4509
+ io.log("✅ " + env + ":" + componentName);
4510
+ io.log("--------------------------------\n");
4511
+ }
4512
+ await mirrorSecretsToCiBackends(io, config, writes);
4513
+ };
4514
+ exports.writeSecretsAndMirror = writeSecretsAndMirror;
4515
+ /**
4516
+ * the vault is the source of truth; every enabled CI backend receives
4517
+ * a mirrored copy of the written secrets (gitlab variables, github
4518
+ * secrets), so all CI systems stay in sync
4519
+ */
4520
+ const mirrorSecretsToCiBackends = async (io, config, writes) => {
4521
+ const enabled = (0, pipeline_1.getEnabledPipelineTypes)(config);
4522
+ // gitlab mirror (unless gitlab is the vault itself, then it is
4523
+ // already written)
4524
+ if ((0, pipeline_1.getVaultConfig)(config).type !== "gitlab" && enabled.includes("gitlab")) {
4525
+ for (const { env, componentName, secrets } of writes) {
4526
+ await (0, gitlab_1.upsertAllVariables)(io, secrets, env, componentName);
4527
+ }
4528
+ io.log("✅ mirrored to gitlab");
4529
+ }
4530
+ if (!enabled.includes("github")) {
4531
+ return;
4532
+ }
4533
+ const kinds = new Map();
4534
+ for (const { env, componentName } of writes) {
4535
+ const { secretEnvVarKeys, jobOnlyVars } = await (0, getProjectConfig_1.getEnvironment)(env, componentName);
4536
+ [
4537
+ ...secretEnvVarKeys,
4538
+ ...jobOnlyVars.build.secretEnvVarKeys,
4539
+ ...jobOnlyVars.deploy.secretEnvVarKeys,
4540
+ ].forEach(({ key, kind }) => kinds.set((0, pipeline_1.getSecretVarName)(env, componentName, key), kind !== null && kind !== void 0 ? kind : "secret"));
4541
+ }
4542
+ const secrets = writes.flatMap(({ env, componentName, secrets }) => Object.entries(secrets).map(([key, value]) => {
4543
+ var _a;
4544
+ const name = (0, pipeline_1.getSecretVarName)(env, componentName, key);
4545
+ return {
4546
+ name,
4547
+ value: typeof value === "string" ? value : JSON.stringify(value !== null && value !== void 0 ? value : ""),
4548
+ kind: (_a = kinds.get(name)) !== null && _a !== void 0 ? _a : "secret",
4549
+ };
4550
+ }));
4551
+ if (secrets.length === 0) {
4552
+ return;
4553
+ }
4554
+ if (!(await (0, github_1.isGhAuthenticated)())) {
4555
+ io.log("⚠️ github pipeline is enabled, but the github cli (gh) is not authenticated — run `project secrets-sync-github` later to mirror the secrets");
4556
+ return;
4557
+ }
4558
+ const repo = await (0, commandSecretsSyncGithub_1.getConfiguredGithubRepo)();
4559
+ if (!repo) {
4560
+ io.log("⚠️ github pipeline is enabled, but no github repository was found — run `project secrets-sync-github` later to mirror the secrets");
4561
+ return;
4562
+ }
4563
+ io.log(`mirroring ${secrets.length} secrets to github ('${repo}')...`);
4564
+ await (0, commandSecretsSyncGithub_1.pushSecretsToGithub)(io, repo, secrets, await (0, commandSecretsSyncGithub_1.collectSecretTargets)(config));
4565
+ };
4566
+ //# sourceMappingURL=document.js.map
4567
+
4568
+ /***/ }),
4569
+
4570
+ /***/ 55443:
4571
+ /***/ ((__unused_webpack_module, exports) => {
4572
+
4573
+ "use strict";
4574
+
4575
+ Object.defineProperty(exports, "__esModule", ({ value: true }));
4576
+ exports.parseKeysInput = void 0;
4577
+ /** parses a comma-separated `--key` input into a list of keys */
4578
+ const parseKeysInput = (input) => {
4579
+ const keys = input === null || input === void 0 ? void 0 : input.split(",").map((key) => key.trim()).filter(Boolean);
4580
+ return keys && keys.length > 0 ? keys : undefined;
4581
+ };
4582
+ exports.parseKeysInput = parseKeysInput;
4583
+ //# sourceMappingURL=parseKeysInput.js.map
4584
+
4585
+ /***/ }),
4586
+
4587
+ /***/ 17712:
4588
+ /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => {
4589
+
4590
+ "use strict";
4591
+
4592
+ Object.defineProperty(exports, "__esModule", ({ value: true }));
4593
+ exports.formatScope = exports.getScopedSecretKeys = exports.getDeclaredSecretKeys = exports.secretsScopeChoices = exports.resolveSecretsScope = void 0;
4594
+ const getProjectConfig_1 = __nccwpck_require__(50001);
4595
+ /**
4596
+ * resolves a scope expression to component → envs:
4597
+ * - (empty): all components, all envs
4598
+ * - `env:component`: one env of one component
4599
+ * - `env` / `env:`: one env, every component that has it
4600
+ * - `:component`: every env of one component
4601
+ */
4602
+ const resolveSecretsScope = async (scope) => {
4603
+ const all = await (0, getProjectConfig_1.getAllComponentsWithAllEnvsHierarchical)();
4604
+ if (Object.keys(all).length === 0) {
4605
+ throw new Error("no catladder config found");
4606
+ }
4607
+ if (!scope) {
4608
+ return all;
4609
+ }
4610
+ const [env, componentName] = scope.split(":").map((x) => x || null);
4611
+ if (componentName && !(componentName in all)) {
4612
+ throw new Error(`unknown component "${componentName}" — components: ${Object.keys(all).join(", ")}`);
4613
+ }
4614
+ const scoped = Object.fromEntries(Object.entries(all)
4615
+ .filter(([name]) => !componentName || name === componentName)
4616
+ .map(([name, envs]) => [
4617
+ name,
4618
+ env ? envs.filter((e) => e === env) : envs,
4619
+ ])
4620
+ .filter(([, envs]) => envs.length > 0));
4621
+ if (Object.keys(scoped).length === 0) {
4622
+ const allEnvs = [...new Set(Object.values(all).flat())];
4623
+ throw new Error(`no component has the environment "${env}" — environments: ${allEnvs.join(", ")}`);
4624
+ }
4625
+ return scoped;
4626
+ };
4627
+ exports.resolveSecretsScope = resolveSecretsScope;
4628
+ /**
4629
+ * all valid scope expressions, for autocompletion: `env:component`
4630
+ * pairs plus the `env:` (one env, all components) and `:component`
4631
+ * (one component, all envs) forms
4632
+ */
4633
+ const secretsScopeChoices = async () => {
4634
+ const all = await (0, getProjectConfig_1.getAllComponentsWithAllEnvsHierarchical)();
4635
+ const pairs = Object.entries(all).flatMap(([componentName, envs]) => envs.map((env) => `${env}:${componentName}`));
4636
+ const envs = [...new Set(Object.values(all).flat())];
4637
+ return [
4638
+ ...pairs,
4639
+ ...envs.map((env) => `${env}:`),
4640
+ ...Object.keys(all).map((componentName) => `:${componentName}`),
4641
+ ];
4642
+ };
4643
+ exports.secretsScopeChoices = secretsScopeChoices;
4644
+ /** the secret keys declared in the config for one env of one component */
4645
+ const getDeclaredSecretKeys = async (env, componentName) => {
4646
+ const { secretEnvVarKeys, jobOnlyVars } = await (0, getProjectConfig_1.getEnvironment)(env, componentName);
4647
+ return [
4648
+ ...jobOnlyVars.build.secretEnvVarKeys,
4649
+ ...jobOnlyVars.deploy.secretEnvVarKeys,
4650
+ ...secretEnvVarKeys,
4651
+ ]
4652
+ .filter((k) => !k.hidden)
4653
+ .map((k) => k.key);
4654
+ };
4655
+ exports.getDeclaredSecretKeys = getDeclaredSecretKeys;
4656
+ /**
4657
+ * expands a scope to its declared secret keys, optionally filtered to
4658
+ * `onlyKeys`. Throws when a requested key is not declared anywhere in
4659
+ * the scope (typo protection); env/component pairs without matching
4660
+ * keys are dropped.
4661
+ */
4662
+ const getScopedSecretKeys = async (scope, onlyKeys) => {
4663
+ const scoped = [];
4664
+ const declaredAnywhere = new Set();
4665
+ for (const [componentName, envs] of Object.entries(scope)) {
4666
+ for (const env of envs) {
4667
+ const declared = await (0, exports.getDeclaredSecretKeys)(env, componentName);
4668
+ declared.forEach((key) => declaredAnywhere.add(key));
4669
+ const keys = onlyKeys
4670
+ ? declared.filter((key) => onlyKeys.includes(key))
4671
+ : declared;
4672
+ if (keys.length > 0) {
4673
+ scoped.push({ env, componentName, keys });
4674
+ }
4675
+ }
4676
+ }
4677
+ const unknownKeys = (onlyKeys !== null && onlyKeys !== void 0 ? onlyKeys : []).filter((key) => !declaredAnywhere.has(key));
4678
+ if (unknownKeys.length > 0) {
4679
+ throw new Error(`these secrets are not declared in this scope: ${unknownKeys.join(", ")}` +
4680
+ `\ndeclared secrets: ${[...declaredAnywhere].join(", ")}`);
4681
+ }
4682
+ if (scoped.length === 0) {
4683
+ throw new Error("there are no secrets to configure in this scope");
4684
+ }
4685
+ return scoped;
4686
+ };
4687
+ exports.getScopedSecretKeys = getScopedSecretKeys;
4688
+ /** human-readable one-line-per-component summary of a scope */
4689
+ const formatScope = (scoped) => {
4690
+ var _a;
4691
+ const byComponent = new Map();
4692
+ for (const { env, componentName } of scoped) {
4693
+ byComponent.set(componentName, [
4694
+ ...((_a = byComponent.get(componentName)) !== null && _a !== void 0 ? _a : []),
4695
+ env,
4696
+ ]);
4697
+ }
4698
+ return [...byComponent.entries()]
4699
+ .map(([componentName, envs]) => `- ${componentName}: ${envs.join(", ")}`)
4700
+ .join("\n");
4701
+ };
4702
+ exports.formatScope = formatScope;
4703
+ //# sourceMappingURL=scope.js.map
4704
+
4705
+ /***/ }),
4706
+
4251
4707
  /***/ 45498:
4252
4708
  /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => {
4253
4709
 
@@ -6515,6 +6971,26 @@ exports.delay = delay;
6515
6971
 
6516
6972
  /***/ }),
6517
6973
 
6974
+ /***/ 185:
6975
+ /***/ ((__unused_webpack_module, exports) => {
6976
+
6977
+ "use strict";
6978
+
6979
+ Object.defineProperty(exports, "__esModule", ({ value: true }));
6980
+ exports.readStdin = void 0;
6981
+ /** reads the whole stdin stream (for piped input in non-interactive use) */
6982
+ const readStdin = async () => {
6983
+ const chunks = [];
6984
+ for await (const chunk of process.stdin) {
6985
+ chunks.push(chunk);
6986
+ }
6987
+ return Buffer.concat(chunks).toString("utf-8");
6988
+ };
6989
+ exports.readStdin = readStdin;
6990
+ //# sourceMappingURL=readStdin.js.map
6991
+
6992
+ /***/ }),
6993
+
6518
6994
  /***/ 45381:
6519
6995
  /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => {
6520
6996