@catladder/cli 5.0.1 → 5.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (100) hide show
  1. package/dist/apps/cli/src/apps/cli/commands/project/doctor/checkGithub.js +3 -3
  2. package/dist/apps/cli/src/apps/cli/commands/project/doctor/checkGithub.js.map +1 -1
  3. package/dist/apps/cli/src/apps/cli/commands/project/setup/setupAccessTokens.js +2 -2
  4. package/dist/apps/cli/src/apps/cli/commands/project/setup/setupAccessTokens.js.map +1 -1
  5. package/dist/apps/cli/src/apps/cli/commands/project/setup/setupGithub.js +2 -2
  6. package/dist/apps/cli/src/apps/cli/commands/project/setup/setupGithub.js.map +1 -1
  7. package/dist/apps/cli/src/apps/cli/commands/project/setup/setupKubernetes.js +23 -7
  8. package/dist/apps/cli/src/apps/cli/commands/project/setup/setupKubernetes.js.map +1 -1
  9. package/dist/apps/cli/src/commands/index.d.ts +1 -0
  10. package/dist/apps/cli/src/commands/index.js +4 -1
  11. package/dist/apps/cli/src/commands/index.js.map +1 -1
  12. package/dist/apps/cli/src/commands/project/commandConfigSecrets.js +2 -1
  13. package/dist/apps/cli/src/commands/project/commandConfigSecrets.js.map +1 -1
  14. package/dist/apps/cli/src/commands/project/commandMrPin.d.ts +21 -0
  15. package/dist/apps/cli/src/commands/project/commandMrPin.js +119 -0
  16. package/dist/apps/cli/src/commands/project/commandMrPin.js.map +1 -0
  17. package/dist/apps/cli/src/commands/project/commandSecretsSyncGithub.d.ts +1 -42
  18. package/dist/apps/cli/src/commands/project/commandSecretsSyncGithub.js +5 -124
  19. package/dist/apps/cli/src/commands/project/commandSecretsSyncGithub.js.map +1 -1
  20. package/dist/apps/cli/src/commands/project/secrets/commandSecretsPush.js +2 -1
  21. package/dist/apps/cli/src/commands/project/secrets/commandSecretsPush.js.map +1 -1
  22. package/dist/apps/cli/src/commands/project/secrets/commandSecretsSet.js +2 -2
  23. package/dist/apps/cli/src/commands/project/secrets/commandSecretsSet.js.map +1 -1
  24. package/dist/apps/cli/src/commands/project/secrets/document.d.ts +1 -12
  25. package/dist/apps/cli/src/commands/project/secrets/document.js +1 -79
  26. package/dist/apps/cli/src/commands/project/secrets/document.js.map +1 -1
  27. package/dist/apps/cli/src/gcloud/serviceAccounts.js +15 -4
  28. package/dist/apps/cli/src/gcloud/serviceAccounts.js.map +1 -1
  29. package/dist/apps/cli/src/secrets/github.d.ts +41 -0
  30. package/dist/apps/cli/src/secrets/github.js +127 -0
  31. package/dist/apps/cli/src/secrets/github.js.map +1 -0
  32. package/dist/apps/cli/src/secrets/index.d.ts +2 -0
  33. package/dist/apps/cli/src/secrets/index.js +19 -0
  34. package/dist/apps/cli/src/secrets/index.js.map +1 -0
  35. package/dist/apps/cli/src/secrets/writeSecrets.d.ts +18 -0
  36. package/dist/apps/cli/src/secrets/writeSecrets.js +88 -0
  37. package/dist/apps/cli/src/secrets/writeSecrets.js.map +1 -0
  38. package/dist/apps/cli/src/utils/gitlab.d.ts +10 -0
  39. package/dist/apps/cli/src/utils/gitlab.js +30 -5
  40. package/dist/apps/cli/src/utils/gitlab.js.map +1 -1
  41. package/dist/apps/cli/src/vault/BitwardenVault.d.ts +6 -0
  42. package/dist/apps/cli/src/vault/BitwardenVault.js +11 -1
  43. package/dist/apps/cli/src/vault/BitwardenVault.js.map +1 -1
  44. package/dist/apps/cli/src/vault/GitlabVault.d.ts +2 -2
  45. package/dist/apps/cli/src/vault/GitlabVault.js +3 -2
  46. package/dist/apps/cli/src/vault/GitlabVault.js.map +1 -1
  47. package/dist/apps/cli/src/vault/VaultManager.d.ts +2 -2
  48. package/dist/apps/cli/src/vault/VaultManager.js +2 -2
  49. package/dist/apps/cli/src/vault/VaultManager.js.map +1 -1
  50. package/dist/apps/cli/src/vault/types.d.ts +16 -2
  51. package/dist/bundles/catenv/index.js +115 -13
  52. package/dist/bundles/cli/index.js +749 -431
  53. package/dist/bundles/skills/catladder-cli/references/commands.md +13 -0
  54. package/dist/bundles/skills/catladder-deploys/SKILL.md +7 -0
  55. package/dist/bundles/skills/catladder-migrate-ci-backend/SKILL.md +7 -2
  56. package/dist/bundles/skills/catladder-pipelines/SKILL.md +27 -1
  57. package/dist/bundles/skills/catladder-secrets/SKILL.md +16 -0
  58. package/dist/packages/pipeline/src/autoStop.d.ts +22 -0
  59. package/dist/packages/pipeline/src/autoStop.js +30 -0
  60. package/dist/packages/pipeline/src/autoStop.js.map +1 -0
  61. package/dist/packages/pipeline/src/backends/gitlab/GitlabBackend.js +2 -0
  62. package/dist/packages/pipeline/src/backends/gitlab/GitlabBackend.js.map +1 -1
  63. package/dist/packages/pipeline/src/backends/gitlab/createGitlabPipeline.d.ts +4 -1
  64. package/dist/packages/pipeline/src/backends/gitlab/createGitlabPipeline.js +21 -1
  65. package/dist/packages/pipeline/src/backends/gitlab/createGitlabPipeline.js.map +1 -1
  66. package/dist/packages/pipeline/src/deploy/base/deploy.js +8 -2
  67. package/dist/packages/pipeline/src/deploy/base/deploy.js.map +1 -1
  68. package/dist/packages/pipeline/src/index.d.ts +1 -0
  69. package/dist/packages/pipeline/src/index.js +1 -0
  70. package/dist/packages/pipeline/src/index.js.map +1 -1
  71. package/dist/packages/pipeline/src/types/config.d.ts +32 -0
  72. package/dist/skills/catladder-cli/references/commands.md +13 -0
  73. package/dist/skills/catladder-deploys/SKILL.md +7 -0
  74. package/dist/skills/catladder-migrate-ci-backend/SKILL.md +7 -2
  75. package/dist/skills/catladder-pipelines/SKILL.md +27 -1
  76. package/dist/skills/catladder-secrets/SKILL.md +16 -0
  77. package/dist/tsconfig.tsbuildinfo +1 -1
  78. package/package.json +1 -1
  79. package/src/apps/cli/commands/project/doctor/checkGithub.ts +1 -1
  80. package/src/apps/cli/commands/project/setup/setupAccessTokens.ts +3 -2
  81. package/src/apps/cli/commands/project/setup/setupGithub.ts +1 -1
  82. package/src/apps/cli/commands/project/setup/setupKubernetes.ts +31 -20
  83. package/src/commands/index.ts +1 -0
  84. package/src/commands/project/commandConfigSecrets.ts +1 -1
  85. package/src/commands/project/commandMrPin.ts +184 -0
  86. package/src/commands/project/commandSecretsSyncGithub.ts +7 -154
  87. package/src/commands/project/secrets/commandSecretsPush.ts +2 -5
  88. package/src/commands/project/secrets/commandSecretsSet.ts +2 -2
  89. package/src/commands/project/secrets/document.ts +2 -127
  90. package/src/gcloud/serviceAccounts.ts +16 -7
  91. package/src/secrets/__tests__/writeSecrets.test.ts +147 -0
  92. package/src/secrets/github.ts +157 -0
  93. package/src/secrets/index.ts +2 -0
  94. package/src/secrets/writeSecrets.ts +138 -0
  95. package/src/utils/gitlab.ts +34 -7
  96. package/src/vault/BitwardenVault.ts +10 -1
  97. package/src/vault/GitlabVault.ts +9 -2
  98. package/src/vault/VaultManager.ts +3 -2
  99. package/src/vault/__tests__/BitwardenVault.test.ts +88 -0
  100. package/src/vault/types.ts +17 -1
@@ -616,7 +616,7 @@ exports.checkCloudRun = checkCloudRun;
616
616
  Object.defineProperty(exports, "__esModule", ({ value: true }));
617
617
  exports.checkGithub = void 0;
618
618
  const pipeline_1 = __nccwpck_require__(53695);
619
- const commandSecretsSyncGithub_1 = __nccwpck_require__(27565);
619
+ const secrets_1 = __nccwpck_require__(85200);
620
620
  const concurrency_1 = __nccwpck_require__(59723);
621
621
  const github_1 = __nccwpck_require__(41353);
622
622
  const githubMergeGating_1 = __nccwpck_require__(14966);
@@ -637,14 +637,14 @@ const checkGithub = async (report, config) => {
637
637
  report.warn("github cli (gh) not authenticated — github checks skipped", "run: gh auth login");
638
638
  return;
639
639
  }
640
- const repo = await (0, commandSecretsSyncGithub_1.getConfiguredGithubRepo)();
640
+ const repo = await (0, secrets_1.getConfiguredGithubRepo)();
641
641
  if (!repo) {
642
642
  report.fail(`no github repository found on the '${(0, pipeline_1.getPipelineGitRemote)(config, "github")}' remote`);
643
643
  return;
644
644
  }
645
645
  let targets;
646
646
  try {
647
- targets = await (0, commandSecretsSyncGithub_1.collectSecretTargets)(config);
647
+ targets = await (0, secrets_1.collectSecretTargets)(config);
648
648
  }
