@catladder/cli 0.0.0-next-4e7c6848 → 0.0.0-next-8b9ad5ca

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 (103) hide show
  1. package/dist/bundles/catenv/index.js +617 -19
  2. package/dist/bundles/cli/index.js +811 -18
  3. package/dist/cli/src/adapters/baseContext.d.ts +4 -0
  4. package/dist/cli/src/adapters/baseContext.js +5 -0
  5. package/dist/cli/src/adapters/baseContext.js.map +1 -1
  6. package/dist/cli/src/adapters/nonInteractive.d.ts +9 -0
  7. package/dist/cli/src/adapters/nonInteractive.js +32 -0
  8. package/dist/cli/src/adapters/nonInteractive.js.map +1 -0
  9. package/dist/cli/src/adapters/programmatic.d.ts +1 -0
  10. package/dist/cli/src/adapters/programmatic.js +6 -1
  11. package/dist/cli/src/adapters/programmatic.js.map +1 -1
  12. package/dist/cli/src/adapters/terminal.d.ts +1 -0
  13. package/dist/cli/src/adapters/terminal.js +10 -1
  14. package/dist/cli/src/adapters/terminal.js.map +1 -1
  15. package/dist/cli/src/adapters/vaultManagerAccess.d.ts +8 -0
  16. package/dist/cli/src/adapters/vaultManagerAccess.js +26 -0
  17. package/dist/cli/src/adapters/vaultManagerAccess.js.map +1 -0
  18. package/dist/cli/src/apps/catenv/catenv.d.ts +2 -0
  19. package/dist/cli/src/apps/catenv/catenv.js +8 -1
  20. package/dist/cli/src/apps/catenv/catenv.js.map +1 -1
  21. package/dist/cli/src/apps/catenv/printVariables.d.ts +2 -2
  22. package/dist/cli/src/apps/catenv/printVariables.js +4 -4
  23. package/dist/cli/src/apps/catenv/printVariables.js.map +1 -1
  24. package/dist/cli/src/apps/catenv/types.d.ts +9 -0
  25. package/dist/cli/src/apps/catenv/writeDotEnvFiles.d.ts +2 -2
  26. package/dist/cli/src/apps/catenv/writeDotEnvFiles.js +1 -1
  27. package/dist/cli/src/apps/catenv/writeDotEnvFiles.js.map +1 -1
  28. package/dist/cli/src/apps/cli/commands/project/commandConfigSecrets.js +52 -3
  29. package/dist/cli/src/apps/cli/commands/project/commandConfigSecrets.js.map +1 -1
  30. package/dist/cli/src/catenv.js +3 -2
  31. package/dist/cli/src/catenv.js.map +1 -1
  32. package/dist/cli/src/commands/index.d.ts +1 -0
  33. package/dist/cli/src/commands/index.js +8 -1
  34. package/dist/cli/src/commands/index.js.map +1 -1
  35. package/dist/cli/src/commands/project/commandSecretsSyncGithub.d.ts +37 -0
  36. package/dist/cli/src/commands/project/commandSecretsSyncGithub.js +127 -0
  37. package/dist/cli/src/commands/project/commandSecretsSyncGithub.js.map +1 -0
  38. package/dist/cli/src/config/getProjectConfig.d.ts +1 -1
  39. package/dist/cli/src/config/getProjectConfig.js +12 -2
  40. package/dist/cli/src/config/getProjectConfig.js.map +1 -1
  41. package/dist/cli/src/core/types.d.ts +15 -0
  42. package/dist/cli/src/core/types.js +8 -1
  43. package/dist/cli/src/core/types.js.map +1 -1
  44. package/dist/cli/src/git/gitProjectInformation.d.ts +1 -1
  45. package/dist/cli/src/git/gitProjectInformation.js +3 -3
  46. package/dist/cli/src/git/gitProjectInformation.js.map +1 -1
  47. package/dist/cli/src/utils/github.d.ts +15 -0
  48. package/dist/cli/src/utils/github.js +54 -0
  49. package/dist/cli/src/utils/github.js.map +1 -0
  50. package/dist/cli/src/vault/BitwardenVault.d.ts +24 -0
  51. package/dist/cli/src/vault/BitwardenVault.js +107 -0
  52. package/dist/cli/src/vault/BitwardenVault.js.map +1 -0
  53. package/dist/cli/src/vault/GitlabVault.d.ts +11 -0
  54. package/dist/cli/src/vault/GitlabVault.js +24 -0
  55. package/dist/cli/src/vault/GitlabVault.js.map +1 -0
  56. package/dist/cli/src/vault/VaultManager.d.ts +43 -0
  57. package/dist/cli/src/vault/VaultManager.js +131 -0
  58. package/dist/cli/src/vault/VaultManager.js.map +1 -0
  59. package/dist/cli/src/vault/bw.d.ts +15 -0
  60. package/dist/cli/src/vault/bw.js +89 -0
  61. package/dist/cli/src/vault/bw.js.map +1 -0
  62. package/dist/cli/src/vault/cache.d.ts +38 -0
  63. package/dist/cli/src/vault/cache.js +53 -0
  64. package/dist/cli/src/vault/cache.js.map +1 -0
  65. package/dist/cli/src/vault/index.d.ts +5 -0
  66. package/dist/cli/src/vault/index.js +48 -0
  67. package/dist/cli/src/vault/index.js.map +1 -0
  68. package/dist/cli/src/vault/types.d.ts +33 -0
  69. package/dist/cli/src/vault/types.js +5 -0
  70. package/dist/cli/src/vault/types.js.map +1 -0
  71. package/dist/pipeline/src/backends/index.d.ts +17 -1
  72. package/dist/pipeline/src/backends/index.js +33 -1
  73. package/dist/pipeline/src/backends/index.js.map +1 -1
  74. package/dist/pipeline/src/constants.js +1 -1
  75. package/dist/pipeline/src/deploy/cloudRun/index.js +11 -4
  76. package/dist/pipeline/src/deploy/cloudRun/index.js.map +1 -1
  77. package/dist/pipeline/src/types/config.d.ts +37 -3
  78. package/dist/tsconfig.tsbuildinfo +1 -1
  79. package/package.json +1 -1
  80. package/src/adapters/baseContext.ts +10 -0
  81. package/src/adapters/nonInteractive.ts +30 -0
  82. package/src/adapters/programmatic.ts +7 -1
  83. package/src/adapters/terminal.ts +9 -1
  84. package/src/adapters/vaultManagerAccess.ts +25 -0
  85. package/src/apps/catenv/catenv.ts +5 -1
  86. package/src/apps/catenv/printVariables.ts +16 -6
  87. package/src/apps/catenv/types.ts +9 -0
  88. package/src/apps/catenv/writeDotEnvFiles.ts +3 -3
  89. package/src/apps/cli/commands/project/commandConfigSecrets.ts +94 -9
  90. package/src/catenv.ts +22 -2
  91. package/src/commands/index.ts +1 -0
  92. package/src/commands/project/commandSecretsSyncGithub.ts +149 -0
  93. package/src/config/getProjectConfig.ts +21 -7
  94. package/src/core/types.ts +23 -0
  95. package/src/git/gitProjectInformation.ts +5 -3
  96. package/src/utils/github.ts +59 -0
  97. package/src/vault/BitwardenVault.ts +161 -0
  98. package/src/vault/GitlabVault.ts +25 -0
  99. package/src/vault/VaultManager.ts +211 -0
  100. package/src/vault/bw.ts +110 -0
  101. package/src/vault/cache.ts +80 -0
  102. package/src/vault/index.ts +5 -0
  103. package/src/vault/types.ts +42 -0
