@catladder/cli 5.1.3 → 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 (92) 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 +419 -106
  10. package/dist/bundles/cli/index.js +449 -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 +42 -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 +24 -1
  25. package/dist/packages/pipeline/src/backends/gitlab/createGitlabPipeline.js.map +1 -1
  26. package/dist/packages/pipeline/src/backends/gitlab/gitlabReleaseJobs.d.ts +17 -0
  27. package/dist/packages/pipeline/src/backends/gitlab/gitlabReleaseJobs.js +13 -0
  28. package/dist/packages/pipeline/src/backends/gitlab/gitlabReleaseJobs.js.map +1 -1
  29. package/dist/packages/pipeline/src/config/configruedEnvs.d.ts +9 -2
  30. package/dist/packages/pipeline/src/config/configruedEnvs.js +54 -5
  31. package/dist/packages/pipeline/src/config/configruedEnvs.js.map +1 -1
  32. package/dist/packages/pipeline/src/context/createAllComponentsContext.d.ts +2 -2
  33. package/dist/packages/pipeline/src/context/createComponentContext.d.ts +2 -2
  34. package/dist/packages/pipeline/src/context/createWorkspaceContext.d.ts +2 -2
  35. package/dist/packages/pipeline/src/context/getEnvConfig.js +35 -19
  36. package/dist/packages/pipeline/src/context/getEnvConfig.js.map +1 -1
  37. package/dist/packages/pipeline/src/context/getEnvInherit.d.ts +20 -0
  38. package/dist/packages/pipeline/src/context/getEnvInherit.js +72 -0
  39. package/dist/packages/pipeline/src/context/getEnvInherit.js.map +1 -0
  40. package/dist/packages/pipeline/src/context/getEnvInstance.d.ts +4 -0
  41. package/dist/packages/pipeline/src/context/{getReviewSlug.js → getEnvInstance.js} +13 -9
  42. package/dist/packages/pipeline/src/context/getEnvInstance.js.map +1 -0
  43. package/dist/packages/pipeline/src/context/getEnvOn.d.ts +13 -0
  44. package/dist/packages/pipeline/src/context/getEnvOn.js +28 -0
  45. package/dist/packages/pipeline/src/context/getEnvOn.js.map +1 -0
  46. package/dist/packages/pipeline/src/context/getEnvType.d.ts +8 -2
  47. package/dist/packages/pipeline/src/context/getEnvType.js +27 -5
  48. package/dist/packages/pipeline/src/context/getEnvType.js.map +1 -1
  49. package/dist/packages/pipeline/src/context/getEnvironment.js +3 -0
  50. package/dist/packages/pipeline/src/context/getEnvironment.js.map +1 -1
  51. package/dist/packages/pipeline/src/context/getEnvironmentContext.js +18 -8
  52. package/dist/packages/pipeline/src/context/getEnvironmentContext.js.map +1 -1
  53. package/dist/packages/pipeline/src/context/getEnvironmentVariables.d.ts +1 -1
  54. package/dist/packages/pipeline/src/context/getEnvironmentVariables.js +4 -4
  55. package/dist/packages/pipeline/src/context/getEnvironmentVariables.js.map +1 -1
  56. package/dist/packages/pipeline/src/context/index.d.ts +1 -0
  57. package/dist/packages/pipeline/src/context/index.js +1 -0
  58. package/dist/packages/pipeline/src/context/index.js.map +1 -1
  59. package/dist/packages/pipeline/src/deploy/base/deploy.js +11 -1
  60. package/dist/packages/pipeline/src/deploy/base/deploy.js.map +1 -1
  61. package/dist/packages/pipeline/src/deploy/cloudRun/artifactsRegistry.js +5 -4
  62. package/dist/packages/pipeline/src/deploy/cloudRun/artifactsRegistry.js.map +1 -1
  63. package/dist/packages/pipeline/src/deploy/cloudRun/cleanup.js +8 -2
  64. package/dist/packages/pipeline/src/deploy/cloudRun/cleanup.js.map +1 -1
  65. package/dist/packages/pipeline/src/deploy/cloudRun/index.js +2 -2
  66. package/dist/packages/pipeline/src/deploy/cloudRun/index.js.map +1 -1
  67. package/dist/packages/pipeline/src/deploy/cloudRun/utils/getServiceName.js +2 -2
  68. package/dist/packages/pipeline/src/deploy/cloudRun/utils/getServiceName.js.map +1 -1
  69. package/dist/packages/pipeline/src/deploy/kubernetes/index.js +3 -3
  70. package/dist/packages/pipeline/src/deploy/kubernetes/index.js.map +1 -1
  71. package/dist/packages/pipeline/src/deploy/pages/deployJob.js +3 -2
  72. package/dist/packages/pipeline/src/deploy/pages/deployJob.js.map +1 -1
  73. package/dist/packages/pipeline/src/deploy/types/googleCloudRun.d.ts +9 -0
  74. package/dist/packages/pipeline/src/types/config.d.ts +126 -9
  75. package/dist/packages/pipeline/src/types/config.js +11 -10
  76. package/dist/packages/pipeline/src/types/config.js.map +1 -1
  77. package/dist/packages/pipeline/src/types/context.d.ts +22 -3
  78. package/dist/packages/pipeline/src/types/context.js.map +1 -1
  79. package/dist/packages/pipeline/src/types/environmentContext.d.ts +29 -0
  80. package/dist/packages/pipeline/src/types/gitlab-types.d.ts +29 -1
  81. package/dist/skills/catladder-config/SKILL.md +34 -2
  82. package/dist/skills/catladder-deploys/SKILL.md +5 -0
  83. package/dist/skills/catladder-pipelines/SKILL.md +42 -1
  84. package/dist/skills/catladder-secrets/SKILL.md +6 -0
  85. package/dist/tsconfig.tsbuildinfo +1 -1
  86. package/package.json +1 -1
  87. package/src/commands/project/commandPortForward.ts +1 -1
  88. package/src/commands/project/secrets/scope.ts +38 -2
  89. package/src/config/getProjectConfig.ts +9 -1
  90. package/src/secrets/github.ts +8 -5
  91. package/dist/packages/pipeline/src/context/getReviewSlug.d.ts +0 -4
  92. 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,
