@catladder/cli 5.2.0 → 5.3.1

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 (101) 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/release/changesetCheckJob.js +17 -10
  8. package/dist/apps/cli/src/release/changesetCheckJob.js.map +1 -1
  9. package/dist/apps/cli/src/secrets/github.js +4 -2
  10. package/dist/apps/cli/src/secrets/github.js.map +1 -1
  11. package/dist/bundles/catci/index.js +1 -1
  12. package/dist/bundles/catenv/index.js +402 -111
  13. package/dist/bundles/cli/index.js +432 -116
  14. package/dist/bundles/skills/catladder-config/SKILL.md +34 -2
  15. package/dist/bundles/skills/catladder-deploys/SKILL.md +13 -1
  16. package/dist/bundles/skills/catladder-deploys/references/deploy-types.md +1 -1
  17. package/dist/bundles/skills/catladder-migrate-release-method/SKILL.md +4 -3
  18. package/dist/bundles/skills/catladder-pipelines/SKILL.md +10 -1
  19. package/dist/bundles/skills/catladder-releases/SKILL.md +12 -7
  20. package/dist/bundles/skills/catladder-secrets/SKILL.md +6 -0
  21. package/dist/packages/pipeline/src/backends/github/GithubBackend.js +23 -3
  22. package/dist/packages/pipeline/src/backends/github/GithubBackend.js.map +1 -1
  23. package/dist/packages/pipeline/src/backends/github/createGithubJobs.js +2 -2
  24. package/dist/packages/pipeline/src/backends/github/createGithubJobs.js.map +1 -1
  25. package/dist/packages/pipeline/src/backends/gitlab/GitlabBackend.js +18 -2
  26. package/dist/packages/pipeline/src/backends/gitlab/GitlabBackend.js.map +1 -1
  27. package/dist/packages/pipeline/src/backends/gitlab/createGitlabJobs.js +9 -4
  28. package/dist/packages/pipeline/src/backends/gitlab/createGitlabJobs.js.map +1 -1
  29. package/dist/packages/pipeline/src/backends/gitlab/createGitlabPipeline.d.ts +6 -1
  30. package/dist/packages/pipeline/src/backends/gitlab/createGitlabPipeline.js +8 -1
  31. package/dist/packages/pipeline/src/backends/gitlab/createGitlabPipeline.js.map +1 -1
  32. package/dist/packages/pipeline/src/backends/gitlab/gitlabReleaseJobs.js +5 -3
  33. package/dist/packages/pipeline/src/backends/gitlab/gitlabReleaseJobs.js.map +1 -1
  34. package/dist/packages/pipeline/src/config/configruedEnvs.d.ts +9 -2
  35. package/dist/packages/pipeline/src/config/configruedEnvs.js +54 -5
  36. package/dist/packages/pipeline/src/config/configruedEnvs.js.map +1 -1
  37. package/dist/packages/pipeline/src/context/createAllComponentsContext.d.ts +2 -2
  38. package/dist/packages/pipeline/src/context/createComponentContext.d.ts +2 -2
  39. package/dist/packages/pipeline/src/context/createWorkspaceContext.d.ts +2 -2
  40. package/dist/packages/pipeline/src/context/getEnvConfig.js +35 -19
  41. package/dist/packages/pipeline/src/context/getEnvConfig.js.map +1 -1
  42. package/dist/packages/pipeline/src/context/getEnvInherit.d.ts +20 -0
  43. package/dist/packages/pipeline/src/context/getEnvInherit.js +72 -0
  44. package/dist/packages/pipeline/src/context/getEnvInherit.js.map +1 -0
  45. package/dist/packages/pipeline/src/context/getEnvInstance.d.ts +4 -0
  46. package/dist/packages/pipeline/src/context/{getReviewSlug.js → getEnvInstance.js} +13 -9
  47. package/dist/packages/pipeline/src/context/getEnvInstance.js.map +1 -0
  48. package/dist/packages/pipeline/src/context/getEnvOn.d.ts +13 -0
  49. package/dist/packages/pipeline/src/context/getEnvOn.js +28 -0
  50. package/dist/packages/pipeline/src/context/getEnvOn.js.map +1 -0
  51. package/dist/packages/pipeline/src/context/getEnvType.d.ts +8 -2
  52. package/dist/packages/pipeline/src/context/getEnvType.js +27 -5
  53. package/dist/packages/pipeline/src/context/getEnvType.js.map +1 -1
  54. package/dist/packages/pipeline/src/context/getEnvironment.js +3 -0
  55. package/dist/packages/pipeline/src/context/getEnvironment.js.map +1 -1
  56. package/dist/packages/pipeline/src/context/getEnvironmentContext.js +18 -8
  57. package/dist/packages/pipeline/src/context/getEnvironmentContext.js.map +1 -1
  58. package/dist/packages/pipeline/src/context/getEnvironmentVariables.d.ts +1 -1
  59. package/dist/packages/pipeline/src/context/getEnvironmentVariables.js +4 -4
  60. package/dist/packages/pipeline/src/context/getEnvironmentVariables.js.map +1 -1
  61. package/dist/packages/pipeline/src/context/index.d.ts +1 -0
  62. package/dist/packages/pipeline/src/context/index.js +1 -0
  63. package/dist/packages/pipeline/src/context/index.js.map +1 -1
  64. package/dist/packages/pipeline/src/deploy/base/deploy.js +11 -1
  65. package/dist/packages/pipeline/src/deploy/base/deploy.js.map +1 -1
  66. package/dist/packages/pipeline/src/deploy/cloudRun/artifactsRegistry.js +5 -4
  67. package/dist/packages/pipeline/src/deploy/cloudRun/artifactsRegistry.js.map +1 -1
  68. package/dist/packages/pipeline/src/deploy/cloudRun/cleanup.js +8 -2
  69. package/dist/packages/pipeline/src/deploy/cloudRun/cleanup.js.map +1 -1
  70. package/dist/packages/pipeline/src/deploy/cloudRun/index.js +2 -2
  71. package/dist/packages/pipeline/src/deploy/cloudRun/index.js.map +1 -1
  72. package/dist/packages/pipeline/src/deploy/cloudRun/utils/getServiceName.js +2 -2
  73. package/dist/packages/pipeline/src/deploy/cloudRun/utils/getServiceName.js.map +1 -1
  74. package/dist/packages/pipeline/src/deploy/kubernetes/index.js +3 -3
  75. package/dist/packages/pipeline/src/deploy/kubernetes/index.js.map +1 -1
  76. package/dist/packages/pipeline/src/deploy/pages/deployJob.js +3 -2
  77. package/dist/packages/pipeline/src/deploy/pages/deployJob.js.map +1 -1
  78. package/dist/packages/pipeline/src/deploy/types/googleCloudRun.d.ts +9 -0
  79. package/dist/packages/pipeline/src/types/config.d.ts +126 -9
  80. package/dist/packages/pipeline/src/types/config.js +11 -10
  81. package/dist/packages/pipeline/src/types/config.js.map +1 -1
  82. package/dist/packages/pipeline/src/types/context.d.ts +22 -3
  83. package/dist/packages/pipeline/src/types/context.js.map +1 -1
  84. package/dist/packages/pipeline/src/types/environmentContext.d.ts +29 -0
  85. package/dist/skills/catladder-config/SKILL.md +34 -2
  86. package/dist/skills/catladder-deploys/SKILL.md +13 -1
  87. package/dist/skills/catladder-deploys/references/deploy-types.md +1 -1
  88. package/dist/skills/catladder-migrate-release-method/SKILL.md +4 -3
  89. package/dist/skills/catladder-pipelines/SKILL.md +10 -1
  90. package/dist/skills/catladder-releases/SKILL.md +12 -7
  91. package/dist/skills/catladder-secrets/SKILL.md +6 -0
  92. package/dist/tsconfig.tsbuildinfo +1 -1
  93. package/package.json +1 -1
  94. package/src/commands/project/commandPortForward.ts +1 -1
  95. package/src/commands/project/secrets/scope.ts +38 -2
  96. package/src/config/getProjectConfig.ts +9 -1
  97. package/src/release/__tests__/changesetCheckJob.test.ts +68 -0
  98. package/src/release/changesetCheckJob.ts +18 -10
  99. package/src/secrets/github.ts +8 -5
  100. package/dist/packages/pipeline/src/context/getReviewSlug.d.ts +0 -4
  101. 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,16 +11212,21 @@ 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
