@catladder/cli 0.0.0-next-16388362 → 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 (135) hide show
  1. package/dist/bundles/catenv/index.js +1209 -28
  2. package/dist/bundles/cli/index.js +1406 -30
  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/github/GithubBackend.d.ts +12 -0
  72. package/dist/pipeline/src/backends/github/GithubBackend.js +170 -0
  73. package/dist/pipeline/src/backends/github/GithubBackend.js.map +1 -0
  74. package/dist/pipeline/src/backends/github/ciVariables.d.ts +14 -0
  75. package/dist/pipeline/src/backends/github/ciVariables.js +31 -0
  76. package/dist/pipeline/src/backends/github/ciVariables.js.map +1 -0
  77. package/dist/pipeline/src/backends/github/createGithubJobs.d.ts +17 -0
  78. package/dist/pipeline/src/backends/github/createGithubJobs.js +208 -0
  79. package/dist/pipeline/src/backends/github/createGithubJobs.js.map +1 -0
  80. package/dist/pipeline/src/backends/github/githubReleaseJobs.d.ts +15 -0
  81. package/dist/pipeline/src/backends/github/githubReleaseJobs.js +65 -0
  82. package/dist/pipeline/src/backends/github/githubReleaseJobs.js.map +1 -0
  83. package/dist/pipeline/src/backends/github/index.d.ts +2 -0
  84. package/dist/pipeline/src/backends/github/index.js +26 -0
  85. package/dist/pipeline/src/backends/github/index.js.map +1 -0
  86. package/dist/pipeline/src/backends/github/scriptFunctions.d.ts +6 -0
  87. package/dist/pipeline/src/backends/github/scriptFunctions.js +30 -0
  88. package/dist/pipeline/src/backends/github/scriptFunctions.js.map +1 -0
  89. package/dist/pipeline/src/backends/gitlab/GitlabBackend.d.ts +1 -0
  90. package/dist/pipeline/src/backends/gitlab/GitlabBackend.js +7 -0
  91. package/dist/pipeline/src/backends/gitlab/GitlabBackend.js.map +1 -1
  92. package/dist/pipeline/src/backends/index.d.ts +18 -1
  93. package/dist/pipeline/src/backends/index.js +37 -2
  94. package/dist/pipeline/src/backends/index.js.map +1 -1
  95. package/dist/pipeline/src/backends/types.d.ts +7 -3
  96. package/dist/pipeline/src/bash/ciVariables.js +4 -2
  97. package/dist/pipeline/src/bash/ciVariables.js.map +1 -1
  98. package/dist/pipeline/src/constants.js +1 -1
  99. package/dist/pipeline/src/context/getReviewSlug.js +3 -1
  100. package/dist/pipeline/src/context/getReviewSlug.js.map +1 -1
  101. package/dist/pipeline/src/deploy/cloudRun/index.js +11 -4
  102. package/dist/pipeline/src/deploy/cloudRun/index.js.map +1 -1
  103. package/dist/pipeline/src/pipeline/generatePipelineFiles.js +2 -5
  104. package/dist/pipeline/src/pipeline/generatePipelineFiles.js.map +1 -1
  105. package/dist/pipeline/src/types/config.d.ts +37 -3
  106. package/dist/pipeline/src/types/github-types.d.ts +47 -0
  107. package/dist/pipeline/src/types/github-types.js +9 -0
  108. package/dist/pipeline/src/types/github-types.js.map +1 -0
  109. package/dist/pipeline/src/types/pipeline.d.ts +4 -3
  110. package/dist/tsconfig.tsbuildinfo +1 -1
  111. package/package.json +1 -1
  112. package/src/adapters/baseContext.ts +10 -0
  113. package/src/adapters/nonInteractive.ts +30 -0
  114. package/src/adapters/programmatic.ts +7 -1
  115. package/src/adapters/terminal.ts +9 -1
  116. package/src/adapters/vaultManagerAccess.ts +25 -0
  117. package/src/apps/catenv/catenv.ts +5 -1
  118. package/src/apps/catenv/printVariables.ts +16 -6
  119. package/src/apps/catenv/types.ts +9 -0
  120. package/src/apps/catenv/writeDotEnvFiles.ts +3 -3
  121. package/src/apps/cli/commands/project/commandConfigSecrets.ts +94 -9
  122. package/src/catenv.ts +22 -2
  123. package/src/commands/index.ts +1 -0
  124. package/src/commands/project/commandSecretsSyncGithub.ts +149 -0
  125. package/src/config/getProjectConfig.ts +21 -7
  126. package/src/core/types.ts +23 -0
  127. package/src/git/gitProjectInformation.ts +5 -3
  128. package/src/utils/github.ts +59 -0
  129. package/src/vault/BitwardenVault.ts +161 -0
  130. package/src/vault/GitlabVault.ts +25 -0
  131. package/src/vault/VaultManager.ts +211 -0
  132. package/src/vault/bw.ts +110 -0
  133. package/src/vault/cache.ts +80 -0
  134. package/src/vault/index.ts +5 -0
  135. 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,1017 @@ 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