@@ -2,7 +2,7 @@
2
2
  /******/ var __webpack_modules__ = ({
3
3
 
4
4
  /***/ 48618:
5
- /***/ ((__unused_webpack_module, exports) => {
5
+ /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => {
6
6
 
7
7
  "use strict";
8
8
 
@@ -11,6 +11,7 @@ Object.defineProperty(exports, "__esModule", ({
11
11
  value: true
12
12
  }));
13
13
  exports.BaseContext = void 0;
14
+ const vaultManagerAccess_1 = __nccwpck_require__(37881);
14
15
  /**
15
16
  * Base class for CommandContext implementations.
16
17
  * Handles caching, the `get()` method, and `promptDirect` delegation.
@@ -20,6 +21,10 @@ class BaseContext {
20
21
  constructor(command) {
21
22
  this.command = command;
22
23
  this.cache = new Map();
24
+ this.vaultManagerGetter = (0, vaultManagerAccess_1.createVaultManagerGetter)(() => this);
25
+ }
26
+ getVaultManager() {
27
+ return this.vaultManagerGetter();
23
28
  }
24
29
  get(name) {
25
30
  if (!this.cache.has(name)) {
@@ -91,6 +96,7 @@ Object.defineProperty(exports, "__esModule", ({
91
96
  exports.createTerminalIO = exports.createTerminalContext = void 0;
92
97
  const types_1 = __nccwpck_require__(92284);
93
98
  const baseContext_1 = __nccwpck_require__(48618);
99
+ const vaultManagerAccess_1 = __nccwpck_require__(37881);
94
100
  /**
95
101
  * Resolve choices from a choices function.
96
102
  */
@@ -155,6 +161,10 @@ class TerminalContext extends baseContext_1.BaseContext {
155
161
  super(command);
156
162
  this.options = options;
157
163
  }
164
+ get interactive() {
165
+ var _a;
166
+ return (_a = this.options.interactive) !== null && _a !== void 0 ? _a : process.stdin.isTTY === true;
167
+ }
158
168
  log(message) {
159
169
  console.log(message);
160
170
  }
@@ -229,7 +239,10 @@ exports.createTerminalContext = createTerminalContext;
229
239
  * Create a bare IO instance for terminal helper functions.
230
240
  */
231
241
  function createTerminalIO(options = {}) {
232
- return {
242
+ var _a;
243
+ const io = {
244
+ interactive: (_a = options.interactive) !== null && _a !== void 0 ? _a : process.stdin.isTTY === true,
245
+ getVaultManager: (0, vaultManagerAccess_1.createVaultManagerGetter)(() => io),
233
246
  log(message) {
234
247
  console.log(message);
235
248
  },
@@ -255,11 +268,45 @@ function createTerminalIO(options = {}) {
255
268
  throw new types_1.MissingInputError(spec.name, spec.message);
256
269
  }
257
270
  };
271
+ return io;
258
272
  }
259
273
  exports.createTerminalIO = createTerminalIO;
260
274
 
261
275
  /***/ }),
262
276
 
277
+ /***/ 37881:
278
+ /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => {
279
+
280
+ "use strict";
281
+
282
+
283
+ Object.defineProperty(exports, "__esModule", ({
284
+ value: true
285
+ }));
286
+ exports.createVaultManagerGetter = void 0;
287
+ const getProjectConfig_1 = __nccwpck_require__(50001);
288
+ const VaultManager_1 = __nccwpck_require__(55771);
289
+ /**
290
+ * lazily creates the vault manager of a context (once per context),
291
+ * loading the project config on first use
292
+ */
293
+ const createVaultManagerGetter = (io, options = {}) => {
294
+ let managerPromise;
295
+ return () => managerPromise !== null && managerPromise !== void 0 ? managerPromise : managerPromise = (async () => {
296
+ const config = await (0, getProjectConfig_1.getProjectConfig)();
297
+ if (!config) {
298
+ throw new Error("no catladder config found — the secrets vault needs a project config");
299
+ }
300
+ return new VaultManager_1.VaultManager(config, {
301
+ ...options,
302
+ io: io()
303
+ });
304
+ })();
305
+ };
306
+ exports.createVaultManagerGetter = createVaultManagerGetter;
307
+
308
+ /***/ }),
309
+
263
310
  /***/ 38365:
264
311
  /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => {
265
312
 
@@ -355,7 +402,9 @@ const pipeline_1 = __nccwpck_require__(60241);
355
402
  const common_tags_1 = __nccwpck_require__(63779);
356
403
  const lodash_es_1 = __nccwpck_require__(64284);
357
404
  const getProjectConfig_1 = __nccwpck_require__(50001);
405
+ const commandSecretsSyncGithub_1 = __nccwpck_require__(50962);
358
406
  const editAsFile_1 = __nccwpck_require__(82805);
407
+ const github_1 = __nccwpck_require__(18036);
359
408
  const gitlab_1 = __nccwpck_require__(4508);
360
409
  const promise_1 = __nccwpck_require__(51762);
361
410
  /* for convenience, parse json objects. that makes it easier to edit secrets that are object */
@@ -433,21 +482,68 @@ const doItFor = async (instance, envAndComponents) => {
433
482
  }
434
483
  }
435
484
  }
485
+ const config = await (0, getProjectConfig_1.getProjectConfig)();
486
+ if (!config) {
487
+ throw new Error("no catladder config found");
488
+ }
436
489
  instance.log("");
437
- instance.log("upserting all variables, please wait...");
490
+ instance.log("writing all secrets to the vault, please wait...");
438
491
  instance.log("");
439
492
  for (const [componentName, envs] of Object.entries(envAndComponents)) {
440
493
  for (const env of envs) {
441
- instance.log("upserting " + env + ":" + componentName + "...\n");
442
- await (0, gitlab_1.upsertAllVariables)(instance, valuesToEdit[componentName][env], env, componentName);
494
+ instance.log("writing " + env + ":" + componentName + "...\n");
495
+ await (await instance.getVaultManager()).writeSecrets(env, componentName, valuesToEdit[componentName][env]);
443
496
  instance.log("");
444
497
  instance.log("✅ " + env + ":" + componentName);
445
498
  instance.log("--------------------------------\n");
446
499
  }
447
500
  }
501
+ await mirrorSecretsToCiBackends(instance, config, envAndComponents, valuesToEdit);
448
502
  instance.log("done! 😻");
449
503
  instance.log("");
450
504
  };
505
+ /**
506
+ * the vault is the source of truth; every enabled CI backend receives
507
+ * a mirrored copy of the edited secrets (gitlab variables, github
508
+ * secrets), so all CI systems stay in sync
509
+ */
510
+ const mirrorSecretsToCiBackends = async (instance, config, envAndComponents, valuesToEdit) => {
511
+ const enabled = (0, pipeline_1.getEnabledPipelineTypes)(config);
512
+ // gitlab mirror (unless gitlab is the vault itself, then it is
513
+ // already written)
514
+ if ((0, pipeline_1.getVaultConfig)(config).type !== "gitlab" && enabled.includes("gitlab")) {
515
+ for (const [componentName, envs] of Object.entries(envAndComponents)) {
516
+ for (const env of envs) {
517
+ await (0, gitlab_1.upsertAllVariables)(instance, valuesToEdit[componentName][env], env, componentName);
518
+ }
519
+ }
520
+ instance.log("✅ mirrored to gitlab");
521
+ }
522
+ if (!enabled.includes("github")) {
523
+ return;
524
+ }
525
+ const secrets = Object.entries(envAndComponents).flatMap(([componentName, envs]) => envs.flatMap(env => {
526
+ var _a;
527
+ return Object.entries((_a = valuesToEdit[componentName][env]) !== null && _a !== void 0 ? _a : {}).map(([key, value]) => ({
528
+ name: (0, pipeline_1.getSecretVarName)(env, componentName, key),
529
+ value: typeof value === "string" ? value : JSON.stringify(value !== null && value !== void 0 ? value : "")
530
+ }));
531
+ }));
532
+ if (secrets.length === 0) {
533
+ return;
534
+ }
535
+ if (!(await (0, github_1.isGhAuthenticated)())) {
536
+ instance.log("⚠️ github pipeline is enabled, but the github cli (gh) is not authenticated — run `project secrets-sync-github` later to mirror the secrets");
537
+ return;
538
+ }
539
+ const repo = await (0, commandSecretsSyncGithub_1.getConfiguredGithubRepo)();
540
+ if (!repo) {
541
+ instance.log("⚠️ github pipeline is enabled, but no github repository was found — run `project secrets-sync-github` later to mirror the secrets");
542
+ return;
543
+ }
544
+ instance.log(`mirroring ${secrets.length} secrets to github ('${repo}')...`);
545
+ await (0, commandSecretsSyncGithub_1.pushSecretsToGithub)(instance, repo, secrets);
546
+ };
451
547
  const projectConfigSecrets = async (io, envComponent) => {
452
548
  if (!envComponent) {
453
549
  const allEnvAndcomponents = await (0, getProjectConfig_1.getAllComponentsWithAllEnvsHierarchical)();
@@ -1874,7 +1970,7 @@ exports.commandKubeStopPortforward = (0, defineCommand_1.defineCommand)({
1874
1970
  Object.defineProperty(exports, "__esModule", ({
1875
1971
  value: true
1876
1972
  }));
1877
- 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.commandSetup = exports.commandGetMyTotalWorktime = exports.commandRenewToken = exports.commandTriggerCronjobGeneral = exports.commandOpenLogs = 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;
1973
+ 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.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;
1878
1974
  // General commands
1879
1975
  var commandKubeCurrentContext_1 = __nccwpck_require__(70243);
1880
1976
  Object.defineProperty(exports, "commandKubeCurrentContext", ({
@@ -1975,6 +2071,13 @@ Object.defineProperty(exports, "commandSecretsClearBackups", ({
1975
2071
  return commandSecretsClearBackups_1.commandSecretsClearBackups;
1976
2072
  }
1977
2073
  }));
2074
+ var commandSecretsSyncGithub_1 = __nccwpck_require__(50962);
2075
+ Object.defineProperty(exports, "commandSecretsSyncGithub", ({
2076
+ enumerable: true,
2077
+ get: function () {
2078
+ return commandSecretsSyncGithub_1.commandSecretsSyncGithub;
2079
+ }
2080
+ }));
1978
2081
  var commandOpenLogs_1 = __nccwpck_require__(98720);
1979
2082
  Object.defineProperty(exports, "commandOpenLogs", ({
1980
2083
  enumerable: true,
@@ -3186,6 +3289,140 @@ exports.commandSecretsClearBackups = (0, defineCommand_1.defineCommand)({
3186
3289
 
3187
3290
  /***/ }),
3188
3291
 
3292
+ /***/ 50962:
3293
+ /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => {
3294
+
3295
+ "use strict";
3296
+
3297
+
3298
+ Object.defineProperty(exports, "__esModule", ({
3299
+ value: true
3300
+ }));
3301
+ exports.commandSecretsSyncGithub = exports.getConfiguredGithubRepo = exports.pushSecretsToGithub = exports.collectSecretsFromVault = void 0;
3302
+ const pipeline_1 = __nccwpck_require__(60241);
3303
+ const getProjectConfig_1 = __nccwpck_require__(50001);
3304
+ const defineCommand_1 = __nccwpck_require__(39489);
3305
+ const github_1 = __nccwpck_require__(18036);
3306
+ /**
3307
+ * all secrets declared in the config, with their values read from the
3308
+ * vault (github secrets are write-only and can never act as a source)
3309
+ */
3310
+ const collectSecretsFromVault = async io => {
3311
+ const envAndComponents = await (0, getProjectConfig_1.getAllComponentsWithAllEnvsHierarchical)();
3312
+ const names = [];
3313
+ for (const [componentName, envs] of Object.entries(envAndComponents)) {
3314
+ for (const env of envs) {
3315
+ const {
3316
+ secretEnvVarKeys,
3317
+ jobOnlyVars
3318
+ } = await (0, getProjectConfig_1.getEnvironment)(env, componentName);
3319
+ const keys = [...secretEnvVarKeys, ...jobOnlyVars.build.secretEnvVarKeys, ...jobOnlyVars.deploy.secretEnvVarKeys];
3320
+ keys.forEach(({
3321
+ key
3322
+ }) => names.push((0, pipeline_1.getSecretVarName)(env, componentName, key)));
3323
+ }
3324
+ }
3325
+ const values = await (await io.getVaultManager()).readSecrets(names, io);
3326
+ const secrets = names.filter(name => values[name] !== undefined).map(name => ({
3327
+ name,
3328
+ value: values[name]
3329
+ }));
3330
+ const missing = names.filter(name => values[name] === undefined);
3331
+ return {
3332
+ secrets,
3333
+ missing
3334
+ };
3335
+ };
3336
+ exports.collectSecretsFromVault = collectSecretsFromVault;
3337
+ /**
3338
+ * pushes secrets to github repository secrets via the gh cli
3339
+ */
3340
+ const pushSecretsToGithub = async (io, repo, secrets) => {
3341
+ for (const {
3342
+ name,
3343
+ value
3344
+ } of secrets) {
3345
+ await (0, github_1.setGithubSecret)(repo, name, value);
3346
+ io.log(`✅ ${name}`);
3347
+ }
3348
+ };
3349
+ exports.pushSecretsToGithub = pushSecretsToGithub;
3350
+ /**
3351
+ * resolves the github repository from the configured git remote
3352
+ * (`pipelines.github.gitRemote`, defaults to origin)
3353
+ */
3354
+ const getConfiguredGithubRepo = async () => {
3355
+ const config = await (0, getProjectConfig_1.getProjectConfig)();
3356
+ if (!config) {
3357
+ return undefined;
3358
+ }
3359
+ return (0, github_1.getGithubRepoFromRemote)((0, pipeline_1.getPipelineGitRemote)(config, "github"));
3360
+ };
3361
+ exports.getConfiguredGithubRepo = getConfiguredGithubRepo;
3362
+ /**
3363
+ * copies the catladder secrets from the vault to github actions
3364
+ * repository secrets, keeping the exact same names, so the github
3365
+ * pipeline can reference them as ${{ secrets.<NAME> }}.
3366
+ */
3367
+ exports.commandSecretsSyncGithub = (0, defineCommand_1.defineCommand)({
3368
+ name: "project secrets-sync-github",
3369
+ description: "copies all secrets from the vault to github repo secrets (same names), for the github pipeline",
3370
+ group: "project",
3371
+ inputs: {
3372
+ repo: {
3373
+ type: "string",
3374
+ message: "github repository (owner/name)",
3375
+ positional: true,
3376
+ required: false
3377
+ }
3378
+ },
3379
+ execute: async ctx => {
3380
+ var _a;
3381
+ const config = await (0, getProjectConfig_1.getProjectConfig)();
3382
+ if (!config) {
3383
+ throw new Error("no catladder config found");
3384
+ }
3385
+ const enabled = (0, pipeline_1.getEnabledPipelineTypes)(config);
3386
+ if (!enabled.includes("github")) {
3387
+ throw new Error("the github pipeline is not enabled in `pipelines` — nothing to sync to");
3388
+ }
3389
+ if (!(await (0, github_1.isGhAuthenticated)())) {
3390
+ throw new Error("the github cli (gh) is not installed or not authenticated — run `gh auth login` first");
3391
+ }
3392
+ const repo = (_a = await ctx.get("repo")) !== null && _a !== void 0 ? _a : await (0, exports.getConfiguredGithubRepo)();
3393
+ if (!repo) {
3394
+ throw new Error(`no github repository found on the '${(0, pipeline_1.getPipelineGitRemote)(config, "github")}' remote — pass it as owner/name or configure pipelines.github.gitRemote`);
3395
+ }
3396
+ const {
3397
+ secrets,
3398
+ missing
3399
+ } = await (0, exports.collectSecretsFromVault)(ctx);
3400
+ if (missing.length > 0) {
3401
+ ctx.log(`⚠️ ${missing.length} secrets are not set in the vault and will be skipped:`);
3402
+ missing.forEach(name => ctx.log(` - ${name}`));
3403
+ ctx.log("");
3404
+ }
3405
+ if (secrets.length === 0) {
3406
+ ctx.log("no secrets to sync 🤷");
3407
+ return;
3408
+ }
3409
+ ctx.log(`about to set ${secrets.length} secrets on '${repo}':`);
3410
+ secrets.forEach(({
3411
+ name
3412
+ }) => ctx.log(` - ${name}`));
3413
+ ctx.log("");
3414
+ const confirmed = await ctx.confirm("continue? 🤔");
3415
+ if (!confirmed) {
3416
+ throw new Error("abort");
3417
+ }
3418
+ await (0, exports.pushSecretsToGithub)(ctx, repo, secrets);
3419
+ ctx.log("");
3420
+ ctx.log("done! 😻 the github pipeline can use them as ${{ secrets.<NAME> }}");
3421
+ }
3422
+ });
3423
+
3424
+ /***/ }),
3425
+
3189
3426
  /***/ 92494:
3190
3427
  /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => {
3191
3428
 
@@ -4179,14 +4416,24 @@ const getGitlabVar = async (io, env, componentName, variableName) => {
4179
4416
  };
4180
4417
  exports.getGitlabVar = getGitlabVar;
4181
4418
  const resolveSecrets = async (io, varSets) => {
4182
- const allVariablesInGitlab = await (0, gitlab_1.getAllVariables)(io);
4419
+ // the secret variables actually referenced by the env var values —
4420
+ // when they are all in the local cache, the vault is not contacted
4421
+ const referencedNames = [...new Set(varSets.flatMap(({
4422
+ envVars
4423
+ }) => Object.values(envVars).flatMap(value => {
4424
+ var _a;
4425
+ return (_a = `${value}`.match(/\$CL_\w+/g)) !== null && _a !== void 0 ? _a : [];
4426
+ })))].map(reference => reference.slice(1));
4427
+ const vaultManager = await io.getVaultManager();
4428
+ const secrets = await vaultManager.readSecrets(referencedNames, io);
4429
+ const secretEntries = Object.entries(secrets);
4183
4430
  return Object.fromEntries(varSets.flatMap(({
4184
4431
  envVars,
4185
4432
  secretEnvVarKeys
4186
4433
  }) => Object.entries(envVars).filter(([key, value]) => {
4187
4434
  var _a;
4188
4435
  return value !== undefined && value !== null && !((_a = secretEnvVarKeys.find(k => k.key === key)) === null || _a === void 0 ? void 0 : _a.hidden);
4189
- }).map(([key, value]) => [key, allVariablesInGitlab.reduce((acc, curr) => acc.replace(new RegExp("\\$" + curr.key, "g"), curr.value), `${value}`)])));
4436
+ }).map(([key, value]) => [key, secretEntries.reduce((acc, [name, secretValue]) => acc.replace(new RegExp("\\$" + name, "g"), secretValue), `${value}`)])));
4190
4437
  };
4191
4438
  const getEnvVarsResolved = async (io, env, componentName) => {
4192
4439
  if (!componentName) {
@@ -4291,8 +4538,15 @@ exports.defineCommand = defineCommand;
4291
4538
  Object.defineProperty(exports, "__esModule", ({
4292
4539
  value: true
4293
4540
  }));
4294
- exports.MissingInputError = void 0;
4541
+ exports.MissingInputError = exports.NonInteractiveError = void 0;
4295
4542
  // ─── Errors ──────────────────────────────────────────────────────────
4543
+ class NonInteractiveError extends Error {
4544
+ constructor(interaction) {
4545
+ super(`${interaction} requires an interactive terminal, but this context is not interactive`);
4546
+ this.name = "NonInteractiveError";
4547
+ }
4548
+ }
4549
+ exports.NonInteractiveError = NonInteractiveError;
4296
4550
  class MissingInputError extends Error {
4297
4551
  constructor(inputName, promptMessage) {
4298
4552
  super(`Missing required input: "${inputName}" (${promptMessage}). ` + `Provide it via --${inputName}=<value> or --inputs='{"${inputName}": ...}'`);
@@ -4742,14 +4996,14 @@ exports.getProjectRootPath = getProjectRootPath;
4742
4996
  /**
4743
4997
  * Get the git remote hostname and project path from the git config.
4744
4998
  */
4745
- const getGitRemoteHostAndPath = async () => {
4999
+ const getGitRemoteHostAndPath = async (remoteName = "origin") => {
4746
5000
  var _a;
4747
- const remoteUrl = await (0, gitConfig_1.gitConfigGet)("remote.origin.url");
5001
+ const remoteUrl = await (0, gitConfig_1.gitConfigGet)(`remote.${remoteName}.url`);
4748
5002
  const remoteReg = /(https:\/\/|git@)([^:/]+)[:/]([^.]*)(\.git)?/;
4749
5003
  const match = (_a = remoteUrl.match(remoteReg)) !== null && _a !== void 0 ? _a : [];
4750
5004
  const [,, gitRemoteHost, gitRemotePath] = match;
4751
5005
  if (!(gitRemoteHost === null || gitRemoteHost === void 0 ? void 0 : gitRemoteHost.length) || !(gitRemotePath === null || gitRemotePath === void 0 ? void 0 : gitRemotePath.length)) {
4752
- throw new Error(`Failed to parse git remote hostname and path from git configs remote.origin.url! ${remoteUrl}`);
5006
+ throw new Error(`Failed to parse git remote hostname and path from git configs remote.${remoteName}.url! ${remoteUrl}`);
4753
5007
  }
4754
5008
  return {
4755
5009
  gitRemoteHost,
@@ -5306,6 +5560,67 @@ exports["default"] = async () => {
5306
5560
 
5307
5561
  /***/ }),
5308
5562
 
5563
+ /***/ 18036:
5564
+ /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => {
5565
+
5566
+ "use strict";
5567
+
5568
+
5569
+ Object.defineProperty(exports, "__esModule", ({
5570
+ value: true
5571
+ }));
5572
+ exports.setGithubSecret = exports.getGithubRepoFromRemote = exports.isGhAuthenticated = void 0;
5573
+ const child_process_1 = __nccwpck_require__(35317);
5574
+ const util_1 = __nccwpck_require__(39023);
5575
+ const execFile = (0, util_1.promisify)(child_process_1.execFile);
5576
+ /**
5577
+ * whether the github cli (gh) is installed and authenticated
5578
+ */
5579
+ const isGhAuthenticated = async () => {
5580
+ try {
5581
+ await execFile("gh", ["auth", "status"]);
5582
+ return true;
5583
+ } catch (_a) {
5584
+ return false;
5585
+ }
5586
+ };
5587
+ exports.isGhAuthenticated = isGhAuthenticated;
5588
+ /**
5589
+ * the github repository (owner/name) a git remote points to.
5590
+ * Which remote belongs to github is configured per pipeline type
5591
+ * (`pipelines.github.gitRemote`, defaults to origin).
5592
+ */
5593
+ const getGithubRepoFromRemote = async remoteName => {
5594
+ try {
5595
+ const {
5596
+ stdout
5597
+ } = await execFile("git", ["remote", "get-url", remoteName]);
5598
+ const match = stdout.match(/github\.com[:/]([\w.-]+\/[\w.-]+?)(\.git)?\s*$/);
5599
+ return match === null || match === void 0 ? void 0 : match[1];
5600
+ } catch (_a) {
5601
+ return undefined;
5602
+ }
5603
+ };
5604
+ exports.getGithubRepoFromRemote = getGithubRepoFromRemote;
5605
+ /**
5606
+ * sets a github actions repository secret via the gh cli.
5607
+ * The value is passed via stdin so it never shows up in a process list.
5608
+ */
5609
+ const setGithubSecret = async (repo, name, value) => new Promise((resolve, reject) => {
5610
+ var _a, _b;
5611
+ const child = (0, child_process_1.spawn)("gh", ["secret", "set", name, "--repo", repo], {
5612
+ stdio: ["pipe", "ignore", "pipe"]
5613
+ });
5614
+ let stderr = "";
5615
+ (_a = child.stderr) === null || _a === void 0 ? void 0 : _a.on("data", data => stderr += data);
5616
+ child.on("error", reject);
5617
+ child.on("close", code => code === 0 ? resolve() : reject(new Error(`gh secret set ${name} failed: ${stderr.trim()}`)));
5618
+ (_b = child.stdin) === null || _b === void 0 ? void 0 : _b.end(value);
5619
+ });
5620
+ exports.setGithubSecret = setGithubSecret;
5621
+
5622
+ /***/ }),
5623
+
5309
5624
  /***/ 4508:
5310
5625
  /***/ (function(__unused_webpack_module, exports, __nccwpck_require__) {
5311
5626
 
@@ -5845,6 +6160,445 @@ exports.getShell = getShell;
5845
6160
 
5846
6161
  /***/ }),
5847
6162
 
6163
+ /***/ 63510:
6164
+ /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => {
6165
+
6166
+ "use strict";
6167
+
6168
+
6169
+ Object.defineProperty(exports, "__esModule", ({
6170
+ value: true
6171
+ }));
6172
+ exports.BitwardenVault = void 0;
6173
+ const pipeline_1 = __nccwpck_require__(60241);
6174
+ const yaml_1 = __nccwpck_require__(6637);
6175
+ const bw_1 = __nccwpck_require__(52211);
6176
+ const DEFAULT_COLLECTION = "catladder";
6177
+ const SECURE_NOTE_TYPE = 2;
6178
+ /**
6179
+ * one yaml note per env and component:
6180
+ * `<customerName>/<appName>/<env>/<componentName>/secrets.yml`
6181
+ */
6182
+ const itemName = (config, env, componentName) => `${config.customerName}/${config.appName}/${env}/${componentName}/secrets.yml`;
6183
+ const itemPrefix = config => `${config.customerName}/${config.appName}/`;
6184
+ /**
6185
+ * parses env and componentName back out of an item name
6186
+ */
6187
+ const parseItemName = (config, name) => {
6188
+ const match = name.slice(itemPrefix(config).length).match(/^([^/]+)\/([^/]+)\/secrets\.yml$/);
6189
+ return match ? {
6190
+ env: match[1],
6191
+ componentName: match[2]
6192
+ } : null;
6193
+ };
6194
+ const findCollection = async (session, collectionName) => {
6195
+ const collections = await (0, bw_1.runBwJson)(session, ["list", "collections", "--search", collectionName]);
6196
+ const collection = collections.find(c => c.name === collectionName);
6197
+ if (!collection) {
6198
+ throw new Error(`bitwarden collection '${collectionName}' not found — create it and make sure you have access`);
6199
+ }
6200
+ return collection;
6201
+ };
6202
+ const encodePayload = payload => Buffer.from(JSON.stringify(payload), "utf-8").toString("base64");
6203
+ class BitwardenVault {
6204
+ constructor(config, options,
6205
+ /**
6206
+ * whether a locked bitwarden may be unlocked interactively
6207
+ * (false with catenv --vault-mode no-prompt)
6208
+ */
6209
+ allowPrompt) {
6210
+ var _a;
6211
+ this.config = config;
6212
+ this.allowPrompt = allowPrompt;
6213
+ this.collectionName = (_a = options.collection) !== null && _a !== void 0 ? _a : DEFAULT_COLLECTION;
6214
+ this.id = `bitwarden:${this.collectionName}`;
6215
+ }
6216
+ session(io) {
6217
+ return (0, bw_1.getBwSession)(io, {
6218
+ allowPrompt: this.allowPrompt
6219
+ });
6220
+ }
6221
+ async readAllSecrets(io) {
6222
+ const {
6223
+ config,
6224
+ collectionName
6225
+ } = this;
6226
+ const bwSession = await this.session(io);
6227
+ const collection = await findCollection(bwSession, collectionName);
6228
+ const items = await (0, bw_1.runBwJson)(bwSession, ["list", "items", "--collectionid", collection.id]);
6229
+ const secrets = {};
6230
+ for (const item of items) {
6231
+ const parsed = parseItemName(config, item.name);
6232
+ if (!parsed || !item.notes) {
6233
+ continue;
6234
+ }
6235
+ const values = (0, yaml_1.parse)(item.notes);
6236
+ Object.entries(values !== null && values !== void 0 ? values : {}).forEach(([key, value]) => {
6237
+ secrets[(0, pipeline_1.getSecretVarName)(parsed.env, parsed.componentName, key)] = typeof value === "string" ? value : JSON.stringify(value);
6238
+ });
6239
+ }
6240
+ return secrets;
6241
+ }
6242
+ async writeSecrets(io, env, componentName, secrets) {
6243
+ const {
6244
+ config,
6245
+ collectionName
6246
+ } = this;
6247
+ const bwSession = await this.session(io);
6248
+ const collection = await findCollection(bwSession, collectionName);
6249
+ const name = itemName(config, env, componentName);
6250
+ const existing = (await (0, bw_1.runBwJson)(bwSession, ["list", "items", "--collectionid", collection.id, "--search", name])).find(item => item.name === name);
6251
+ const notes = (0, yaml_1.stringify)(secrets);
6252
+ if (existing) {
6253
+ await (0, bw_1.runBw)(bwSession, ["edit", "item", existing.id, encodePayload({
6254
+ ...existing,
6255
+ notes
6256
+ })]);
6257
+ } else {
6258
+ await (0, bw_1.runBw)(bwSession, ["create", "item", encodePayload({
6259
+ type: SECURE_NOTE_TYPE,
6260
+ secureNote: {
6261
+ type: 0
6262
+ },
6263
+ name,
6264
+ notes,
6265
+ ...(collection.organizationId ? {
6266
+ organizationId: collection.organizationId,
6267
+ collectionIds: [collection.id]
6268
+ } : {})
6269
+ })]);
6270
+ }
6271
+ }
6272
+ }
6273
+ exports.BitwardenVault = BitwardenVault;
6274
+
6275
+ /***/ }),
6276
+
6277
+ /***/ 31107:
6278
+ /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => {
6279
+
6280
+ "use strict";
6281
+
6282
+
6283
+ Object.defineProperty(exports, "__esModule", ({
6284
+ value: true
6285
+ }));
6286
+ exports.GitlabVault = void 0;
6287
+ const gitlab_1 = __nccwpck_require__(4508);
6288
+ /**
6289
+ * the legacy (and default) vault: the gitlab project variables double
6290
+ * as the secret store
6291
+ */
6292
+ class GitlabVault {
6293
+ constructor() {
6294
+ this.id = "gitlab";
6295
+ }
6296
+ async readAllSecrets(io) {
6297
+ const variables = await (0, gitlab_1.getAllVariables)(io);
6298
+ return Object.fromEntries(variables.map(v => [v.key, v.value]));
6299
+ }
6300
+ async writeSecrets(io, env, componentName, secrets) {
6301
+ await (0, gitlab_1.upsertAllVariables)(io, secrets, env, componentName);
6302
+ }
6303
+ }
6304
+ exports.GitlabVault = GitlabVault;
6305
+
6306
+ /***/ }),
6307
+
6308
+ /***/ 55771:
6309
+ /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => {
6310
+
6311
+ "use strict";
6312
+
6313
+
6314
+ Object.defineProperty(exports, "__esModule", ({
6315
+ value: true
6316
+ }));
6317
+ exports.VaultManager = void 0;
6318
+ const pipeline_1 = __nccwpck_require__(60241);
6319
+ const BitwardenVault_1 = __nccwpck_require__(63510);
6320
+ const cache_1 = __nccwpck_require__(14236);
6321
+ const GitlabVault_1 = __nccwpck_require__(31107);
6322
+ /**
6323
+ * only secrets of this env are cached locally — the other envs'
6324
+ * secrets have no business sitting on developer machines
6325
+ */
6326
+ const CACHED_ENV = "local";
6327
+ /**
6328
+ * owns the configured vault, the secrets mode and the local cache.
6329
+ * The vault is the readable source of truth for secrets; CI backends
6330
+ * only receive mirrored copies.
6331
+ */
6332
+ class VaultManager {
6333
+ constructor(config, options = {}) {
6334
+ var _a, _b;
6335
+ this.config = config;
6336
+ /** refresh mode bypasses the cache only once per manager */
6337
+ this.refreshed = false;
6338
+ this.mode = (_a = options.mode) !== null && _a !== void 0 ? _a : "auto";
6339
+ this.io = (_b = options.io) !== null && _b !== void 0 ? _b : null;
6340
+ this.vault = this.createVault();
6341
+ }
6342
+ createVault() {
6343
+ const vaultConfig = (0, pipeline_1.getVaultConfig)(this.config);
6344
+ switch (vaultConfig.type) {
6345
+ case "gitlab":
6346
+ return new GitlabVault_1.GitlabVault();
6347
+ case "bitwarden":
6348
+ return new BitwardenVault_1.BitwardenVault(this.config, vaultConfig, this.mode === "auto" || this.mode === "refresh");
6349
+ }
6350
+ }
6351
+ /**
6352
+ * the vault secrets, served from the local cache when it contains
6353
+ * all required names; otherwise (depending on the mode) refreshed
6354
+ * from the vault. Only the "local" env is (re-)cached.
6355
+ */
6356
+ async readSecrets(requiredNames, io = this.io) {
6357
+ const bypassCache = this.mode === "refresh" && !this.refreshed;
6358
+ const cache = bypassCache ? null : await (0, cache_1.readSecretsCache)();
6359
+ // known entries include recorded misses (null values), so declared
6360
+ // but intentionally unset secrets don't force a vault refresh
6361
+ const known = (cache === null || cache === void 0 ? void 0 : cache.vaultId) === this.vault.id ? flattenSecrets(cache.secrets) : undefined;
6362
+ const missing = requiredNames.filter(name => !(name in (known !== null && known !== void 0 ? known : {})));
6363
+ // nothing missing (or nothing required at all): don't touch the vault
6364
+ if (missing.length === 0 && (known || requiredNames.length === 0)) {
6365
+ return withoutMisses(known !== null && known !== void 0 ? known : {});
6366
+ }
6367
+ if (this.mode === "offline") {
6368
+ if (missing.length > 0) {
6369
+ io === null || io === void 0 ? void 0 : io.log(`⚠️ vault is offline (--vault-mode offline) and ${missing.length} secrets are not in the local cache: ${missing.join(", ")}`);
6370
+ }
6371
+ return withoutMisses(known !== null && known !== void 0 ? known : {});
6372
+ }
6373
+ const secrets = await this.vault.readAllSecrets(io);
6374
+ this.refreshed = true;
6375
+ await (0, cache_1.writeSecretsCache)(this.vault.id, await this.cacheableSubset(secrets));
6376
+ return secrets;
6377
+ }
6378
+ /**
6379
+ * writes secrets of one env/component to the vault and updates the
6380
+ * local cache (write-through, "local" env only)
6381
+ */
6382
+ async writeSecrets(env, componentName, secrets, io = this.io) {
6383
+ if (!io) {
6384
+ throw new Error("writing to the vault requires an interactive context");
6385
+ }
6386
+ await this.vault.writeSecrets(io, env, componentName, secrets);
6387
+ if (env === CACHED_ENV) {
6388
+ await (0, cache_1.updateSecretsCache)(this.vault.id, env, componentName, Object.fromEntries(Object.entries(secrets).map(([key, value]) => [key, typeof value === "string" ? value : JSON.stringify(value)])));
6389
+ }
6390
+ }
6391
+ /**
6392
+ * the subset of the vault secrets that may be cached: the declared
6393
+ * secrets of the "local" env, per component, as a hierarchy
6394
+ */
6395
+ async cacheableSubset(flatSecrets) {
6396
+ const local = {};
6397
+ for (const componentName of Object.keys(this.config.components)) {
6398
+ const keys = await this.declaredSecretKeys(componentName);
6399
+ const values = Object.fromEntries(keys.map(key => {
6400
+ var _a;
6401
+ return [key,
6402
+ // null records "absent in the vault" (a known miss)
6403
+ (_a = flatSecrets[(0, pipeline_1.getSecretVarName)(CACHED_ENV, componentName, key)]) !== null && _a !== void 0 ? _a : null];
6404
+ }));
6405
+ if (Object.keys(values).length > 0) {
6406
+ local[componentName] = values;
6407
+ }
6408
+ }
6409
+ return Object.keys(local).length > 0 ? {
6410
+ [CACHED_ENV]: local
6411
+ } : {};
6412
+ }
6413
+ async declaredSecretKeys(componentName) {
6414
+ try {
6415
+ const {
6416
+ secretEnvVarKeys,
6417
+ jobOnlyVars
6418
+ } = await (0, pipeline_1.getEnvironment)({
6419
+ config: this.config,
6420
+ componentName,
6421
+ env: CACHED_ENV
6422
+ });
6423
+ return [...secretEnvVarKeys, ...jobOnlyVars.build.secretEnvVarKeys, ...jobOnlyVars.deploy.secretEnvVarKeys].map(({
6424
+ key
6425
+ }) => key);
6426
+ } catch (_a) {
6427
+ // components without a local env simply have nothing to cache
6428
+ return [];
6429
+ }
6430
+ }
6431
+ }
6432
+ exports.VaultManager = VaultManager;
6433
+ /**
6434
+ * the cache hierarchy as a flat map of raw secret variable names
6435
+ * (null values = recorded misses)
6436
+ */
6437
+ const flattenSecrets = secrets => Object.fromEntries(Object.entries(secrets).flatMap(([env, components]) => Object.entries(components).flatMap(([componentName, values]) => Object.entries(values).map(([key, value]) => [(0, pipeline_1.getSecretVarName)(env, componentName, key), value]))));
6438
+ /**
6439
+ * only actual values — recorded misses stay unresolved, like any other
6440
+ * unset variable
6441
+ */
6442
+ const withoutMisses = known => Object.fromEntries(Object.entries(known).filter(entry => entry[1] !== null));
6443
+
6444
+ /***/ }),
6445
+
6446
+ /***/ 52211:
6447
+ /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => {
6448
+
6449
+ "use strict";
6450
+
6451
+
6452
+ Object.defineProperty(exports, "__esModule", ({
6453
+ value: true
6454
+ }));
6455
+ exports.runBwJson = exports.runBw = exports.getBwSession = void 0;
6456
+ const child_process_1 = __nccwpck_require__(35317);
6457
+ const util_1 = __nccwpck_require__(39023);
6458
+ const preferences_1 = __nccwpck_require__(66261);
6459
+ const execFile = (0, util_1.promisify)(child_process_1.execFile);
6460
+ const SESSION_PREFERENCE = "bwsession";
6461
+ class BitwardenLockedError extends Error {
6462
+ constructor() {
6463
+ super("bitwarden is locked and prompting is disabled — run `bw unlock` or catenv without --vault-mode no-prompt");
6464
+ }
6465
+ }
6466
+ /**
6467
+ * runs an interactive bw command that prints a session key on stdout
6468
+ * (login/unlock) while the user types the password on the terminal
6469
+ */
6470
+ const interactiveSessionCommand = args => new Promise((resolve, reject) => {
6471
+ var _a;
6472
+ const child = (0, child_process_1.spawn)("bw", [...args, "--raw"], {
6473
+ stdio: ["inherit", "pipe", "inherit"]
6474
+ });
6475
+ let session = "";
6476
+ (_a = child.stdout) === null || _a === void 0 ? void 0 : _a.on("data", data => session += data.toString("utf-8"));
6477
+ child.on("error", reject);
6478
+ child.on("close", code => code === 0 && session.trim() ? resolve(session.trim()) : reject(new Error(`bw ${args[0]} failed`)));
6479
+ });
6480
+ const getBwStatus = async session => {
6481
+ try {
6482
+ const {
6483
+ stdout
6484
+ } = await execFile("bw", ["status"], {
6485
+ env: {
6486
+ ...process.env,
6487
+ ...(session ? {
6488
+ BW_SESSION: session
6489
+ } : {})
6490
+ }
6491
+ });
6492
+ return JSON.parse(stdout).status;
6493
+ } catch (_a) {
6494
+ return "unauthenticated";
6495
+ }
6496
+ };
6497
+ /**
6498
+ * a working bw session: reuses the cached one, otherwise unlocks (or
6499
+ * logs in) interactively — unless prompting is disabled
6500
+ */
6501
+ const getBwSession = async (io, {
6502
+ allowPrompt
6503
+ }) => {
6504
+ const cached = (await (0, preferences_1.hasPreference)(SESSION_PREFERENCE)) ? String(await (0, preferences_1.getPreference)(SESSION_PREFERENCE)) : null;
6505
+ const status = await getBwStatus(cached);
6506
+ if (status === "unlocked" && cached) {
6507
+ return cached;
6508
+ }
6509
+ if (!allowPrompt) {
6510
+ throw new BitwardenLockedError();
6511
+ }
6512
+ io === null || io === void 0 ? void 0 : io.log("");
6513
+ io === null || io === void 0 ? void 0 : io.log(status === "unauthenticated" ? "# Please login to Bitwarden:" : "# Bitwarden is locked, please unlock:");
6514
+ io === null || io === void 0 ? void 0 : io.log("");
6515
+ const session = await interactiveSessionCommand([status === "unauthenticated" ? "login" : "unlock"]);
6516
+ await (0, preferences_1.setPreference)(SESSION_PREFERENCE, session);
6517
+ await (0, exports.runBw)(session, ["sync"]);
6518
+ return session;
6519
+ };
6520
+ exports.getBwSession = getBwSession;
6521
+ /**
6522
+ * runs a bw command with the given session and returns stdout
6523
+ */
6524
+ const runBw = async (session, args) => {
6525
+ const {
6526
+ stdout
6527
+ } = await execFile("bw", [...args, "--raw", "--nointeraction"], {
6528
+ env: {
6529
+ ...process.env,
6530
+ BW_SESSION: session
6531
+ },
6532
+ maxBuffer: 50 * 1024 * 1024
6533
+ });
6534
+ return stdout;
6535
+ };
6536
+ exports.runBw = runBw;
6537
+ const runBwJson = async (session, args) => JSON.parse(await (0, exports.runBw)(session, args));
6538
+ exports.runBwJson = runBwJson;
6539
+
6540
+ /***/ }),
6541
+
6542
+ /***/ 14236:
6543
+ /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => {
6544
+
6545
+ "use strict";
6546
+
6547
+
6548
+ Object.defineProperty(exports, "__esModule", ({
6549
+ value: true
6550
+ }));
6551
+ exports.updateSecretsCache = exports.writeSecretsCache = exports.readSecretsCache = void 0;
6552
+ const promises_1 = __nccwpck_require__(91943);
6553
+ const path_1 = __nccwpck_require__(16928);
6554
+ const gitProjectInformation_1 = __nccwpck_require__(21111);
6555
+ const getCacheFolder = async () => (0, path_1.join)(await (0, gitProjectInformation_1.getProjectRootPath)(), ".catladder");
6556
+ const getCachePath = async () => (0, path_1.join)(await getCacheFolder(), "secrets.json");
6557
+ const readSecretsCache = async () => {
6558
+ try {
6559
+ return JSON.parse(await (0, promises_1.readFile)(await getCachePath(), "utf-8"));
6560
+ } catch (_a) {
6561
+ return null;
6562
+ }
6563
+ };
6564
+ exports.readSecretsCache = readSecretsCache;
6565
+ const writeSecretsCache = async (vaultId, secrets) => {
6566
+ const folder = await getCacheFolder();
6567
+ await (0, promises_1.mkdir)(folder, {
6568
+ recursive: true
6569
+ });
6570
+ // the folder ignores itself, so the cache can never be committed
6571
+ await (0, promises_1.writeFile)((0, path_1.join)(folder, ".gitignore"), "*\n");
6572
+ const cache = {
6573
+ vaultId,
6574
+ updatedAt: new Date().toISOString(),
6575
+ secrets
6576
+ };
6577
+ await (0, promises_1.writeFile)(await getCachePath(), JSON.stringify(cache, null, 2));
6578
+ };
6579
+ exports.writeSecretsCache = writeSecretsCache;
6580
+ /**
6581
+ * merges freshly written secrets of one env/component into the cache
6582
+ * (write-through from config-secrets), so the next catenv run doesn't
6583
+ * need the vault
6584
+ */
6585
+ const updateSecretsCache = async (vaultId, env, componentName, values) => {
6586
+ var _a;
6587
+ const existing = await (0, exports.readSecretsCache)();
6588
+ const secrets = (existing === null || existing === void 0 ? void 0 : existing.vaultId) === vaultId ? existing.secrets : {};
6589
+ secrets[env] = {
6590
+ ...secrets[env],
6591
+ [componentName]: {
6592
+ ...((_a = secrets[env]) === null || _a === void 0 ? void 0 : _a[componentName]),
6593
+ ...values
6594
+ }
6595
+ };
6596
+ await (0, exports.writeSecretsCache)(vaultId, secrets);
6597
+ };
6598
+ exports.updateSecretsCache = updateSecretsCache;
6599
+
6600
+ /***/ }),
6601
+
5848
6602
  /***/ 54038:
5849
6603
  /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => {
5850
6604
 
@@ -7163,7 +7917,7 @@ var __exportStar = this && this.__exportStar || function (m, exports) {
7163
7917
  Object.defineProperty(exports, "__esModule", ({
7164
7918
  value: true
7165
7919
  }));
7166
- exports.getEnabledPipelineTypes = exports.getPipelineBackend = void 0;
7920
+ exports.getEnabledPipelineTypes = exports.getVaultConfig = exports.getPipelineGitRemote = exports.getPipelineOptions = exports.getPipelineBackend = void 0;
7167
7921
  const github_1 = __nccwpck_require__(99635);
7168
7922
  const gitlab_1 = __nccwpck_require__(8475);
7169
7923
  __exportStar(__nccwpck_require__(92474), exports);
@@ -7181,6 +7935,38 @@ const getPipelineBackend = type => {
7181
7935
  return createBackend();
7182
7936
  };
7183
7937
  exports.getPipelineBackend = getPipelineBackend;
7938
+ /**
7939
+ * the options of an enabled pipeline type (empty object when enabled
7940
+ * with `true`)
7941
+ */
7942
+ const getPipelineOptions = (config, type) => {
7943
+ var _a;
7944
+ const value = (_a = config.pipelines) === null || _a === void 0 ? void 0 : _a[type];
7945
+ return typeof value === "object" ? value : {};
7946
+ };
7947
+ exports.getPipelineOptions = getPipelineOptions;
7948
+ /**
7949
+ * the git remote pointing to the repository of this pipeline type's CI
7950
+ * system (e.g. during a migration, gitlab may be `origin` and github a
7951
+ * second remote)
7952
+ */
7953
+ const getPipelineGitRemote = (config, type) => {
7954
+ var _a;
7955
+ return (_a = (0, exports.getPipelineOptions)(config, type).gitRemote) !== null && _a !== void 0 ? _a : "origin";
7956
+ };
7957
+ exports.getPipelineGitRemote = getPipelineGitRemote;
7958
+ /**
7959
+ * the normalized secrets vault of a config (string shorthands resolved
7960
+ * to their object form; defaults to the legacy gitlab store)
7961
+ */
7962
+ const getVaultConfig = config => {
7963
+ var _a, _b;
7964
+ const vault = (_b = (_a = config.secrets) === null || _a === void 0 ? void 0 : _a.vault) !== null && _b !== void 0 ? _b : "gitlab";
7965
+ return typeof vault === "string" ? {
7966
+ type: vault
7967
+ } : vault;
7968
+ };
7969
+ exports.getVaultConfig = getVaultConfig;
7184
7970
  /**
7185
7971
  * the pipeline types enabled for this config. Multiple types can be
7186
7972
  * enabled at once (parallel generation, e.g. during a CI migration).
@@ -9147,7 +9933,7 @@ Object.defineProperty(exports, "__esModule", ({
9147
9933
  value: true
9148
9934
  }));
9149
9935
  exports.DOCKER_REGISTRY = exports.PIPELINE_IMAGE_TAG = void 0;
9150
- exports.PIPELINE_IMAGE_TAG = "next-4e7c6848" || 0;
9936
+ exports.PIPELINE_IMAGE_TAG = "next-8b9ad5ca" || 0;
9151
9937
  exports.DOCKER_REGISTRY = "git.panter.ch:5001/catladder/catladder" || 0;
9152
9938
 
9153
9939
  /***/ }),
@@ -11052,13 +11838,15 @@ exports.GCLOUD_RUN_DEPLOY_TYPE = {
11052
11838
  }
11053
11839
  return {};
11054
11840
  },
11055
- additionalSecretKeys: ctx => [{
11841
+ additionalSecretKeys: ctx => [
11842
+ // deploy-time credentials don't exist for the local env
11843
+ ...(ctx.envType !== "local" ? [{
11056
11844
  key: exports.GCLOUD_DEPLOY_CREDENTIALS_KEY,
11057
11845
  hidden: true
11058
11846
  }, {
11059
11847
  key: exports.GCLOUD_RUN_CANONICAL_HOST_SUFFIX,
11060
11848
  hidden: true
11061
- }, ...(ctx.deployConfigRaw && ctx.deployConfigRaw.cloudSql ? [{
11849
+ }] : []), ...(ctx.deployConfigRaw && ctx.deployConfigRaw.cloudSql ? [{
11062
11850
  key: "DB_PASSWORD"
11063
11851
  }] : [])],
11064
11852
  getAdditionalEnvVars: ctx => {
@@ -11066,7 +11854,8 @@ exports.GCLOUD_RUN_DEPLOY_TYPE = {
11066
11854
  fullName,
11067
11855
  env,
11068
11856
  componentName,
11069
- deployConfigRaw
11857
+ deployConfigRaw,
11858
+ envType
11070
11859
  } = ctx;
11071
11860
  const HOSTNAME_INTERNAL = (0, BashExpression_1.joinBashExpressions)([fullName, (0, BashExpression_1.getBashVariable)((0, context_1.getSecretVarName)(env, componentName, exports.GCLOUD_RUN_CANONICAL_HOST_SUFFIX))], "-").toLowerCase();
11072
11861
  const jobTriggers = deployConfigRaw && deployConfigRaw.jobs ? Object.fromEntries(Object.entries(deployConfigRaw.jobs).filter(([name, job]) => Boolean(job)).map(([name, job]) => ["CLOUD_RUN_JOB_TRIGGER_URL_" + (0, gitlab_1.sanitizeForBashVariable)(name), (0, cloudRunExecutionUrl_1.getCloudRunJobExecuteUrl)(name, {
@@ -11075,7 +11864,11 @@ exports.GCLOUD_RUN_DEPLOY_TYPE = {
11075
11864
  region: deployConfigRaw.region
11076
11865
  })])) : {};
11077
11866
  return {
11078
- HOSTNAME_INTERNAL,
11867
+ // references the canonical host suffix secret, which doesn't
11868
+ // exist for the local env
11869
+ ...(envType !== "local" ? {
11870
+ HOSTNAME_INTERNAL
11871
+ } : {}),
11079
11872
  ...getCloudSqlVariables(ctx),
11080
11873
  ...jobTriggers,
11081
11874
  DEPLOY_CLOUD_RUN_SERVICE_NAME: (0, getServiceName_1.getServiceNameForEnvContext)(ctx),