@@ -11355,9 +11419,20 @@ const createGitlabPipelineWithDefaults = ({ image, variables, before_script, aut
11355
11419
  ],
11356
11420
  workflow: {
11357
11421
  name: "$PIPELINE_ICON $PIPELINE_NAME",
11422
+ // superseded pipelines must actually die: "interruptible" cancels
11423
+ // every interruptible job, while gitlab's default "conservative"
11424
+ // lets one started `interruptible: false` job shield the whole
11425
+ // pipeline from cancellation. The rules below opt the pipelines
11426
+ // that must run to completion back out.
11427
+ // NOTE: this only tunes the project setting "Auto-cancel
11428
+ // redundant pipelines" — with that setting off nothing cancels.
11429
+ auto_cancel: { on_new_commit: "interruptible" },
11358
11430
  rules: [
11359
11431
  {
11360
11432
  if: '$CI_PIPELINE_SOURCE == "trigger"',
11433
+ // an agent run is not redundant work: a commit landing while
11434
+ // it thinks must not kill it
11435
+ auto_cancel: { on_new_commit: "none" },
11361
11436
  variables: {
11362
11437
  PIPELINE_ICON: "🤖",
11363
11438
  PIPELINE_NAME: "Thinking...",
@@ -11386,6 +11461,11 @@ const createGitlabPipelineWithDefaults = ({ image, variables, before_script, aut
11386
11461
  },
11387
11462
  {
11388
11463
  if: rules_1.RULE_IS_TAGGED_RELEASE.if,
11464
+ // a release pipeline always runs through: tags are immutable,
11465
+ // so this only bites when someone force-moves one — and a
11466
+ // release cancelled halfway leaves the version published in
11467
+ // some places and missing in others
11468
+ auto_cancel: { on_new_commit: "none" },
11389
11469
  variables: {
11390
11470
  PIPELINE_ICON: "🐱📦",
11391
11471
  PIPELINE_NAME: "Release $CI_COMMIT_TAG",
@@ -11398,6 +11478,13 @@ const createGitlabPipelineWithDefaults = ({ image, variables, before_script, aut
11398
11478
  PIPELINE_NAME: "Main - $CI_COMMIT_TITLE",
11399
11479
  },
11400
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
+ })),
11401
11488
  {
11402
11489
  when: "always", // fallback
11403
11490
  variables: {
@@ -11502,6 +11589,12 @@ mainBranchJobNames) => {
11502
11589
  image: releaseImage,
11503
11590
  script: [method.script],
11504
11591
  after_script: [EXPIRED_TOKEN_HELP],
11592
+ // a release in flight must never be killed by a new commit — a
11593
+ // half-published version (tag pushed, npm publish missing) is not
11594
+ // recoverable by re-running. Under gitlab's default "conservative"
11595
+ // auto-cancel this also shields the rest of the pipeline, but only
11596
+ // once the release actually started, which is late by construction.
11597
+ interruptible: false,
11505
11598
  };
11506
11599
  const forceReleaseJob = {
11507
11600
  ["⚠️ force create release"]: {
@@ -11525,6 +11618,10 @@ mainBranchJobNames) => {
11525
11618
  stage: "test",
11526
11619
  image: releaseImage,
11527
11620
  script: [method.checkScript],
11621
+ // a redundant report is worthless: without this the job
11622
+ // defaults to interruptible: false and, once started, stops
11623
+ // gitlab from auto-cancelling the superseded MR pipeline
11624
+ interruptible: true,
11528
11625
  allow_failure: true,
11529
11626
  artifacts: {
11530
11627
  paths: ["changeset-report.md"],
@@ -11594,6 +11691,9 @@ mainBranchJobNames) => {
11594
11691
  script: [
11595
11692
  `echo "release queued — '${exports.RELEASE_EXECUTOR_JOB_NAME}' runs it as soon as every other job in this pipeline succeeded"`,
11596
11693
  ],
11694
+ // the recorded intent must survive a new commit: cancelling it
11695
+ // would silently drop the queued release
11696
+ interruptible: false,
11597
11697
  needs: [],
11598
11698
  // allow_failure keeps the unclicked button from blocking the
11599
11699
  // pipeline AND makes the executor's needs treat it as optional
@@ -13811,8 +13911,10 @@ exports.createCatenvContext = createCatenvContext;
13811
13911
  "use strict";
13812
13912
 
13813
13913
  Object.defineProperty(exports, "__esModule", ({ value: true }));
13814
- exports.getAllEnvsByTrigger = exports.getAllEnvsInAllComponents = exports.getAllEnvs = void 0;
13914
+ exports.getConfiguredBranchTriggers = exports.getAllEnvsByTrigger = exports.getAllEnvsInAllComponents = exports.getAllEnvs = void 0;
13815
13915
  const types_1 = __nccwpck_require__(63557);
13916
+ const getEnvOn_1 = __nccwpck_require__(60601);
13917
+ const getEnvType_1 = __nccwpck_require__(46080);
13816
13918
  const getConfiguredAndDefaultEnvs = (config, componentName, envTypes) => {
13817
13919
  var _a, _b;
13818
13920
  const configuredEnvs = (_a = config.components[componentName].env) !== null && _a !== void 0 ? _a : {};
@@ -13820,13 +13922,33 @@ const getConfiguredAndDefaultEnvs = (config, componentName, envTypes) => {
13820
13922
  // these can be disabled with settimg them to `false`
13821
13923
  // this is the list of all not disabled envs. These are always returned
13822
13924
  const enabledDefaultEnvs = envTypes.filter((e) => configuredEnvs[e] !== false);
13823
- 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)
13824
13940
  .filter(([, config]) => config &&
13825
13941
  "type" in config &&
13826
13942
  config.type &&
13827
13943
  envTypes.includes(config.type))
13828
13944
  .map(([envName]) => envName);
13829
- return [...new Set([...enabledDefaultEnvs, ...configuredCustomEnvs])];
13945
+ return [
13946
+ ...new Set([
13947
+ ...enabledDefaultEnvs,
13948
+ ...declaredEnvs,
13949
+ ...configuredCustomEnvs,
13950
+ ]),
13951
+ ];
13830
13952
  };
13831
13953
  const getAllEnvs = (config, componentName) => {
13832
13954
  return getConfiguredAndDefaultEnvs(config, componentName, types_1.DEFAULT_ENV_TYPES);
@@ -13838,11 +13960,38 @@ const getAllEnvsInAllComponents = (config) => {
13838
13960
  ];
13839
13961
  };
13840
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
+ };
13841
13972
  const getAllEnvsByTrigger = (config, componentName, trigger) => {
13842
- const envTypesByTrigger = (0, types_1.getEnvTypesByTrigger)(trigger);
13843
- return getConfiguredAndDefaultEnvs(config, componentName, envTypesByTrigger);
13973
+ return (0, exports.getAllEnvs)(config, componentName).filter((env) => (0, types_1.envTriggerEquals)(getEnvOnForComponentEnv(config, componentName, env), trigger));
13844
13974
  };
13845
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;
13846
13995
  //# sourceMappingURL=configruedEnvs.js.map
13847
13996
 
13848
13997
  /***/ }),
@@ -14173,53 +14322,237 @@ exports.getBuildInfoVariables = getBuildInfoVariables;
14173
14322
  Object.defineProperty(exports, "__esModule", ({ value: true }));
14174
14323
  exports.getEnvConfig = void 0;
14175
14324
  const lodash_es_1 = __nccwpck_require__(50210);
14325
+ const getEnvInherit_1 = __nccwpck_require__(52181);
14176
14326
  const getEnvConfig = (config, componentName, env) => {
14177
- var _a, _b;
14327
+ var _a;
14178
14328
  const defaultConfig = config.components[componentName];
14179
14329
  if (!defaultConfig) {
14180
14330
  throw new Error("unknown component " + componentName);
14181
14331
  }
14182
- const envCustomizations = (_b = (_a = defaultConfig.env) === null || _a === void 0 ? void 0 : _a[env]) !== null && _b !== void 0 ? _b : {};
14183
- if (envCustomizations === false) {
14184
- // env is disabled, still return the default config
14185
- return defaultConfig;
14186
- }
14187
- /**
14188
- * env config is merged with default. Arrays are not merged.
14189
- * you can customize this by providing a function that takes the default value as argument
14190
- */
14191
- 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];
14192
14336
  // mergeWith unfortunatly mutates the first object, so we need to clone it.
14193
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)
14194
- (0, lodash_es_1.cloneDeep)(defaultConfig), envCustomizations, (defaultValue, customValue, key, obj, source) => {
14195
- // check if custom value is a function (and default is not),
14196
- // we currently don't have config options, that are functions, but we might in the future (customJobs is an exception)
14197
- if (typeof customValue === "function" &&
14198
- typeof defaultValue !== "function") {
14199
- 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;
14200
14347
  }
14201
- return undefined;
14202
- });
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;
14203
14368
  };
14204
14369
  exports.getEnvConfig = getEnvConfig;
14205
14370
  //# sourceMappingURL=getEnvConfig.js.map
14206
14371
 
14207
14372
  /***/ }),
14208
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
+
14209
14520
  /***/ 46080:
14210
14521
  /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => {
14211
14522
 
14212
14523
  "use strict";
14213
14524
 
14214
14525
  Object.defineProperty(exports, "__esModule", ({ value: true }));
14215
- exports.getEnvType = void 0;
14526
+ exports.getEnvType = exports.getDeclaredEnvType = void 0;
14216
14527
  const types_1 = __nccwpck_require__(63557);
14217
- const getEnvType = (env, envConfig) => {
14218
- if (envConfig.type)
14219
- return envConfig.type;
14220
- 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))
14221
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);
14222
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;
14223
14556
  throw new Error("unknown env type: " + env);
14224
14557
  };