- const dotEnvFile = "gitlab_environment.env";
11218
+ // absolute: deploy scripts may `cd` into the component dir (pages,
11219
+ // custom), while gitlab resolves `reports.dotenv` relative to the
11220
+ // project dir — a relative file would land in <dir>/ and never be
11221
+ // picked up ("no matching files"), leaving the environment without url
11222
+ const dotEnvFileName = "gitlab_environment.env";
11223
+ const dotEnvFile = `"$CI_PROJECT_DIR/${dotEnvFileName}"`;
11155
11224
  const createsJobEnv = !catladderJobEnvironment.action ||
11156
11225
  catladderJobEnvironment.action === "start";
11157
11226
  const artifacts = (0, lodash_es_1.merge)((_a = job.artifacts) !== null && _a !== void 0 ? _a : {}, createsJobEnv
11158
11227
  ? {
11159
11228
  reports: {
11160
- dotenv: dotEnvFile,
11229
+ dotenv: dotEnvFileName,
11161
11230
  },
11162
11231
  }
11163
11232
  : {});
@@ -11165,7 +11234,7 @@ const addGitlabEnvironment = (context, catladderJobEnvironment, job, allJobs) =>
11165
11234
  `echo "${exports.GITLAB_ENVIRONMENT_URL_VARIABLE}=${(0, bash_1.getBashVariable)("ROOT_URL")}" >> ${dotEnvFile}`,
11166
11235
  ];
11167
11236
  // this is NOT a bashVariable since it NEEDS to be used as a string in gitlab
11168
- const gitlabEnvironmentName = envType === "review"
11237
+ const gitlabEnvironmentName = instance.type === "review"
11169
11238
  ? `${env}/$CI_COMMIT_REF_NAME/${name}` // FIXME: should be replaced with mr name as well
11170
11239
  : `${env}/${name}`;