649
649
  catch (e) {
650
650
  report.warn(`could not generate the github workflows to derive secret references (${e.message})`);
@@ -1260,10 +1260,10 @@ const setupAccessTokens = async (instance) => {
1260
1260
  await (0, gitlab_1.doGitlabRequest)(instance, `projects/${projectId}/variables/GL_TOKEN`, { value: newToken.token }, "PUT");
1261
1261
  }
1262
1262
  catch (e) {
1263
- if (e.message !== "not found") {
1263
+ if (!(e instanceof gitlab_1.GitlabNotFoundError)) {
1264
1264
  throw e;
1265
1265
  }
1266
- // not found
1266
+ // the variable does not exist yet: create it
1267
1267
  await (0, gitlab_1.doGitlabRequest)(instance, `projects/${projectId}/variables`, {
1268
1268
  key: "GL_TOKEN",
1269
1269
  value: newToken.token,
@@ -1566,7 +1566,7 @@ const promises_1 = __nccwpck_require__(91943);
1566
1566
  const os_1 = __nccwpck_require__(70857);
1567
1567
  const path_1 = __nccwpck_require__(16928);
1568
1568
  const util_1 = __nccwpck_require__(39023);
1569
- const commandSecretsSyncGithub_1 = __nccwpck_require__(27565);
1569
+ const secrets_1 = __nccwpck_require__(85200);
1570
1570
  const github_1 = __nccwpck_require__(41353);
1571
1571
  const githubMergeGating_1 = __nccwpck_require__(14966);
1572
1572
  const execFile = (0, util_1.promisify)(child_process_1.execFile);
@@ -1600,7 +1600,7 @@ const setupGithub = async (instance, config) => {
1600
1600
  instance.log("⚠️ github cli (gh) not authenticated — skipping github merge-gating setup (run: gh auth login)");
1601
1601
  return;
1602
1602
  }
1603
- const repo = await (0, commandSecretsSyncGithub_1.getConfiguredGithubRepo)();
1603
+ const repo = await (0, secrets_1.getConfiguredGithubRepo)();
1604
1604
  if (!repo) {
1605
1605
  instance.log(`⚠️ no github repository found on the '${(0, pipeline_1.getPipelineGitRemote)(config, "github")}' remote — skipping github merge-gating setup`);
1606
1606
  return;
@@ -1770,15 +1770,20 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
1770
1770
  Object.defineProperty(exports, "__esModule", ({ value: true }));
1771
1771
  exports.setupKubernetes = void 0;
1772
1772
  const pipeline_1 = __nccwpck_require__(53695);
1773
+ const getProjectConfig_1 = __nccwpck_require__(70478);
1773
1774
  const child_process_promise_1 = __nccwpck_require__(11542);
1775
+ const secrets_1 = __nccwpck_require__(85200);
1774
1776
  const cluster_1 = __nccwpck_require__(8034);
1775
1777
  const gitlab_1 = __nccwpck_require__(17697);
1776
1778
  const ensureNamespace_1 = __importDefault(__nccwpck_require__(308));
1777
- const setupKubernetes = async (instance, context) => {
1778
- var _a;
1779
- const deployConfig = (_a = context.deploy) === null || _a === void 0 ? void 0 : _a.config;
1780
- if (!(0, pipeline_1.isOfDeployType)(deployConfig, "kubernetes")) {
1781
- throw new Error("cannot run setupKubernetes on non-kubernetes deployments");
1779
+ /**
1780
+ * the cluster pulls the images from the gitlab container registry, and
1781
+ * a deploy token is how it authenticates there. Nothing to do when the
1782
+ * project has no gitlab pipeline (the images live elsewhere then).
1783
+ */
1784
+ const ensureGitlabDeployToken = async (instance, config) => {
1785
+ if (!config || !(0, pipeline_1.getEnabledPipelineTypes)(config).includes("gitlab")) {
1786
+ return;
1782
1787
  }
1783
1788
  const { id: projectId } = await (0, gitlab_1.getProjectInfo)(instance);
1784
1789
  const deploy_tokens = await (0, gitlab_1.doGitlabRequest)(instance, `projects/${projectId}/deploy_tokens`);
@@ -1790,6 +1795,14 @@ const setupKubernetes = async (instance, context) => {
1790
1795
  scopes: ["read_registry"],
1791
1796
  }, "POST");
1792
1797
  }
1798
+ };
1799
+ const setupKubernetes = async (instance, context) => {
1800
+ var _a;
1801
+ const deployConfig = (_a = context.deploy) === null || _a === void 0 ? void 0 : _a.config;
1802
+ if (!(0, pipeline_1.isOfDeployType)(deployConfig, "kubernetes")) {
1803
+ throw new Error("cannot run setupKubernetes on non-kubernetes deployments");
1804
+ }
1805
+ await ensureGitlabDeployToken(instance, await (0, getProjectConfig_1.getProjectConfig)());
1793
1806
  const fullName = (0, pipeline_1.getFullKubernetesClusterName)(deployConfig.cluster);
1794
1807
  instance.log(`cluster: ${fullName}`);
1795
1808
  await (0, cluster_1.connectToCluster)(fullName);
@@ -1872,8 +1885,11 @@ EOF
1872
1885
  }
1873
1886
  instance.log("service accounts created / updated!");
1874
1887
  instance.log("");
1875
- instance.log("pusing secrets to gitlab...");
1876
- await (0, gitlab_1.upsertAllVariables)(instance, vars, context.env, context.name, false);
1888
+ instance.log("storing the cluster credentials...");
1889
+ await (0, secrets_1.writeSecretsAndMirror)(instance, [{ env: context.env, componentName: context.name, secrets: vars }],
1890
+ // the service account token is recreated on every run — the
1891
+ // previous one is worth nothing
1892
+ { backup: false });
1877
1893
  instance.log("done!");
1878
1894
  };
1879
1895
  exports.setupKubernetes = setupKubernetes;
@@ -2719,7 +2735,7 @@ exports.commandKubeStopPortforward = (0, defineCommand_1.defineCommand)({
2719
2735
  "use strict";
2720
2736
 
2721
2737
  Object.defineProperty(exports, "__esModule", ({ value: true }));
2722
- exports.commandStarwars = exports.commandDadjoke = exports.commandSecurityAuditCreate = exports.commandSecurityAuditEvaluate = exports.commandSecurityAuditCiJob = exports.commandRestoreDb = exports.commandMongoDestroyMember = exports.commandMongoPortForward = exports.commandMongoGetShell = exports.commandTriggerCronjobProject = exports.commandDeleteProject = exports.commandPauseProject = exports.commandDeletePods = exports.commandGetShell = exports.commandListPods = exports.commandNamespace = exports.commandSecurityEvaluate = exports.commandProjectRestoreDb = exports.commandCiJobLog = exports.commandCiJobOpen = exports.commandDoctor = exports.commandSetup = exports.commandGetMyTotalWorktime = exports.commandRenewToken = exports.commandTriggerCronjobGeneral = exports.commandOpenLogs = exports.commandSecretsSyncGithub = exports.commandSecretsClearBackups = exports.commandSecretsList = exports.commandSecretsSet = exports.commandSecretsPush = exports.commandSecretsPull = exports.commandConfigSecrets = exports.commandCloudSqlProxy = exports.commandOpenGit = exports.commandOpenEnv = exports.commandPortForward = exports.commandEnvVars = exports.commandKubePortForward = exports.commandKubeGetShell = exports.commandKubeStopPortforward = exports.commandKubeListPods = exports.commandKubeListSecrets = exports.commandKubeListNamespaces = exports.commandKubeCurrentContext = void 0;
2738
+ 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.commandMrUnpin = exports.commandMrPin = exports.commandSecurityEvaluate = exports.commandProjectRestoreDb = exports.commandCiJobLog = exports.commandCiJobOpen = exports.commandDoctor = exports.commandSetup = exports.commandGetMyTotalWorktime = exports.commandRenewToken = exports.commandTriggerCronjobGeneral = exports.commandOpenLogs = exports.commandSecretsSyncGithub = exports.commandSecretsClearBackups = exports.commandSecretsList = exports.commandSecretsSet = exports.commandSecretsPush = exports.commandSecretsPull = exports.commandConfigSecrets = exports.commandCloudSqlProxy = exports.commandOpenGit = exports.commandOpenEnv = exports.commandPortForward = exports.commandEnvVars = exports.commandKubePortForward = exports.commandKubeGetShell = exports.commandKubeStopPortforward = exports.commandKubeListPods = exports.commandKubeListSecrets = exports.commandKubeListNamespaces = exports.commandKubeCurrentContext = void 0;
2723
2739
  // General commands
2724
2740
  var commandKubeCurrentContext_1 = __nccwpck_require__(78034);
2725
2741
  Object.defineProperty(exports, "commandKubeCurrentContext", ({ enumerable: true, get: function () { return commandKubeCurrentContext_1.commandKubeCurrentContext; } }));
@@ -2779,6 +2795,9 @@ var commandProjectRestoreDb_1 = __nccwpck_require__(69233);
2779
2795
  Object.defineProperty(exports, "commandProjectRestoreDb", ({ enumerable: true, get: function () { return commandProjectRestoreDb_1.commandProjectRestoreDb; } }));
2780
2796
  var commandSecurityEvaluate_1 = __nccwpck_require__(86851);
2781
2797
  Object.defineProperty(exports, "commandSecurityEvaluate", ({ enumerable: true, get: function () { return commandSecurityEvaluate_1.commandSecurityEvaluate; } }));
2798
+ var commandMrPin_1 = __nccwpck_require__(31318);
2799
+ Object.defineProperty(exports, "commandMrPin", ({ enumerable: true, get: function () { return commandMrPin_1.commandMrPin; } }));
2800
+ Object.defineProperty(exports, "commandMrUnpin", ({ enumerable: true, get: function () { return commandMrPin_1.commandMrUnpin; } }));
2782
2801
  // Project k8s commands
2783
2802
  var commandNamespace_1 = __nccwpck_require__(25918);
2784
2803
  Object.defineProperty(exports, "commandNamespace", ({ enumerable: true, get: function () { return commandNamespace_1.commandNamespace; } }));
@@ -3175,6 +3194,7 @@ const common_tags_1 = __nccwpck_require__(110);
3175
3194
  const defineCommand_1 = __nccwpck_require__(19096);
3176
3195
  const editAsFile_1 = __nccwpck_require__(3072);
3177
3196
  const promise_1 = __nccwpck_require__(23161);
3197
+ const secrets_1 = __nccwpck_require__(85200);
3178
3198
  const document_1 = __nccwpck_require__(96716);
3179
3199
  const scope_1 = __nccwpck_require__(8513);
3180
3200
  const parseKeysInput_1 = __nccwpck_require__(93560);
@@ -3237,7 +3257,7 @@ exports.commandConfigSecrets = (0, defineCommand_1.defineCommand)({
3237
3257
  ctx.log("⏭️ these secrets are still unset and will not be written:");
3238
3258
  skipped.forEach((entry) => ctx.log(` - ${entry}`));
3239
3259
  }
3240
- await (0, document_1.writeSecretsAndMirror)(ctx, writes);
3260
+ await (0, secrets_1.writeSecretsAndMirror)(ctx, writes);
3241
3261
  ctx.log("done! 😻");
3242
3262
  ctx.log("");
3243
3263
  },
@@ -3442,6 +3462,132 @@ exports.commandCiJobLog = (0, defineCommand_1.defineCommand)({
3442
3462
 
3443
3463
  /***/ }),
3444
3464
 
3465
+ /***/ 31318:
3466
+ /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => {
3467
+
3468
+ "use strict";
3469
+
3470
+ Object.defineProperty(exports, "__esModule", ({ value: true }));
3471
+ exports.commandMrUnpin = exports.commandMrPin = void 0;
3472
+ const child_process_promise_1 = __nccwpck_require__(11542);
3473
+ const pipeline_1 = __nccwpck_require__(53695);
3474
+ const getProjectConfig_1 = __nccwpck_require__(70478);
3475
+ const defineCommand_1 = __nccwpck_require__(19096);
3476
+ const gitlab_1 = __nccwpck_require__(17697);
3477
+ const PIN_LABEL_COLOR = "#428fdc";
3478
+ /**
3479
+ * the configured pin label — errors when the project cannot pin
3480
+ * (github-only, or pinning disabled)
3481
+ */
3482
+ const getPinLabel = async () => {
3483
+ const config = await (0, getProjectConfig_1.getProjectConfig)();
3484
+ if (!config) {
3485
+ throw new Error("no catladder config found");
3486
+ }
3487
+ if (!(0, pipeline_1.getEnabledPipelineTypes)(config).includes("gitlab")) {
3488
+ throw new Error("pinning review apps needs the gitlab pipeline — github has no auto-stop, review apps live until the pull request is closed");
3489
+ }
3490
+ const { pinLabel } = (0, pipeline_1.getAutoStopConfig)(config);
3491
+ if (pinLabel === false) {
3492
+ throw new Error("review-app pinning is disabled in this project (autoStop.pinLabel: false)");
3493
+ }
3494
+ return pinLabel;
3495
+ };
3496
+ const findMr = async (io, projectId, mrIid) => {
3497
+ if (mrIid) {
3498
+ return (0, gitlab_1.doGitlabRequest)(io, `projects/${projectId}/merge_requests/${mrIid}`);
3499
+ }
3500
+ const branch = (await (0, child_process_promise_1.exec)("git rev-parse --abbrev-ref HEAD")).stdout.trim();
3501
+ const mrs = await (0, gitlab_1.doGitlabRequest)(io, `projects/${projectId}/merge_requests?state=opened&source_branch=${encodeURIComponent(branch)}`);
3502
+ if (!(mrs === null || mrs === void 0 ? void 0 : mrs.length)) {
3503
+ throw new Error(`no open merge request found for branch '${branch}' — pass one with --mr <iid>`);
3504
+ }
3505
+ return mrs[0];
3506
+ };
3507
+ const ensurePinLabelExists = async (io, projectId, pinLabel) => {
3508
+ const existing = await (0, gitlab_1.doGitlabRequest)(io, `projects/${projectId}/labels?search=${encodeURIComponent(pinLabel)}`);
3509
+ if (existing === null || existing === void 0 ? void 0 : existing.some((label) => label.name === pinLabel)) {
3510
+ return;
3511
+ }
3512
+ await (0, gitlab_1.doGitlabRequest)(io, `projects/${projectId}/labels`, {
3513
+ name: pinLabel,
3514
+ color: PIN_LABEL_COLOR,
3515
+ description: "review apps of this MR are pinned — deploys use auto_stop_in: never (managed by catladder)",
3516
+ }, "POST");
3517
+ io.log(`created the '${pinLabel}' label in the project`);
3518
+ };
3519
+ exports.commandMrPin = (0, defineCommand_1.defineCommand)({
3520
+ name: "mr pin",
3521
+ description: "pin the review apps of a merge request (disable their auto-stop) by adding the pin label; merging or closing the MR still stops them",
3522
+ group: "mr",
3523
+ inputs: {
3524
+ mr: {
3525
+ type: "number",
3526
+ message: "merge request IID (defaults to the open MR of the current branch)",
3527
+ positional: true,
3528
+ required: false,
3529
+ },
3530
+ pipeline: {
3531
+ type: "boolean",
3532
+ message: "trigger a merge-request pipeline so the pin takes effect immediately",
3533
+ required: false,
3534
+ },
3535
+ },
3536
+ execute: async (ctx) => {
3537
+ var _a, _b;
3538
+ const pinLabel = await getPinLabel();
3539
+ const { id: projectId } = await (0, gitlab_1.getProjectInfo)(ctx);
3540
+ const mr = await findMr(ctx, projectId, await ctx.get("mr"));
3541
+ await ensurePinLabelExists(ctx, projectId, pinLabel);
3542
+ if ((_a = mr.labels) === null || _a === void 0 ? void 0 : _a.includes(pinLabel)) {
3543
+ ctx.log(`already pinned: mr!${mr.iid} carries the '${pinLabel}' label`);
3544
+ }
3545
+ else {
3546
+ await (0, gitlab_1.doGitlabRequest)(ctx, `projects/${projectId}/merge_requests/${mr.iid}`, { add_labels: pinLabel }, "PUT");
3547
+ ctx.log(`📌 pinned mr!${mr.iid} "${mr.title}" — label '${pinLabel}' added`);
3548
+ }
3549
+ // the label only affects pipelines created after it was set, so
3550
+ // trigger one — otherwise the currently deployed review apps keep
3551
+ // their old auto-stop timer until the next push
3552
+ if ((_b = (await ctx.get("pipeline"))) !== null && _b !== void 0 ? _b : true) {
3553
+ const pipeline = await (0, gitlab_1.doGitlabRequest)(ctx, `projects/${projectId}/merge_requests/${mr.iid}/pipelines`, {}, "POST");
3554
+ ctx.log(`🚀 triggered a pipeline so the pin takes effect now: ${pipeline.web_url}`);
3555
+ }
3556
+ else {
3557
+ ctx.log("the pin takes effect with the next pipeline of the MR");
3558
+ }
3559
+ },
3560
+ });
3561
+ exports.commandMrUnpin = (0, defineCommand_1.defineCommand)({
3562
+ name: "mr unpin",
3563
+ description: "unpin the review apps of a merge request — the auto-stop timer re-arms with the next deploy",
3564
+ group: "mr",
3565
+ inputs: {
3566
+ mr: {
3567
+ type: "number",
3568
+ message: "merge request IID (defaults to the open MR of the current branch)",
3569
+ positional: true,
3570
+ required: false,
3571
+ },
3572
+ },
3573
+ execute: async (ctx) => {
3574
+ var _a;
3575
+ const pinLabel = await getPinLabel();
3576
+ const { id: projectId } = await (0, gitlab_1.getProjectInfo)(ctx);
3577
+ const mr = await findMr(ctx, projectId, await ctx.get("mr"));
3578
+ if (!((_a = mr.labels) === null || _a === void 0 ? void 0 : _a.includes(pinLabel))) {
3579
+ ctx.log(`mr!${mr.iid} is not pinned (no '${pinLabel}' label)`);
3580
+ return;
3581
+ }
3582
+ await (0, gitlab_1.doGitlabRequest)(ctx, `projects/${projectId}/merge_requests/${mr.iid}`, { remove_labels: pinLabel }, "PUT");
3583
+ ctx.log(`unpinned mr!${mr.iid} — label '${pinLabel}' removed`);
3584
+ ctx.log("the deployed review apps stay pinned until the next deploy re-arms their auto-stop timer");
3585
+ },
3586
+ });
3587
+ //# sourceMappingURL=commandMrPin.js.map
3588
+
3589
+ /***/ }),
3590
+
3445
3591
  /***/ 30119:
3446
3592
  /***/ (function(__unused_webpack_module, exports, __nccwpck_require__) {
3447
3593
 
@@ -3874,131 +4020,12 @@ exports.commandSecretsClearBackups = (0, defineCommand_1.defineCommand)({
3874
4020
  "use strict";
3875
4021
 
3876
4022
  Object.defineProperty(exports, "__esModule", ({ value: true }));
3877
- exports.commandSecretsSyncGithub = exports.getConfiguredGithubRepo = exports.pushSecretsToGithub = exports.collectSecretTargets = exports.collectSecretsFromVault = void 0;
4023
+ exports.commandSecretsSyncGithub = void 0;
3878
4024
  const pipeline_1 = __nccwpck_require__(53695);
3879
4025
  const getProjectConfig_1 = __nccwpck_require__(70478);
3880
4026
  const defineCommand_1 = __nccwpck_require__(19096);
4027
+ const secrets_1 = __nccwpck_require__(85200);
3881
4028
  const github_1 = __nccwpck_require__(41353);
3882
- /**
3883
- * all secrets declared in the config, with their values read from the
3884
- * vault (github secrets are write-only and can never act as a source)
3885
- */
3886
- const collectSecretsFromVault = async (io, envFilter) => {
3887
- const envAndComponents = await (0, getProjectConfig_1.getAllComponentsWithAllEnvsHierarchical)();
3888
- const kinds = new Map();
3889
- for (const [componentName, envs] of Object.entries(envAndComponents)) {
3890
- for (const env of envs) {
3891
- if (envFilter && !envFilter.includes(env)) {
3892
- continue;
3893
- }
3894
- const { secretEnvVarKeys, jobOnlyVars } = await (0, getProjectConfig_1.getEnvironment)(env, componentName);
3895
- const keys = [
3896
- ...secretEnvVarKeys,
3897
- ...jobOnlyVars.build.secretEnvVarKeys,
3898
- ...jobOnlyVars.deploy.secretEnvVarKeys,
3899
- ];
3900
- keys.forEach(({ key, kind }) => kinds.set((0, pipeline_1.getSecretVarName)(env, componentName, key), kind !== null && kind !== void 0 ? kind : "secret"));
3901
- }
3902
- }
3903
- const names = [...kinds.keys()];
3904
- const values = await (await io.getVaultManager()).readSecrets(names, io);
3905
- const secrets = names
3906
- .filter((name) => values[name] !== undefined)
3907
- .map((name) => {
3908
- var _a;
3909
- return ({
3910
- name,
3911
- value: values[name],
3912
- kind: (_a = kinds.get(name)) !== null && _a !== void 0 ? _a : "secret",
3913
- });
3914
- });
3915
- const missing = names.filter((name) => values[name] === undefined);
3916
- return { secrets, missing };
3917
- };
3918
- exports.collectSecretsFromVault = collectSecretsFromVault;
3919
- /** marker for repo-level (environment-less) targets */
3920
- const REPO_LEVEL = "";
3921
- /**
3922
- * derives which github environment(s) each secret/variable must live in
3923
- * by scanning the GENERATED workflows: a `${{ secrets.X }}` / `${{
3924
- * vars.X }}` reference in a job's env targets that job's environment
3925
- * (or the repo level when the job has none). Deriving from the real
3926
- * output means the sync can never drift from what the workflows expect.
3927
- *
3928
- * Environment secrets are the answer to github's cap of 100 secrets
3929
- * per REPO (a multi-env project easily exceeds it): the cap applies
3930
- * per environment instead.
3931
- */
3932
- const collectSecretTargets = async (config) => {
3933
- var _a;
3934
- const workflows = await new pipeline_1.GithubBackend().createWorkflows(config);
3935
- const targets = new Map();
3936
- for (const workflow of Object.values(workflows)) {
3937
- for (const job of Object.values(workflow.jobs)) {
3938
- const environment = typeof job.environment === "object"
3939
- ? job.environment.name
3940
- : job.environment;
3941
- for (const value of Object.values((_a = job.env) !== null && _a !== void 0 ? _a : {})) {
3942
- if (typeof value !== "string")
3943
- continue;
3944
- for (const match of value.matchAll(/\$\{\{\s*(?:secrets|vars)\.([A-Za-z0-9_]+)\s*\}\}/g)) {
3945
- const name = match[1];
3946
- if (!targets.has(name)) {
3947
- targets.set(name, new Set());
3948
- }
3949
- targets.get(name).add(environment !== null && environment !== void 0 ? environment : REPO_LEVEL);
3950
- }
3951
- }
3952
- }
3953
- }
3954
- return targets;
3955
- };
3956
- exports.collectSecretTargets = collectSecretTargets;
3957
- /**
3958
- * pushes vault values to github via the gh cli: sensitive ones as
3959
- * secrets (masked), "variable"-kind ones as variables (plain) — each
3960
- * into the environment(s) whose jobs reference them
3961
- */
3962
- const pushSecretsToGithub = async (io, repo, secrets, targets) => {
3963
- const environments = [
3964
- ...new Set([...targets.values()].flatMap((envs) => [...envs].filter((env) => env !== REPO_LEVEL))),
3965
- ];
3966
- for (const environment of environments) {
3967
- await (0, github_1.ensureGithubEnvironment)(repo, environment);
3968
- io.log(`🌍 environment ${environment}`);
3969
- }
3970
- for (const { name, value, kind } of secrets) {
3971
- const envs = targets.get(name);
3972
- if (!envs) {
3973
- io.log(`⏭️ ${name} (not referenced by any generated workflow)`);
3974
- continue;
3975
- }
3976
- for (const environment of envs) {
3977
- const suffix = environment === REPO_LEVEL ? "" : ` → ${environment}`;
3978
- if (kind === "variable") {
3979
- await (0, github_1.setGithubVariable)(repo, name, value, environment || undefined);
3980
- io.log(`📖 ${name} (variable)${suffix}`);
3981
- }
3982
- else {
3983
- await (0, github_1.setGithubSecret)(repo, name, value, environment || undefined);
3984
- io.log(`✅ ${name}${suffix}`);
3985
- }
3986
- }
3987
- }
3988
- };
3989
- exports.pushSecretsToGithub = pushSecretsToGithub;
3990
- /**
3991
- * resolves the github repository from the configured git remote
3992
- * (`pipelines.github.gitRemote`, defaults to origin)
3993
- */
3994
- const getConfiguredGithubRepo = async () => {
3995
- const config = await (0, getProjectConfig_1.getProjectConfig)();
3996
- if (!config) {
3997
- return undefined;
3998
- }
3999
- return (0, github_1.getGithubRepoFromRemote)((0, pipeline_1.getPipelineGitRemote)(config, "github"));
4000
- };
4001
- exports.getConfiguredGithubRepo = getConfiguredGithubRepo;
4002
4029
  /**
4003
4030
  * copies the catladder secrets from the vault to github actions
4004
4031
  * repository secrets, keeping the exact same names, so the github
@@ -4035,12 +4062,12 @@ exports.commandSecretsSyncGithub = (0, defineCommand_1.defineCommand)({
4035
4062
  if (!(await (0, github_1.isGhAuthenticated)())) {
4036
4063
  throw new Error("the github cli (gh) is not installed or not authenticated — run `gh auth login` first");
4037
4064
  }
4038
- const repo = (_a = (await ctx.get("repo"))) !== null && _a !== void 0 ? _a : (await (0, exports.getConfiguredGithubRepo)());
4065
+ const repo = (_a = (await ctx.get("repo"))) !== null && _a !== void 0 ? _a : (await (0, secrets_1.getConfiguredGithubRepo)());
4039
4066
  if (!repo) {
4040
4067
  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`);
4041
4068
  }
4042
4069
  const envFilter = (_b = (await ctx.get("env"))) === null || _b === void 0 ? void 0 : _b.split(",").map((e) => e.trim());
4043
- const { secrets, missing } = await (0, exports.collectSecretsFromVault)(ctx, envFilter);
4070
+ const { secrets, missing } = await (0, secrets_1.collectSecretsFromVault)(ctx, envFilter);
4044
4071
  if (missing.length > 0) {
4045
4072
  ctx.log(`⚠️ ${missing.length} secrets are not set in the vault and will be skipped:`);
4046
4073
  missing.forEach((name) => ctx.log(` - ${name}`));
@@ -4057,7 +4084,7 @@ exports.commandSecretsSyncGithub = (0, defineCommand_1.defineCommand)({
4057
4084
  if (!confirmed) {
4058
4085
  throw new Error("abort");
4059
4086
  }
4060
- await (0, exports.pushSecretsToGithub)(ctx, repo, secrets, await (0, exports.collectSecretTargets)(config));
4087
+ await (0, secrets_1.pushSecretsToGithub)(ctx, repo, secrets, await (0, secrets_1.collectSecretTargets)(config));
4061
4088
  ctx.log("");
4062
4089
  ctx.log("done! 😻 the github pipeline can use them as ${{ secrets.<NAME> }}");
4063
4090
  },
@@ -4621,6 +4648,7 @@ const fs_extra_1 = __nccwpck_require__(20925);
4621
4648
  const yaml_1 = __nccwpck_require__(99292);
4622
4649
  const defineCommand_1 = __nccwpck_require__(19096);
4623
4650
  const readStdin_1 = __nccwpck_require__(89973);
4651
+ const secrets_1 = __nccwpck_require__(85200);
4624
4652
  const document_1 = __nccwpck_require__(96716);
4625
4653
  const scope_1 = __nccwpck_require__(8513);
4626
4654
  const parseKeysInput_1 = __nccwpck_require__(93560);
@@ -4683,7 +4711,7 @@ exports.commandSecretsPush = (0, defineCommand_1.defineCommand)({
4683
4711
  if (!confirmed) {
4684
4712
  throw new Error("abort");
4685
4713
  }
4686
- await (0, document_1.writeSecretsAndMirror)(ctx, writes);
4714
+ await (0, secrets_1.writeSecretsAndMirror)(ctx, writes);
4687
4715
  ctx.log("done! 😻");
4688
4716
  },
4689
4717
  });
@@ -4701,7 +4729,7 @@ exports.commandSecretsSet = void 0;
4701
4729
  const fs_extra_1 = __nccwpck_require__(20925);
4702
4730
  const defineCommand_1 = __nccwpck_require__(19096);
4703
4731
  const readStdin_1 = __nccwpck_require__(89973);
4704
- const document_1 = __nccwpck_require__(96716);
4732
+ const secrets_1 = __nccwpck_require__(85200);
4705
4733
  const scope_1 = __nccwpck_require__(8513);
4706
4734
  const resolveValue = async (io, value, valueFile) => {
4707
4735
  if (valueFile) {
@@ -4772,7 +4800,7 @@ exports.commandSecretsSet = (0, defineCommand_1.defineCommand)({
4772
4800
  componentName,
4773
4801
  secrets: { [key]: value },
4774
4802
  }));
4775
- await (0, document_1.writeSecretsAndMirror)(ctx, writes);
4803
+ await (0, secrets_1.writeSecretsAndMirror)(ctx, writes);
4776
4804
  ctx.log("done! 😻");
4777
4805
  },
4778
4806
  });
@@ -4786,12 +4814,8 @@ exports.commandSecretsSet = (0, defineCommand_1.defineCommand)({
4786
4814
  "use strict";
4787
4815
 
4788
4816
  Object.defineProperty(exports, "__esModule", ({ value: true }));
4789
- exports.writeSecretsAndMirror = exports.collectSecretsWrites = exports.checkSecretsDocument = exports.buildSecretsDocument = exports.FILL_ME = void 0;
4817
+ exports.collectSecretsWrites = exports.checkSecretsDocument = exports.buildSecretsDocument = exports.FILL_ME = void 0;
4790
4818
  const pipeline_1 = __nccwpck_require__(53695);
4791
- const getProjectConfig_1 = __nccwpck_require__(70478);
4792
- const github_1 = __nccwpck_require__(41353);
4793
- const gitlab_1 = __nccwpck_require__(17697);
4794
- const commandSecretsSyncGithub_1 = __nccwpck_require__(27565);
4795
4819
  /** placeholder for secrets that have no value in the vault yet */
4796
4820
  exports.FILL_ME = "🚨 FILL ME";
4797
4821
  /* for convenience, parse json values. that makes it easier to edit secrets that are objects */
@@ -4889,123 +4913,49 @@ const collectSecretsWrites = (document, scoped) => {
4889
4913
  return { writes, skipped };
4890
4914
  };
4891
4915
  exports.collectSecretsWrites = collectSecretsWrites;
4916
+ //# sourceMappingURL=document.js.map
4917
+
4918
+ /***/ }),
4919
+
4920
+ /***/ 93560:
4921
+ /***/ ((__unused_webpack_module, exports) => {
4922
+
4923
+ "use strict";
4924
+
4925
+ Object.defineProperty(exports, "__esModule", ({ value: true }));
4926
+ exports.parseKeysInput = void 0;
4927
+ /** parses a comma-separated `--key` input into a list of keys */
4928
+ const parseKeysInput = (input) => {
4929
+ const keys = input === null || input === void 0 ? void 0 : input.split(",").map((key) => key.trim()).filter(Boolean);
4930
+ return keys && keys.length > 0 ? keys : undefined;
4931
+ };
4932
+ exports.parseKeysInput = parseKeysInput;
4933
+ //# sourceMappingURL=parseKeysInput.js.map
4934
+
4935
+ /***/ }),
4936
+
4937
+ /***/ 8513:
4938
+ /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => {
4939
+
4940
+ "use strict";
4941
+
4942
+ Object.defineProperty(exports, "__esModule", ({ value: true }));
4943
+ exports.formatScope = exports.getScopedSecretKeys = exports.getDeclaredSecretKeys = exports.secretsScopeChoices = exports.resolveSecretsScope = void 0;
4944
+ const getProjectConfig_1 = __nccwpck_require__(70478);
4892
4945
  /**
4893
- * writes secrets to the vault (the source of truth) and mirrors them
4894
- * to every enabled CI backend — which backends exist is invisible to
4895
- * the caller
4946
+ * resolves a scope expression to component envs:
4947
+ * - (empty): all components, all envs
4948
+ * - `env:component`: one env of one component
4949
+ * - `env` / `env:`: one env, every component that has it
4950
+ * - `:component`: every env of one component
4896
4951
  */
4897
- const writeSecretsAndMirror = async (io, writes) => {
4898
- const config = await (0, getProjectConfig_1.getProjectConfig)();
4899
- if (!config) {
4952
+ const resolveSecretsScope = async (scope) => {
4953
+ const all = await (0, getProjectConfig_1.getAllComponentsWithAllEnvsHierarchical)();
4954
+ if (Object.keys(all).length === 0) {
4900
4955
  throw new Error("no catladder config found");
4901
4956
  }
4902
- io.log("");
4903
- io.log("writing secrets to the vault, please wait...");
4904
- io.log("");
4905
- for (const { env, componentName, secrets } of writes) {
4906
- io.log("writing " + env + ":" + componentName + "...\n");
4907
- await (await io.getVaultManager()).writeSecrets(env, componentName, secrets);
4908
- io.log("");
4909
- io.log("✅ " + env + ":" + componentName);
4910
- io.log("--------------------------------\n");
4911
- }
4912
- await mirrorSecretsToCiBackends(io, config, writes);
4913
- };
4914
- exports.writeSecretsAndMirror = writeSecretsAndMirror;
4915
- /**
4916
- * the vault is the source of truth; every enabled CI backend receives
4917
- * a mirrored copy of the written secrets (gitlab variables, github
4918
- * secrets), so all CI systems stay in sync
4919
- */
4920
- const mirrorSecretsToCiBackends = async (io, config, writes) => {
4921
- const enabled = (0, pipeline_1.getEnabledPipelineTypes)(config);
4922
- // gitlab mirror (unless gitlab is the vault itself, then it is
4923
- // already written)
4924
- if ((0, pipeline_1.getVaultConfig)(config).type !== "gitlab" && enabled.includes("gitlab")) {
4925
- for (const { env, componentName, secrets } of writes) {
4926
- await (0, gitlab_1.upsertAllVariables)(io, secrets, env, componentName);
4927
- }
4928
- io.log("✅ mirrored to gitlab");
4929
- }
4930
- if (!enabled.includes("github")) {
4931
- return;
4932
- }
4933
- const kinds = new Map();
4934
- for (const { env, componentName } of writes) {
4935
- const { secretEnvVarKeys, jobOnlyVars } = await (0, getProjectConfig_1.getEnvironment)(env, componentName);
4936
- [
4937
- ...secretEnvVarKeys,
4938
- ...jobOnlyVars.build.secretEnvVarKeys,
4939
- ...jobOnlyVars.deploy.secretEnvVarKeys,
4940
- ].forEach(({ key, kind }) => kinds.set((0, pipeline_1.getSecretVarName)(env, componentName, key), kind !== null && kind !== void 0 ? kind : "secret"));
4941
- }
4942
- const secrets = writes.flatMap(({ env, componentName, secrets }) => Object.entries(secrets).map(([key, value]) => {
4943
- var _a;
4944
- const name = (0, pipeline_1.getSecretVarName)(env, componentName, key);
4945
- return {
4946
- name,
4947
- value: typeof value === "string" ? value : JSON.stringify(value !== null && value !== void 0 ? value : ""),
4948
- kind: (_a = kinds.get(name)) !== null && _a !== void 0 ? _a : "secret",
4949
- };
4950
- }));
4951
- if (secrets.length === 0) {
4952
- return;
4953
- }
4954
- if (!(await (0, github_1.isGhAuthenticated)())) {
4955
- io.log("⚠️ github pipeline is enabled, but the github cli (gh) is not authenticated — run `project secrets-sync-github` later to mirror the secrets");
4956
- return;
4957
- }
4958
- const repo = await (0, commandSecretsSyncGithub_1.getConfiguredGithubRepo)();
4959
- if (!repo) {
4960
- io.log("⚠️ github pipeline is enabled, but no github repository was found — run `project secrets-sync-github` later to mirror the secrets");
4961
- return;
4962
- }
4963
- io.log(`mirroring ${secrets.length} secrets to github ('${repo}')...`);
4964
- await (0, commandSecretsSyncGithub_1.pushSecretsToGithub)(io, repo, secrets, await (0, commandSecretsSyncGithub_1.collectSecretTargets)(config));
4965
- };
4966
- //# sourceMappingURL=document.js.map
4967
-
4968
- /***/ }),
4969
-
4970
- /***/ 93560:
4971
- /***/ ((__unused_webpack_module, exports) => {
4972
-
4973
- "use strict";
4974
-
4975
- Object.defineProperty(exports, "__esModule", ({ value: true }));
4976
- exports.parseKeysInput = void 0;
4977
- /** parses a comma-separated `--key` input into a list of keys */
4978
- const parseKeysInput = (input) => {
4979
- const keys = input === null || input === void 0 ? void 0 : input.split(",").map((key) => key.trim()).filter(Boolean);
4980
- return keys && keys.length > 0 ? keys : undefined;
4981
- };
4982
- exports.parseKeysInput = parseKeysInput;
4983
- //# sourceMappingURL=parseKeysInput.js.map
4984
-
4985
- /***/ }),
4986
-
4987
- /***/ 8513:
4988
- /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => {
4989
-
4990
- "use strict";
4991
-
4992
- Object.defineProperty(exports, "__esModule", ({ value: true }));
4993
- exports.formatScope = exports.getScopedSecretKeys = exports.getDeclaredSecretKeys = exports.secretsScopeChoices = exports.resolveSecretsScope = void 0;
4994
- const getProjectConfig_1 = __nccwpck_require__(70478);
4995
- /**
4996
- * resolves a scope expression to component → envs:
4997
- * - (empty): all components, all envs
4998
- * - `env:component`: one env of one component
4999
- * - `env` / `env:`: one env, every component that has it
5000
- * - `:component`: every env of one component
5001
- */
5002
- const resolveSecretsScope = async (scope) => {
5003
- const all = await (0, getProjectConfig_1.getAllComponentsWithAllEnvsHierarchical)();
5004
- if (Object.keys(all).length === 0) {
5005
- throw new Error("no catladder config found");
5006
- }
5007
- if (!scope) {
5008
- return all;
4957
+ if (!scope) {
4958
+ return all;
5009
4959
  }
5010
4960
  const [env, componentName] = scope.split(":").map((x) => x || null);
5011
4961
  if (componentName && !(componentName in all)) {
@@ -6060,7 +6010,7 @@ const hash = (str, length) => {
6060
6010
  Object.defineProperty(exports, "__esModule", ({ value: true }));
6061
6011
  exports.upsertGcloudServiceAccountAndSaveSecret = exports.accountExists = void 0;
6062
6012
  const child_process_promise_1 = __nccwpck_require__(11542);
6063
- const gitlab_1 = __nccwpck_require__(17697);
6013
+ const secrets_1 = __nccwpck_require__(85200);
6064
6014
  const serviceAccountNames_1 = __nccwpck_require__(67851);
6065
6015
  const accountExists = async (fullIdentifier) => {
6066
6016
  try {
@@ -6097,9 +6047,20 @@ const upsertGcloudServiceAccount = async (context, account) => {
6097
6047
  const upsertGcloudServiceAccountAndSaveSecret = async (instance, context, account, secretName) => {
6098
6048
  instance.log("upserting service account " + account.name + "...");
6099
6049
  const key = await upsertGcloudServiceAccount(context, account);
6100
- await (0, gitlab_1.upsertAllVariables)(instance, {
6101
- [secretName]: key,
6102
- }, context.env, context.name);
6050
+ // through the vault (and from there to every enabled ci backend)
6051
+ // the key is a secret like any other, the deploy jobs read it under
6052
+ // its declared name. Writing it to gitlab directly would break every
6053
+ // project that has no gitlab.
6054
+ await (0, secrets_1.writeSecretsAndMirror)(instance, [
6055
+ {
6056
+ env: context.env,
6057
+ componentName: context.name,
6058
+ secrets: { [secretName]: key },
6059
+ },
6060
+ ],
6061
+ // a fresh key is created on every run, the previous one is deleted
6062
+ // at google — a backup copy of it would only be a dead secret
6063
+ { backup: false });
6103
6064
  instance.log("done!");
6104
6065
  };
6105
6066
  exports.upsertGcloudServiceAccountAndSaveSecret = upsertGcloudServiceAccountAndSaveSecret;
@@ -6176,23 +6137,338 @@ const getGitRemoteHostAndPath = async (remoteName = "origin") => {
6176
6137
  if (!(gitRemoteHost === null || gitRemoteHost === void 0 ? void 0 : gitRemoteHost.length) || !(gitRemotePath === null || gitRemotePath === void 0 ? void 0 : gitRemotePath.length)) {
6177
6138
  throw new Error(`Failed to parse git remote hostname and path from git configs remote.${remoteName}.url! ${remoteUrl}`);
6178
6139
  }
6179
- return { gitRemoteHost, gitRemotePath };
6180
- };
6181
- exports.getGitRemoteHostAndPath = getGitRemoteHostAndPath;
6182
- const gitProjectInformation = async () => {
6183
- const [{ gitRemoteHost, gitRemotePath }, projectRootPath] = await Promise.all([(0, exports.getGitRemoteHostAndPath)(), (0, exports.getProjectRootPath)()]);
6184
- return {
6185
- gitRemoteHost,
6186
- gitRemotePath,
6187
- projectRootPath,
6188
- };
6140
+ return { gitRemoteHost, gitRemotePath };
6141
+ };
6142
+ exports.getGitRemoteHostAndPath = getGitRemoteHostAndPath;
6143
+ const gitProjectInformation = async () => {
6144
+ const [{ gitRemoteHost, gitRemotePath }, projectRootPath] = await Promise.all([(0, exports.getGitRemoteHostAndPath)(), (0, exports.getProjectRootPath)()]);
6145
+ return {
6146
+ gitRemoteHost,
6147
+ gitRemotePath,
6148
+ projectRootPath,
6149
+ };
6150
+ };
6151
+ exports.gitProjectInformation = gitProjectInformation;
6152
+ //# sourceMappingURL=gitProjectInformation.js.map
6153
+
6154
+ /***/ }),
6155
+
6156
+ /***/ 77721:
6157
+ /***/ (function(__unused_webpack_module, exports, __nccwpck_require__) {
6158
+
6159
+ "use strict";
6160
+
6161
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
6162
+ if (k2 === undefined) k2 = k;
6163
+ var desc = Object.getOwnPropertyDescriptor(m, k);
6164
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6165
+ desc = { enumerable: true, get: function() { return m[k]; } };
6166
+ }
6167
+ Object.defineProperty(o, k2, desc);
6168
+ }) : (function(o, m, k, k2) {
6169
+ if (k2 === undefined) k2 = k;
6170
+ o[k2] = m[k];
6171
+ }));
6172
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
6173
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
6174
+ };
6175
+ Object.defineProperty(exports, "__esModule", ({ value: true }));
6176
+ __exportStar(__nccwpck_require__(40403), exports);
6177
+ __exportStar(__nccwpck_require__(86884), exports);
6178
+ __exportStar(__nccwpck_require__(3328), exports);
6179
+ //# sourceMappingURL=index.js.map
6180
+
6181
+ /***/ }),
6182
+
6183
+ /***/ 32839:
6184
+ /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => {
6185
+
6186
+ "use strict";
6187
+
6188
+ Object.defineProperty(exports, "__esModule", ({ value: true }));
6189
+ exports.getk8sApiBatchBeta = exports.getk8sApiBatch = exports.getk8sApi = void 0;
6190
+ const client_node_1 = __nccwpck_require__(67235);
6191
+ const getKubeConfig = () => {
6192
+ const kc = new client_node_1.KubeConfig();
6193
+ kc.loadFromDefault();
6194
+ return kc;
6195
+ };
6196
+ /**
6197
+ * get kubernetes client. avoid reusing the instance when context get changed
6198
+ * @returns kuberenetes client
6199
+ */
6200
+ const getk8sApi = () => {
6201
+ const kc = getKubeConfig();
6202
+ return kc.makeApiClient(client_node_1.CoreV1Api);
6203
+ };
6204
+ exports.getk8sApi = getk8sApi;
6205
+ const getk8sApiBatch = () => {
6206
+ const kc = getKubeConfig();
6207
+ return kc.makeApiClient(client_node_1.BatchV1Api);
6208
+ };
6209
+ exports.getk8sApiBatch = getk8sApiBatch;
6210
+ const getk8sApiBatchBeta = () => {
6211
+ const kc = getKubeConfig();
6212
+ return kc.makeApiClient(client_node_1.BatchV1beta1Api);
6213
+ };
6214
+ exports.getk8sApiBatchBeta = getk8sApiBatchBeta;
6215
+ //# sourceMappingURL=index.js.map
6216
+
6217
+ /***/ }),
6218
+
6219
+ /***/ 79107:
6220
+ /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => {
6221
+
6222
+ "use strict";
6223
+
6224
+ Object.defineProperty(exports, "__esModule", ({ value: true }));
6225
+ exports.getProjectPodNames = exports.getProjectPvcs = exports.getProjectPods = void 0;
6226
+ const parseChoice_1 = __nccwpck_require__(70769);
6227
+ const k8sApi_1 = __nccwpck_require__(32839);
6228
+ const projects_1 = __nccwpck_require__(20575);
6229
+ const getProjectPods = async (envComponent) => {
6230
+ const namespace = await (0, projects_1.getProjectNamespace)(envComponent);
6231
+ const k8sApi = (0, k8sApi_1.getk8sApi)();
6232
+ const res = await k8sApi.listNamespacedPod(namespace);
6233
+ const { componentName } = (0, parseChoice_1.parseChoice)(envComponent);
6234
+ return res.body.items.filter((item) => { var _a, _b; return componentName ? (_b = (_a = item.metadata) === null || _a === void 0 ? void 0 : _a.name) === null || _b === void 0 ? void 0 : _b.includes(componentName + "-") : true; });
6235
+ };
6236
+ exports.getProjectPods = getProjectPods;
6237
+ const getProjectPvcs = async (envComponent) => {
6238
+ const namespace = await (0, projects_1.getProjectNamespace)(envComponent);
6239
+ const k8sApi = (0, k8sApi_1.getk8sApi)();
6240
+ const res = await k8sApi.listNamespacedPersistentVolumeClaim(namespace);
6241
+ return res.body.items;
6242
+ };
6243
+ exports.getProjectPvcs = getProjectPvcs;
6244
+ const getProjectPodNames = async (envComponent) => {
6245
+ const pods = await (0, exports.getProjectPods)(envComponent);
6246
+ return pods.map((n) => n.metadata.name);
6247
+ };
6248
+ exports.getProjectPodNames = getProjectPodNames;
6249
+ //# sourceMappingURL=index.js.map
6250
+
6251
+ /***/ }),
6252
+
6253
+ /***/ 14178:
6254
+ /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => {
6255
+
6256
+ "use strict";
6257
+
6258
+ Object.defineProperty(exports, "__esModule", ({ value: true }));
6259
+ exports.openGoogleCloudKubernetesDashboard = exports.openGoogleCloudLogs = void 0;
6260
+ const pipeline_1 = __nccwpck_require__(53695);
6261
+ const openDashboard_1 = __nccwpck_require__(74241);
6262
+ const openGoogleCloudLogs = async (instance, context) => {
6263
+ var _a;
6264
+ const deployConfig = (_a = context.deploy) === null || _a === void 0 ? void 0 : _a.config;
6265
+ if (!(0, pipeline_1.isOfDeployType)(deployConfig, "kubernetes")) {
6266
+ throw new Error("context is not of type kubernetes");
6267
+ }
6268
+ if (!deployConfig.cluster || deployConfig.cluster.type !== "gcloud") {
6269
+ throw new Error("no gcloud custer configured");
6270
+ }
6271
+ const namespace = context.environment.envVars.KUBE_NAMESPACE;
6272
+ const cluster = deployConfig.cluster;
6273
+ const resource = `k8s_container/cluster_name/${cluster.name}${namespace ? `/namespace_name/${namespace}` : ""}`;
6274
+ await (0, openDashboard_1.openGoogleCloudDashboard)(instance, "logs/viewer", {
6275
+ project: cluster.projectId,
6276
+ resource: resource,
6277
+ });
6278
+ };
6279
+ exports.openGoogleCloudLogs = openGoogleCloudLogs;
6280
+ const openGoogleCloudKubernetesDashboard = async (instance, context) => {
6281
+ var _a;
6282
+ const deployConfig = (_a = context.deploy) === null || _a === void 0 ? void 0 : _a.config;
6283
+ if (!(0, pipeline_1.isOfDeployType)(deployConfig, "kubernetes")) {
6284
+ throw new Error("context is not of type kubernetes");
6285
+ }
6286
+ if (!deployConfig.cluster || deployConfig.cluster.type !== "gcloud") {
6287
+ throw new Error("no gcloud custer configured");
6288
+ }
6289
+ const namespace = context.environment.envVars.KUBE_NAMESPACE;
6290
+ const cluster = deployConfig.cluster;
6291
+ const pageState = `("savedViews":("c":["gke/${cluster.region}/${cluster.name}"],"n":["${namespace}"],"i":"4e42e0b9cd6147f8a4fba7516752ec48"))`;
6292
+ await (0, openDashboard_1.openGoogleCloudDashboard)(instance, "kubernetes/workload", {
6293
+ project: cluster.projectId,
6294
+ pageState: pageState,
6295
+ });
6296
+ };
6297
+ exports.openGoogleCloudKubernetesDashboard = openGoogleCloudKubernetesDashboard;
6298
+ //# sourceMappingURL=openKubernetesDashboards.js.map
6299
+
6300
+ /***/ }),
6301
+
6302
+ /***/ 76775:
6303
+ /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => {
6304
+
6305
+ "use strict";
6306
+
6307
+ Object.defineProperty(exports, "__esModule", ({ value: true }));
6308
+ exports.startKubePortForward = void 0;
6309
+ const portForwards_1 = __nccwpck_require__(5368);
6310
+ const startKubePortForward = async (podname, localPort, remotePort, namespace) => {
6311
+ const name = `kube/${namespace}/${podname}/${localPort}:${remotePort}`;
6312
+ await (0, portForwards_1.startPortForwardCommand)(name, `kubectl port-forward ${podname} ${localPort}:${remotePort} -n ${namespace}`);
6313
+ };
6314
+ exports.startKubePortForward = startKubePortForward;
6315
+ //# sourceMappingURL=portForward.js.map
6316
+
6317
+ /***/ }),
6318
+
6319
+ /***/ 55689:
6320
+ /***/ ((__unused_webpack_module, exports) => {
6321
+
6322
+ "use strict";
6323
+
6324
+ // we can't import the package.json directly, because the bundling process will inline it
6325
+ // but the version is only set during deployement, when its already bundled
6326
+ // instead we need to use require.main.path to get the path of the main module, which is actually the file in the bin
6327
+ // this will point to the root package.json
6328
+ var _a;
6329
+ Object.defineProperty(exports, "__esModule", ({ value: true }));
6330
+ const path = ((_a = require.main) === null || _a === void 0 ? void 0 : _a.path) + "/../package.json";
6331
+ const ownPkg = require(path);
6332
+ exports["default"] = ownPkg;
6333
+ //# sourceMappingURL=packageInfos.js.map
6334
+
6335
+ /***/ }),
6336
+
6337
+ /***/ 97245:
6338
+ /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => {
6339
+
6340
+ "use strict";
6341
+
6342
+ Object.defineProperty(exports, "__esModule", ({ value: true }));
6343
+ exports.getConfiguredGithubRepo = exports.pushSecretsToGithub = exports.collectSecretTargets = exports.collectSecretsFromVault = void 0;
6344
+ const pipeline_1 = __nccwpck_require__(53695);
6345
+ const getProjectConfig_1 = __nccwpck_require__(70478);
6346
+ const github_1 = __nccwpck_require__(41353);
6347
+ /**
6348
+ * all secrets declared in the config, with their values read from the
6349
+ * vault (github secrets are write-only and can never act as a source)
6350
+ */
6351
+ const collectSecretsFromVault = async (io, envFilter) => {
6352
+ const envAndComponents = await (0, getProjectConfig_1.getAllComponentsWithAllEnvsHierarchical)();
6353
+ const kinds = new Map();
6354
+ for (const [componentName, envs] of Object.entries(envAndComponents)) {
6355
+ for (const env of envs) {
6356
+ if (envFilter && !envFilter.includes(env)) {
6357
+ continue;
6358
+ }
6359
+ const { secretEnvVarKeys, jobOnlyVars } = await (0, getProjectConfig_1.getEnvironment)(env, componentName);
6360
+ const keys = [
6361
+ ...secretEnvVarKeys,
6362
+ ...jobOnlyVars.build.secretEnvVarKeys,
6363
+ ...jobOnlyVars.deploy.secretEnvVarKeys,
6364
+ ];
6365
+ keys.forEach(({ key, kind }) => kinds.set((0, pipeline_1.getSecretVarName)(env, componentName, key), kind !== null && kind !== void 0 ? kind : "secret"));
6366
+ }
6367
+ }
6368
+ const names = [...kinds.keys()];
6369
+ const values = await (await io.getVaultManager()).readSecrets(names, io);
6370
+ const secrets = names
6371
+ .filter((name) => values[name] !== undefined)
6372
+ .map((name) => {
6373
+ var _a;
6374
+ return ({
6375
+ name,
6376
+ value: values[name],
6377
+ kind: (_a = kinds.get(name)) !== null && _a !== void 0 ? _a : "secret",
6378
+ });
6379
+ });
6380
+ const missing = names.filter((name) => values[name] === undefined);
6381
+ return { secrets, missing };
6382
+ };
6383
+ exports.collectSecretsFromVault = collectSecretsFromVault;
6384
+ /** marker for repo-level (environment-less) targets */
6385
+ const REPO_LEVEL = "";
6386
+ /**
6387
+ * derives which github environment(s) each secret/variable must live in
6388
+ * by scanning the GENERATED workflows: a `${{ secrets.X }}` / `${{
6389
+ * vars.X }}` reference in a job's env targets that job's environment
6390
+ * (or the repo level when the job has none). Deriving from the real
6391
+ * output means the sync can never drift from what the workflows expect.
6392
+ *
6393
+ * Environment secrets are the answer to github's cap of 100 secrets
6394
+ * per REPO (a multi-env project easily exceeds it): the cap applies
6395
+ * per environment instead.
6396
+ */
6397
+ const collectSecretTargets = async (config) => {
6398
+ var _a;
6399
+ const workflows = await new pipeline_1.GithubBackend().createWorkflows(config);
6400
+ const targets = new Map();
6401
+ for (const workflow of Object.values(workflows)) {
6402
+ for (const job of Object.values(workflow.jobs)) {
6403
+ const environment = typeof job.environment === "object"
6404
+ ? job.environment.name
6405
+ : job.environment;
6406
+ for (const value of Object.values((_a = job.env) !== null && _a !== void 0 ? _a : {})) {
6407
+ if (typeof value !== "string")
6408
+ continue;
6409
+ for (const match of value.matchAll(/\$\{\{\s*(?:secrets|vars)\.([A-Za-z0-9_]+)\s*\}\}/g)) {
6410
+ const name = match[1];
6411
+ if (!targets.has(name)) {
6412
+ targets.set(name, new Set());
6413
+ }
6414
+ targets.get(name).add(environment !== null && environment !== void 0 ? environment : REPO_LEVEL);
6415
+ }
6416
+ }
6417
+ }
6418
+ }
6419
+ return targets;
6420
+ };
6421
+ exports.collectSecretTargets = collectSecretTargets;
6422
+ /**
6423
+ * pushes vault values to github via the gh cli: sensitive ones as
6424
+ * secrets (masked), "variable"-kind ones as variables (plain) — each
6425
+ * into the environment(s) whose jobs reference them
6426
+ */
6427
+ const pushSecretsToGithub = async (io, repo, secrets, targets) => {
6428
+ const environments = [
6429
+ ...new Set([...targets.values()].flatMap((envs) => [...envs].filter((env) => env !== REPO_LEVEL))),
6430
+ ];
6431
+ for (const environment of environments) {
6432
+ await (0, github_1.ensureGithubEnvironment)(repo, environment);
6433
+ io.log(`🌍 environment ${environment}`);
6434
+ }
6435
+ for (const { name, value, kind } of secrets) {
6436
+ const envs = targets.get(name);
6437
+ if (!envs) {
6438
+ io.log(`⏭️ ${name} (not referenced by any generated workflow)`);
6439
+ continue;
6440
+ }
6441
+ for (const environment of envs) {
6442
+ const suffix = environment === REPO_LEVEL ? "" : ` → ${environment}`;
6443
+ if (kind === "variable") {
6444
+ await (0, github_1.setGithubVariable)(repo, name, value, environment || undefined);
6445
+ io.log(`📖 ${name} (variable)${suffix}`);
6446
+ }
6447
+ else {
6448
+ await (0, github_1.setGithubSecret)(repo, name, value, environment || undefined);
6449
+ io.log(`✅ ${name}${suffix}`);
6450
+ }
6451
+ }
6452
+ }
6453
+ };
6454
+ exports.pushSecretsToGithub = pushSecretsToGithub;
6455
+ /**
6456
+ * resolves the github repository from the configured git remote
6457
+ * (`pipelines.github.gitRemote`, defaults to origin)
6458
+ */
6459
+ const getConfiguredGithubRepo = async () => {
6460
+ const config = await (0, getProjectConfig_1.getProjectConfig)();
6461
+ if (!config) {
6462
+ return undefined;
6463
+ }
6464
+ return (0, github_1.getGithubRepoFromRemote)((0, pipeline_1.getPipelineGitRemote)(config, "github"));
6189
6465
  };
6190
- exports.gitProjectInformation = gitProjectInformation;
6191
- //# sourceMappingURL=gitProjectInformation.js.map
6466
+ exports.getConfiguredGithubRepo = getConfiguredGithubRepo;
6467
+ //# sourceMappingURL=github.js.map
6192
6468
 
6193
6469
  /***/ }),
6194
6470
 
6195
- /***/ 77721:
6471
+ /***/ 85200:
6196
6472
  /***/ (function(__unused_webpack_module, exports, __nccwpck_require__) {
6197
6473
 
6198
6474
  "use strict";
@@ -6212,164 +6488,104 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
6212
6488
  for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
6213
6489
  };
6214
6490
  Object.defineProperty(exports, "__esModule", ({ value: true }));
6215
- __exportStar(__nccwpck_require__(40403), exports);
6216
- __exportStar(__nccwpck_require__(86884), exports);
6217
- __exportStar(__nccwpck_require__(3328), exports);
6491
+ __exportStar(__nccwpck_require__(97245), exports);
6492
+ __exportStar(__nccwpck_require__(31298), exports);
6218
6493
  //# sourceMappingURL=index.js.map
6219
6494
 
6220
6495
  /***/ }),
6221
6496
 
6222
- /***/ 32839:
6497
+ /***/ 31298:
6223
6498
  /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => {
6224
6499
 
6225
6500
  "use strict";
6226
6501
 
6227
6502
  Object.defineProperty(exports, "__esModule", ({ value: true }));
6228
- exports.getk8sApiBatchBeta = exports.getk8sApiBatch = exports.getk8sApi = void 0;
6229
- const client_node_1 = __nccwpck_require__(67235);
6230
- const getKubeConfig = () => {
6231
- const kc = new client_node_1.KubeConfig();
6232
- kc.loadFromDefault();
6233
- return kc;
6234
- };
6503
+ exports.writeSecretsAndMirror = void 0;
6504
+ const pipeline_1 = __nccwpck_require__(53695);
6505
+ const getProjectConfig_1 = __nccwpck_require__(70478);
6506
+ const github_1 = __nccwpck_require__(41353);
6507
+ const gitlab_1 = __nccwpck_require__(17697);
6508
+ const github_2 = __nccwpck_require__(97245);
6235
6509
  /**
6236
- * get kubernetes client. avoid reusing the instance when context get changed
6237
- * @returns kuberenetes client
6510
+ * the one door for writing secret values: they go into the vault (the
6511
+ * source of truth) and are mirrored to every enabled CI backend —
6512
+ * which vault and which backends exist is invisible to the caller.
6513
+ *
6514
+ * Everything that produces a secret value must come through here: the
6515
+ * secrets commands, but also the credentials `project setup`
6516
+ * provisions (gcloud service account keys, kubernetes tokens).
6238
6517
  */
6239
- const getk8sApi = () => {
6240
- const kc = getKubeConfig();
6241
- return kc.makeApiClient(client_node_1.CoreV1Api);
6242
- };
6243
- exports.getk8sApi = getk8sApi;
6244
- const getk8sApiBatch = () => {
6245
- const kc = getKubeConfig();
6246
- return kc.makeApiClient(client_node_1.BatchV1Api);
6247
- };
6248
- exports.getk8sApiBatch = getk8sApiBatch;
6249
- const getk8sApiBatchBeta = () => {
6250
- const kc = getKubeConfig();
6251
- return kc.makeApiClient(client_node_1.BatchV1beta1Api);
6252
- };
6253
- exports.getk8sApiBatchBeta = getk8sApiBatchBeta;
6254
- //# sourceMappingURL=index.js.map
6255
-
6256
- /***/ }),
6257
-
6258
- /***/ 79107:
6259
- /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => {
6260
-
6261
- "use strict";
6262
-
6263
- Object.defineProperty(exports, "__esModule", ({ value: true }));
6264
- exports.getProjectPodNames = exports.getProjectPvcs = exports.getProjectPods = void 0;
6265
- const parseChoice_1 = __nccwpck_require__(70769);
6266
- const k8sApi_1 = __nccwpck_require__(32839);
6267
- const projects_1 = __nccwpck_require__(20575);
6268
- const getProjectPods = async (envComponent) => {
6269
- const namespace = await (0, projects_1.getProjectNamespace)(envComponent);
6270
- const k8sApi = (0, k8sApi_1.getk8sApi)();
6271
- const res = await k8sApi.listNamespacedPod(namespace);
6272
- const { componentName } = (0, parseChoice_1.parseChoice)(envComponent);
6273
- return res.body.items.filter((item) => { var _a, _b; return componentName ? (_b = (_a = item.metadata) === null || _a === void 0 ? void 0 : _a.name) === null || _b === void 0 ? void 0 : _b.includes(componentName + "-") : true; });
6274
- };
6275
- exports.getProjectPods = getProjectPods;
6276
- const getProjectPvcs = async (envComponent) => {
6277
- const namespace = await (0, projects_1.getProjectNamespace)(envComponent);
6278
- const k8sApi = (0, k8sApi_1.getk8sApi)();
6279
- const res = await k8sApi.listNamespacedPersistentVolumeClaim(namespace);
6280
- return res.body.items;
6281
- };
6282
- exports.getProjectPvcs = getProjectPvcs;
6283
- const getProjectPodNames = async (envComponent) => {
6284
- const pods = await (0, exports.getProjectPods)(envComponent);
6285
- return pods.map((n) => n.metadata.name);
6286
- };
6287
- exports.getProjectPodNames = getProjectPodNames;
6288
- //# sourceMappingURL=index.js.map
6289
-
6290
- /***/ }),
6291
-
6292
- /***/ 14178:
6293
- /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => {
6294
-
6295
- "use strict";
6296
-
6297
- Object.defineProperty(exports, "__esModule", ({ value: true }));
6298
- exports.openGoogleCloudKubernetesDashboard = exports.openGoogleCloudLogs = void 0;
6299
- const pipeline_1 = __nccwpck_require__(53695);
6300
- const openDashboard_1 = __nccwpck_require__(74241);
6301
- const openGoogleCloudLogs = async (instance, context) => {
6302
- var _a;
6303
- const deployConfig = (_a = context.deploy) === null || _a === void 0 ? void 0 : _a.config;
6304
- if (!(0, pipeline_1.isOfDeployType)(deployConfig, "kubernetes")) {
6305
- throw new Error("context is not of type kubernetes");
6518
+ const writeSecretsAndMirror = async (io, writes, options = {}) => {
6519
+ const config = await (0, getProjectConfig_1.getProjectConfig)();
6520
+ if (!config) {
6521
+ throw new Error("no catladder config found");
6306
6522
  }
6307
- if (!deployConfig.cluster || deployConfig.cluster.type !== "gcloud") {
6308
- throw new Error("no gcloud custer configured");
6523
+ io.log("");
6524
+ io.log("writing secrets to the vault, please wait...");
6525
+ io.log("");
6526
+ for (const { env, componentName, secrets } of writes) {
6527
+ io.log("writing " + env + ":" + componentName + "...\n");
6528
+ await (await io.getVaultManager()).writeSecrets(env, componentName, secrets, io, options);
6529
+ io.log("");
6530
+ io.log("✅ " + env + ":" + componentName);
6531
+ io.log("--------------------------------\n");
6309
6532
  }
6310
- const namespace = context.environment.envVars.KUBE_NAMESPACE;
6311
- const cluster = deployConfig.cluster;
6312
- const resource = `k8s_container/cluster_name/${cluster.name}${namespace ? `/namespace_name/${namespace}` : ""}`;
6313
- await (0, openDashboard_1.openGoogleCloudDashboard)(instance, "logs/viewer", {
6314
- project: cluster.projectId,
6315
- resource: resource,
6316
- });
6533
+ await mirrorSecretsToCiBackends(io, config, writes, options);
6317
6534
  };
6318
- exports.openGoogleCloudLogs = openGoogleCloudLogs;
6319
- const openGoogleCloudKubernetesDashboard = async (instance, context) => {
6535
+ exports.writeSecretsAndMirror = writeSecretsAndMirror;
6536
+ /**
6537
+ * the vault is the source of truth; every enabled CI backend receives
6538
+ * a mirrored copy of the written secrets (gitlab variables, github
6539
+ * secrets), so all CI systems stay in sync
6540
+ */
6541
+ const mirrorSecretsToCiBackends = async (io, config, writes, options) => {
6320
6542
  var _a;
6321
- const deployConfig = (_a = context.deploy) === null || _a === void 0 ? void 0 : _a.config;
6322
- if (!(0, pipeline_1.isOfDeployType)(deployConfig, "kubernetes")) {
6323
- throw new Error("context is not of type kubernetes");
6543
+ const enabled = (0, pipeline_1.getEnabledPipelineTypes)(config);
6544
+ // gitlab mirror (unless gitlab is the vault itself, then it is
6545
+ // already written)
6546
+ if ((0, pipeline_1.getVaultConfig)(config).type !== "gitlab" && enabled.includes("gitlab")) {
6547
+ for (const { env, componentName, secrets } of writes) {
6548
+ await (0, gitlab_1.upsertAllVariables)(io, secrets, env, componentName, (_a = options.backup) !== null && _a !== void 0 ? _a : true);
6549
+ }
6550
+ io.log("✅ mirrored to gitlab");
6324
6551
  }
6325
- if (!deployConfig.cluster || deployConfig.cluster.type !== "gcloud") {
6326
- throw new Error("no gcloud custer configured");
6552
+ if (!enabled.includes("github")) {
6553
+ return;
6327
6554
  }
6328
- const namespace = context.environment.envVars.KUBE_NAMESPACE;
6329
- const cluster = deployConfig.cluster;
6330
- const pageState = `("savedViews":("c":["gke/${cluster.region}/${cluster.name}"],"n":["${namespace}"],"i":"4e42e0b9cd6147f8a4fba7516752ec48"))`;
6331
- await (0, openDashboard_1.openGoogleCloudDashboard)(instance, "kubernetes/workload", {
6332
- project: cluster.projectId,
6333
- pageState: pageState,
6334
- });
6335
- };
6336
- exports.openGoogleCloudKubernetesDashboard = openGoogleCloudKubernetesDashboard;
6337
- //# sourceMappingURL=openKubernetesDashboards.js.map
6338
-
6339
- /***/ }),
6340
-
6341
- /***/ 76775:
6342
- /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => {
6343
-
6344
- "use strict";
6345
-
6346
- Object.defineProperty(exports, "__esModule", ({ value: true }));
6347
- exports.startKubePortForward = void 0;
6348
- const portForwards_1 = __nccwpck_require__(5368);
6349
- const startKubePortForward = async (podname, localPort, remotePort, namespace) => {
6350
- const name = `kube/${namespace}/${podname}/${localPort}:${remotePort}`;
6351
- await (0, portForwards_1.startPortForwardCommand)(name, `kubectl port-forward ${podname} ${localPort}:${remotePort} -n ${namespace}`);
6555
+ const kinds = new Map();
6556
+ for (const { env, componentName } of writes) {
6557
+ const { secretEnvVarKeys, jobOnlyVars } = await (0, getProjectConfig_1.getEnvironment)(env, componentName);
6558
+ [
6559
+ ...secretEnvVarKeys,
6560
+ ...jobOnlyVars.build.secretEnvVarKeys,
6561
+ ...jobOnlyVars.deploy.secretEnvVarKeys,
6562
+ ].forEach(({ key, kind }) => kinds.set((0, pipeline_1.getSecretVarName)(env, componentName, key), kind !== null && kind !== void 0 ? kind : "secret"));
6563
+ }
6564
+ const secrets = writes.flatMap(({ env, componentName, secrets }) => Object.entries(secrets).map(([key, value]) => {
6565
+ var _a;
6566
+ const name = (0, pipeline_1.getSecretVarName)(env, componentName, key);
6567
+ return {
6568
+ name,
6569
+ value: typeof value === "string" ? value : JSON.stringify(value !== null && value !== void 0 ? value : ""),
6570
+ kind: (_a = kinds.get(name)) !== null && _a !== void 0 ? _a : "secret",
6571
+ };
6572
+ }));
6573
+ if (secrets.length === 0) {
6574
+ return;
6575
+ }
6576
+ if (!(await (0, github_1.isGhAuthenticated)())) {
6577
+ io.log("⚠️ github pipeline is enabled, but the github cli (gh) is not authenticated — run `project secrets-sync-github` later to mirror the secrets");
6578
+ return;
6579
+ }
6580
+ const repo = await (0, github_2.getConfiguredGithubRepo)();
6581
+ if (!repo) {
6582
+ io.log("⚠️ github pipeline is enabled, but no github repository was found — run `project secrets-sync-github` later to mirror the secrets");
6583
+ return;
6584
+ }
6585
+ io.log(`mirroring ${secrets.length} secrets to github ('${repo}')...`);
6586
+ await (0, github_2.pushSecretsToGithub)(io, repo, secrets, await (0, github_2.collectSecretTargets)(config));
6352
6587
  };
6353
- exports.startKubePortForward = startKubePortForward;
6354
- //# sourceMappingURL=portForward.js.map
6355
-
6356
- /***/ }),
6357
-
6358
- /***/ 55689:
6359
- /***/ ((__unused_webpack_module, exports) => {
6360
-
6361
- "use strict";
6362
-
6363
- // we can't import the package.json directly, because the bundling process will inline it
6364
- // but the version is only set during deployement, when its already bundled
6365
- // instead we need to use require.main.path to get the path of the main module, which is actually the file in the bin
6366
- // this will point to the root package.json
6367
- var _a;
6368
- Object.defineProperty(exports, "__esModule", ({ value: true }));
6369
- const path = ((_a = require.main) === null || _a === void 0 ? void 0 : _a.path) + "/../package.json";
6370
- const ownPkg = require(path);
6371
- exports["default"] = ownPkg;
6372
- //# sourceMappingURL=packageInfos.js.map
6588
+ //# sourceMappingURL=writeSecrets.js.map
6373
6589
 
6374
6590
  /***/ }),
6375
6591
 
@@ -6910,7 +7126,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
6910
7126
  return (mod && mod.__esModule) ? mod : { "default": mod };
6911
7127
  };
6912
7128
  Object.defineProperty(exports, "__esModule", ({ value: true }));
6913
- exports.upsertAllVariables = exports.clearBackups = exports.getVariableValueByRawName = exports.getAllVariables = exports.getProjectInfo = exports.doGitlabRequestAllPages = exports.doGitlabRequest = exports.getGitlabToken = exports.setupGitlabToken = exports.hasGitlabToken = void 0;
7129
+ exports.upsertAllVariables = exports.clearBackups = exports.getVariableValueByRawName = exports.getAllVariables = exports.getProjectInfo = exports.doGitlabRequestAllPages = exports.doGitlabRequest = exports.GitlabNotFoundError = exports.getGitlabToken = exports.setupGitlabToken = exports.hasGitlabToken = void 0;
6914
7130
  const pipeline_1 = __nccwpck_require__(53695);
6915
7131
  const lodash_es_1 = __nccwpck_require__(50210);
6916
7132
  const memoizee_1 = __importDefault(__nccwpck_require__(6575));
@@ -6962,6 +7178,20 @@ const getGitlabToken = async (io) => {
6962
7178
  return (0, preferences_1.getPreference)(TOKEN_KEY);
6963
7179
  };
6964
7180
  exports.getGitlabToken = getGitlabToken;
7181
+ /**
7182
+ * a gitlab api call that came back 404. Carries the path so a caller
7183
+ * that wants to treat "does not exist yet" as normal can say so
7184
+ * (`instanceof`) instead of matching on a message.
7185
+ */
7186
+ class GitlabNotFoundError extends Error {
7187
+ constructor(path, host) {
7188
+ super(`gitlab api: ${path} not found on ${host}`);
7189
+ this.path = path;
7190
+ this.host = host;
7191
+ this.name = "GitlabNotFoundError";
7192
+ }
7193
+ }
7194
+ exports.GitlabNotFoundError = GitlabNotFoundError;
6965
7195
  const doGitlabRequest = async (io, path, data = undefined, method = "GET") => {
6966
7196
  const [rootToken, { gitRemoteHost }] = await Promise.all([
6967
7197
  (0, exports.getGitlabToken)(io),
@@ -6983,7 +7213,7 @@ const doGitlabRequest = async (io, path, data = undefined, method = "GET") => {
6983
7213
  return null;
6984
7214
  }
6985
7215
  if (result.status === 404) {
6986
- throw new Error("not found");
7216
+ throw new GitlabNotFoundError(path, gitRemoteHost);
6987
7217
  }
6988
7218
  throw new Error(`Could not send request to gitlab api ${path}: ${result.status} "${result.statusText}".\nResponse: ${JSON.stringify(await result.json(), null, 2)}`);
6989
7219
  };
@@ -7009,9 +7239,20 @@ const doGitlabRequestAllPages = async (io, path) => {
7009
7239
  };
7010
7240
  exports.doGitlabRequestAllPages = doGitlabRequestAllPages;
7011
7241
  const getProjectInfo = async (io) => {
7012
- const { gitRemotePath } = await (0, gitProjectInformation_1.getGitRemoteHostAndPath)();
7013
- const project = await (0, exports.doGitlabRequest)(io, `projects/${encodeURIComponent(gitRemotePath)}`);
7014
- return project;
7242
+ const { gitRemoteHost, gitRemotePath } = await (0, gitProjectInformation_1.getGitRemoteHostAndPath)();
7243
+ try {
7244
+ return await (0, exports.doGitlabRequest)(io, `projects/${encodeURIComponent(gitRemotePath)}`);
7245
+ }
7246
+ catch (e) {
7247
+ if (e instanceof GitlabNotFoundError) {
7248
+ // the classic: the remote is not a gitlab at all (a github one,
7249
+ // say), so the request goes to a host without /api/v4 and every
7250
+ // path answers 404
7251
+ throw new Error(`no gitlab project '${gitRemotePath}' on '${gitRemoteHost}'. ` +
7252
+ "Check the git remote and `pipelines` in catladder.ts — this command talks to the gitlab api.");
7253
+ }
7254
+ throw e;
7255
+ }
7015
7256
  };
7016
7257
  exports.getProjectInfo = getProjectInfo;
7017
7258
  exports.getAllVariables = (0, memoizee_1.default)(async (io, n = 5) => {
@@ -7566,7 +7807,14 @@ class BitwardenVault {
7566
7807
  }
7567
7808
  return secrets;
7568
7809
  }
7810
+ /**
7811
+ * upserts into the item's yaml note. The note is rewritten as a
7812
+ * whole, so the values that are not part of this write have to be
7813
+ * carried over — a single-key write (`secrets-set`, a credential
7814
+ * provisioned by `project setup`) must not drop the rest.
7815
+ */
7569
7816
  async writeSecrets(io, env, componentName, secrets) {
7817
+ var _a;
7570
7818
  const { config, collectionName } = this;
7571
7819
  const bwSession = await this.session(io);
7572
7820
  const collection = await findCollection(bwSession, collectionName);
@@ -7579,7 +7827,10 @@ class BitwardenVault {
7579
7827
  "--search",
7580
7828
  name,
7581
7829
  ])).find((item) => item.name === name);
7582
- const notes = (0, yaml_1.stringify)(secrets);
7830
+ const previous = (existing === null || existing === void 0 ? void 0 : existing.notes)
7831
+ ? ((_a = (0, yaml_1.parse)(existing.notes)) !== null && _a !== void 0 ? _a : {})
7832
+ : {};
7833
+ const notes = (0, yaml_1.stringify)({ ...previous, ...secrets });
7583
7834
  if (existing) {
7584
7835
  await (0, bw_1.runBw)(bwSession, [
7585
7836
  "edit",
@@ -7633,8 +7884,9 @@ class GitlabVault {
7633
7884
  const variables = await (0, gitlab_1.getAllVariables)(io);
7634
7885
  return Object.fromEntries(variables.map((v) => [v.key, v.value]));
7635
7886
  }
7636
- async writeSecrets(io, env, componentName, secrets) {
7637
- await (0, gitlab_1.upsertAllVariables)(io, secrets, env, componentName);
7887
+ async writeSecrets(io, env, componentName, secrets, options = {}) {
7888
+ var _a;
7889
+ await (0, gitlab_1.upsertAllVariables)(io, secrets, env, componentName, (_a = options.backup) !== null && _a !== void 0 ? _a : true);
7638
7890
  }
7639
7891
  }
7640
7892
  exports.GitlabVault = GitlabVault;
@@ -7720,11 +7972,11 @@ class VaultManager {
7720
7972
  * writes secrets of one env/component to the vault and updates the
7721
7973
  * local cache (write-through, "local" env only)
7722
7974
  */
7723
- async writeSecrets(env, componentName, secrets, io = this.io) {
7975
+ async writeSecrets(env, componentName, secrets, io = this.io, options = {}) {
7724
7976
  if (!io) {
7725
7977
  throw new Error("writing to the vault requires an interactive context");
7726
7978
  }
7727
- await this.vault.writeSecrets(io, env, componentName, secrets);
7979
+ await this.vault.writeSecrets(io, env, componentName, secrets, options);
7728
7980
  if (env === CACHED_ENV) {
7729
7981
  await (0, cache_1.updateSecretsCache)(this.vault.id, env, componentName, Object.fromEntries(Object.entries(secrets).map(([key, value]) => [
7730
7982
  key,
@@ -8723,6 +8975,43 @@ exports.getAgentSkillTargets = getAgentSkillTargets;
8723
8975
 
8724
8976
  /***/ }),
8725
8977
 
8978
+ /***/ 72792:
8979
+ /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => {
8980
+
8981
+ "use strict";
8982
+
8983
+ Object.defineProperty(exports, "__esModule", ({ value: true }));
8984
+ exports.getPinLabelRegexSource = exports.getAutoStopConfig = exports.DEFAULT_PIN_LABEL = exports.DEFAULT_DEV_AUTO_STOP = exports.DEFAULT_REVIEW_AUTO_STOP = exports.REVIEW_AUTO_STOP_VARIABLE = void 0;
8985
+ const lodash_es_1 = __nccwpck_require__(50210);
8986
+ /**
8987
+ * pipeline-level variable holding the `auto_stop_in` of review
8988
+ * environments. Review deploy jobs reference it instead of a literal,
8989
+ * so the pin label's workflow rule can override it to `never` for the
8990
+ * whole pipeline (all components at once).
8991
+ */
8992
+ exports.REVIEW_AUTO_STOP_VARIABLE = "CL_REVIEW_AUTO_STOP";
8993
+ exports.DEFAULT_REVIEW_AUTO_STOP = "1 week";
8994
+ exports.DEFAULT_DEV_AUTO_STOP = "4 weeks";
8995
+ exports.DEFAULT_PIN_LABEL = "catladder::pin-review";
8996
+ const getAutoStopConfig = (config) => {
8997
+ var _a, _b, _c, _d, _e, _f;
8998
+ return ({
8999
+ review: (_b = (_a = config.autoStop) === null || _a === void 0 ? void 0 : _a.review) !== null && _b !== void 0 ? _b : exports.DEFAULT_REVIEW_AUTO_STOP,
9000
+ dev: (_d = (_c = config.autoStop) === null || _c === void 0 ? void 0 : _c.dev) !== null && _d !== void 0 ? _d : exports.DEFAULT_DEV_AUTO_STOP,
9001
+ pinLabel: (_f = (_e = config.autoStop) === null || _e === void 0 ? void 0 : _e.pinLabel) !== null && _f !== void 0 ? _f : exports.DEFAULT_PIN_LABEL,
9002
+ });
9003
+ };
9004
+ exports.getAutoStopConfig = getAutoStopConfig;
9005
+ /**
9006
+ * matches the pin label inside `$CI_MERGE_REQUEST_LABELS`
9007
+ * (comma-separated) — anchored so `pin` never matches `no-pin`
9008
+ */
9009
+ const getPinLabelRegexSource = (pinLabel) => `(^|,)${(0, lodash_es_1.escapeRegExp)(pinLabel).replaceAll("/", "\\/")}(,|$)`;
9010
+ exports.getPinLabelRegexSource = getPinLabelRegexSource;
9011
+ //# sourceMappingURL=autoStop.js.map
9012
+
9013
+ /***/ }),
9014
+
8726
9015
  /***/ 48476:
8727
9016
  /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => {
8728
9017
 
@@ -10101,6 +10390,7 @@ exports.GitlabBackend = void 0;
10101
10390
  const promises_1 = __nccwpck_require__(91943);
10102
10391
  const rules_1 = __nccwpck_require__(78503);
10103
10392
  const config_1 = __nccwpck_require__(75591);
10393
+ const autoStop_1 = __nccwpck_require__(72792);
10104
10394
  const createAllJobs_1 = __nccwpck_require__(83477);
10105
10395
  const jobImagesPlan_1 = __nccwpck_require__(59623);
10106
10396
  const shippedCatci_1 = __nccwpck_require__(27967);
@@ -10286,6 +10576,7 @@ class GitlabBackend {
10286
10576
  // per-pipeline-type variables (pipelines.gitlab.runnerVariables)
10287
10577
  ...(0, index_1.getPipelineOptions)(config, this.type).runnerVariables,
10288
10578
  },
10579
+ autoStop: (0, autoStop_1.getAutoStopConfig)(config),
10289
10580
  });
10290
10581
  }
10291
10582
  }
@@ -10730,6 +11021,7 @@ function getGitlabNeedsForWorkspaceJob(context, { needs }, allJobs) {
10730
11021
  Object.defineProperty(exports, "__esModule", ({ value: true }));
10731
11022
  exports.createGitlabPipelineWithDefaults = void 0;
10732
11023
  const bash_1 = __nccwpck_require__(94143);
11024
+ const autoStop_1 = __nccwpck_require__(72792);
10733
11025
  const rules_1 = __nccwpck_require__(78503);
10734
11026
  /**
10735
11027
  * gitlab requires a top-level default image for jobs that don't set their
@@ -10738,7 +11030,8 @@ const rules_1 = __nccwpck_require__(78503);
10738
11030
  * build-free image (a catladder job image would drag in a build job).
10739
11031
  */
10740
11032
  const DEFAULT_PIPELINE_IMAGE = "node:22";
10741
- const createGitlabPipelineWithDefaults = ({ image, variables, before_script, ...config }) => {
11033
+ const createGitlabPipelineWithDefaults = ({ image, variables, before_script, autoStop, ...config }) => {
11034
+ const pinLabel = autoStop && autoStop.pinLabel;
10742
11035
  return {
10743
11036
  image: image !== null && image !== void 0 ? image : DEFAULT_PIPELINE_IMAGE,
10744
11037
  variables: {
@@ -10747,6 +11040,10 @@ const createGitlabPipelineWithDefaults = ({ image, variables, before_script, ...
10747
11040
  CACHE_COMPRESSION_LEVEL: "fast", // same as above, but for caches
10748
11041
  TRANSFER_METER_FREQUENCY: "5s", // how often we should update the transfer meter for cache upload/download
10749
11042
  GIT_DEPTH: "1", // no need the full depth
11043
+ // review env lifetime — the pin-label workflow rule below
11044
+ // overrides this to "never" (workflow rule variables take
11045
+ // precedence over pipeline-level variables)
11046
+ ...(pinLabel ? { [autoStop_1.REVIEW_AUTO_STOP_VARIABLE]: autoStop.review } : {}),
10750
11047
  ...(variables !== null && variables !== void 0 ? variables : {}),
10751
11048
  },
10752
11049
  before_script: [
@@ -10763,6 +11060,20 @@ const createGitlabPipelineWithDefaults = ({ image, variables, before_script, ...
10763
11060
  PIPELINE_NAME: "Thinking...",
10764
11061
  },
10765
11062
  },
11063
+ // pinned MRs (rules stop at the first match, so this must come
11064
+ // before the plain merge-request rule)
11065
+ ...(pinLabel
11066
+ ? [
11067
+ {
11068
+ if: `${rules_1.RULE_IS_MERGE_REQUEST.if} && $CI_MERGE_REQUEST_LABELS =~ /${(0, autoStop_1.getPinLabelRegexSource)(pinLabel)}/`,
11069
+ variables: {
11070
+ PIPELINE_ICON: "🐱📌",
11071
+ PIPELINE_NAME: "mr$CI_MERGE_REQUEST_IID - $CI_MERGE_REQUEST_TITLE",
11072
+ [autoStop_1.REVIEW_AUTO_STOP_VARIABLE]: "never",
11073
+ },
11074
+ },
11075
+ ]
11076
+ : []),
10766
11077
  {
10767
11078
  if: rules_1.RULE_IS_MERGE_REQUEST.if,
10768
11079
  variables: {
@@ -14477,6 +14788,7 @@ const docker_1 = __nccwpck_require__(12354);
14477
14788
  const context_1 = __nccwpck_require__(73938);
14478
14789
  const jobs_1 = __nccwpck_require__(98521);
14479
14790
  const utils_1 = __nccwpck_require__(71286);
14791
+ const autoStop_1 = __nccwpck_require__(72792);
14480
14792
  const stop_1 = __nccwpck_require__(35937);
14481
14793
  const variables_1 = __nccwpck_require__(54768);
14482
14794
  exports.DEPLOY_JOB_NAME = "🚀 Deploy";
@@ -14485,10 +14797,15 @@ class DeployJob extends jobs_1.CatladderJob {
14485
14797
  var _a, _b, _c, _d, _e, _f, _g;
14486
14798
  const hasDocker = (0, docker_1.requiresDockerBuild)(context);
14487
14799
  const isStoppable = (0, utils_1.contextIsStoppable)(context);
14800
+ const autoStopConfig = (0, autoStop_1.getAutoStopConfig)(context.fullConfig);
14488
14801
  const autoStop = context.environment.envType === "review"
14489
- ? "1 week"
14802
+ ? // review envs read the lifetime from a pipeline variable, so
14803
+ // the pin label's workflow rule can override it to "never"
14804
+ autoStopConfig.pinLabel === false
14805
+ ? autoStopConfig.review
14806
+ : `$${autoStop_1.REVIEW_AUTO_STOP_VARIABLE}`
14490
14807
  : context.environment.envType === "dev"
14491
- ? "4 weeks"
14808
+ ? autoStopConfig.dev
14492
14809
  : undefined;
14493
14810
  const deployConfig = (_a = context.deploy) === null || _a === void 0 ? void 0 : _a.config;
14494
14811
  // if auto or manual is configured explicitly, use that
@@ -17317,6 +17634,7 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
17317
17634
  };
17318
17635
  Object.defineProperty(exports, "__esModule", ({ value: true }));
17319
17636
  __exportStar(__nccwpck_require__(46734), exports);
17637
+ __exportStar(__nccwpck_require__(72792), exports);
17320
17638
  __exportStar(__nccwpck_require__(62853), exports);
17321
17639
  __exportStar(__nccwpck_require__(63557), exports);
17322
17640
  __exportStar(__nccwpck_require__(78503), exports);