14225
14558
  exports.getEnvType = getEnvType;
@@ -14245,8 +14578,11 @@ const getEnvironment = async (ctx) => {
14245
14578
  envType,
14246
14579
  fullName: envContext.fullName,
14247
14580
  slugPrefix: envContext.environmentSlugPrefix,
14581
+ instance: envContext.instance,
14248
14582
  reviewSlug: envContext.reviewSlug,
14249
14583
  slug: envContext.environmentSlug,
14584
+ autoStop: envContext.autoStop,
14585
+ secretsEnv: envContext.secretsEnv,
14250
14586
  ...variables,
14251
14587
  };
14252
14588
  };
@@ -14264,19 +14600,26 @@ Object.defineProperty(exports, "__esModule", ({ value: true }));
14264
14600
  exports.getEnvironmentContext = void 0;
14265
14601
  const bash_1 = __nccwpck_require__(94143);
14266
14602
  const getEnvConfig_1 = __nccwpck_require__(69578);
14603
+ const getEnvInstance_1 = __nccwpck_require__(13055);
14604
+ const getEnvInherit_1 = __nccwpck_require__(52181);
14267
14605
  const getEnvType_1 = __nccwpck_require__(46080);
14268
- const getReviewSlug_1 = __nccwpck_require__(77360);
14269
- const getEnvironmentSlugPrefix = (env, reviewSlug) => {
14270
- if (reviewSlug) {
14271
- 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], "-");
14272
14609
  }