11171
11240
  return {
@@ -11333,7 +11402,7 @@ const rules_1 = __nccwpck_require__(78503);
11333
11402
  * build-free image (a catladder job image would drag in a build job).
11334
11403
  */
11335
11404
  const DEFAULT_PIPELINE_IMAGE = "node:22";
11336
- const createGitlabPipelineWithDefaults = ({ image, variables, before_script, autoStop, ...config }) => {
11405
+ const createGitlabPipelineWithDefaults = ({ image, variables, before_script, branchPipelines = [], autoStop, ...config }) => {
11337
11406
  const pinLabel = autoStop && autoStop.pinLabel;
11338
11407
  return {
11339
11408
  image: image !== null && image !== void 0 ? image : DEFAULT_PIPELINE_IMAGE,
@@ -11414,6 +11483,13 @@ const createGitlabPipelineWithDefaults = ({ image, variables, before_script, aut
11414
11483
  PIPELINE_NAME: "Main - $CI_COMMIT_TITLE",
11415
11484
  },
11416
11485
  },
11486
+ ...branchPipelines.map((branch) => ({
11487
+ if: `$CI_COMMIT_BRANCH == "${branch}"`,
11488
+ variables: {
11489
+ PIPELINE_ICON: "🐱🔨",
11490
+ PIPELINE_NAME: `${branch} - $CI_COMMIT_TITLE`,
11491
+ },
11492
+ })),
11417
11493
  {
11418
11494
  when: "always", // fallback
11419
11495
  variables: {
@@ -11539,8 +11615,10 @@ mainBranchJobNames) => {
11539
11615
  },
11540
11616
  };
11541
11617
  // informational MR check of the release method (e.g. the changeset
11542
- // check): warns via allow_failure, the report lands in the MR
11543
- // widget as an exposed artifact
11618
+ // check): the report lands in the MR widget as an exposed artifact.
11619
+ // The check script exits 0 even when it warns — gitlab only resolves
11620
+ // exposed artifacts of successful jobs, a failed (allow_failure) job
11621
+ // leaves the widget spinning on "Loading artifacts" forever
11544
11622
  const checkJob = method.checkScript
11545
11623
  ? {
11546
11624
  ["🦋 changeset check"]: {
@@ -11555,7 +11633,7 @@ mainBranchJobNames) => {
11555
11633
  artifacts: {
11556
11634
  paths: ["changeset-report.md"],
11557
11635
  expose_as: "changeset report",
11558
- // the report must survive the warning exit
11636
+ // keep the report should the check itself crash
11559
11637
  when: "always",
11560
11638
  },
11561
11639
  rules: [
@@ -13840,8 +13918,10 @@ exports.createCatenvContext = createCatenvContext;
13840
13918
  "use strict";
13841
13919
 
13842
13920
  Object.defineProperty(exports, "__esModule", ({ value: true }));
13843
- exports.getAllEnvsByTrigger = exports.getAllEnvsInAllComponents = exports.getAllEnvs = void 0;
13921
+ exports.getConfiguredBranchTriggers = exports.getAllEnvsByTrigger = exports.getAllEnvsInAllComponents = exports.getAllEnvs = void 0;
13844
13922
  const types_1 = __nccwpck_require__(63557);
13923
+ const getEnvOn_1 = __nccwpck_require__(60601);
13924
+ const getEnvType_1 = __nccwpck_require__(46080);
13845
13925
  const getConfiguredAndDefaultEnvs = (config, componentName, envTypes) => {
13846
13926
  var _a, _b;
13847
13927
  const configuredEnvs = (_a = config.components[componentName].env) !== null && _a !== void 0 ? _a : {};
@@ -13849,13 +13929,33 @@ const getConfiguredAndDefaultEnvs = (config, componentName, envTypes) => {
13849
13929
  // these can be disabled with settimg them to `false`
13850
13930
  // this is the list of all not disabled envs. These are always returned
13851
13931
  const enabledDefaultEnvs = envTypes.filter((e) => configuredEnvs[e] !== false);
13852
- const configuredCustomEnvs = Object.entries((_b = config.components[componentName].env) !== null && _b !== void 0 ? _b : {})
13932
+ // envs declared project-wide (top-level `environments`): every
13933
+ // component deploys to them unless it opts out with `env.<name>: false`
13934
+ const declaredEnvs = Object.keys((_b = config.environments) !== null && _b !== void 0 ? _b : {})
13935
+ .filter((envName) => !(0, types_1.isKnowEnvType)(envName)) // default envs are already handled above
13936
+ .map((envName) => {
13937
+ const envType = (0, getEnvType_1.getDeclaredEnvType)(config.environments, envName);
13938
+ if (!envType) {
13939
+ throw new Error(`environment "${envName}" needs a type (dev, review, stage, prod) or an inherit`);
13940
+ }
13941
+ return [envName, envType];
13942
+ })
13943
+ .filter(([envName, envType]) => envTypes.includes(envType) && configuredEnvs[envName] !== false)
13944
+ .map(([envName]) => envName);
13945
+ // legacy: custom envs declared per component via their `type`
13946
+ const configuredCustomEnvs = Object.entries(configuredEnvs)
13853
13947
  .filter(([, config]) => config &&
13854
13948
  "type" in config &&
13855
13949
  config.type &&
13856
13950
  envTypes.includes(config.type))
13857
13951
  .map(([envName]) => envName);
13858
- return [...new Set([...enabledDefaultEnvs, ...configuredCustomEnvs])];
13952
+ return [
13953
+ ...new Set([
13954
+ ...enabledDefaultEnvs,
13955
+ ...declaredEnvs,
13956
+ ...configuredCustomEnvs,
13957
+ ]),
13958
+ ];
13859
13959
  };
13860
13960
  const getAllEnvs = (config, componentName) => {
13861
13961
  return getConfiguredAndDefaultEnvs(config, componentName, types_1.DEFAULT_ENV_TYPES);
@@ -13867,11 +13967,38 @@ const getAllEnvsInAllComponents = (config) => {
13867
13967
  ];
13868
13968
  };
13869
13969
  exports.getAllEnvsInAllComponents = getAllEnvsInAllComponents;
13970
+ /**
13971
+ * the resolved `on` of one env of a component (the project-wide `on`
13972
+ * of the env, falling back to the env type's default trigger)
13973
+ */
13974
+ const getEnvOnForComponentEnv = (config, componentName, env) => {
13975
+ var _a;
13976
+ const entry = (_a = config.components[componentName].env) === null || _a === void 0 ? void 0 : _a[env];
13977
+ return (0, getEnvOn_1.getEnvOn)(env, entry && entry !== false ? entry : {}, config.environments);
13978
+ };
13870
13979
  const getAllEnvsByTrigger = (config, componentName, trigger) => {
13871
- const envTypesByTrigger = (0, types_1.getEnvTypesByTrigger)(trigger);
13872
- return getConfiguredAndDefaultEnvs(config, componentName, envTypesByTrigger);
13980
+ return (0, exports.getAllEnvs)(config, componentName).filter((env) => (0, types_1.envTriggerEquals)(getEnvOnForComponentEnv(config, componentName, env), trigger));
13873
13981
  };
13874
13982
  exports.getAllEnvsByTrigger = getAllEnvsByTrigger;
13983
+ /**
13984
+ * all branch triggers declared by any env of any component (via
13985
+ * `on: { branch: "..." }`), deduplicated and sorted for deterministic
13986
+ * pipeline generation. Each gets its own pipeline next to the built-in
13987
+ * triggers.
13988
+ */
13989
+ const getConfiguredBranchTriggers = (config) => {
13990
+ const branches = new Set();
13991
+ for (const componentName of Object.keys(config.components)) {
13992
+ for (const env of (0, exports.getAllEnvs)(config, componentName)) {
13993
+ const on = getEnvOnForComponentEnv(config, componentName, env);
13994
+ if (on && (0, types_1.isBranchTrigger)(on)) {
13995
+ branches.add(on.branch);
13996
+ }
13997
+ }
13998
+ }
13999
+ return [...branches].sort().map((branch) => ({ branch }));
14000
+ };
14001
+ exports.getConfiguredBranchTriggers = getConfiguredBranchTriggers;
13875
14002
  //# sourceMappingURL=configruedEnvs.js.map
13876
14003
 
13877
14004
  /***/ }),
@@ -14202,53 +14329,237 @@ exports.getBuildInfoVariables = getBuildInfoVariables;
14202
14329
  Object.defineProperty(exports, "__esModule", ({ value: true }));
14203
14330
  exports.getEnvConfig = void 0;
14204
14331
  const lodash_es_1 = __nccwpck_require__(50210);
14332
+ const getEnvInherit_1 = __nccwpck_require__(52181);
14205
14333
  const getEnvConfig = (config, componentName, env) => {
14206
- var _a, _b;
14334
+ var _a;
14207
14335
  const defaultConfig = config.components[componentName];
14208
14336
  if (!defaultConfig) {
14209
14337
  throw new Error("unknown component " + componentName);
14210
14338
  }
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)(
14339
+ // env config is layered onto the component config: the per-component
14340
+ // overrides of inherited envs first (oldest ancestor first, see
14341
+ // `environments.<name>.inherit`), the env's own overrides last
14342
+ const chain = [...(0, getEnvInherit_1.getInheritedConfigChain)(config.environments, env), env];
14221
14343
  // mergeWith unfortunatly mutates the first object, so we need to clone it.
14222
14344
  // 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);
14345
+ const result = (0, lodash_es_1.cloneDeep)(defaultConfig);
14346
+ for (const chainEnv of chain) {
14347
+ const envCustomizations = (_a = defaultConfig.env) === null || _a === void 0 ? void 0 : _a[chainEnv];
14348
+ if (!envCustomizations || envCustomizations === false) {
14349
+ if (chainEnv === env && envCustomizations === false) {
14350
+ // env is disabled, still return the default config
14351
+ return defaultConfig;
14352
+ }
14353
+ continue;
14229
14354
  }
14230
- return undefined;
14231
- });
14355
+ /**
14356
+ * env config is merged with default. Arrays are not merged.
14357
+ * you can customize this by providing a function that takes the default value as argument
14358
+ */
14359
+ (0, lodash_es_1.mergeWith)(result,
14360
+ // `host` must stay unique per env and `type` is the inherited
14361
+ // env's identity — neither is inherited
14362
+ chainEnv === env
14363
+ ? envCustomizations
14364
+ : (0, lodash_es_1.omit)(envCustomizations, ["host", "type"]), (defaultValue, customValue, key, obj, source) => {
14365
+ // check if custom value is a function (and default is not),
14366
+ // we currently don't have config options, that are functions, but we might in the future (customJobs is an exception)
14367
+ if (typeof customValue === "function" &&
14368
+ typeof defaultValue !== "function") {
14369
+ return customValue(defaultValue);
14370
+ }
14371
+ return undefined;
14372
+ });
14373
+ }
14374
+ return result;
14232
14375
  };
14233
14376
  exports.getEnvConfig = getEnvConfig;
14234
14377
  //# sourceMappingURL=getEnvConfig.js.map
14235
14378
 
14236
14379
  /***/ }),
14237
14380
 
14381
+ /***/ 52181:
14382
+ /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => {
14383
+
14384
+ "use strict";
14385
+
14386
+ Object.defineProperty(exports, "__esModule", ({ value: true }));
14387
+ exports.getSecretsEnv = exports.getInheritedConfigChain = exports.normalizeEnvInherit = void 0;
14388
+ const config_1 = __nccwpck_require__(75591);
14389
+ const normalizeEnvInherit = (inherit) => {
14390
+ if (!inherit)
14391
+ return {};
14392
+ if (typeof inherit === "string") {
14393
+ return { config: inherit, secrets: inherit };
14394
+ }
14395
+ return {
14396
+ config: inherit.config,
14397
+ secrets: inherit.secrets === false ? undefined : inherit.secrets,
14398
+ };
14399
+ };
14400
+ exports.normalizeEnvInherit = normalizeEnvInherit;
14401
+ const assertValidInheritTarget = (environments, env, target) => {
14402
+ if (target === "local") {
14403
+ throw new Error(`environment "${env}" cannot inherit from "local" — local is not a deployment environment`);
14404
+ }
14405
+ if (!(0, config_1.isKnowEnvType)(target) && !(environments === null || environments === void 0 ? void 0 : environments[target])) {
14406
+ throw new Error(`environment "${env}" inherits from unknown environment "${target}"`);
14407
+ }
14408
+ };
14409
+ /**
14410
+ * the chain of envs whose per-component config overrides feed into
14411
+ * `env`, oldest ancestor first (env itself not included). Cycles and
14412
+ * unknown/`local` targets are an error.
14413
+ */
14414
+ const getInheritedConfigChain = (environments, env) => {
14415
+ var _a;
14416
+ const chain = [];
14417
+ const seen = new Set([env]);
14418
+ let current = env;
14419
+ for (;;) {
14420
+ const target = (0, exports.normalizeEnvInherit)((_a = environments === null || environments === void 0 ? void 0 : environments[current]) === null || _a === void 0 ? void 0 : _a.inherit).config;
14421
+ if (!target)
14422
+ break;
14423
+ assertValidInheritTarget(environments, current, target);
14424
+ if (seen.has(target)) {
14425
+ throw new Error(`environment "${env}" has a cyclic config inheritance via "${target}"`);
14426
+ }
14427
+ seen.add(target);
14428
+ chain.unshift(target);
14429
+ current = target;
14430
+ }
14431
+ return chain;
14432
+ };
14433
+ exports.getInheritedConfigChain = getInheritedConfigChain;
14434
+ /**
14435
+ * the env whose secret values `env` uses (follows `inherit` secret
14436
+ * sharing transitively; the env itself when it has its own secrets).
14437
+ * All secret variable names must be built with this env.
14438
+ */
14439
+ const getSecretsEnv = (environments, env) => {
14440
+ var _a;
14441
+ const seen = new Set([env]);
14442
+ let current = env;
14443
+ for (;;) {
14444
+ const target = (0, exports.normalizeEnvInherit)((_a = environments === null || environments === void 0 ? void 0 : environments[current]) === null || _a === void 0 ? void 0 : _a.inherit).secrets;
14445
+ if (!target)
14446
+ return current;
14447
+ assertValidInheritTarget(environments, current, target);
14448
+ if (seen.has(target)) {
14449
+ throw new Error(`environment "${env}" has a cyclic secrets inheritance via "${target}"`);
14450
+ }
14451
+ seen.add(target);
14452
+ current = target;
14453
+ }
14454
+ };
14455
+ exports.getSecretsEnv = getSecretsEnv;
14456
+ //# sourceMappingURL=getEnvInherit.js.map
14457
+
14458
+ /***/ }),
14459
+
14460
+ /***/ 13055:
14461
+ /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => {
14462
+
14463
+ "use strict";
14464
+
14465
+ Object.defineProperty(exports, "__esModule", ({ value: true }));
14466
+ exports.getEnvInstance = void 0;
14467
+ const bash_1 = __nccwpck_require__(94143);
14468
+ const bashExpressionPerPipelineType_1 = __nccwpck_require__(6495);
14469
+ const getEnvOn_1 = __nccwpck_require__(60601);
14470
+ const REVIEW_SLUG = {
14471
+ default: "unknown-review-slug",
14472
+ 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"; })`),
14473
+ // CL_PR_NUMBER is injected by the github backend at the workflow level
14474
+ github: new bash_1.BashExpression(`$([ -n "$CL_PR_NUMBER" ] && echo "pr$CL_PR_NUMBER" || echo "unknown")`),
14475
+ };
14476
+ const getEnvInstance = (envConfig, env, pipelineType, environments) => {
14477
+ // an env deploying per merge request is a review app — one dynamic
14478
+ // instance per MR, identified by the review slug
14479
+ if ((0, getEnvOn_1.getEnvOn)(env, envConfig, environments) === "mr") {
14480
+ return {
14481
+ type: "review",
14482
+ reviewSlug: (0, bashExpressionPerPipelineType_1.getBashExpressionPerPipelineType)(REVIEW_SLUG, pipelineType),
14483
+ };
14484
+ }
14485
+ return { type: "stable" };
14486
+ };
14487
+ exports.getEnvInstance = getEnvInstance;
14488
+ //# sourceMappingURL=getEnvInstance.js.map
14489
+
14490
+ /***/ }),
14491
+
14492
+ /***/ 60601:
14493
+ /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => {
14494
+
14495
+ "use strict";
14496
+
14497
+ Object.defineProperty(exports, "__esModule", ({ value: true }));
14498
+ exports.getEnvOn = void 0;
14499
+ const types_1 = __nccwpck_require__(63557);
14500
+ const getEnvType_1 = __nccwpck_require__(46080);
14501
+ const getDefaultOnForType = (envType) => {
14502
+ var _a;
14503
+ const triggers = types_1.ENV_TYPES[envType].triggers;
14504
+ return (_a = triggers[0]) !== null && _a !== void 0 ? _a : false;
14505
+ };
14506
+ /**
14507
+ * resolves when an env deploys: the project-wide `on` config of the env
14508
+ * (top-level `environments`), falling back to the default of its env
14509
+ * type (dev → mainBranch, review → mr, stage/prod → taggedRelease,
14510
+ * local → false).
14511
+ *
14512
+ * `on` is deliberately not configurable per component — an environment
14513
+ * deploys at one point in time for the whole project.
14514
+ */
14515
+ const getEnvOn = (env, envConfig, environments) => {
14516
+ var _a;
14517
+ const on = (_a = environments === null || environments === void 0 ? void 0 : environments[env]) === null || _a === void 0 ? void 0 : _a.on;
14518
+ if (on !== undefined)
14519
+ return on;
14520
+ return getDefaultOnForType((0, getEnvType_1.getEnvType)(env, envConfig, environments));
14521
+ };
14522
+ exports.getEnvOn = getEnvOn;
14523
+ //# sourceMappingURL=getEnvOn.js.map
14524
+
14525
+ /***/ }),
14526
+
14238
14527
  /***/ 46080:
14239
14528
  /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => {
14240
14529
 
14241
14530
  "use strict";
14242
14531
 
14243
14532
  Object.defineProperty(exports, "__esModule", ({ value: true }));
14244
- exports.getEnvType = void 0;
14533
+ exports.getEnvType = exports.getDeclaredEnvType = void 0;
14245
14534
  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)) {
14535
+ const getEnvInherit_1 = __nccwpck_require__(52181);
14536
+ /**
14537
+ * the type of a project-wide declared env: its explicit `type`, its
14538
+ * name (when it is itself an env type), or — for envs that inherit —
14539
+ * the type of the env they inherit their config from
14540
+ */
14541
+ const getDeclaredEnvType = (environments, env, seen = new Set()) => {
14542
+ const declared = environments === null || environments === void 0 ? void 0 : environments[env];
14543
+ if (declared === null || declared === void 0 ? void 0 : declared.type)
14544
+ return declared.type;
14545
+ if ((0, types_1.isKnowEnvType)(env))
14250
14546
  return env;
14547
+ const inheritTarget = (0, getEnvInherit_1.normalizeEnvInherit)(declared === null || declared === void 0 ? void 0 : declared.inherit).config;
14548
+ if (inheritTarget && !seen.has(inheritTarget)) {
14549
+ seen.add(inheritTarget);
14550
+ return (0, exports.getDeclaredEnvType)(environments, inheritTarget, seen);
14251
14551
  }
14552
+ return undefined;
14553
+ };
14554
+ exports.getDeclaredEnvType = getDeclaredEnvType;
14555
+ const getEnvType = (env, envConfig, environments) => {
14556
+ // legacy: per-component custom envs declare their type themselves
14557
+ if (envConfig.type)
14558
+ return envConfig.type;
14559
+ // project-wide declaration (top-level `environments`)
14560
+ const declaredType = (0, exports.getDeclaredEnvType)(environments, env);
14561
+ if (declaredType)
14562
+ return declaredType;
14252
14563
  throw new Error("unknown env type: " + env);
14253
14564
  };
14254
14565
  exports.getEnvType = getEnvType;
@@ -14274,8 +14585,11 @@ const getEnvironment = async (ctx) => {
14274
14585
  envType,
14275
14586
  fullName: envContext.fullName,
14276
14587
  slugPrefix: envContext.environmentSlugPrefix,
14588
+ instance: envContext.instance,
14277
14589
  reviewSlug: envContext.reviewSlug,
14278
14590
  slug: envContext.environmentSlug,
14591
+ autoStop: envContext.autoStop,
14592
+ secretsEnv: envContext.secretsEnv,
14279
14593
  ...variables,
14280
14594
  };
14281
14595
  };
