@catladder/cli 5.2.0 → 5.3.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 (88) hide show
  1. package/dist/apps/cli/src/commands/project/commandPortForward.js +1 -1
  2. package/dist/apps/cli/src/commands/project/commandPortForward.js.map +1 -1
  3. package/dist/apps/cli/src/commands/project/secrets/scope.js +25 -2
  4. package/dist/apps/cli/src/commands/project/secrets/scope.js.map +1 -1
  5. package/dist/apps/cli/src/config/getProjectConfig.js +4 -1
  6. package/dist/apps/cli/src/config/getProjectConfig.js.map +1 -1
  7. package/dist/apps/cli/src/secrets/github.js +4 -2
  8. package/dist/apps/cli/src/secrets/github.js.map +1 -1
  9. package/dist/bundles/catenv/index.js +390 -106
  10. package/dist/bundles/cli/index.js +420 -111
  11. package/dist/bundles/skills/catladder-config/SKILL.md +34 -2
  12. package/dist/bundles/skills/catladder-deploys/SKILL.md +5 -0
  13. package/dist/bundles/skills/catladder-pipelines/SKILL.md +10 -1
  14. package/dist/bundles/skills/catladder-secrets/SKILL.md +6 -0
  15. package/dist/packages/pipeline/src/backends/github/GithubBackend.js +23 -3
  16. package/dist/packages/pipeline/src/backends/github/GithubBackend.js.map +1 -1
  17. package/dist/packages/pipeline/src/backends/github/createGithubJobs.js +2 -2
  18. package/dist/packages/pipeline/src/backends/github/createGithubJobs.js.map +1 -1
  19. package/dist/packages/pipeline/src/backends/gitlab/GitlabBackend.js +18 -2
  20. package/dist/packages/pipeline/src/backends/gitlab/GitlabBackend.js.map +1 -1
  21. package/dist/packages/pipeline/src/backends/gitlab/createGitlabJobs.js +2 -2
  22. package/dist/packages/pipeline/src/backends/gitlab/createGitlabJobs.js.map +1 -1
  23. package/dist/packages/pipeline/src/backends/gitlab/createGitlabPipeline.d.ts +6 -1
  24. package/dist/packages/pipeline/src/backends/gitlab/createGitlabPipeline.js +8 -1
  25. package/dist/packages/pipeline/src/backends/gitlab/createGitlabPipeline.js.map +1 -1
  26. package/dist/packages/pipeline/src/config/configruedEnvs.d.ts +9 -2
  27. package/dist/packages/pipeline/src/config/configruedEnvs.js +54 -5
  28. package/dist/packages/pipeline/src/config/configruedEnvs.js.map +1 -1
  29. package/dist/packages/pipeline/src/context/createAllComponentsContext.d.ts +2 -2
  30. package/dist/packages/pipeline/src/context/createComponentContext.d.ts +2 -2
  31. package/dist/packages/pipeline/src/context/createWorkspaceContext.d.ts +2 -2
  32. package/dist/packages/pipeline/src/context/getEnvConfig.js +35 -19
  33. package/dist/packages/pipeline/src/context/getEnvConfig.js.map +1 -1
  34. package/dist/packages/pipeline/src/context/getEnvInherit.d.ts +20 -0
  35. package/dist/packages/pipeline/src/context/getEnvInherit.js +72 -0
  36. package/dist/packages/pipeline/src/context/getEnvInherit.js.map +1 -0
  37. package/dist/packages/pipeline/src/context/getEnvInstance.d.ts +4 -0
  38. package/dist/packages/pipeline/src/context/{getReviewSlug.js → getEnvInstance.js} +13 -9
  39. package/dist/packages/pipeline/src/context/getEnvInstance.js.map +1 -0
  40. package/dist/packages/pipeline/src/context/getEnvOn.d.ts +13 -0
  41. package/dist/packages/pipeline/src/context/getEnvOn.js +28 -0
  42. package/dist/packages/pipeline/src/context/getEnvOn.js.map +1 -0
  43. package/dist/packages/pipeline/src/context/getEnvType.d.ts +8 -2
  44. package/dist/packages/pipeline/src/context/getEnvType.js +27 -5
  45. package/dist/packages/pipeline/src/context/getEnvType.js.map +1 -1
  46. package/dist/packages/pipeline/src/context/getEnvironment.js +3 -0
  47. package/dist/packages/pipeline/src/context/getEnvironment.js.map +1 -1
  48. package/dist/packages/pipeline/src/context/getEnvironmentContext.js +18 -8
  49. package/dist/packages/pipeline/src/context/getEnvironmentContext.js.map +1 -1
  50. package/dist/packages/pipeline/src/context/getEnvironmentVariables.d.ts +1 -1
  51. package/dist/packages/pipeline/src/context/getEnvironmentVariables.js +4 -4
  52. package/dist/packages/pipeline/src/context/getEnvironmentVariables.js.map +1 -1
  53. package/dist/packages/pipeline/src/context/index.d.ts +1 -0
  54. package/dist/packages/pipeline/src/context/index.js +1 -0
  55. package/dist/packages/pipeline/src/context/index.js.map +1 -1
  56. package/dist/packages/pipeline/src/deploy/base/deploy.js +11 -1
  57. package/dist/packages/pipeline/src/deploy/base/deploy.js.map +1 -1
  58. package/dist/packages/pipeline/src/deploy/cloudRun/artifactsRegistry.js +5 -4
  59. package/dist/packages/pipeline/src/deploy/cloudRun/artifactsRegistry.js.map +1 -1
  60. package/dist/packages/pipeline/src/deploy/cloudRun/cleanup.js +8 -2
  61. package/dist/packages/pipeline/src/deploy/cloudRun/cleanup.js.map +1 -1
  62. package/dist/packages/pipeline/src/deploy/cloudRun/index.js +2 -2
  63. package/dist/packages/pipeline/src/deploy/cloudRun/index.js.map +1 -1
  64. package/dist/packages/pipeline/src/deploy/cloudRun/utils/getServiceName.js +2 -2
  65. package/dist/packages/pipeline/src/deploy/cloudRun/utils/getServiceName.js.map +1 -1
  66. package/dist/packages/pipeline/src/deploy/kubernetes/index.js +3 -3
  67. package/dist/packages/pipeline/src/deploy/kubernetes/index.js.map +1 -1
  68. package/dist/packages/pipeline/src/deploy/pages/deployJob.js +3 -2
  69. package/dist/packages/pipeline/src/deploy/pages/deployJob.js.map +1 -1
  70. package/dist/packages/pipeline/src/deploy/types/googleCloudRun.d.ts +9 -0
  71. package/dist/packages/pipeline/src/types/config.d.ts +126 -9
  72. package/dist/packages/pipeline/src/types/config.js +11 -10
  73. package/dist/packages/pipeline/src/types/config.js.map +1 -1
  74. package/dist/packages/pipeline/src/types/context.d.ts +22 -3
  75. package/dist/packages/pipeline/src/types/context.js.map +1 -1
  76. package/dist/packages/pipeline/src/types/environmentContext.d.ts +29 -0
  77. package/dist/skills/catladder-config/SKILL.md +34 -2
  78. package/dist/skills/catladder-deploys/SKILL.md +5 -0
  79. package/dist/skills/catladder-pipelines/SKILL.md +10 -1
  80. package/dist/skills/catladder-secrets/SKILL.md +6 -0
  81. package/dist/tsconfig.tsbuildinfo +1 -1
  82. package/package.json +1 -1
  83. package/src/commands/project/commandPortForward.ts +1 -1
  84. package/src/commands/project/secrets/scope.ts +38 -2
  85. package/src/config/getProjectConfig.ts +9 -1
  86. package/src/secrets/github.ts +8 -5
  87. package/dist/packages/pipeline/src/context/getReviewSlug.d.ts +0 -4
  88. package/dist/packages/pipeline/src/context/getReviewSlug.js.map +0 -1