14273
14610
  return env;
14274
14611
  };
14275
14612
  const getEnvironmentContext = ({ env, componentName, config, pipelineType, }) => {
14613
+ var _a, _b;
14276
14614
  const envConfigRaw = (0, getEnvConfig_1.getEnvConfig)(config, componentName, env);
14277
- const envType = (0, getEnvType_1.getEnvType)(env, envConfigRaw);
14278
- const reviewSlug = (0, getReviewSlug_1.getReviewSlug)(envConfigRaw, env, pipelineType);
14279
- 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);
14280
14623
  const environmentSlug = environmentSlugPrefix.concat(`-${componentName}`);
14281
14624
  const fullName = (0, bash_1.joinBashExpressions)([config.customerName, config.appName, environmentSlug], "-");
14282
14625
  return {
@@ -14285,10 +14628,13 @@ const getEnvironmentContext = ({ env, componentName, config, pipelineType, }) =>
14285
14628
  buildConfigRaw: envConfigRaw.build,
14286
14629
  environmentSlugPrefix,
14287
14630
  environmentSlug,
14288
- reviewSlug,
14631
+ instance,
14632
+ reviewSlug: instance.type === "review" ? instance.reviewSlug : null,
14289
14633
  pipelineType,
14290
14634
  fullName,
14291
14635
  envType,
14636
+ autoStop,
14637
+ secretsEnv: (0, getEnvInherit_1.getSecretsEnv)(config.environments, env),
14292
14638
  componentName,
14293
14639
  env,
14294
14640
  fullConfig: config,
@@ -14373,7 +14719,7 @@ const getEnvironmentVariables = async (ctx, options = {}) => {
14373
14719
  ...(0, envVars_1.normalizeSecretVarsConfig)((_f = envConfigRaw.vars) === null || _f === void 0 ? void 0 : _f.secret),
14374
14720
  ...additionalSecretKeys,
14375
14721
  ];
14376
- const secretEnvVars = (0, envVars_1.makeSecretEnvVarMapping)(env, componentName, secretEnvVarKeys);
14722
+ const secretEnvVars = (0, envVars_1.makeSecretEnvVarMapping)(environmentContext.secretsEnv, componentName, secretEnvVarKeys);
14377
14723
  const publicEnvVarsRawSanitized = (0, envVars_1.stringifyValues)(publicEnvVarsRaw);
14378
14724
  const publicEnvVarsUnresolved = Object.fromEntries(Object.entries(publicEnvVarsRawSanitized).map(([key, value]) => [
14379
14725
  key,
@@ -14407,11 +14753,11 @@ const getEnvironmentVariables = async (ctx, options = {}) => {
14407
14753
  envVars,
14408
14754
  secretEnvVarKeys,
14409
14755
  jobOnlyVars: {
14410
- build: await (0, transformJobOnlyVars_1.transformJobOnlyVars)(env, componentName, (buildConfigRaw &&
14756
+ build: await (0, transformJobOnlyVars_1.transformJobOnlyVars)(environmentContext.secretsEnv, componentName, (buildConfigRaw &&
14411
14757
  (0, types_1.isStandaloneBuildConfig)(buildConfigRaw) &&
14412
14758
  buildConfigRaw.jobVars) ||
14413
14759
  null),
14414
- 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),
14415
14761
  },
14416
14762
  };
14417
14763
  };
@@ -14423,7 +14769,7 @@ const addIndexVar = (vars) => ({
14423
14769
  ...vars,
14424
14770
  _ALL_ENV_VAR_KEYS: JSON.stringify(Object.keys(vars)),
14425
14771
  });
14426
- 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);
14427
14773
  exports.getSecretVarNameForContext = getSecretVarNameForContext;
14428
14774
  //# sourceMappingURL=getEnvironmentVariables.js.map
14429
14775
 
@@ -14467,34 +14813,6 @@ exports.getLabels = getLabels;
14467
14813
 
14468
14814
  /***/ }),
14469
14815
 
14470
- /***/ 77360:
14471
- /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => {
14472
-
14473
- "use strict";
14474
-
14475
- Object.defineProperty(exports, "__esModule", ({ value: true }));
14476
- exports.getReviewSlug = void 0;
14477
- const bash_1 = __nccwpck_require__(94143);
14478
- const bashExpressionPerPipelineType_1 = __nccwpck_require__(6495);
14479
- const getEnvType_1 = __nccwpck_require__(46080);
14480
- const REVIEW_SLUG = {
14481
- default: "unknown-review-slug",
14482
- 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"; })`),
14483
- // CL_PR_NUMBER is injected by the github backend at the workflow level
14484
- github: new bash_1.BashExpression(`$([ -n "$CL_PR_NUMBER" ] && echo "pr$CL_PR_NUMBER" || echo "unknown")`),
14485
- };
14486
- const getReviewSlug = (envConfig, env, pipelineType) => {
14487
- const envType = (0, getEnvType_1.getEnvType)(env, envConfig);
14488
- if (envType === "review") {
14489
- return (0, bashExpressionPerPipelineType_1.getBashExpressionPerPipelineType)(REVIEW_SLUG, pipelineType);
14490
- }
14491
- return null; // not a review app;
14492
- };
14493
- exports.getReviewSlug = getReviewSlug;
14494
- //# sourceMappingURL=getReviewSlug.js.map
14495
-
14496
- /***/ }),
14497
-
14498
14816
  /***/ 15707:
14499
14817
  /***/ (function(__unused_webpack_module, exports, __nccwpck_require__) {
14500
14818
 
@@ -14517,6 +14835,7 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14517
14835
  Object.defineProperty(exports, "__esModule", ({ value: true }));
14518
14836
  __exportStar(__nccwpck_require__(53373), exports);
14519
14837
  __exportStar(__nccwpck_require__(69578), exports);
14838
+ __exportStar(__nccwpck_require__(52181), exports);
14520
14839
  __exportStar(__nccwpck_require__(21494), exports);
14521
14840
  __exportStar(__nccwpck_require__(17429), exports);
14522
14841
  //# sourceMappingURL=index.js.map
@@ -15126,8 +15445,11 @@ class DeployJob extends jobs_1.CatladderJob {
15126
15445
  var _a, _b, _c, _d, _e, _f, _g;
15127
15446
  const hasDocker = (0, docker_1.requiresDockerBuild)(context);
15128
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;
15129
15451
  const autoStopConfig = (0, autoStop_1.getAutoStopConfig)(context.fullConfig);
15130
- const autoStop = context.environment.envType === "review"
15452
+ const autoStopDefault = context.environment.envType === "review"
15131
15453
  ? // review envs read the lifetime from a pipeline variable, so
15132
15454
  // the pin label's workflow rule can override it to "never"
15133
15455
  autoStopConfig.pinLabel === false
@@ -15136,6 +15458,13 @@ class DeployJob extends jobs_1.CatladderJob {
15136
15458
  : context.environment.envType === "dev"
15137
15459
  ? autoStopConfig.dev
15138
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;
15139
15468
  const deployConfig = (_a = context.deploy) === null || _a === void 0 ? void 0 : _a.config;
15140
15469
  // if auto or manual is configured explicitly, use that
15141
15470
  const whenDeployDefined = deployConfig && deployConfig.when ? deployConfig.when : undefined;
@@ -15403,7 +15732,8 @@ exports.getArtifactsRegistryDockerUrl = getArtifactsRegistryDockerUrl;
15403
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
15404
15733
  */
15405
15734
  const getArtifactsRegistryImageName = (context, lecacyReviewImageName = false) => {
15406
- if (lecacyReviewImageName && context.environment.envType !== "review") {
15735
+ const { instance } = context.environment;
15736
+ if (lecacyReviewImageName && instance.type !== "review") {
15407
15737
  throw new Error("lecacyReviewImageName is only allowed for review app");
15408
15738
  }
15409
15739
  const dockerUrl = (0, exports.getArtifactsRegistryDockerUrl)(context);
@@ -15411,8 +15741,8 @@ const getArtifactsRegistryImageName = (context, lecacyReviewImageName = false) =
15411
15741
  dockerUrl,
15412
15742
  context.env,
15413
15743
  context.name,
15414
- ...(context.environment.reviewSlug && !lecacyReviewImageName
15415
- ? [context.environment.reviewSlug]
15744
+ ...(instance.type === "review" && !lecacyReviewImageName
15745
+ ? [instance.reviewSlug]
15416
15746
  : []),
15417
15747
  ];
15418
15748
  return (0, bash_1.joinBashExpressions)(gcloudImagePath, "/");
@@ -15462,7 +15792,7 @@ const getDeleteUnusedImagesCommands = (context, keep = 0) => {
15462
15792
  ...getDeleteImageCommands(buildCacheImageName, 1),
15463
15793
  // because a recent version of catladder had no review-slug in the image name, we have to delete those as well
15464
15794
  // we can later remove this line in some months
15465
- ...(context.environment.envType === "review"
15795
+ ...(context.environment.instance.type === "review"
15466
15796
  ? (0, gitlab_1.allowFailureInScripts)(getDeleteImageCommands((0, exports.getArtifactsRegistryImageName)(context, true), 0))
15467
15797
  : []),
15468
15798
  ];
@@ -15483,12 +15813,18 @@ const gitlab_1 = __nccwpck_require__(86962);
15483
15813
  const artifactsRegistry_1 = __nccwpck_require__(44330);
15484
15814
  const cloudRunRevisions_1 = __nccwpck_require__(55698);
15485
15815
  const getRemoveOldRevisionsAndImagesCommand = (context, when) => {
15816
+ var _a;
15486
15817
  if (when === "onStop") {
15487
15818
  // service is already deleted, so we don't need to delete old revisions, just delete all images
15488
15819
  return (0, artifactsRegistry_1.getDeleteUnusedImagesCommands)(context);
15489
15820
  }
15490
- // this number only targets inactive revisions
15491
- 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);
15492
15828
  // this number needs to be higher than inactive after deploy, so we add one
15493
15829
  const imagesToKeep = revisionsToKeep + 1;
15494
15830
  const deleteOldRevisionsCommands = (0, cloudRunRevisions_1.getDeleteUnusedRevisionsCommands)(context, revisionsToKeep);
@@ -16395,7 +16731,7 @@ const cloudRunExecutionUrl_1 = __nccwpck_require__(49963);
16395
16731
  const database_1 = __nccwpck_require__(11595);
16396
16732
  const getServiceName_1 = __nccwpck_require__(33102);
16397
16733
  exports.GCLOUD_DEPLOY_CREDENTIALS_KEY = "GCLOUD_DEPLOY_credentialsKey";
16398
- const getCloudSqlVariables = ({ deployConfigRaw, environmentSlugPrefix, env, componentName, fullConfig, }) => {
16734
+ const getCloudSqlVariables = ({ deployConfigRaw, environmentSlugPrefix, secretsEnv, componentName, fullConfig, }) => {
16399
16735
  var _a, _b, _c;
16400
16736
  if (deployConfigRaw && deployConfigRaw.cloudSql) {
16401
16737
  const DB_NAME = (0, utils_1.getFullDbName)(deployConfigRaw.cloudSql, fullConfig, environmentSlugPrefix, componentName);
@@ -16406,7 +16742,7 @@ const getCloudSqlVariables = ({ deployConfigRaw, environmentSlugPrefix, env, com
16406
16742
  CLOUD_SQL_INSTANCE_CONNECTION_NAME: deployConfigRaw.cloudSql.instanceConnectionName,
16407
16743
  DB_NAME: DB_NAME,
16408
16744
  DB_USER: (_b = deployConfigRaw.cloudSql.dbUser) !== null && _b !== void 0 ? _b : "postgres",
16409
- DB_PASSWORD: "$" + (0, context_1.getSecretVarName)(env, componentName, "DB_PASSWORD"),
16745
+ DB_PASSWORD: "$" + (0, context_1.getSecretVarName)(secretsEnv, componentName, "DB_PASSWORD"),
16410
16746
  };
16411
16747
  return {
16412
16748
  ...dbVars,
@@ -16879,7 +17215,7 @@ const fromComponentContext = (context) => {
16879
17215
  environmentSlugPrefix: context.environment.slugPrefix,
16880
17216
  componentName: context.name,
16881
17217
  env: context.env,
16882
- isReviewEnv: context.environment.envType === "review",
17218
+ isReviewEnv: context.environment.instance.type === "review",
16883
17219
  deployConfig: deployConfig && deployConfig.type === "google-cloudrun"
16884
17220
  ? deployConfig
16885
17221
  : null,
@@ -16891,7 +17227,7 @@ const fromEnvironmentContext = (context) => ({
16891
17227
  environmentSlugPrefix: context.environmentSlugPrefix,
16892
17228
  componentName: context.componentName,
16893
17229
  env: context.env,
16894
- isReviewEnv: context.envType === "review",
17230
+ isReviewEnv: context.instance.type === "review",
16895
17231
  deployConfig: context.deployConfigRaw || null,
16896
17232
  config: context.fullConfig,
16897
17233
  });
@@ -17345,9 +17681,9 @@ exports.KUBERNETES_DEPLOY_TYPE = {
17345
17681
  jobs: deployJob_1.createKubernetesDeployJobs,
17346
17682
  defaults: () => ({}),
17347
17683
  additionalSecretKeys: additionalSecretKeys_1.additionalKubernetesSecretKeys,
17348
- getAdditionalEnvVars: ({ componentName, fullConfig, deployConfigRaw, env, reviewSlug, envType, }) => {
17684
+ getAdditionalEnvVars: ({ componentName, fullConfig, deployConfigRaw, env, instance, }) => {
17349
17685
  var _a;
17350
- const KUBE_APP_NAME_PREFIX = envType === "review" && reviewSlug ? reviewSlug.concat("-") : "";
17686
+ const KUBE_APP_NAME_PREFIX = instance.type === "review" ? instance.reviewSlug.concat("-") : "";
17351
17687
  const KUBE_APP_NAME = KUBE_APP_NAME_PREFIX.concat(componentName);
17352
17688
  const KUBE_NAMESPACE = (0, __1.getKubernetesNamespace)(fullConfig, env);
17353
17689
  const componentSlug = (0, slugify_1.default)(componentName);
@@ -17356,7 +17692,7 @@ exports.KUBERNETES_DEPLOY_TYPE = {
17356
17692
  "panter.cloud";
17357
17693
  const HOSTNAME_INTERNAL = (0, bash_1.joinBashExpressions)([
17358
17694
  componentSlug,
17359
- ...(envType === "review" && reviewSlug ? [reviewSlug] : []),
17695
+ ...(instance.type === "review" ? [instance.reviewSlug] : []),
17360
17696
  env,
17361
17697
  fullConfig.appName,
17362
17698
  fullConfig.customerName,
@@ -17714,7 +18050,8 @@ const createPagesDeployJobs = async (context) => {
17714
18050
  throw new Error("deploy config is not pages");
17715
18051
  }
17716
18052
  const onGithub = context.pipelineType === "github";
17717
- if (onGithub && context.environment.envType === "review") {
18053
+ const isReviewApp = context.environment.instance.type === "review";
18054
+ if (onGithub && isReviewApp) {
17718
18055
  // github pages serves ONE site per repository and deploy-pages
17719
18056
  // replaces all of it — there is no path_prefix equivalent, so per-PR
17720
18057
  // previews cannot exist (deploy-pages has a `preview` input, but it
@@ -17725,7 +18062,7 @@ const createPagesDeployJobs = async (context) => {
17725
18062
  }
17726
18063
  // review environments publish under their own path prefix (gitlab
17727
18064
  // parallel deployments) — every merge request gets a site preview
17728
- const pathPrefix = context.environment.envType === "review" ? "mr-$CI_MERGE_REQUEST_IID" : "";
18065
+ const pathPrefix = isReviewApp ? "mr-$CI_MERGE_REQUEST_IID" : "";
17729
18066
  const publishDir = (_b = deployConfig.publishDir) !== null && _b !== void 0 ? _b : "public";
17730
18067
  const packageManagerInstall = await (0, packageManagerInstall_1.getPackageManagerInstall)(context, {
17731
18068
  noCustomPostInstall: true,
@@ -19422,12 +19759,22 @@ Object.defineProperty(exports, "__esModule", ({ value: true }));
19422
19759
  "use strict";
19423
19760
 
19424
19761
  Object.defineProperty(exports, "__esModule", ({ value: true }));
19425
- 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;
19426
19763
  exports.ALL_PIPELINE_TRIGGERS = [
19427
19764
  "mainBranch",
19428
19765
  "mr",
19429
19766
  "taggedRelease",
19430
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;
19431
19778
  /**
19432
19779
  * all env types with their trigger.
19433
19780
  * Each env type has a default env with the same name which is always included
@@ -19449,15 +19796,6 @@ exports.ENV_TYPES = {
19449
19796
  triggers: [],
19450
19797
  },
19451
19798
  };
19452
- /**
19453
- *
19454
- * @param trigger a trigger
19455
- * @returns array of env types for that trigger. this is also the list of default envs
19456
- */
19457
- const getEnvTypesByTrigger = (trigger) => Object.entries(exports.ENV_TYPES)
19458
- .filter(([, e]) => e.triggers.includes(trigger))
19459
- .map(([e]) => e);
19460
- exports.getEnvTypesByTrigger = getEnvTypesByTrigger;
19461
19799
  exports.DEFAULT_ENVS = Object.keys(exports.ENV_TYPES);
19462
19800
  exports.DEFAULT_ENV_TYPES = exports.DEFAULT_ENVS;
19463
19801
  const isKnowEnvType = (env) => {