@@ -14293,19 +14607,26 @@ Object.defineProperty(exports, "__esModule", ({ value: true }));
14293
14607
  exports.getEnvironmentContext = void 0;
14294
14608
  const bash_1 = __nccwpck_require__(94143);
14295
14609
  const getEnvConfig_1 = __nccwpck_require__(69578);
14610
+ const getEnvInstance_1 = __nccwpck_require__(13055);
14611
+ const getEnvInherit_1 = __nccwpck_require__(52181);
14296
14612
  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], "-");
14613
+ const getEnvironmentSlugPrefix = (env, instance) => {
14614
+ if (instance.type === "review") {
14615
+ return (0, bash_1.joinBashExpressions)([env, instance.reviewSlug], "-");
14301
14616
  }
14302
14617
  return env;
14303
14618
  };
14304
14619
  const getEnvironmentContext = ({ env, componentName, config, pipelineType, }) => {
14620
+ var _a, _b;
14305
14621
  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);
14622
+ const envType = (0, getEnvType_1.getEnvType)(env, envConfigRaw, config.environments);
14623
+ const instance = (0, getEnvInstance_1.getEnvInstance)(envConfigRaw, env, pipelineType, config.environments);
14624
+ // component-level autoStop overrides the project-wide environment
14625
+ // config; undefined means the env type's default applies
14626
+ const autoStop = envConfigRaw.autoStop !== undefined
14627
+ ? envConfigRaw.autoStop
14628
+ : (_b = (_a = config.environments) === null || _a === void 0 ? void 0 : _a[env]) === null || _b === void 0 ? void 0 : _b.autoStop;
14629
+ const environmentSlugPrefix = getEnvironmentSlugPrefix(env, instance);
14309
14630
  const environmentSlug = environmentSlugPrefix.concat(`-${componentName}`);