@@ -3939,7 +3939,7 @@ exports.commandPortForward = (0, defineCommand_1.defineCommand)({
3939
3939
  }
3940
3940
  const { fullName } = context.environment;
3941
3941
  let serviceName = fullName.toString();
3942
- if (context.environment.envType === "review") {
3942
+ if (context.environment.instance.type === "review") {
3943
3943
  const mr = await ctx.get("mr");
3944
3944
  serviceName = serviceName
3945
3945
  .toString()
@@ -5111,7 +5111,23 @@ exports.parseKeysInput = parseKeysInput;
5111
5111
 
5112
5112
  Object.defineProperty(exports, "__esModule", ({ value: true }));
5113
5113
  exports.formatScope = exports.getScopedSecretKeys = exports.getDeclaredSecretKeys = exports.secretsScopeChoices = exports.resolveSecretsScope = void 0;
5114
+ const pipeline_1 = __nccwpck_require__(53695);
5114
5115
  const getProjectConfig_1 = __nccwpck_require__(70478);
5116
+ /**
5117
+ * envs that share another env's secrets (`environments.<name>.inherit`)
5118
+ * have no secret store of their own — the secrets commands operate on
5119
+ * the source env instead
5120
+ */
5121
+ const withoutSharedSecretEnvs = async (all) => {
5122
+ const config = await (0, getProjectConfig_1.getProjectConfig)();
5123
+ const environments = config === null || config === void 0 ? void 0 : config.environments;
5124
+ return Object.fromEntries(Object.entries(all)
5125
+ .map(([componentName, envs]) => [
5126
+ componentName,
5127
+ envs.filter((env) => (0, pipeline_1.getSecretsEnv)(environments, env) === env),
5128
+ ])
5129
+ .filter(([, envs]) => envs.length > 0));
5130
+ };
5115
5131
  /**
5116
5132
  * resolves a scope expression to component → envs:
5117
5133
  * - (empty): all components, all envs
@@ -5120,7 +5136,7 @@ const getProjectConfig_1 = __nccwpck_require__(70478);
5120
5136
  * - `:component`: every env of one component
5121
5137
  */
5122
5138
  const resolveSecretsScope = async (scope) => {
5123
- const all = await (0, getProjectConfig_1.getAllComponentsWithAllEnvsHierarchical)();
5139
+ const all = await withoutSharedSecretEnvs(await (0, getProjectConfig_1.getAllComponentsWithAllEnvsHierarchical)());
5124
5140
  if (Object.keys(all).length === 0) {
5125
5141
  throw new Error("no catladder config found");
5126
5142
  }
@@ -5128,6 +5144,13 @@ const resolveSecretsScope = async (scope) => {
5128
5144
  return all;
5129
5145
  }
5130
5146
  const [env, componentName] = scope.split(":").map((x) => x || null);
5147
+ if (env) {
5148
+ const config = await (0, getProjectConfig_1.getProjectConfig)();
5149
+ const secretsEnv = (0, pipeline_1.getSecretsEnv)(config === null || config === void 0 ? void 0 : config.environments, env);
5150
+ if (secretsEnv !== env) {
5151
+ throw new Error(`environment "${env}" shares its secrets with "${secretsEnv}" (environments.${env}.inherit) — manage them via "${secretsEnv}"`);
5152
+ }
5153
+ }
5131
5154
  if (componentName && !(componentName in all)) {
5132
5155
  throw new Error(`unknown component "${componentName}" — components: ${Object.keys(all).join(", ")}`);
5133
5156
  }
@@ -5151,7 +5174,7 @@ exports.resolveSecretsScope = resolveSecretsScope;
5151
5174
  * (one component, all envs) forms
5152
5175
  */
5153
5176
  const secretsScopeChoices = async () => {
5154
- const all = await (0, getProjectConfig_1.getAllComponentsWithAllEnvsHierarchical)();
5177
+ const all = await withoutSharedSecretEnvs(await (0, getProjectConfig_1.getAllComponentsWithAllEnvsHierarchical)());
5155
5178
  const pairs = Object.entries(all).flatMap(([componentName, envs]) => envs.map((env) => `${env}:${componentName}`));
5156
5179
  const envs = [...new Set(Object.values(all).flat())];
5157
5180
  return [
@@ -5742,7 +5765,10 @@ const getEnvironment = async (env, componentName) => {
5742
5765
  };
5743
5766
  exports.getEnvironment = getEnvironment;
5744
5767
  const getGitlabVar = async (io, env, componentName, variableName) => {
5745
- const rawVariableName = (0, pipeline_1.getSecretVarName)(env, componentName, variableName);
5768
+ const config = await (0, exports.getProjectConfig)();
5769
+ // an env sharing another env's secrets (inherit) reads that env's variables
5770
+ const secretsEnv = (0, pipeline_1.getSecretsEnv)(config === null || config === void 0 ? void 0 : config.environments, env);
5771
+ const rawVariableName = (0, pipeline_1.getSecretVarName)(secretsEnv, componentName, variableName);
5746
5772
  return await (0, gitlab_1.getVariableValueByRawName)(io, rawVariableName);
5747
5773
  };
5748
5774
  exports.getGitlabVar = getGitlabVar;
@@ -6526,13 +6552,15 @@ const collectSecretsFromVault = async (io, envFilter) => {
6526
6552
  if (envFilter && !envFilter.includes(env)) {
6527
6553
  continue;
6528
6554
  }
6529
- const { secretEnvVarKeys, jobOnlyVars } = await (0, getProjectConfig_1.getEnvironment)(env, componentName);
6555
+ const { secretEnvVarKeys, jobOnlyVars, secretsEnv } = await (0, getProjectConfig_1.getEnvironment)(env, componentName);
6530
6556
  const keys = [
6531
6557
  ...secretEnvVarKeys,
6532
6558
  ...jobOnlyVars.build.secretEnvVarKeys,
6533
6559
  ...jobOnlyVars.deploy.secretEnvVarKeys,
6534
6560
  ];
6535
- keys.forEach(({ key, kind }) => kinds.set((0, pipeline_1.getSecretVarName)(env, componentName, key), kind !== null && kind !== void 0 ? kind : "secret"));
6561
+ // secretsEnv: an env sharing another env's secrets (inherit)
6562
+ // references that env's variable names
6563
+ keys.forEach(({ key, kind }) => kinds.set((0, pipeline_1.getSecretVarName)(secretsEnv !== null && secretsEnv !== void 0 ? secretsEnv : env, componentName, key), kind !== null && kind !== void 0 ? kind : "secret"));
6536
6564
  }
6537
6565
  }
6538
6566
  const names = [...kinds.keys()];
@@ -9193,6 +9221,7 @@ const promises_1 = __nccwpck_require__(91943);
9193
9221
  const path_1 = __nccwpck_require__(16928);
9194
9222
  const createAllJobs_1 = __nccwpck_require__(83477);
9195
9223
  const config_1 = __nccwpck_require__(75591);
9224
+ const configruedEnvs_1 = __nccwpck_require__(38866);
9196
9225
  const ciVariables_1 = __nccwpck_require__(17300);
9197
9226
  const registryImage_1 = __nccwpck_require__(24333);
9198
9227
  const index_1 = __nccwpck_require__(62853);
@@ -9245,6 +9274,12 @@ const TRIGGER_WORKFLOWS = {
9245
9274
  },
9246
9275
  },
9247
9276
  };
9277
+ const getTriggerWorkflow = (trigger) => (0, config_1.isBranchTrigger)(trigger)
9278
+ ? {
9279
+ name: `🛠️ catladder ${trigger.branch}`,
9280
+ on: { push: { branches: [trigger.branch] } },
9281
+ }
9282
+ : TRIGGER_WORKFLOWS[trigger];
9248
9283
  class GithubBackend {
9249
9284
  constructor() {
9250
9285
  this.type = "github";
@@ -9305,7 +9340,13 @@ class GithubBackend {
9305
9340
  stop: { jobs: {}, conditions: {}, options: [], envTypes: [] },
9306
9341
  rollback: { jobs: {}, conditions: {}, options: [], envTypes: [] },
9307
9342
  };
9308
- for (const trigger of config_1.ALL_PIPELINE_TRIGGERS) {
9343
+ // branch triggers (envs with `on: { branch: ... }`) each get their
9344
+ // own push-triggered workflow next to the built-in ones
9345
+ const allTriggers = [
9346
+ ...config_1.ALL_PIPELINE_TRIGGERS,
9347
+ ...(0, configruedEnvs_1.getConfiguredBranchTriggers)(config),
9348
+ ];
9349
+ for (const trigger of allTriggers) {
9309
9350
  const allJobs = await (0, createAllJobs_1.createAllJobs)({
9310
9351
  config,
9311
9352
  trigger,
@@ -9432,7 +9473,7 @@ class GithubBackend {
9432
9473
  if (Object.keys(jobs).length > 0) {
9433
9474
  const workflowJobs = { ...(0, ensureImageJobs_1.makeEnsureImageGithubJobs)(images), ...jobs };
9434
9475
  workflows[`${GENERATED_FILE_PREFIX}${workflowFileName(trigger)}`] = {
9435
- ...TRIGGER_WORKFLOWS[trigger],
9476
+ ...getTriggerWorkflow(trigger),
9436
9477
  ...workflowPermissions(images),
9437
9478
  env: workflowEnv,
9438
9479
  // the review workflow additionally gets the aggregate job —
@@ -9532,6 +9573,11 @@ const workflowPermissions = (_images) => ({
9532
9573
  permissions: { contents: "read", packages: "write" },
9533
9574
  });
9534
9575
  const workflowFileName = (trigger) => {
9576
+ if ((0, config_1.isBranchTrigger)(trigger)) {
9577
+ // branch names may contain characters that don't belong in a file
9578
+ // name (e.g. "release/next")
9579
+ return `branch-${trigger.branch.replace(/[^a-zA-Z0-9]+/g, "-")}.yml`;
9580
+ }
9535
9581
  switch (trigger) {
9536
9582
  case "mr":
9537
9583
  return "review.yml";
@@ -9550,7 +9596,9 @@ const isReviewStopJob = (context, job) => {
9550
9596
  var _a;
9551
9597
  return ((_a = job.environment) === null || _a === void 0 ? void 0 : _a.action) === "stop" &&
9552
9598
  context.type === "component" &&
9553
- context.environment.envType === "review";
9599
+ // a review app (one instance per pull request) is torn down when its
9600
+ // pull request closes; stable envs get dispatch-triggered stop jobs
9601
+ context.environment.instance.type === "review";
9554
9602
  };
9555
9603
  /**
9556
9604
  * stop/rollback jobs of non-review environments have no automatic
@@ -9881,7 +9929,7 @@ const collectSecretKinds = (allJobs) => {
9881
9929
  ...((_a = context.environment.jobOnlyVars.build.secretEnvVarKeys) !== null && _a !== void 0 ? _a : []),
9882
9930
  ...((_b = context.environment.jobOnlyVars.deploy.secretEnvVarKeys) !== null && _b !== void 0 ? _b : []),
9883
9931
  ];
9884
- entries.forEach(({ key, kind }) => kinds.set((0, getEnvironmentVariables_1.getSecretVarName)(context.env, context.name, key), kind !== null && kind !== void 0 ? kind : "secret"));
9932
+ entries.forEach(({ key, kind }) => kinds.set((0, getEnvironmentVariables_1.getSecretVarName)(context.environment.secretsEnv, context.name, key), kind !== null && kind !== void 0 ? kind : "secret"));
9885
9933
  });
9886
9934
  return kinds;
9887
9935
  };
@@ -9894,7 +9942,7 @@ const getSecretsEnv = (context, kinds) => {
9894
9942
  ...((_b = context.environment.jobOnlyVars.deploy.secretEnvVarKeys) !== null && _b !== void 0 ? _b : []),
9895
9943
  ];
9896
9944
  return Object.fromEntries(keys.map(({ key }) => {
9897
- const name = (0, getEnvironmentVariables_1.getSecretVarName)(context.env, context.name, key);
9945
+ const name = (0, getEnvironmentVariables_1.getSecretVarName)(context.environment.secretsEnv, context.name, key);
9898
9946
  return [name, vaultValueExpression(name, kinds)];
9899
9947
  }));
9900
9948
  };
@@ -10693,6 +10741,7 @@ exports.GitlabBackend = void 0;
10693
10741
  const promises_1 = __nccwpck_require__(91943);
10694
10742
  const rules_1 = __nccwpck_require__(78503);
10695
10743
  const config_1 = __nccwpck_require__(75591);
10744
+ const configruedEnvs_1 = __nccwpck_require__(38866);
10696
10745
  const autoStop_1 = __nccwpck_require__(72792);
10697
10746
  const createAllJobs_1 = __nccwpck_require__(83477);
10698
10747
  const jobImagesPlan_1 = __nccwpck_require__(59623);
@@ -10800,8 +10849,15 @@ class GitlabBackend {
10800
10849
  // release jobs themselves are created afterwards — they need the
10801
10850
  // main-branch job list for the queued-release executor's needs.
10802
10851
  images.resolveRef({ catladderImage: (0, release_1.getReleaseMethod)(config).image });
10803
- // for all triggers create jobs and add base rules
10804
- const jobsPerTrigger = await Promise.all(config_1.ALL_PIPELINE_TRIGGERS.map(async (trigger) => ({
10852
+ // for all triggers create jobs and add base rules. Branch triggers
10853
+ // (envs with `on: { branch: ... }`) each get their own pipeline next
10854
+ // to the built-in ones.
10855
+ const branchTriggers = (0, configruedEnvs_1.getConfiguredBranchTriggers)(config);
10856
+ const allTriggers = [
10857
+ ...config_1.ALL_PIPELINE_TRIGGERS,
10858
+ ...branchTriggers,
10859
+ ];
10860
+ const jobsPerTrigger = await Promise.all(allTriggers.map(async (trigger) => ({
10805
10861
  trigger,
10806
10862
  jobs: await (0, createGitlabJobs_1.createGitlabJobs)(await (0, createAllJobs_1.createAllJobs)({ config, trigger, pipelineType: this.type }), images, getGitlabRulesForTrigger(trigger)),
10807
10863
  })));
@@ -10879,6 +10935,7 @@ class GitlabBackend {
10879
10935
  // per-pipeline-type variables (pipelines.gitlab.runnerVariables)
10880
10936
  ...(0, index_1.getPipelineOptions)(config, this.type).runnerVariables,
10881
10937
  },
10938
+ branchPipelines: branchTriggers.map(({ branch }) => branch),
10882
10939
  autoStop: (0, autoStop_1.getAutoStopConfig)(config),
10883
10940
  });
10884
10941
  }
@@ -10896,6 +10953,13 @@ function getGitlabRulesForTrigger(trigger) {
10896
10953
  // mainBranch: on push to main branch except it's a release commit
10897
10954
  // mr: on merge request
10898
10955
  // taggedRelease: on tag
10956
+ // { branch }: on push to that specific branch
10957
+ if ((0, config_1.isBranchTrigger)(trigger)) {
10958
+ return [
10959
+ rules_1.RULE_NEVER_ON_AGENT_TRIGGER,
10960
+ { if: `$CI_COMMIT_BRANCH == "${trigger.branch}"` },
10961
+ ];
10962
+ }
10899
10963
  switch (trigger) {
10900
10964
  case "mainBranch":
10901
10965
  return [
@@ -11148,7 +11212,7 @@ const addGitlabEnvironment = (context, catladderJobEnvironment, job, allJobs) =>
11148
11212
  return job;
11149
11213
  }
11150
11214
  const { env, name, environment } = context;
11151
- const { envVars, envType } = environment;
11215
+ const { envVars, instance } = environment;
11152
11216
  const { onStop, autoStopIn, action } = catladderJobEnvironment;
11153
11217
  // those can be dynamic, so we therefore have to do this: https://docs.gitlab.com/ee/ci/environments/#set-a-dynamic-environment-url
11154
11218
  const dotEnvFile = "gitlab_environment.env";
@@ -11165,7 +11229,7 @@ const addGitlabEnvironment = (context, catladderJobEnvironment, job, allJobs) =>
11165
11229
  `echo "${exports.GITLAB_ENVIRONMENT_URL_VARIABLE}=${(0, bash_1.getBashVariable)("ROOT_URL")}" >> ${dotEnvFile}`,
11166
11230
  ];
11167
11231
  // this is NOT a bashVariable since it NEEDS to be used as a string in gitlab
11168
- const gitlabEnvironmentName = envType === "review"
11232
+ const gitlabEnvironmentName = instance.type === "review"
11169
11233
  ? `${env}/$CI_COMMIT_REF_NAME/${name}` // FIXME: should be replaced with mr name as well
11170
11234
  : `${env}/${name}`;
11171
11235
  return {
@@ -11333,7 +11397,7 @@ const rules_1 = __nccwpck_require__(78503);
11333
11397
  * build-free image (a catladder job image would drag in a build job).
11334
11398
  */
11335
11399
  const DEFAULT_PIPELINE_IMAGE = "node:22";
11336
- const createGitlabPipelineWithDefaults = ({ image, variables, before_script, autoStop, ...config }) => {
11400
+ const createGitlabPipelineWithDefaults = ({ image, variables, before_script, branchPipelines = [], autoStop, ...config }) => {
11337
11401
  const pinLabel = autoStop && autoStop.pinLabel;
11338
11402
  return {
11339
11403
  image: image !== null && image !== void 0 ? image : DEFAULT_PIPELINE_IMAGE,
@@ -11414,6 +11478,13 @@ const createGitlabPipelineWithDefaults = ({ image, variables, before_script, aut
11414
11478
  PIPELINE_NAME: "Main - $CI_COMMIT_TITLE",
11415
11479
  },
11416
11480
  },
11481
+ ...branchPipelines.map((branch) => ({
11482
+ if: `$CI_COMMIT_BRANCH == "${branch}"`,
11483
+ variables: {
11484
+ PIPELINE_ICON: "🐱🔨",
11485
+ PIPELINE_NAME: `${branch} - $CI_COMMIT_TITLE`,
11486
+ },
11487
+ })),
11417
11488
  {
11418
11489
  when: "always", // fallback
11419
11490
  variables: {
@@ -13840,8 +13911,10 @@ exports.createCatenvContext = createCatenvContext;
13840
13911
  "use strict";
13841
13912
 
13842
13913
  Object.defineProperty(exports, "__esModule", ({ value: true }));
13843
- exports.getAllEnvsByTrigger = exports.getAllEnvsInAllComponents = exports.getAllEnvs = void 0;
13914
+ exports.getConfiguredBranchTriggers = exports.getAllEnvsByTrigger = exports.getAllEnvsInAllComponents = exports.getAllEnvs = void 0;
13844
13915
  const types_1 = __nccwpck_require__(63557);
13916
+ const getEnvOn_1 = __nccwpck_require__(60601);
13917
+ const getEnvType_1 = __nccwpck_require__(46080);
13845
13918
  const getConfiguredAndDefaultEnvs = (config, componentName, envTypes) => {
13846
13919
  var _a, _b;
13847
13920
  const configuredEnvs = (_a = config.components[componentName].env) !== null && _a !== void 0 ? _a : {};
@@ -13849,13 +13922,33 @@ const getConfiguredAndDefaultEnvs = (config, componentName, envTypes) => {
13849
13922
  // these can be disabled with settimg them to `false`
13850
13923
  // this is the list of all not disabled envs. These are always returned
13851
13924
  const enabledDefaultEnvs = envTypes.filter((e) => configuredEnvs[e] !== false);
13852
- const configuredCustomEnvs = Object.entries((_b = config.components[componentName].env) !== null && _b !== void 0 ? _b : {})
13925
+ // envs declared project-wide (top-level `environments`): every
13926
+ // component deploys to them unless it opts out with `env.<name>: false`
13927
+ const declaredEnvs = Object.keys((_b = config.environments) !== null && _b !== void 0 ? _b : {})
13928
+ .filter((envName) => !(0, types_1.isKnowEnvType)(envName)) // default envs are already handled above
13929
+ .map((envName) => {
13930
+ const envType = (0, getEnvType_1.getDeclaredEnvType)(config.environments, envName);
13931
+ if (!envType) {
13932
+ throw new Error(`environment "${envName}" needs a type (dev, review, stage, prod) or an inherit`);
13933
+ }
13934
+ return [envName, envType];
13935
+ })
13936
+ .filter(([envName, envType]) => envTypes.includes(envType) && configuredEnvs[envName] !== false)
13937
+ .map(([envName]) => envName);
13938
+ // legacy: custom envs declared per component via their `type`
13939
+ const configuredCustomEnvs = Object.entries(configuredEnvs)
13853
13940
  .filter(([, config]) => config &&
13854
13941
  "type" in config &&
13855
13942
  config.type &&
13856
13943
  envTypes.includes(config.type))
13857
13944
  .map(([envName]) => envName);
13858
- return [...new Set([...enabledDefaultEnvs, ...configuredCustomEnvs])];
13945
+ return [
13946
+ ...new Set([
13947
+ ...enabledDefaultEnvs,
13948
+ ...declaredEnvs,
13949
+ ...configuredCustomEnvs,
13950
+ ]),
13951
+ ];
13859
13952
  };
13860
13953
  const getAllEnvs = (config, componentName) => {
13861
13954
  return getConfiguredAndDefaultEnvs(config, componentName, types_1.DEFAULT_ENV_TYPES);
@@ -13867,11 +13960,38 @@ const getAllEnvsInAllComponents = (config) => {
13867
13960
  ];
13868
13961
  };
13869
13962
  exports.getAllEnvsInAllComponents = getAllEnvsInAllComponents;
13963
+ /**
13964
+ * the resolved `on` of one env of a component (the project-wide `on`
13965
+ * of the env, falling back to the env type's default trigger)
13966
+ */
13967
+ const getEnvOnForComponentEnv = (config, componentName, env) => {
13968
+ var _a;
13969
+ const entry = (_a = config.components[componentName].env) === null || _a === void 0 ? void 0 : _a[env];
13970
+ return (0, getEnvOn_1.getEnvOn)(env, entry && entry !== false ? entry : {}, config.environments);
13971
+ };
13870
13972
  const getAllEnvsByTrigger = (config, componentName, trigger) => {
13871
- const envTypesByTrigger = (0, types_1.getEnvTypesByTrigger)(trigger);
13872
- return getConfiguredAndDefaultEnvs(config, componentName, envTypesByTrigger);
13973
+ return (0, exports.getAllEnvs)(config, componentName).filter((env) => (0, types_1.envTriggerEquals)(getEnvOnForComponentEnv(config, componentName, env), trigger));
13873
13974
  };
13874
13975
  exports.getAllEnvsByTrigger = getAllEnvsByTrigger;
13976
+ /**
13977
+ * all branch triggers declared by any env of any component (via
13978
+ * `on: { branch: "..." }`), deduplicated and sorted for deterministic
13979
+ * pipeline generation. Each gets its own pipeline next to the built-in
13980
+ * triggers.
13981
+ */
13982
+ const getConfiguredBranchTriggers = (config) => {
13983
+ const branches = new Set();
13984
+ for (const componentName of Object.keys(config.components)) {
13985
+ for (const env of (0, exports.getAllEnvs)(config, componentName)) {
13986
+ const on = getEnvOnForComponentEnv(config, componentName, env);
13987
+ if (on && (0, types_1.isBranchTrigger)(on)) {
13988
+ branches.add(on.branch);
13989
+ }
13990
+ }
13991
+ }
13992
+ return [...branches].sort().map((branch) => ({ branch }));
13993
+ };
13994
+ exports.getConfiguredBranchTriggers = getConfiguredBranchTriggers;
13875
13995
  //# sourceMappingURL=configruedEnvs.js.map
13876
13996
 
13877
13997
  /***/ }),
@@ -14202,53 +14322,237 @@ exports.getBuildInfoVariables = getBuildInfoVariables;
14202
14322
  Object.defineProperty(exports, "__esModule", ({ value: true }));
14203
14323
  exports.getEnvConfig = void 0;
14204
14324
  const lodash_es_1 = __nccwpck_require__(50210);
14325
+ const getEnvInherit_1 = __nccwpck_require__(52181);
14205
14326
  const getEnvConfig = (config, componentName, env) => {
14206
- var _a, _b;
14327
+ var _a;
14207
14328
  const defaultConfig = config.components[componentName];
14208
14329
  if (!defaultConfig) {
14209
14330
  throw new Error("unknown component " + componentName);
14210
14331
  }
14211
- const envCustomizations = (_b = (_a = defaultConfig.env) === null || _a === void 0 ? void 0 : _a[env]) !== null && _b !== void 0 ? _b : {};
14212
- if (envCustomizations === false) {
14213
- // env is disabled, still return the default config
14214
- return defaultConfig;
14215
- }
14216
- /**
14217
- * env config is merged with default. Arrays are not merged.
14218
- * you can customize this by providing a function that takes the default value as argument
14219
- */
14220
- return (0, lodash_es_1.mergeWith)(
14332
+ // env config is layered onto the component config: the per-component
14333
+ // overrides of inherited envs first (oldest ancestor first, see
14334
+ // `environments.<name>.inherit`), the env's own overrides last
14335
+ const chain = [...(0, getEnvInherit_1.getInheritedConfigChain)(config.environments, env), env];
14221
14336
  // mergeWith unfortunatly mutates the first object, so we need to clone it.
14222
14337
  // Passing empty object also doesn't work for us, because that will mess with the customizer function (second argument isn't nessecary the customized value)
14223
- (0, lodash_es_1.cloneDeep)(defaultConfig), envCustomizations, (defaultValue, customValue, key, obj, source) => {
14224
- // check if custom value is a function (and default is not),
14225
- // we currently don't have config options, that are functions, but we might in the future (customJobs is an exception)
14226
- if (typeof customValue === "function" &&
14227
- typeof defaultValue !== "function") {
14228
- return customValue(defaultValue);
14338
+ const result = (0, lodash_es_1.cloneDeep)(defaultConfig);
14339
+ for (const chainEnv of chain) {
14340
+ const envCustomizations = (_a = defaultConfig.env) === null || _a === void 0 ? void 0 : _a[chainEnv];
14341
+ if (!envCustomizations || envCustomizations === false) {
14342
+ if (chainEnv === env && envCustomizations === false) {
14343
+ // env is disabled, still return the default config
14344
+ return defaultConfig;
14345
+ }
14346
+ continue;
14229
14347
  }
14230
- return undefined;
14231
- });
14348
+ /**
14349
+ * env config is merged with default. Arrays are not merged.
14350
+ * you can customize this by providing a function that takes the default value as argument
14351
+ */
14352
+ (0, lodash_es_1.mergeWith)(result,
14353
+ // `host` must stay unique per env and `type` is the inherited
14354
+ // env's identity — neither is inherited
14355
+ chainEnv === env
14356
+ ? envCustomizations
14357
+ : (0, lodash_es_1.omit)(envCustomizations, ["host", "type"]), (defaultValue, customValue, key, obj, source) => {
14358
+ // check if custom value is a function (and default is not),
14359
+ // we currently don't have config options, that are functions, but we might in the future (customJobs is an exception)
14360
+ if (typeof customValue === "function" &&
14361
+ typeof defaultValue !== "function") {
14362
+ return customValue(defaultValue);
14363
+ }
14364
+ return undefined;
14365
+ });
14366
+ }
14367
+ return result;
14232
14368
  };
14233
14369
  exports.getEnvConfig = getEnvConfig;
14234
14370
  //# sourceMappingURL=getEnvConfig.js.map
14235
14371
 
14236
14372
  /***/ }),
14237
14373
 
14374
+ /***/ 52181:
14375
+ /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => {
14376
+
14377
+ "use strict";
14378
+
14379
+ Object.defineProperty(exports, "__esModule", ({ value: true }));
14380
+ exports.getSecretsEnv = exports.getInheritedConfigChain = exports.normalizeEnvInherit = void 0;
14381
+ const config_1 = __nccwpck_require__(75591);
14382
+ const normalizeEnvInherit = (inherit) => {
14383
+ if (!inherit)
14384
+ return {};
14385
+ if (typeof inherit === "string") {
14386
+ return { config: inherit, secrets: inherit };
14387
+ }
14388
+ return {
14389
+ config: inherit.config,
14390
+ secrets: inherit.secrets === false ? undefined : inherit.secrets,
14391
+ };
14392
+ };
14393
+ exports.normalizeEnvInherit = normalizeEnvInherit;
14394
+ const assertValidInheritTarget = (environments, env, target) => {
14395
+ if (target === "local") {
14396
+ throw new Error(`environment "${env}" cannot inherit from "local" — local is not a deployment environment`);
14397
+ }
14398
+ if (!(0, config_1.isKnowEnvType)(target) && !(environments === null || environments === void 0 ? void 0 : environments[target])) {
14399
+ throw new Error(`environment "${env}" inherits from unknown environment "${target}"`);
14400
+ }
14401
+ };
14402
+ /**
14403
+ * the chain of envs whose per-component config overrides feed into
14404
+ * `env`, oldest ancestor first (env itself not included). Cycles and
14405
+ * unknown/`local` targets are an error.
14406
+ */
14407
+ const getInheritedConfigChain = (environments, env) => {
14408
+ var _a;
14409
+ const chain = [];
14410
+ const seen = new Set([env]);
14411
+ let current = env;
14412
+ for (;;) {
14413
+ const target = (0, exports.normalizeEnvInherit)((_a = environments === null || environments === void 0 ? void 0 : environments[current]) === null || _a === void 0 ? void 0 : _a.inherit).config;
14414
+ if (!target)
14415
+ break;
14416
+ assertValidInheritTarget(environments, current, target);
14417
+ if (seen.has(target)) {
14418
+ throw new Error(`environment "${env}" has a cyclic config inheritance via "${target}"`);
14419
+ }
14420
+ seen.add(target);
14421
+ chain.unshift(target);
14422
+ current = target;
14423
+ }
14424
+ return chain;
14425
+ };
14426
+ exports.getInheritedConfigChain = getInheritedConfigChain;
14427
+ /**
14428
+ * the env whose secret values `env` uses (follows `inherit` secret
14429
+ * sharing transitively; the env itself when it has its own secrets).
14430
+ * All secret variable names must be built with this env.
14431
+ */
14432
+ const getSecretsEnv = (environments, env) => {
14433
+ var _a;
14434
+ const seen = new Set([env]);
14435
+ let current = env;
14436
+ for (;;) {
14437
+ const target = (0, exports.normalizeEnvInherit)((_a = environments === null || environments === void 0 ? void 0 : environments[current]) === null || _a === void 0 ? void 0 : _a.inherit).secrets;
14438
+ if (!target)
14439
+ return current;
14440
+ assertValidInheritTarget(environments, current, target);
14441
+ if (seen.has(target)) {
14442
+ throw new Error(`environment "${env}" has a cyclic secrets inheritance via "${target}"`);
14443
+ }
14444
+ seen.add(target);
14445
+ current = target;
14446
+ }
14447
+ };
14448
+ exports.getSecretsEnv = getSecretsEnv;
14449
+ //# sourceMappingURL=getEnvInherit.js.map
14450
+
14451
+ /***/ }),
14452
+
14453
+ /***/ 13055:
14454
+ /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => {
14455
+
14456
+ "use strict";
14457
+
14458
+ Object.defineProperty(exports, "__esModule", ({ value: true }));
14459
+ exports.getEnvInstance = void 0;
14460
+ const bash_1 = __nccwpck_require__(94143);
14461
+ const bashExpressionPerPipelineType_1 = __nccwpck_require__(6495);
14462
+ const getEnvOn_1 = __nccwpck_require__(60601);
14463
+ const REVIEW_SLUG = {
14464
+ default: "unknown-review-slug",
14465
+ gitlab: new bash_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"; })`),
14466
+ // CL_PR_NUMBER is injected by the github backend at the workflow level
14467
+ github: new bash_1.BashExpression(`$([ -n "$CL_PR_NUMBER" ] && echo "pr$CL_PR_NUMBER" || echo "unknown")`),
14468
+ };
14469
+ const getEnvInstance = (envConfig, env, pipelineType, environments) => {
14470
+ // an env deploying per merge request is a review app — one dynamic
14471
+ // instance per MR, identified by the review slug
14472
+ if ((0, getEnvOn_1.getEnvOn)(env, envConfig, environments) === "mr") {
14473
+ return {
14474
+ type: "review",
14475
+ reviewSlug: (0, bashExpressionPerPipelineType_1.getBashExpressionPerPipelineType)(REVIEW_SLUG, pipelineType),
14476
+ };
14477
+ }
14478
+ return { type: "stable" };
14479
+ };
14480
+ exports.getEnvInstance = getEnvInstance;
14481
+ //# sourceMappingURL=getEnvInstance.js.map
14482
+
14483
+ /***/ }),
14484
+
14485
+ /***/ 60601:
14486
+ /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => {
14487
+
14488
+ "use strict";
14489
+
14490
+ Object.defineProperty(exports, "__esModule", ({ value: true }));
14491
+ exports.getEnvOn = void 0;
14492
+ const types_1 = __nccwpck_require__(63557);
14493
+ const getEnvType_1 = __nccwpck_require__(46080);
14494
+ const getDefaultOnForType = (envType) => {
14495
+ var _a;
14496
+ const triggers = types_1.ENV_TYPES[envType].triggers;
14497
+ return (_a = triggers[0]) !== null && _a !== void 0 ? _a : false;
14498
+ };
14499
+ /**
14500
+ * resolves when an env deploys: the project-wide `on` config of the env
14501
+ * (top-level `environments`), falling back to the default of its env
14502
+ * type (dev → mainBranch, review → mr, stage/prod → taggedRelease,
14503
+ * local → false).
14504
+ *
14505
+ * `on` is deliberately not configurable per component — an environment
14506
+ * deploys at one point in time for the whole project.
14507
+ */
14508
+ const getEnvOn = (env, envConfig, environments) => {
14509
+ var _a;
14510
+ const on = (_a = environments === null || environments === void 0 ? void 0 : environments[env]) === null || _a === void 0 ? void 0 : _a.on;
14511
+ if (on !== undefined)
14512
+ return on;
14513
+ return getDefaultOnForType((0, getEnvType_1.getEnvType)(env, envConfig, environments));
14514
+ };
14515
+ exports.getEnvOn = getEnvOn;
14516
+ //# sourceMappingURL=getEnvOn.js.map
14517
+
14518
+ /***/ }),
14519
+
14238
14520
  /***/ 46080:
14239
14521
  /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => {
14240
14522
 
14241
14523
  "use strict";
14242
14524
 
14243
14525
  Object.defineProperty(exports, "__esModule", ({ value: true }));
14244
- exports.getEnvType = void 0;
14526
+ exports.getEnvType = exports.getDeclaredEnvType = void 0;
14245
14527
  const types_1 = __nccwpck_require__(63557);
14246
- const getEnvType = (env, envConfig) => {
14247
- if (envConfig.type)
14248
- return envConfig.type;
14249
- if ((0, types_1.isKnowEnvType)(env)) {
14528
+ const getEnvInherit_1 = __nccwpck_require__(52181);
14529
+ /**
14530
+ * the type of a project-wide declared env: its explicit `type`, its
14531
+ * name (when it is itself an env type), or — for envs that inherit —
14532
+ * the type of the env they inherit their config from
14533
+ */
14534
+ const getDeclaredEnvType = (environments, env, seen = new Set()) => {
14535
+ const declared = environments === null || environments === void 0 ? void 0 : environments[env];
14536
+ if (declared === null || declared === void 0 ? void 0 : declared.type)
14537
+ return declared.type;
14538
+ if ((0, types_1.isKnowEnvType)(env))
14250
14539
  return env;
14540
+ const inheritTarget = (0, getEnvInherit_1.normalizeEnvInherit)(declared === null || declared === void 0 ? void 0 : declared.inherit).config;
14541
+ if (inheritTarget && !seen.has(inheritTarget)) {
14542
+ seen.add(inheritTarget);
14543
+ return (0, exports.getDeclaredEnvType)(environments, inheritTarget, seen);
14251
14544
  }
14545
+ return undefined;
14546
+ };
14547
+ exports.getDeclaredEnvType = getDeclaredEnvType;
14548
+ const getEnvType = (env, envConfig, environments) => {
14549
+ // legacy: per-component custom envs declare their type themselves
14550
+ if (envConfig.type)
14551
+ return envConfig.type;
14552
+ // project-wide declaration (top-level `environments`)
14553
+ const declaredType = (0, exports.getDeclaredEnvType)(environments, env);
14554
+ if (declaredType)
14555
+ return declaredType;
14252
14556
  throw new Error("unknown env type: " + env);
14253
14557
  };
14254
14558
  exports.getEnvType = getEnvType;
@@ -14274,8 +14578,11 @@ const getEnvironment = async (ctx) => {
14274
14578
  envType,
14275
14579
  fullName: envContext.fullName,
14276
14580
  slugPrefix: envContext.environmentSlugPrefix,
14581
+ instance: envContext.instance,
14277
14582
  reviewSlug: envContext.reviewSlug,
14278
14583
  slug: envContext.environmentSlug,
14584
+ autoStop: envContext.autoStop,
14585
+ secretsEnv: envContext.secretsEnv,
14279
14586
  ...variables,
14280
14587
  };
14281
14588
  };
@@ -14293,19 +14600,26 @@ Object.defineProperty(exports, "__esModule", ({ value: true }));
14293
14600
  exports.getEnvironmentContext = void 0;
14294
14601
  const bash_1 = __nccwpck_require__(94143);
14295
14602
  const getEnvConfig_1 = __nccwpck_require__(69578);
14603
+ const getEnvInstance_1 = __nccwpck_require__(13055);
14604
+ const getEnvInherit_1 = __nccwpck_require__(52181);
14296
14605
  const getEnvType_1 = __nccwpck_require__(46080);
14297
- const getReviewSlug_1 = __nccwpck_require__(77360);
14298
- const getEnvironmentSlugPrefix = (env, reviewSlug) => {
14299
- if (reviewSlug) {
14300
- return (0, bash_1.joinBashExpressions)([env, reviewSlug], "-");
14606
+ const getEnvironmentSlugPrefix = (env, instance) => {
14607
+ if (instance.type === "review") {
14608
+ return (0, bash_1.joinBashExpressions)([env, instance.reviewSlug], "-");
14301
14609
  }
14302
14610
  return env;
14303
14611
  };
14304
14612
  const getEnvironmentContext = ({ env, componentName, config, pipelineType, }) => {
14613
+ var _a, _b;
14305
14614
  const envConfigRaw = (0, getEnvConfig_1.getEnvConfig)(config, componentName, env);
14306
- const envType = (0, getEnvType_1.getEnvType)(env, envConfigRaw);
14307
- const reviewSlug = (0, getReviewSlug_1.getReviewSlug)(envConfigRaw, env, pipelineType);
14308
- const environmentSlugPrefix = getEnvironmentSlugPrefix(env, reviewSlug);
14615
+ const envType = (0, getEnvType_1.getEnvType)(env, envConfigRaw, config.environments);
14616
+ const instance = (0, getEnvInstance_1.getEnvInstance)(envConfigRaw, env, pipelineType, config.environments);
14617
+ // component-level autoStop overrides the project-wide environment
14618
+ // config; undefined means the env type's default applies
14619
+ const autoStop = envConfigRaw.autoStop !== undefined
14620
+ ? envConfigRaw.autoStop
14621
+ : (_b = (_a = config.environments) === null || _a === void 0 ? void 0 : _a[env]) === null || _b === void 0 ? void 0 : _b.autoStop;
14622
+ const environmentSlugPrefix = getEnvironmentSlugPrefix(env, instance);
14309
14623
  const environmentSlug = environmentSlugPrefix.concat(`-${componentName}`);
14310
14624
  const fullName = (0, bash_1.joinBashExpressions)([config.customerName, config.appName, environmentSlug], "-");
14311
14625
  return {
@@ -14314,10 +14628,13 @@ const getEnvironmentContext = ({ env, componentName, config, pipelineType, }) =>
14314
14628
  buildConfigRaw: envConfigRaw.build,
14315
14629
  environmentSlugPrefix,
14316
14630
  environmentSlug,
14317
- reviewSlug,
14631
+ instance,
14632
+ reviewSlug: instance.type === "review" ? instance.reviewSlug : null,
14318
14633
  pipelineType,
14319
14634
  fullName,
14320
14635
  envType,
14636
+ autoStop,
14637
+ secretsEnv: (0, getEnvInherit_1.getSecretsEnv)(config.environments, env),
14321
14638
  componentName,
14322
14639
  env,
14323
14640
  fullConfig: config,
@@ -14402,7 +14719,7 @@ const getEnvironmentVariables = async (ctx, options = {}) => {
14402
14719
  ...(0, envVars_1.normalizeSecretVarsConfig)((_f = envConfigRaw.vars) === null || _f === void 0 ? void 0 : _f.secret),
14403
14720
  ...additionalSecretKeys,
14404
14721
  ];
14405
- const secretEnvVars = (0, envVars_1.makeSecretEnvVarMapping)(env, componentName, secretEnvVarKeys);
14722
+ const secretEnvVars = (0, envVars_1.makeSecretEnvVarMapping)(environmentContext.secretsEnv, componentName, secretEnvVarKeys);
14406
14723
  const publicEnvVarsRawSanitized = (0, envVars_1.stringifyValues)(publicEnvVarsRaw);
14407
14724
  const publicEnvVarsUnresolved = Object.fromEntries(Object.entries(publicEnvVarsRawSanitized).map(([key, value]) => [
14408
14725
  key,
@@ -14436,11 +14753,11 @@ const getEnvironmentVariables = async (ctx, options = {}) => {
14436
14753
  envVars,
14437
14754
  secretEnvVarKeys,
14438
14755
  jobOnlyVars: {
14439
- build: await (0, transformJobOnlyVars_1.transformJobOnlyVars)(env, componentName, (buildConfigRaw &&
14756
+ build: await (0, transformJobOnlyVars_1.transformJobOnlyVars)(environmentContext.secretsEnv, componentName, (buildConfigRaw &&
14440
14757
  (0, types_1.isStandaloneBuildConfig)(buildConfigRaw) &&
14441
14758
  buildConfigRaw.jobVars) ||
14442
14759
  null),
14443
- deploy: await (0, transformJobOnlyVars_1.transformJobOnlyVars)(env, componentName, (deployConfigRaw && deployConfigRaw.jobVars) || null),
14760
+ deploy: await (0, transformJobOnlyVars_1.transformJobOnlyVars)(environmentContext.secretsEnv, componentName, (deployConfigRaw && deployConfigRaw.jobVars) || null),
14444
14761
  },
14445
14762
  };
14446
14763
  };
@@ -14452,7 +14769,7 @@ const addIndexVar = (vars) => ({
14452
14769
  ...vars,
14453
14770
  _ALL_ENV_VAR_KEYS: JSON.stringify(Object.keys(vars)),
14454
14771
  });
14455
- const getSecretVarNameForContext = (context, key) => (0, exports.getSecretVarName)(context.env, context.name, key);
14772
+ const getSecretVarNameForContext = (context, key) => (0, exports.getSecretVarName)(context.environment.secretsEnv, context.name, key);
14456
14773
  exports.getSecretVarNameForContext = getSecretVarNameForContext;
14457
14774
  //# sourceMappingURL=getEnvironmentVariables.js.map
14458
14775
 
@@ -14496,34 +14813,6 @@ exports.getLabels = getLabels;
14496
14813
 
14497
14814
  /***/ }),
14498
14815
 
14499
- /***/ 77360:
14500
- /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => {
14501
-
14502
- "use strict";
14503
-
14504
- Object.defineProperty(exports, "__esModule", ({ value: true }));
14505
- exports.getReviewSlug = void 0;
14506
- const bash_1 = __nccwpck_require__(94143);
14507
- const bashExpressionPerPipelineType_1 = __nccwpck_require__(6495);
14508
- const getEnvType_1 = __nccwpck_require__(46080);
14509
- const REVIEW_SLUG = {
14510
- default: "unknown-review-slug",
14511
- gitlab: new bash_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"; })`),
14512
- // CL_PR_NUMBER is injected by the github backend at the workflow level
14513
- github: new bash_1.BashExpression(`$([ -n "$CL_PR_NUMBER" ] && echo "pr$CL_PR_NUMBER" || echo "unknown")`),
14514
- };
14515
- const getReviewSlug = (envConfig, env, pipelineType) => {
14516
- const envType = (0, getEnvType_1.getEnvType)(env, envConfig);
14517
- if (envType === "review") {
14518
- return (0, bashExpressionPerPipelineType_1.getBashExpressionPerPipelineType)(REVIEW_SLUG, pipelineType);
14519
- }
14520
- return null; // not a review app;
14521
- };
14522
- exports.getReviewSlug = getReviewSlug;
14523
- //# sourceMappingURL=getReviewSlug.js.map
14524
-
14525
- /***/ }),
14526
-
14527
14816
  /***/ 15707:
14528
14817
  /***/ (function(__unused_webpack_module, exports, __nccwpck_require__) {
14529
14818
 
@@ -14546,6 +14835,7 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14546
14835
  Object.defineProperty(exports, "__esModule", ({ value: true }));
14547
14836
  __exportStar(__nccwpck_require__(53373), exports);
14548
14837
  __exportStar(__nccwpck_require__(69578), exports);
14838
+ __exportStar(__nccwpck_require__(52181), exports);
14549
14839
  __exportStar(__nccwpck_require__(21494), exports);
14550
14840
  __exportStar(__nccwpck_require__(17429), exports);
14551
14841
  //# sourceMappingURL=index.js.map
@@ -15155,8 +15445,11 @@ class DeployJob extends jobs_1.CatladderJob {
15155
15445
  var _a, _b, _c, _d, _e, _f, _g;
15156
15446
  const hasDocker = (0, docker_1.requiresDockerBuild)(context);
15157
15447
  const isStoppable = (0, utils_1.contextIsStoppable)(context);
15448
+ // env-level autoStop config wins; the env type only supplies the
15449
+ // default (via the project-wide `autoStop` durations)
15450
+ const autoStopConfigured = context.environment.autoStop;
15158
15451
  const autoStopConfig = (0, autoStop_1.getAutoStopConfig)(context.fullConfig);
15159
- const autoStop = context.environment.envType === "review"
15452
+ const autoStopDefault = context.environment.envType === "review"
15160
15453
  ? // review envs read the lifetime from a pipeline variable, so
15161
15454
  // the pin label's workflow rule can override it to "never"
15162
15455
  autoStopConfig.pinLabel === false
@@ -15165,6 +15458,13 @@ class DeployJob extends jobs_1.CatladderJob {
15165
15458
  : context.environment.envType === "dev"
15166
15459
  ? autoStopConfig.dev
15167
15460
  : undefined;
15461
+ // NOTE: an explicit env-level autoStop is a literal — a review env
15462
+ // overriding it opts out of the pin-label variable indirection
15463
+ const autoStop = autoStopConfigured === undefined
15464
+ ? autoStopDefault
15465
+ : autoStopConfigured === false
15466
+ ? undefined
15467
+ : autoStopConfigured;
15168
15468
  const deployConfig = (_a = context.deploy) === null || _a === void 0 ? void 0 : _a.config;
15169
15469
  // if auto or manual is configured explicitly, use that
15170
15470
  const whenDeployDefined = deployConfig && deployConfig.when ? deployConfig.when : undefined;
@@ -15432,7 +15732,8 @@ exports.getArtifactsRegistryDockerUrl = getArtifactsRegistryDockerUrl;
15432
15732
  * lecacyReviewImageName is only temporary. In old versions the images had no reviewslug in review apps, which makes cleanup harder. We delete all those images now, but need the path
15433
15733
  */
15434
15734
  const getArtifactsRegistryImageName = (context, lecacyReviewImageName = false) => {
15435
- if (lecacyReviewImageName && context.environment.envType !== "review") {
15735
+ const { instance } = context.environment;
15736
+ if (lecacyReviewImageName && instance.type !== "review") {
15436
15737
  throw new Error("lecacyReviewImageName is only allowed for review app");
15437
15738
  }
15438
15739
  const dockerUrl = (0, exports.getArtifactsRegistryDockerUrl)(context);
@@ -15440,8 +15741,8 @@ const getArtifactsRegistryImageName = (context, lecacyReviewImageName = false) =
15440
15741
  dockerUrl,
15441
15742
  context.env,
15442
15743
  context.name,
15443
- ...(context.environment.reviewSlug && !lecacyReviewImageName
15444
- ? [context.environment.reviewSlug]
15744
+ ...(instance.type === "review" && !lecacyReviewImageName
15745
+ ? [instance.reviewSlug]
15445
15746
  : []),
15446
15747
  ];
15447
15748
  return (0, bash_1.joinBashExpressions)(gcloudImagePath, "/");
@@ -15491,7 +15792,7 @@ const getDeleteUnusedImagesCommands = (context, keep = 0) => {
15491
15792
  ...getDeleteImageCommands(buildCacheImageName, 1),
15492
15793
  // because a recent version of catladder had no review-slug in the image name, we have to delete those as well
15493
15794
  // we can later remove this line in some months
15494
- ...(context.environment.envType === "review"
15795
+ ...(context.environment.instance.type === "review"
15495
15796
  ? (0, gitlab_1.allowFailureInScripts)(getDeleteImageCommands((0, exports.getArtifactsRegistryImageName)(context, true), 0))
15496
15797
  : []),
15497
15798
  ];
@@ -15512,12 +15813,18 @@ const gitlab_1 = __nccwpck_require__(86962);
15512
15813
  const artifactsRegistry_1 = __nccwpck_require__(44330);
15513
15814
  const cloudRunRevisions_1 = __nccwpck_require__(55698);
15514
15815
  const getRemoveOldRevisionsAndImagesCommand = (context, when) => {
15816
+ var _a;
15515
15817
  if (when === "onStop") {
15516
15818
  // service is already deleted, so we don't need to delete old revisions, just delete all images
15517
15819
  return (0, artifactsRegistry_1.getDeleteUnusedImagesCommands)(context);
15518
15820
  }
15519
- // this number only targets inactive revisions
15520
- const revisionsToKeep = context.environment.envType === "prod" ? 5 : 0;
15821
+ const deployConfig = (_a = context.deploy) === null || _a === void 0 ? void 0 : _a.config;
15822
+ const configuredRevisionsToKeep = deployConfig && deployConfig.type === "google-cloudrun"
15823
+ ? deployConfig.revisionsToKeep
15824
+ : undefined;
15825
+ // this number only targets inactive revisions; the env type only
15826
+ // supplies the default (prod keeps a rollback history)
15827
+ const revisionsToKeep = configuredRevisionsToKeep !== null && configuredRevisionsToKeep !== void 0 ? configuredRevisionsToKeep : (context.environment.envType === "prod" ? 5 : 0);
15521
15828
  // this number needs to be higher than inactive after deploy, so we add one
15522
15829
  const imagesToKeep = revisionsToKeep + 1;
15523
15830
  const deleteOldRevisionsCommands = (0, cloudRunRevisions_1.getDeleteUnusedRevisionsCommands)(context, revisionsToKeep);
@@ -16424,7 +16731,7 @@ const cloudRunExecutionUrl_1 = __nccwpck_require__(49963);
16424
16731
  const database_1 = __nccwpck_require__(11595);
16425
16732
  const getServiceName_1 = __nccwpck_require__(33102);
16426
16733
  exports.GCLOUD_DEPLOY_CREDENTIALS_KEY = "GCLOUD_DEPLOY_credentialsKey";
16427
- const getCloudSqlVariables = ({ deployConfigRaw, environmentSlugPrefix, env, componentName, fullConfig, }) => {
16734
+ const getCloudSqlVariables = ({ deployConfigRaw, environmentSlugPrefix, secretsEnv, componentName, fullConfig, }) => {
16428
16735
  var _a, _b, _c;
16429
16736
  if (deployConfigRaw && deployConfigRaw.cloudSql) {
16430
16737
  const DB_NAME = (0, utils_1.getFullDbName)(deployConfigRaw.cloudSql, fullConfig, environmentSlugPrefix, componentName);
@@ -16435,7 +16742,7 @@ const getCloudSqlVariables = ({ deployConfigRaw, environmentSlugPrefix, env, com
16435
16742
  CLOUD_SQL_INSTANCE_CONNECTION_NAME: deployConfigRaw.cloudSql.instanceConnectionName,
16436
16743
  DB_NAME: DB_NAME,
16437
16744
  DB_USER: (_b = deployConfigRaw.cloudSql.dbUser) !== null && _b !== void 0 ? _b : "postgres",
16438
- DB_PASSWORD: "$" + (0, context_1.getSecretVarName)(env, componentName, "DB_PASSWORD"),
16745
+ DB_PASSWORD: "$" + (0, context_1.getSecretVarName)(secretsEnv, componentName, "DB_PASSWORD"),
16439
16746
  };
16440
16747
  return {
16441
16748
  ...dbVars,
@@ -16908,7 +17215,7 @@ const fromComponentContext = (context) => {
16908
17215
  environmentSlugPrefix: context.environment.slugPrefix,
16909
17216
  componentName: context.name,
16910
17217
  env: context.env,
16911
- isReviewEnv: context.environment.envType === "review",
17218
+ isReviewEnv: context.environment.instance.type === "review",
16912
17219
  deployConfig: deployConfig && deployConfig.type === "google-cloudrun"
16913
17220
  ? deployConfig
16914
17221
  : null,
@@ -16920,7 +17227,7 @@ const fromEnvironmentContext = (context) => ({
16920
17227
  environmentSlugPrefix: context.environmentSlugPrefix,
16921
17228
  componentName: context.componentName,
16922
17229
  env: context.env,
16923
- isReviewEnv: context.envType === "review",
17230
+ isReviewEnv: context.instance.type === "review",
16924
17231
  deployConfig: context.deployConfigRaw || null,
16925
17232
  config: context.fullConfig,
16926
17233
  });
@@ -17374,9 +17681,9 @@ exports.KUBERNETES_DEPLOY_TYPE = {
17374
17681
  jobs: deployJob_1.createKubernetesDeployJobs,
17375
17682
  defaults: () => ({}),
17376
17683
  additionalSecretKeys: additionalSecretKeys_1.additionalKubernetesSecretKeys,
17377
- getAdditionalEnvVars: ({ componentName, fullConfig, deployConfigRaw, env, reviewSlug, envType, }) => {
17684
+ getAdditionalEnvVars: ({ componentName, fullConfig, deployConfigRaw, env, instance, }) => {
17378
17685
  var _a;
17379
- const KUBE_APP_NAME_PREFIX = envType === "review" && reviewSlug ? reviewSlug.concat("-") : "";
17686
+ const KUBE_APP_NAME_PREFIX = instance.type === "review" ? instance.reviewSlug.concat("-") : "";
17380
17687
  const KUBE_APP_NAME = KUBE_APP_NAME_PREFIX.concat(componentName);
17381
17688
  const KUBE_NAMESPACE = (0, __1.getKubernetesNamespace)(fullConfig, env);
17382
17689
  const componentSlug = (0, slugify_1.default)(componentName);
@@ -17385,7 +17692,7 @@ exports.KUBERNETES_DEPLOY_TYPE = {
17385
17692
  "panter.cloud";
17386
17693
  const HOSTNAME_INTERNAL = (0, bash_1.joinBashExpressions)([
17387
17694
  componentSlug,
17388
- ...(envType === "review" && reviewSlug ? [reviewSlug] : []),
17695
+ ...(instance.type === "review" ? [instance.reviewSlug] : []),
17389
17696
  env,
17390
17697
  fullConfig.appName,
17391
17698
  fullConfig.customerName,
@@ -17743,7 +18050,8 @@ const createPagesDeployJobs = async (context) => {
17743
18050
  throw new Error("deploy config is not pages");
17744
18051
  }
17745
18052
  const onGithub = context.pipelineType === "github";
17746
- if (onGithub && context.environment.envType === "review") {
18053
+ const isReviewApp = context.environment.instance.type === "review";
18054
+ if (onGithub && isReviewApp) {
17747
18055
  // github pages serves ONE site per repository and deploy-pages
17748
18056
  // replaces all of it — there is no path_prefix equivalent, so per-PR
17749
18057
  // previews cannot exist (deploy-pages has a `preview` input, but it
@@ -17754,7 +18062,7 @@ const createPagesDeployJobs = async (context) => {
17754
18062
  }
17755
18063
  // review environments publish under their own path prefix (gitlab
17756
18064
  // parallel deployments) — every merge request gets a site preview
17757
- const pathPrefix = context.environment.envType === "review" ? "mr-$CI_MERGE_REQUEST_IID" : "";
18065
+ const pathPrefix = isReviewApp ? "mr-$CI_MERGE_REQUEST_IID" : "";
17758
18066
  const publishDir = (_b = deployConfig.publishDir) !== null && _b !== void 0 ? _b : "public";
17759
18067
  const packageManagerInstall = await (0, packageManagerInstall_1.getPackageManagerInstall)(context, {
17760
18068
  noCustomPostInstall: true,
@@ -19451,12 +19759,22 @@ Object.defineProperty(exports, "__esModule", ({ value: true }));
19451
19759
  "use strict";
19452
19760
 
19453
19761
  Object.defineProperty(exports, "__esModule", ({ value: true }));
19454
- exports.isKnowEnvType = exports.DEFAULT_ENV_TYPES = exports.DEFAULT_ENVS = exports.getEnvTypesByTrigger = exports.ENV_TYPES = exports.ALL_PIPELINE_TRIGGERS = void 0;
19762
+ exports.isKnowEnvType = exports.DEFAULT_ENV_TYPES = exports.DEFAULT_ENVS = exports.ENV_TYPES = exports.envTriggerEquals = exports.isBranchTrigger = exports.ALL_PIPELINE_TRIGGERS = void 0;
19455
19763
  exports.ALL_PIPELINE_TRIGGERS = [
19456
19764
  "mainBranch",
19457
19765
  "mr",
19458
19766
  "taggedRelease",
19459
19767
  ];
19768
+ const isBranchTrigger = (trigger) => typeof trigger === "object" && trigger !== null && "branch" in trigger;
19769
+ exports.isBranchTrigger = isBranchTrigger;
19770
+ const envTriggerEquals = (a, b) => {
19771
+ if (a === undefined || a === false)
19772
+ return false;
19773
+ if ((0, exports.isBranchTrigger)(b))
19774
+ return (0, exports.isBranchTrigger)(a) && a.branch === b.branch;
19775
+ return a === b;
19776
+ };
19777
+ exports.envTriggerEquals = envTriggerEquals;
19460
19778
  /**
19461
19779
  * all env types with their trigger.
19462
19780
  * Each env type has a default env with the same name which is always included
@@ -19478,15 +19796,6 @@ exports.ENV_TYPES = {
19478
19796
  triggers: [],
19479
19797
  },
19480
19798
  };
19481
- /**
19482
- *
19483
- * @param trigger a trigger
19484
- * @returns array of env types for that trigger. this is also the list of default envs
19485
- */
19486
- const getEnvTypesByTrigger = (trigger) => Object.entries(exports.ENV_TYPES)
19487
- .filter(([, e]) => e.triggers.includes(trigger))
19488
- .map(([e]) => e);
19489
- exports.getEnvTypesByTrigger = getEnvTypesByTrigger;
19490
19799
  exports.DEFAULT_ENVS = Object.keys(exports.ENV_TYPES);
19491
19800
  exports.DEFAULT_ENV_TYPES = exports.DEFAULT_ENVS;
19492
19801
  const isKnowEnvType = (env) => {