+
6602
+ /***/ 54038:
6603
+ /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => {
6604
+
6605
+ "use strict";
6606
+
6607
+
6608
+ Object.defineProperty(exports, "__esModule", ({
6609
+ value: true
6610
+ }));
6611
+ exports.GithubBackend = void 0;
6612
+ const promises_1 = __nccwpck_require__(91943);
6613
+ const path_1 = __nccwpck_require__(16928);
6614
+ const createAllJobs_1 = __nccwpck_require__(83671);
6615
+ const config_1 = __nccwpck_require__(96925);
6616
+ const ciVariables_1 = __nccwpck_require__(25474);
6617
+ const createGithubJobs_1 = __nccwpck_require__(14016);
6618
+ const githubReleaseJobs_1 = __nccwpck_require__(55611);
6619
+ const WORKFLOWS_FOLDER = ".github/workflows";
6620
+ /**
6621
+ * generated workflow files are prefixed so that cleanup never touches
6622
+ * user-maintained workflows in the same folder
6623
+ */
6624
+ const GENERATED_FILE_PREFIX = "catladder-";
6625
+ const TRIGGER_WORKFLOWS = {
6626
+ mr: {
6627
+ name: "catladder review",
6628
+ on: {
6629
+ pull_request: {
6630
+ types: ["opened", "synchronize", "reopened"]
6631
+ }
6632
+ },
6633
+ concurrency: {
6634
+ group: "catladder-review-${{ github.event.number }}",
6635
+ "cancel-in-progress": true
6636
+ }
6637
+ },
6638
+ mainBranch: {
6639
+ name: "catladder main",
6640
+ // TODO: make the default branch configurable
6641
+ on: {
6642
+ push: {
6643
+ branches: ["main"]
6644
+ }
6645
+ }
6646
+ },
6647
+ taggedRelease: {
6648
+ name: "catladder release",
6649
+ on: {
6650
+ push: {
6651
+ tags: ["v*"]
6652
+ }
6653
+ }
6654
+ }
6655
+ };
6656
+ class GithubBackend {
6657
+ constructor() {
6658
+ this.type = "github";
6659
+ }
6660
+ async cleanup() {
6661
+ const entries = await (0, promises_1.readdir)(WORKFLOWS_FOLDER).catch(() => []);
6662
+ await Promise.all(entries.filter(file => file.startsWith(GENERATED_FILE_PREFIX)).map(file => (0, promises_1.unlink)((0, path_1.join)(WORKFLOWS_FOLDER, file))));
6663
+ }
6664
+ async createFiles(config) {
6665
+ const workflows = await this.createWorkflows(config);
6666
+ return Object.entries(workflows).map(([fileName, workflow]) => ({
6667
+ path: (0, path_1.join)(WORKFLOWS_FOLDER, fileName),
6668
+ content: workflow
6669
+ }));
6670
+ }
6671
+ /**
6672
+ * all workflows keyed by file name, mainly for testing purposes
6673
+ */
6674
+ async createWorkflows(config) {
6675
+ const workflows = {};
6676
+ const reviewStopJobs = {};
6677
+ const manualJobs = {};
6678
+ for (const trigger of config_1.ALL_PIPELINE_TRIGGERS) {
6679
+ const allJobs = await (0, createAllJobs_1.createAllJobs)({
6680
+ config,
6681
+ trigger,
6682
+ pipelineType: this.type
6683
+ });
6684
+ const uploadProviderIds = (0, createGithubJobs_1.getUploadProviderIds)(allJobs);
6685
+ const jobs = {};
6686
+ // NOTE: agent jobs are gitlab-only and skipped on github
6687
+ [...allJobs.workspaces, ...allJobs.components].forEach(({
6688
+ context,
6689
+ jobs: contextJobs
6690
+ }) => contextJobs.forEach(job => {
6691
+ const [id, githubJob] = (0, createGithubJobs_1.makeGithubJob)(context, job, allJobs, uploadProviderIds);
6692
+ if (isReviewStopJob(context, job)) {
6693
+ reviewStopJobs[id] = githubJob;
6694
+ } else if (isManualTaskJob(job)) {
6695
+ manualJobs[id] = githubJob;
6696
+ } else {
6697
+ jobs[id] = githubJob;
6698
+ }
6699
+ }));
6700
+ if (trigger === "mainBranch") {
6701
+ const releaseJobs = (0, githubReleaseJobs_1.getGithubReleaseJobs)(config, Object.keys(jobs));
6702
+ Object.assign(jobs, releaseJobs.main);
6703
+ Object.assign(manualJobs, releaseJobs.manual);
6704
+ }
6705
+ if (Object.keys(jobs).length > 0) {
6706
+ workflows[`${GENERATED_FILE_PREFIX}${workflowFileName(trigger)}`] = {
6707
+ ...TRIGGER_WORKFLOWS[trigger],
6708
+ env: ciVariables_1.GITHUB_INJECTED_WORKFLOW_ENV,
6709
+ jobs
6710
+ };
6711
+ }
6712
+ }
6713
+ if (Object.keys(reviewStopJobs).length > 0) {
6714
+ workflows[`${GENERATED_FILE_PREFIX}review-stop.yml`] = {
6715
+ name: "catladder review stop",
6716
+ on: {
6717
+ pull_request: {
6718
+ types: ["closed"]
6719
+ }
6720
+ },
6721
+ env: ciVariables_1.GITHUB_INJECTED_WORKFLOW_ENV,
6722
+ jobs: reviewStopJobs
6723
+ };
6724
+ }
6725
+ if (Object.keys(manualJobs).length > 0) {
6726
+ workflows[`${GENERATED_FILE_PREFIX}manual.yml`] = {
6727
+ name: "catladder manual tasks",
6728
+ on: {
6729
+ workflow_dispatch: {
6730
+ inputs: {
6731
+ job: {
6732
+ description: "the task to run",
6733
+ required: true,
6734
+ type: "choice",
6735
+ options: Object.keys(manualJobs)
6736
+ }
6737
+ }
6738
+ }
6739
+ },
6740
+ env: ciVariables_1.GITHUB_INJECTED_WORKFLOW_ENV,
6741
+ jobs: Object.fromEntries(Object.entries(manualJobs).map(([id, job]) => [id, {
6742
+ ...job,
6743
+ if: `\${{ inputs.job == '${id}' }}`
6744
+ }]))
6745
+ };
6746
+ }
6747
+ return workflows;
6748
+ }
6749
+ }
6750
+ exports.GithubBackend = GithubBackend;
6751
+ const workflowFileName = trigger => {
6752
+ switch (trigger) {
6753
+ case "mr":
6754
+ return "review.yml";
6755
+ case "mainBranch":
6756
+ return "main.yml";
6757
+ case "taggedRelease":
6758
+ return "release.yml";
6759
+ }
6760
+ throw new Error(`${trigger} is not supported`);
6761
+ };
6762
+ /**
6763
+ * stop jobs of review environments run in their own workflow when the
6764
+ * pull request is closed (the counterpart of gitlab's on_stop wiring)
6765
+ */
6766
+ const isReviewStopJob = (context, job) => {
6767
+ var _a;
6768
+ return ((_a = job.environment) === null || _a === void 0 ? void 0 : _a.action) === "stop" && context.type === "component" && context.environment.envType === "review";
6769
+ };
6770
+ /**
6771
+ * stop/rollback jobs of non-review environments have no automatic
6772
+ * trigger; they are collected in a workflow_dispatch workflow
6773
+ * (gitlab runs them as manual jobs inside the pipeline)
6774
+ */
6775
+ const isManualTaskJob = job => job.stage === "stop" || job.stage === "rollback";
6776
+
6777
+ /***/ }),
6778
+
6779
+ /***/ 25474:
6780
+ /***/ ((__unused_webpack_module, exports) => {
6781
+
6782
+ "use strict";
6783
+
6784
+
6785
+ Object.defineProperty(exports, "__esModule", ({
6786
+ value: true
6787
+ }));
6788
+ exports.GITHUB_INJECTED_WORKFLOW_ENV = exports.GITHUB_CI_VARIABLES = void 0;
6789
+ /**
6790
+ * how the github backend names the predefined CI variables.
6791
+ *
6792
+ * GITHUB_* variables are provided by github actions natively; the CL_*
6793
+ * variables are injected by the {@link GithubBackend} itself (workflow or
6794
+ * job level), because github has no direct counterpart.
6795
+ */
6796
+ exports.GITHUB_CI_VARIABLES = {
6797
+ commitSha: "GITHUB_SHA",
6798
+ jobToken: "CL_JOB_TOKEN",
6799
+ jobImage: "CL_JOB_IMAGE",
6800
+ registry: "CL_REGISTRY",
6801
+ registryImage: "CL_REGISTRY_IMAGE"
6802
+ };
6803
+ /**
6804
+ * the workflow-level env the backend injects to provide the CL_*
6805
+ * variables referenced by {@link GITHUB_CI_VARIABLES}
6806
+ */
6807
+ exports.GITHUB_INJECTED_WORKFLOW_ENV = {
6808
+ CL_JOB_TOKEN: "${{ github.token }}",
6809
+ CL_REGISTRY: "ghcr.io",
6810
+ CL_REGISTRY_IMAGE: "ghcr.io/${{ github.repository }}",
6811
+ // empty outside of pull request events
6812
+ CL_PR_NUMBER: "${{ github.event.number }}"
6813
+ };
6814
+
6815
+ /***/ }),
6816
+
6817
+ /***/ 14016:
6818
+ /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => {
6819
+
6820
+ "use strict";
6821
+
6822
+
6823
+ Object.defineProperty(exports, "__esModule", ({
6824
+ value: true
6825
+ }));
6826
+ exports.getUploadProviderIds = exports.makeGithubJob = exports.resolveGithubNeeds = exports.githubJobId = void 0;
6827
+ const lodash_es_1 = __nccwpck_require__(64284);
6828
+ const getInjectVarsScript_1 = __nccwpck_require__(90436);
6829
+ const createJobCache_1 = __nccwpck_require__(65644);
6830
+ const getEnvironmentVariables_1 = __nccwpck_require__(85855);
6831
+ const utils_1 = __nccwpck_require__(20595);
6832
+ const gitlab_1 = __nccwpck_require__(13468);
6833
+ const scriptFunctions_1 = __nccwpck_require__(75759);
6834
+ /**
6835
+ * runner variables that only make sense on gitlab runners and must not
6836
+ * leak into github jobs (docker-in-docker daemon config, kubernetes
6837
+ * executor resource requests, gitlab clone strategy)
6838
+ */
6839
+ const GITLAB_ONLY_RUNNER_VARIABLES = new Set(["DOCKER_HOST", "DOCKER_TLS_CERTDIR", "DOCKER_DRIVER", "KUBERNETES_CPU_REQUEST", "KUBERNETES_MEMORY_REQUEST", "KUBERNETES_CPU_LIMIT", "KUBERNETES_MEMORY_LIMIT", "GIT_STRATEGY"]);
6840
+ /**
6841
+ * github runs docker natively on its runners, gitlab needs a
6842
+ * docker-in-docker service for it
6843
+ */
6844
+ const isDindService = image => /(^|\/)docker:.*dind/.test(image);
6845
+ const slug = value => {
6846
+ const slugged = value.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
6847
+ // job ids must start with a letter or _
6848
+ return /^[a-z_]/.test(slugged) ? slugged : `job-${slugged}`;
6849
+ };
6850
+ const githubJobId = (context, jobName) => slug(`${context.type === "workspace" ? "ws-" : ""}${context.name}-${jobName}-${context.env}`);
6851
+ exports.githubJobId = githubJobId;
6852
+ const githubJobName = (context, jobName) => `${context.name} ${jobName} | ${context.env}`;
6853
+ const resolveNeed = (context, need, allJobs) => {
6854
+ var _a;
6855
+ const env = context.env;
6856
+ const findComponentJob = (componentName, jobName) => {
6857
+ const provider = allJobs.components.find(c => c.context.name === componentName && c.context.env === env);
6858
+ const job = provider === null || provider === void 0 ? void 0 : provider.jobs.find(j => j.name === jobName);
6859
+ if (!provider || !job) {
6860
+ throw new Error(`unknown job referenced: '${jobName}' from '${env}:${componentName}'`);
6861
+ }
6862
+ return (0, exports.githubJobId)(provider.context, jobName);
6863
+ };
6864
+ const findWorkspaceJob = (workspaceName, jobName) => {
6865
+ const provider = allJobs.workspaces.find(w => w.context.name === workspaceName && w.context.env === env);
6866
+ const job = provider === null || provider === void 0 ? void 0 : provider.jobs.find(j => j.name === jobName);
6867
+ if (!provider || !job) {
6868
+ throw new Error(`unknown job referenced: '${jobName}' from workspace '${env}:${workspaceName}'`);
6869
+ }
6870
+ return (0, exports.githubJobId)(provider.context, jobName);
6871
+ };
6872
+ if ((0, lodash_es_1.isObject)(need)) {
6873
+ if ("workspaceName" in need && need.workspaceName) {
6874
+ return {
6875
+ id: findWorkspaceJob(need.workspaceName, need.job),
6876
+ artifacts: need.artifacts
6877
+ };
6878
+ }
6879
+ return {
6880
+ id: findComponentJob((_a = "componentName" in need ? need.componentName : undefined) !== null && _a !== void 0 ? _a : context.name, need.job),
6881
+ artifacts: need.artifacts
6882
+ };
6883
+ }
6884
+ return {
6885
+ id: context.type === "workspace" ? findWorkspaceJob(context.name, need) : findComponentJob(context.name, need),
6886
+ // plain string needs download artifacts by default (gitlab semantics)
6887
+ artifacts: true
6888
+ };
6889
+ };
6890
+ const resolveGithubNeeds = (context, job, allJobs) => {
6891
+ var _a;
6892
+ const resolved = ((_a = job.needs) !== null && _a !== void 0 ? _a : []).map(need => resolveNeed(context, need, allJobs));
6893
+ // deduplicate by provider id, explicit entries win (they come last)
6894
+ return [...new Map(resolved.map(n => [n.id, n])).values()].sort((a, b) => a.id.localeCompare(b.id));
6895
+ };
6896
+ exports.resolveGithubNeeds = resolveGithubNeeds;
6897
+ const createCacheSteps = (context, job) => {
6898
+ var _a, _b;
6899
+ if (!((_a = job.caches) === null || _a === void 0 ? void 0 : _a.length)) {
6900
+ return [];
6901
+ }
6902
+ const lowered = (_b = (0, createJobCache_1.createJobCacheFromCacheConfigs)(context, job.caches)) !== null && _b !== void 0 ? _b : [];
6903
+ return (0, utils_1.ensureArray)(lowered).map(cache => {
6904
+ var _a, _b, _c, _d, _e, _f;
6905
+ const paths = (0, utils_1.ensureArray)((_a = cache.paths) !== null && _a !== void 0 ? _a : []);
6906
+ const key = cache.key;
6907
+ const {
6908
+ cacheKey,
6909
+ restoreKeyPrefix,
6910
+ label
6911
+ } = typeof key === "string" || key === undefined ? {
6912
+ // unique key per run so the cache is re-saved, restored via prefix
6913
+ cacheKey: `${key !== null && key !== void 0 ? key : "cache"}-\${{ github.run_id }}`,
6914
+ restoreKeyPrefix: `${key !== null && key !== void 0 ? key : "cache"}-`,
6915
+ label: key !== null && key !== void 0 ? key : "cache"
6916
+ } : {
6917
+ cacheKey: `${(_b = key.prefix) !== null && _b !== void 0 ? _b : "files"}-\${{ hashFiles(${((_c = key.files) !== null && _c !== void 0 ? _c : []).map(f => `'${f}'`).join(", ")}) }}`,
6918
+ restoreKeyPrefix: `${(_d = key.prefix) !== null && _d !== void 0 ? _d : "files"}-`,
6919
+ label: (_e = key.prefix) !== null && _e !== void 0 ? _e : ((_f = key.files) !== null && _f !== void 0 ? _f : []).join(",")
6920
+ };
6921
+ return {
6922
+ name: `Cache ${label}`,
6923
+ // pull-only caches are restored but never saved
6924
+ uses: cache.policy === "pull" ? "actions/cache/restore@v4" : "actions/cache@v4",
6925
+ with: {
6926
+ path: paths.join("\n"),
6927
+ key: cacheKey,
6928
+ "restore-keys": restoreKeyPrefix
6929
+ }
6930
+ };
6931
+ });
6932
+ };
6933
+ const getSecretsEnv = context => {
6934
+ var _a, _b;
6935
+ const keys = [...context.environment.secretEnvVarKeys, ...((_a = context.environment.jobOnlyVars.build.secretEnvVarKeys) !== null && _a !== void 0 ? _a : []), ...((_b = context.environment.jobOnlyVars.deploy.secretEnvVarKeys) !== null && _b !== void 0 ? _b : [])];
6936
+ return Object.fromEntries(keys.map(({
6937
+ key
6938
+ }) => {
6939
+ const name = (0, getEnvironmentVariables_1.getSecretVarName)(context.env, context.name, key);
6940
+ return [name, `\${{ secrets.${name} }}`];
6941
+ }));
6942
+ };
6943
+ const makeGithubJob = (context, job, allJobs, uploadProviderIds) => {
6944
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j;
6945
+ const id = (0, exports.githubJobId)(context, job.name);
6946
+ const isComponent = context.type === "component";
6947
+ const createsJobEnv = !!job.environment && isComponent && (!job.environment.action || job.environment.action === "start");
6948
+ const skipCheckout = ((_a = job.runnerVariables) === null || _a === void 0 ? void 0 : _a.GIT_STRATEGY) === "none";
6949
+ const needs = (0, exports.resolveGithubNeeds)(context, job, allJobs);
6950
+ const image = typeof job.image === "string" ? job.image : Array.isArray(job.image) ? job.image[0] : (_b = job.image) === null || _b === void 0 ? void 0 : _b.name;
6951
+ const runScript = [...(0, scriptFunctions_1.getGithubScriptFunctionDefinitions)(), ...(0, gitlab_1.collapseableSection)("injectvars", "Injecting variables")((0, getInjectVarsScript_1.getInjectVarsScript)((_c = job.variables) !== null && _c !== void 0 ? _c : {})), ...((_e = (_d = job.script) === null || _d === void 0 ? void 0 : _d.filter(utils_1.notNil)) !== null && _e !== void 0 ? _e : []), ...(createsJobEnv ? [`echo "url=$ROOT_URL" >> "$GITHUB_OUTPUT"`] : [])].join("\n");
6952
+ const artifactPaths = (_g = (_f = job.artifacts) === null || _f === void 0 ? void 0 : _f.paths) !== null && _g !== void 0 ? _g : [];
6953
+ const services = Object.fromEntries(((_h = job.services) !== null && _h !== void 0 ? _h : []).map(service => typeof service === "string" ? {
6954
+ name: service
6955
+ } : service).filter(service => !isDindService(service.name)).map(service => [slug("alias" in service && service.alias ? service.alias : service.name), {
6956
+ image: service.name,
6957
+ ...("variables" in service && service.variables ? {
6958
+ env: service.variables
6959
+ } : {})
6960
+ }]));
6961
+ const env = {
6962
+ ...Object.fromEntries(Object.entries((_j = job.runnerVariables) !== null && _j !== void 0 ? _j : {}).filter(([key]) => !GITLAB_ONLY_RUNNER_VARIABLES.has(key))),
6963
+ ...(isComponent ? getSecretsEnv(context) : {}),
6964
+ CL_JOB_IMAGE: image !== null && image !== void 0 ? image : "runner"
6965
+ };
6966
+ const githubJob = {
6967
+ name: githubJobName(context, job.name),
6968
+ "runs-on": "ubuntu-latest",
6969
+ ...(needs.length > 0 ? {
6970
+ needs: needs.map(n => n.id)
6971
+ } : {}),
6972
+ ...(image ? {
6973
+ container: {
6974
+ image
6975
+ }
6976
+ } : {}),
6977
+ ...(Object.keys(services).length > 0 ? {
6978
+ services
6979
+ } : {}),
6980
+ ...(job.environment && isComponent ? {
6981
+ environment: {
6982
+ name: context.environment.envType === "review" ? `review-pr\${{ github.event.number }}-${context.name}` : `${context.env}-${context.name}`,
6983
+ ...(createsJobEnv ? {
6984
+ url: "${{ steps.main.outputs.url }}"
6985
+ } : {})
6986
+ }
6987
+ } : {}),
6988
+ env,
6989
+ steps: [...(skipCheckout ? [] : [{
6990
+ name: "Checkout",
6991
+ uses: "actions/checkout@v4"
6992
+ }]), ...createCacheSteps(context, job), ...needs.filter(n => n.artifacts).map(n => ({
6993
+ name: `Download artifacts from ${n.id}`,
6994
+ uses: "actions/download-artifact@v4",
6995
+ with: {
6996
+ name: n.id
6997
+ }
6998
+ })), {
6999
+ name: job.name,
7000
+ id: "main",
7001
+ run: runScript,
7002
+ shell: "bash"
7003
+ }, ...(uploadProviderIds.has(id) || artifactPaths.length > 0 ? artifactPaths.length > 0 ? [{
7004
+ name: "Upload artifacts",
7005
+ uses: "actions/upload-artifact@v4",
7006
+ with: {
7007
+ name: id,
7008
+ path: artifactPaths.join("\n"),
7009
+ "if-no-files-found": "warn"
7010
+ }
7011
+ }] : [] : [])]
7012
+ };
7013
+ return [id, githubJob];
7014
+ };
7015
+ exports.makeGithubJob = makeGithubJob;
7016
+ /**
7017
+ * the ids of all jobs whose artifacts are consumed by other jobs
7018
+ * (they need an upload step)
7019
+ */
7020
+ const getUploadProviderIds = allJobs => {
7021
+ const ids = new Set();
7022
+ [...allJobs.workspaces, ...allJobs.components].forEach(({
7023
+ context,
7024
+ jobs
7025
+ }) => jobs.forEach(job => (0, exports.resolveGithubNeeds)(context, job, allJobs).filter(n => n.artifacts).forEach(n => ids.add(n.id))));
7026
+ return ids;
7027
+ };
7028
+ exports.getUploadProviderIds = getUploadProviderIds;
7029
+
7030
+ /***/ }),
7031
+
7032
+ /***/ 55611:
7033
+ /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => {
7034
+
7035
+ "use strict";
7036
+
7037
+
7038
+ Object.defineProperty(exports, "__esModule", ({
7039
+ value: true
7040
+ }));
7041
+ exports.getGithubReleaseJobs = void 0;
7042
+ const runner_1 = __nccwpck_require__(92348);
7043
+ const createReleaseJob = needs => ({
7044
+ name: "create release",
7045
+ "runs-on": "ubuntu-latest",
7046
+ container: {
7047
+ image: (0, runner_1.getRunnerImage)("semantic-release")
7048
+ },
7049
+ // semantic-release pushes the version tag and release commit
7050
+ permissions: {
7051
+ contents: "write"
7052
+ },
7053
+ ...(needs && needs.length > 0 ? {
7054
+ needs
7055
+ } : {}),
7056
+ env: {
7057
+ GITHUB_TOKEN: "${{ github.token }}"
7058
+ },
7059
+ steps: [{
7060
+ name: "Checkout",
7061
+ uses: "actions/checkout@v4",
7062
+ // semantic-release needs the full history to determine the version
7063
+ with: {
7064
+ "fetch-depth": 0
7065
+ }
7066
+ }, {
7067
+ name: "create release",
7068
+ run: "semanticRelease",
7069
+ shell: "bash"
7070
+ }]
7071
+ });
7072
+ /**
7073
+ * the release jobs for the github backend (the counterpart of the
7074
+ * gitlab release jobs):
7075
+ * - releases.when === "auto": the release job runs in the main-branch
7076
+ * workflow after all other jobs; a force variant goes to the manual
7077
+ * tasks workflow
7078
+ * - otherwise (manual, the default): the release job goes to the
7079
+ * manual tasks workflow
7080
+ */
7081
+ const getGithubReleaseJobs = (config, mainWorkflowJobIds) => {
7082
+ var _a;
7083
+ if (((_a = config.releases) === null || _a === void 0 ? void 0 : _a.when) === "auto") {
7084
+ return {
7085
+ main: {
7086
+ "create-release": createReleaseJob(mainWorkflowJobIds)
7087
+ },
7088
+ manual: {
7089
+ "force-create-release": createReleaseJob()
7090
+ }
7091
+ };
7092
+ }
7093
+ return {
7094
+ main: {},
7095
+ manual: {
7096
+ "create-release": createReleaseJob()
7097
+ }
7098
+ };
7099
+ };
7100
+ exports.getGithubReleaseJobs = getGithubReleaseJobs;
7101
+
7102
+ /***/ }),
7103
+
7104
+ /***/ 99635:
7105
+ /***/ (function(__unused_webpack_module, exports, __nccwpck_require__) {
7106
+
7107
+ "use strict";
7108
+
7109
+
7110
+ var __createBinding = this && this.__createBinding || (Object.create ? function (o, m, k, k2) {
7111
+ if (k2 === undefined) k2 = k;
7112
+ var desc = Object.getOwnPropertyDescriptor(m, k);
7113
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
7114
+ desc = {
7115
+ enumerable: true,
7116
+ get: function () {
7117
+ return m[k];
7118
+ }
7119
+ };
7120
+ }
7121
+ Object.defineProperty(o, k2, desc);
7122
+ } : function (o, m, k, k2) {
7123
+ if (k2 === undefined) k2 = k;
7124
+ o[k2] = m[k];
7125
+ });
7126
+ var __exportStar = this && this.__exportStar || function (m, exports) {
7127
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
7128
+ };
7129
+ Object.defineProperty(exports, "__esModule", ({
7130
+ value: true
7131
+ }));
7132
+ __exportStar(__nccwpck_require__(54038), exports);
7133
+ __exportStar(__nccwpck_require__(25474), exports);
7134
+
7135
+ /***/ }),
7136
+
7137
+ /***/ 75759:
7138
+ /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => {
7139
+
7140
+ "use strict";
7141
+
7142
+
7143
+ Object.defineProperty(exports, "__esModule", ({
7144
+ value: true
7145
+ }));
7146
+ exports.getGithubScriptFunctionDefinitions = void 0;
7147
+ const globalScriptFunctions_1 = __nccwpck_require__(53891);
7148
+ /**
7149
+ * github variants of the global script functions where the gitlab
7150
+ * implementation uses gitlab-specific mechanics (e.g. collapsible log
7151
+ * sections use gitlab's `section_start` escape codes, github uses
7152
+ * `::group::` workflow commands). All other functions are plain bash
7153
+ * and shared as-is.
7154
+ */
7155
+ const GITHUB_FUNCTION_OVERRIDES = {
7156
+ collapseable_section_start: `local section_title="\${1}"
7157
+ local section_description="\${2:-$section_title}"
7158
+ echo "::group::\${section_description}"`,
7159
+ collapseable_section_end: `echo "::endgroup::"`
7160
+ };
7161
+ /**
7162
+ * the bash function definitions to prepend to every job script.
7163
+ * github has no pipeline-wide before_script like gitlab, so each job
7164
+ * carries its own definitions.
7165
+ */
7166
+ const getGithubScriptFunctionDefinitions = () => [...globalScriptFunctions_1.globalScriptFunctions.values()].map(fn => {
7167
+ const override = GITHUB_FUNCTION_OVERRIDES[fn.name];
7168
+ return override ? `function ${fn.name} () {\n${override.trim()}\n}` : fn.toBashFunction();
7169
+ });
7170
+ exports.getGithubScriptFunctionDefinitions = getGithubScriptFunctionDefinitions;
7171
+
7172
+ /***/ }),
7173
+
5848
7174
  /***/ 22278:
5849
7175
  /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => {
5850
7176
 
@@ -5855,6 +7181,7 @@ Object.defineProperty(exports, "__esModule", ({
5855
7181
  value: true
5856
7182
  }));
5857
7183
  exports.GitlabBackend = void 0;
7184
+ const promises_1 = __nccwpck_require__(91943);
5858
7185
  const rules_1 = __nccwpck_require__(44565);
5859
7186
  const config_1 = __nccwpck_require__(96925);
5860
7187
  const createAllJobs_1 = __nccwpck_require__(83671);
@@ -5870,6 +7197,12 @@ class GitlabBackend {
5870
7197
  this.type = "gitlab";
5871
7198
  this.generatedFolder = GITLAB_GENERATED_FOLDER;
5872
7199
  }
7200
+ async cleanup() {
7201
+ await (0, promises_1.rm)(this.generatedFolder, {
7202
+ force: true,
7203
+ recursive: true
7204
+ });
7205
+ }
5873
7206
  async createFiles(config) {
5874
7207
  const includes = await this.createIncludes(config);
5875
7208
  const mainFile = {
@@ -6584,12 +7917,15 @@ var __exportStar = this && this.__exportStar || function (m, exports) {
6584
7917
  Object.defineProperty(exports, "__esModule", ({
6585
7918
  value: true
6586
7919
  }));
6587
- exports.getEnabledPipelineTypes = exports.getPipelineBackend = void 0;
7920
+ exports.getEnabledPipelineTypes = exports.getVaultConfig = exports.getPipelineGitRemote = exports.getPipelineOptions = exports.getPipelineBackend = void 0;
7921
+ const github_1 = __nccwpck_require__(99635);
6588
7922
  const gitlab_1 = __nccwpck_require__(8475);
6589
7923
  __exportStar(__nccwpck_require__(92474), exports);
6590
7924
  __exportStar(__nccwpck_require__(8475), exports);
7925
+ __exportStar(__nccwpck_require__(99635), exports);
6591
7926
  const BACKENDS = {
6592
- gitlab: () => new gitlab_1.GitlabBackend()
7927
+ gitlab: () => new gitlab_1.GitlabBackend(),
7928
+ github: () => new github_1.GithubBackend()
6593
7929
  };
6594
7930
  const getPipelineBackend = type => {
6595
7931
  const createBackend = BACKENDS[type];
@@ -6599,6 +7935,38 @@ const getPipelineBackend = type => {
6599
7935
  return createBackend();
6600
7936
  };
6601
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;
6602
7970
  /**
6603
7971
  * the pipeline types enabled for this config. Multiple types can be
6604
7972
  * enabled at once (parallel generation, e.g. during a CI migration).
@@ -6941,10 +8309,12 @@ Object.defineProperty(exports, "__esModule", ({
6941
8309
  value: true
6942
8310
  }));
6943
8311
  exports.getCiVariable = void 0;
6944
- const ciVariables_1 = __nccwpck_require__(78506);
8312
+ const ciVariables_1 = __nccwpck_require__(25474);
8313
+ const ciVariables_2 = __nccwpck_require__(78506);
6945
8314
  const BashExpression_1 = __nccwpck_require__(25354);
6946
8315
  const CI_VARIABLES_PER_PIPELINE_TYPE = {
6947
- gitlab: ciVariables_1.GITLAB_CI_VARIABLES
8316
+ gitlab: ciVariables_2.GITLAB_CI_VARIABLES,
8317
+ github: ciVariables_1.GITHUB_CI_VARIABLES
6948
8318
  };
6949
8319
  /**
6950
8320
  * resolves a platform-neutral CI variable to a bash expression for the
@@ -8563,7 +9933,7 @@ Object.defineProperty(exports, "__esModule", ({
8563
9933
  value: true
8564
9934
  }));
8565
9935
  exports.DOCKER_REGISTRY = exports.PIPELINE_IMAGE_TAG = void 0;
8566
- exports.PIPELINE_IMAGE_TAG = "next-16388362" || 0;
9936
+ exports.PIPELINE_IMAGE_TAG = "next-8b9ad5ca" || 0;
8567
9937
  exports.DOCKER_REGISTRY = "git.panter.ch:5001/catladder/catladder" || 0;
8568
9938
 
8569
9939
  /***/ }),
@@ -9130,7 +10500,9 @@ const bashExpressionPerPipelineType_1 = __nccwpck_require__(61945);
9130
10500
  const getEnvType_1 = __nccwpck_require__(28254);
9131
10501
  const REVIEW_SLUG = {
9132
10502
  default: "unknown-review-slug",
9133
- gitlab: new BashExpression_1.BashExpression(`$([ -n "$CI_MERGE_REQUEST_IID" ] && echo "mr$CI_MERGE_REQUEST_IID" || { [ -n "$CI_COMMIT_REF_SLUG" ] && echo "$CI_COMMIT_REF_SLUG" || echo "unknown"; })`)
10503
+ gitlab: new BashExpression_1.BashExpression(`$([ -n "$CI_MERGE_REQUEST_IID" ] && echo "mr$CI_MERGE_REQUEST_IID" || { [ -n "$CI_COMMIT_REF_SLUG" ] && echo "$CI_COMMIT_REF_SLUG" || echo "unknown"; })`),
10504
+ // CL_PR_NUMBER is injected by the github backend at the workflow level
10505
+ github: new BashExpression_1.BashExpression(`$([ -n "$CL_PR_NUMBER" ] && echo "pr$CL_PR_NUMBER" || echo "unknown")`)
9134
10506
  };
9135
10507
  const getReviewSlug = (envConfig, env, pipelineType) => {
9136
10508
  const envType = (0, getEnvType_1.getEnvType)(env, envConfig);
@@ -10466,13 +11838,15 @@ exports.GCLOUD_RUN_DEPLOY_TYPE = {
10466
11838
  }
10467
11839
  return {};
10468
11840
  },
10469
- additionalSecretKeys: ctx => [{
11841
+ additionalSecretKeys: ctx => [
11842
+ // deploy-time credentials don't exist for the local env
11843
+ ...(ctx.envType !== "local" ? [{
10470
11844
  key: exports.GCLOUD_DEPLOY_CREDENTIALS_KEY,
10471
11845
  hidden: true
10472
11846
  }, {
10473
11847
  key: exports.GCLOUD_RUN_CANONICAL_HOST_SUFFIX,
10474
11848
  hidden: true
10475
- }, ...(ctx.deployConfigRaw && ctx.deployConfigRaw.cloudSql ? [{
11849
+ }] : []), ...(ctx.deployConfigRaw && ctx.deployConfigRaw.cloudSql ? [{
10476
11850
  key: "DB_PASSWORD"
10477
11851
  }] : [])],
10478
11852
  getAdditionalEnvVars: ctx => {
@@ -10480,7 +11854,8 @@ exports.GCLOUD_RUN_DEPLOY_TYPE = {
10480
11854
  fullName,
10481
11855
  env,
10482
11856
  componentName,
10483
- deployConfigRaw
11857
+ deployConfigRaw,
11858
+ envType
10484
11859
  } = ctx;
10485
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();
10486
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, {
@@ -10489,7 +11864,11 @@ exports.GCLOUD_RUN_DEPLOY_TYPE = {
10489
11864
  region: deployConfigRaw.region
10490
11865
  })])) : {};
10491
11866
  return {
10492
- 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
+ } : {}),
10493
11872
  ...getCloudSqlVariables(ctx),
10494
11873
  ...jobTriggers,
10495
11874
  DEPLOY_CLOUD_RUN_SERVICE_NAME: (0, getServiceName_1.getServiceNameForEnvContext)(ctx),
@@ -12320,11 +13699,8 @@ async function generatePipelineFiles(context, pipelineType) {
12320
13699
  exports.generatePipelineFiles = generatePipelineFiles;
12321
13700
  async function generatePipelineFilesForBackend(context, backend) {
12322
13701
  const files = await backend.createFiles(context.config);
12323
- // first clean up the folder
12324
- await (0, promises_1.rm)(backend.generatedFolder, {
12325
- force: true,
12326
- recursive: true
12327
- });
13702
+ // first clean up previously generated files
13703
+ await backend.cleanup();
12328
13704
  // write files
12329
13705
  await Promise.all(files.map(async ({
12330
13706
  path,
@@ -155941,7 +157317,7 @@ function differenceInCalendarMonths(dateLeft, dateRight) {
155941
157317
  "use strict";
155942
157318
 
155943
157319
  exports.differenceInCalendarQuarters = differenceInCalendarQuarters;
155944
- var _index = __nccwpck_require__(14016);
157320
+ var _index = __nccwpck_require__(91635);
155945
157321
  var _index2 = __nccwpck_require__(65073);
155946
157322
 
155947
157323
  /**
@@ -160353,7 +161729,7 @@ function getOverlappingDaysInIntervals(intervalLeft, intervalRight) {
160353
161729
 
160354
161730
  /***/ }),
160355
161731
 
160356
- /***/ 14016:
161732
+ /***/ 91635:
160357
161733
  /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => {
160358
161734
 
160359
161735
  "use strict";
@@ -161921,7 +163297,7 @@ Object.keys(_index92).forEach(function (key) {
161921
163297
  },
161922
163298
  });
161923
163299
  });
161924
- var _index93 = __nccwpck_require__(14016);
163300
+ var _index93 = __nccwpck_require__(91635);
161925
163301
  Object.keys(_index93).forEach(function (key) {
161926
163302
  if (key === "default" || key === "__esModule") return;
161927
163303
  if (key in exports && exports[key] === _index93[key]) return;