14310
14631
  const fullName = (0, bash_1.joinBashExpressions)([config.customerName, config.appName, environmentSlug], "-");
14311
14632
  return {
@@ -14314,10 +14635,13 @@ const getEnvironmentContext = ({ env, componentName, config, pipelineType, }) =>
14314
14635
  buildConfigRaw: envConfigRaw.build,
14315
14636
  environmentSlugPrefix,
14316
14637
  environmentSlug,
14317
- reviewSlug,
14638
+ instance,
14639
+ reviewSlug: instance.type === "review" ? instance.reviewSlug : null,
14318
14640
  pipelineType,
14319
14641
  fullName,
14320
14642
  envType,
14643
+ autoStop,
14644
+ secretsEnv: (0, getEnvInherit_1.getSecretsEnv)(config.environments, env),
14321
14645
  componentName,
14322
14646
  env,
14323
14647
  fullConfig: config,
@@ -14402,7 +14726,7 @@ const getEnvironmentVariables = async (ctx, options = {}) => {
14402
14726
  ...(0, envVars_1.normalizeSecretVarsConfig)((_f = envConfigRaw.vars) === null || _f === void 0 ? void 0 : _f.secret),
14403
14727
  ...additionalSecretKeys,
14404
14728
  ];
14405
- const secretEnvVars = (0, envVars_1.makeSecretEnvVarMapping)(env, componentName, secretEnvVarKeys);
14729
+ const secretEnvVars = (0, envVars_1.makeSecretEnvVarMapping)(environmentContext.secretsEnv, componentName, secretEnvVarKeys);
14406
14730
  const publicEnvVarsRawSanitized = (0, envVars_1.stringifyValues)(publicEnvVarsRaw);
14407
14731
  const publicEnvVarsUnresolved = Object.fromEntries(Object.entries(publicEnvVarsRawSanitized).map(([key, value]) => [
14408
14732
  key,
@@ -14436,11 +14760,11 @@ const getEnvironmentVariables = async (ctx, options = {}) => {
14436
14760
  envVars,
14437
14761
  secretEnvVarKeys,
14438
14762
  jobOnlyVars: {
14439
- build: await (0, transformJobOnlyVars_1.transformJobOnlyVars)(env, componentName, (buildConfigRaw &&
14763
+ build: await (0, transformJobOnlyVars_1.transformJobOnlyVars)(environmentContext.secretsEnv, componentName, (buildConfigRaw &&
14440
14764
  (0, types_1.isStandaloneBuildConfig)(buildConfigRaw) &&
14441
14765
  buildConfigRaw.jobVars) ||
14442
14766
  null),
14443
- deploy: await (0, transformJobOnlyVars_1.transformJobOnlyVars)(env, componentName, (deployConfigRaw && deployConfigRaw.jobVars) || null),
14767
+ deploy: await (0, transformJobOnlyVars_1.transformJobOnlyVars)(environmentContext.secretsEnv, componentName, (deployConfigRaw && deployConfigRaw.jobVars) || null),
14444
14768
  },
14445
14769
  };
14446
14770
  };
@@ -14452,7 +14776,7 @@ const addIndexVar = (vars) => ({
14452
14776
  ...vars,
14453
14777
  _ALL_ENV_VAR_KEYS: JSON.stringify(Object.keys(vars)),
14454
14778
  });
14455
- const getSecretVarNameForContext = (context, key) => (0, exports.getSecretVarName)(context.env, context.name, key);
14779
+ const getSecretVarNameForContext = (context, key) => (0, exports.getSecretVarName)(context.environment.secretsEnv, context.name, key);
14456
14780
  exports.getSecretVarNameForContext = getSecretVarNameForContext;
14457
14781
  //# sourceMappingURL=getEnvironmentVariables.js.map
14458
14782
 
@@ -14496,34 +14820,6 @@ exports.getLabels = getLabels;
14496
14820
 
14497
14821
  /***/ }),
14498
14822
 
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
14823
  /***/ 15707:
14528
14824
  /***/ (function(__unused_webpack_module, exports, __nccwpck_require__) {
14529
14825
 
@@ -14546,6 +14842,7 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14546
14842
  Object.defineProperty(exports, "__esModule", ({ value: true }));
14547
14843
  __exportStar(__nccwpck_require__(53373), exports);
14548
14844
  __exportStar(__nccwpck_require__(69578), exports);
14845
+ __exportStar(__nccwpck_require__(52181), exports);
14549
14846
  __exportStar(__nccwpck_require__(21494), exports);
14550
14847
  __exportStar(__nccwpck_require__(17429), exports);
14551
14848
  //# sourceMappingURL=index.js.map
@@ -15155,8 +15452,11 @@ class DeployJob extends jobs_1.CatladderJob {
15155
15452
  var _a, _b, _c, _d, _e, _f, _g;
15156
15453
  const hasDocker = (0, docker_1.requiresDockerBuild)(context);
15157
15454
  const isStoppable = (0, utils_1.contextIsStoppable)(context);
15455
+ // env-level autoStop config wins; the env type only supplies the
15456
+ // default (via the project-wide `autoStop` durations)
15457
+ const autoStopConfigured = context.environment.autoStop;
15158
15458
  const autoStopConfig = (0, autoStop_1.getAutoStopConfig)(context.fullConfig);
15159
- const autoStop = context.environment.envType === "review"
15459
+ const autoStopDefault = context.environment.envType === "review"
15160
15460
  ? // review envs read the lifetime from a pipeline variable, so
15161
15461
  // the pin label's workflow rule can override it to "never"
15162
15462
  autoStopConfig.pinLabel === false
@@ -15165,6 +15465,13 @@ class DeployJob extends jobs_1.CatladderJob {
15165
15465
  : context.environment.envType === "dev"
15166
15466
  ? autoStopConfig.dev
15167
15467
  : undefined;
15468
+ // NOTE: an explicit env-level autoStop is a literal — a review env
15469
+ // overriding it opts out of the pin-label variable indirection
15470
+ const autoStop = autoStopConfigured === undefined
15471
+ ? autoStopDefault
15472
+ : autoStopConfigured === false
15473
+ ? undefined
15474
+ : autoStopConfigured;
15168
15475
  const deployConfig = (_a = context.deploy) === null || _a === void 0 ? void 0 : _a.config;
15169
15476
  // if auto or manual is configured explicitly, use that
15170
15477
  const whenDeployDefined = deployConfig && deployConfig.when ? deployConfig.when : undefined;
@@ -15432,7 +15739,8 @@ exports.getArtifactsRegistryDockerUrl = getArtifactsRegistryDockerUrl;
15432
15739
  * 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
15740
  */
15434
15741
  const getArtifactsRegistryImageName = (context, lecacyReviewImageName = false) => {
15435
- if (lecacyReviewImageName && context.environment.envType !== "review") {
15742
+ const { instance } = context.environment;
15743
+ if (lecacyReviewImageName && instance.type !== "review") {
15436
15744
  throw new Error("lecacyReviewImageName is only allowed for review app");
15437
15745
  }
15438
15746
  const dockerUrl = (0, exports.getArtifactsRegistryDockerUrl)(context);
@@ -15440,8 +15748,8 @@ const getArtifactsRegistryImageName = (context, lecacyReviewImageName = false) =
15440
15748
  dockerUrl,
15441
15749
  context.env,
15442
15750
  context.name,
15443
- ...(context.environment.reviewSlug && !lecacyReviewImageName
15444
- ? [context.environment.reviewSlug]
15751
+ ...(instance.type === "review" && !lecacyReviewImageName
15752
+ ? [instance.reviewSlug]
15445
15753
  : []),
15446
15754
  ];
15447
15755
  return (0, bash_1.joinBashExpressions)(gcloudImagePath, "/");
@@ -15491,7 +15799,7 @@ const getDeleteUnusedImagesCommands = (context, keep = 0) => {
15491
15799
  ...getDeleteImageCommands(buildCacheImageName, 1),
15492
15800
  // because a recent version of catladder had no review-slug in the image name, we have to delete those as well
15493
15801
  // we can later remove this line in some months
15494
- ...(context.environment.envType === "review"
15802
+ ...(context.environment.instance.type === "review"
15495
15803
  ? (0, gitlab_1.allowFailureInScripts)(getDeleteImageCommands((0, exports.getArtifactsRegistryImageName)(context, true), 0))
15496
15804
  : []),
15497
15805
  ];
@@ -15512,12 +15820,18 @@ const gitlab_1 = __nccwpck_require__(86962);
15512
15820
  const artifactsRegistry_1 = __nccwpck_require__(44330);
15513
15821
  const cloudRunRevisions_1 = __nccwpck_require__(55698);
15514
15822
  const getRemoveOldRevisionsAndImagesCommand = (context, when) => {
15823
+ var _a;
15515
15824
  if (when === "onStop") {
15516
15825
  // service is already deleted, so we don't need to delete old revisions, just delete all images
15517
15826
  return (0, artifactsRegistry_1.getDeleteUnusedImagesCommands)(context);
15518
15827
  }
15519
- // this number only targets inactive revisions
15520
- const revisionsToKeep = context.environment.envType === "prod" ? 5 : 0;
15828
+ const deployConfig = (_a = context.deploy) === null || _a === void 0 ? void 0 : _a.config;
15829
+ const configuredRevisionsToKeep = deployConfig && deployConfig.type === "google-cloudrun"
15830
+ ? deployConfig.revisionsToKeep
15831
+ : undefined;
15832
+ // this number only targets inactive revisions; the env type only
15833
+ // supplies the default (prod keeps a rollback history)
15834
+ const revisionsToKeep = configuredRevisionsToKeep !== null && configuredRevisionsToKeep !== void 0 ? configuredRevisionsToKeep : (context.environment.envType === "prod" ? 5 : 0);
15521
15835
  // this number needs to be higher than inactive after deploy, so we add one
15522
15836
  const imagesToKeep = revisionsToKeep + 1;
15523
15837
  const deleteOldRevisionsCommands = (0, cloudRunRevisions_1.getDeleteUnusedRevisionsCommands)(context, revisionsToKeep);
@@ -16424,7 +16738,7 @@ const cloudRunExecutionUrl_1 = __nccwpck_require__(49963);
16424
16738
  const database_1 = __nccwpck_require__(11595);
16425
16739
  const getServiceName_1 = __nccwpck_require__(33102);
16426
16740
  exports.GCLOUD_DEPLOY_CREDENTIALS_KEY = "GCLOUD_DEPLOY_credentialsKey";
16427
- const getCloudSqlVariables = ({ deployConfigRaw, environmentSlugPrefix, env, componentName, fullConfig, }) => {
16741
+ const getCloudSqlVariables = ({ deployConfigRaw, environmentSlugPrefix, secretsEnv, componentName, fullConfig, }) => {
16428
16742
  var _a, _b, _c;
16429
16743
  if (deployConfigRaw && deployConfigRaw.cloudSql) {
16430
16744
  const DB_NAME = (0, utils_1.getFullDbName)(deployConfigRaw.cloudSql, fullConfig, environmentSlugPrefix, componentName);
@@ -16435,7 +16749,7 @@ const getCloudSqlVariables = ({ deployConfigRaw, environmentSlugPrefix, env, com
16435
16749
  CLOUD_SQL_INSTANCE_CONNECTION_NAME: deployConfigRaw.cloudSql.instanceConnectionName,
16436
16750
  DB_NAME: DB_NAME,
16437
16751
  DB_USER: (_b = deployConfigRaw.cloudSql.dbUser) !== null && _b !== void 0 ? _b : "postgres",
16438
- DB_PASSWORD: "$" + (0, context_1.getSecretVarName)(env, componentName, "DB_PASSWORD"),
16752
+ DB_PASSWORD: "$" + (0, context_1.getSecretVarName)(secretsEnv, componentName, "DB_PASSWORD"),
16439
16753
  };
16440
16754
  return {
16441
16755
  ...dbVars,
@@ -16908,7 +17222,7 @@ const fromComponentContext = (context) => {
16908
17222
  environmentSlugPrefix: context.environment.slugPrefix,
16909
17223
  componentName: context.name,
16910
17224
  env: context.env,
16911
- isReviewEnv: context.environment.envType === "review",
17225
+ isReviewEnv: context.environment.instance.type === "review",
16912
17226
  deployConfig: deployConfig && deployConfig.type === "google-cloudrun"
16913
17227
  ? deployConfig
16914
17228
  : null,
@@ -16920,7 +17234,7 @@ const fromEnvironmentContext = (context) => ({
16920
17234
  environmentSlugPrefix: context.environmentSlugPrefix,
16921
17235
  componentName: context.componentName,
16922
17236
  env: context.env,
16923
- isReviewEnv: context.envType === "review",
17237
+ isReviewEnv: context.instance.type === "review",
16924
17238
  deployConfig: context.deployConfigRaw || null,
16925
17239
  config: context.fullConfig,
16926
17240
  });
@@ -17374,9 +17688,9 @@ exports.KUBERNETES_DEPLOY_TYPE = {
17374
17688
  jobs: deployJob_1.createKubernetesDeployJobs,
17375
17689
  defaults: () => ({}),
17376
17690
  additionalSecretKeys: additionalSecretKeys_1.additionalKubernetesSecretKeys,
17377
- getAdditionalEnvVars: ({ componentName, fullConfig, deployConfigRaw, env, reviewSlug, envType, }) => {
17691
+ getAdditionalEnvVars: ({ componentName, fullConfig, deployConfigRaw, env, instance, }) => {
17378
17692
  var _a;
17379
- const KUBE_APP_NAME_PREFIX = envType === "review" && reviewSlug ? reviewSlug.concat("-") : "";
17693
+ const KUBE_APP_NAME_PREFIX = instance.type === "review" ? instance.reviewSlug.concat("-") : "";
17380
17694
  const KUBE_APP_NAME = KUBE_APP_NAME_PREFIX.concat(componentName);
17381
17695
  const KUBE_NAMESPACE = (0, __1.getKubernetesNamespace)(fullConfig, env);
17382
17696
  const componentSlug = (0, slugify_1.default)(componentName);
@@ -17385,7 +17699,7 @@ exports.KUBERNETES_DEPLOY_TYPE = {
17385
17699
  "panter.cloud";
17386
17700
  const HOSTNAME_INTERNAL = (0, bash_1.joinBashExpressions)([
17387
17701
  componentSlug,
17388
- ...(envType === "review" && reviewSlug ? [reviewSlug] : []),
17702
+ ...(instance.type === "review" ? [instance.reviewSlug] : []),
17389
17703
  env,
17390
17704
  fullConfig.appName,
17391
17705
  fullConfig.customerName,
@@ -17743,7 +18057,8 @@ const createPagesDeployJobs = async (context) => {
17743
18057
  throw new Error("deploy config is not pages");
17744
18058
  }
17745
18059
  const onGithub = context.pipelineType === "github";
17746
- if (onGithub && context.environment.envType === "review") {
18060
+ const isReviewApp = context.environment.instance.type === "review";
18061
+ if (onGithub && isReviewApp) {
17747
18062
  // github pages serves ONE site per repository and deploy-pages
17748
18063
  // replaces all of it — there is no path_prefix equivalent, so per-PR
17749
18064
  // previews cannot exist (deploy-pages has a `preview` input, but it
@@ -17754,7 +18069,7 @@ const createPagesDeployJobs = async (context) => {
17754
18069
  }
17755
18070
  // review environments publish under their own path prefix (gitlab
17756
18071
  // parallel deployments) — every merge request gets a site preview
17757
- const pathPrefix = context.environment.envType === "review" ? "mr-$CI_MERGE_REQUEST_IID" : "";
18072
+ const pathPrefix = isReviewApp ? "mr-$CI_MERGE_REQUEST_IID" : "";
17758
18073
  const publishDir = (_b = deployConfig.publishDir) !== null && _b !== void 0 ? _b : "public";
17759
18074
  const packageManagerInstall = await (0, packageManagerInstall_1.getPackageManagerInstall)(context, {
17760
18075
  noCustomPostInstall: true,
@@ -19451,12 +19766,22 @@ Object.defineProperty(exports, "__esModule", ({ value: true }));
19451
19766
  "use strict";
19452
19767
 
19453
19768
  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;
19769
+ exports.isKnowEnvType = exports.DEFAULT_ENV_TYPES = exports.DEFAULT_ENVS = exports.ENV_TYPES = exports.envTriggerEquals = exports.isBranchTrigger = exports.ALL_PIPELINE_TRIGGERS = void 0;
19455
19770
  exports.ALL_PIPELINE_TRIGGERS = [
19456
19771
  "mainBranch",
19457
19772
  "mr",
19458
19773
  "taggedRelease",
19459
19774
  ];
19775
+ const isBranchTrigger = (trigger) => typeof trigger === "object" && trigger !== null && "branch" in trigger;
19776
+ exports.isBranchTrigger = isBranchTrigger;
19777
+ const envTriggerEquals = (a, b) => {
19778
+ if (a === undefined || a === false)
19779
+ return false;
19780
+ if ((0, exports.isBranchTrigger)(b))
19781
+ return (0, exports.isBranchTrigger)(a) && a.branch === b.branch;
19782
+ return a === b;
19783
+ };
19784
+ exports.envTriggerEquals = envTriggerEquals;
19460
19785
  /**
19461
19786
  * all env types with their trigger.
19462
19787
  * Each env type has a default env with the same name which is always included
@@ -19478,15 +19803,6 @@ exports.ENV_TYPES = {
19478
19803
  triggers: [],
19479
19804
  },
19480
19805
  };
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
19806
  exports.DEFAULT_ENVS = Object.keys(exports.ENV_TYPES);
19491
19807
  exports.DEFAULT_ENV_TYPES = exports.DEFAULT_ENVS;
19492
19808
  const isKnowEnvType = (env) => {