@catladder/cli 0.0.0-main-556a16a2 → 0.0.0-main-53c2b542

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 (85) hide show
  1. package/dist/bundles/catenv/index.js +399 -55
  2. package/dist/bundles/catenv/xdg-open +0 -0
  3. package/dist/bundles/cli/clipboard_i686.exe +0 -0
  4. package/dist/bundles/cli/clipboard_x86_64.exe +0 -0
  5. package/dist/bundles/cli/index.js +399 -55
  6. package/dist/bundles/cli/xdg-open +0 -0
  7. package/dist/bundles/cli/xsel +0 -0
  8. package/dist/bundles/runner-images/changesets/scripts/changesetsRelease +0 -0
  9. package/dist/bundles/runner-images/kubernetes/scripts/kubernetesCreateSecret +0 -0
  10. package/dist/bundles/runner-images/kubernetes/scripts/kubernetesDelete +0 -0
  11. package/dist/bundles/runner-images/kubernetes/scripts/kubernetesDeploy +0 -0
  12. package/dist/bundles/runner-images/kubernetes/scripts/kubernetesRollback +0 -0
  13. package/dist/bundles/skills/catladder-builds/SKILL.md +50 -2
  14. package/dist/bundles/skills/catladder-builds/references/build-types.md +10 -2
  15. package/dist/bundles/skills/catladder-config/SKILL.md +18 -7
  16. package/dist/bundles/skills/catladder-pipelines/SKILL.md +39 -20
  17. package/dist/packages/pipeline/src/backends/github/artifactArchive.d.ts +22 -0
  18. package/dist/packages/pipeline/src/backends/github/artifactArchive.js +84 -0
  19. package/dist/packages/pipeline/src/backends/github/artifactArchive.js.map +1 -0
  20. package/dist/packages/pipeline/src/backends/github/createGithubJobs.js +12 -29
  21. package/dist/packages/pipeline/src/backends/github/createGithubJobs.js.map +1 -1
  22. package/dist/packages/pipeline/src/backends/gitlab/GitlabBackend.js +3 -1
  23. package/dist/packages/pipeline/src/backends/gitlab/GitlabBackend.js.map +1 -1
  24. package/dist/packages/pipeline/src/backends/gitlab/getPipelineStages.d.ts +4 -1
  25. package/dist/packages/pipeline/src/backends/gitlab/getPipelineStages.js +11 -2
  26. package/dist/packages/pipeline/src/backends/gitlab/getPipelineStages.js.map +1 -1
  27. package/dist/packages/pipeline/src/build/types.d.ts +4 -3
  28. package/dist/packages/pipeline/src/build/types.js.map +1 -1
  29. package/dist/packages/pipeline/src/config/configruedEnvs.js +7 -1
  30. package/dist/packages/pipeline/src/config/configruedEnvs.js.map +1 -1
  31. package/dist/packages/pipeline/src/context/getEnvInherit.d.ts +2 -2
  32. package/dist/packages/pipeline/src/context/getEnvInherit.js +5 -2
  33. package/dist/packages/pipeline/src/context/getEnvInherit.js.map +1 -1
  34. package/dist/packages/pipeline/src/context/getEnvInstance.d.ts +2 -2
  35. package/dist/packages/pipeline/src/context/getEnvInstance.js.map +1 -1
  36. package/dist/packages/pipeline/src/context/getEnvOn.d.ts +2 -2
  37. package/dist/packages/pipeline/src/context/getEnvOn.js +1 -1
  38. package/dist/packages/pipeline/src/context/getEnvOn.js.map +1 -1
  39. package/dist/packages/pipeline/src/context/getEnvType.d.ts +3 -3
  40. package/dist/packages/pipeline/src/context/getEnvType.js +1 -1
  41. package/dist/packages/pipeline/src/context/getEnvType.js.map +1 -1
  42. package/dist/packages/pipeline/src/context/getEnvironmentContext.js +3 -2
  43. package/dist/packages/pipeline/src/context/getEnvironmentContext.js.map +1 -1
  44. package/dist/packages/pipeline/src/deploy/base/deploy.js +15 -9
  45. package/dist/packages/pipeline/src/deploy/base/deploy.js.map +1 -1
  46. package/dist/packages/pipeline/src/deploy/cloudRun/createJobs/execute/schedules.js +3 -1
  47. package/dist/packages/pipeline/src/deploy/cloudRun/createJobs/execute/schedules.js.map +1 -1
  48. package/dist/packages/pipeline/src/deploy/cloudRun/utils/cloudRunExecutionUrl.d.ts +1 -1
  49. package/dist/packages/pipeline/src/deploy/cloudRun/utils/cloudRunExecutionUrl.js +6 -1
  50. package/dist/packages/pipeline/src/deploy/cloudRun/utils/cloudRunExecutionUrl.js.map +1 -1
  51. package/dist/packages/pipeline/src/index.d.ts +1 -0
  52. package/dist/packages/pipeline/src/index.js +1 -0
  53. package/dist/packages/pipeline/src/index.js.map +1 -1
  54. package/dist/packages/pipeline/src/pipeline/createJobsForComponent.js +12 -2
  55. package/dist/packages/pipeline/src/pipeline/createJobsForComponent.js.map +1 -1
  56. package/dist/packages/pipeline/src/pipeline/createJobsForWorkspace.js +7 -2
  57. package/dist/packages/pipeline/src/pipeline/createJobsForWorkspace.js.map +1 -1
  58. package/dist/packages/pipeline/src/pipeline/resolveRequirements.js +1 -0
  59. package/dist/packages/pipeline/src/pipeline/resolveRequirements.js.map +1 -1
  60. package/dist/packages/pipeline/src/postBuildTest/createPostBuildTestJobs.d.ts +16 -0
  61. package/dist/packages/pipeline/src/postBuildTest/createPostBuildTestJobs.js +167 -0
  62. package/dist/packages/pipeline/src/postBuildTest/createPostBuildTestJobs.js.map +1 -0
  63. package/dist/packages/pipeline/src/postBuildTest/index.d.ts +2 -0
  64. package/dist/packages/pipeline/src/postBuildTest/index.js +19 -0
  65. package/dist/packages/pipeline/src/postBuildTest/index.js.map +1 -0
  66. package/dist/packages/pipeline/src/postBuildTest/types.d.ts +61 -0
  67. package/dist/packages/pipeline/src/postBuildTest/types.js +3 -0
  68. package/dist/packages/pipeline/src/postBuildTest/types.js.map +1 -0
  69. package/dist/packages/pipeline/src/types/config.d.ts +20 -2
  70. package/dist/packages/pipeline/src/types/config.js +9 -1
  71. package/dist/packages/pipeline/src/types/config.js.map +1 -1
  72. package/dist/packages/pipeline/src/types/jobs.d.ts +2 -2
  73. package/dist/packages/pipeline/src/types/jobs.js +1 -0
  74. package/dist/packages/pipeline/src/types/jobs.js.map +1 -1
  75. package/dist/runner-images/changesets/scripts/changesetsRelease +0 -0
  76. package/dist/runner-images/kubernetes/scripts/kubernetesCreateSecret +0 -0
  77. package/dist/runner-images/kubernetes/scripts/kubernetesDelete +0 -0
  78. package/dist/runner-images/kubernetes/scripts/kubernetesDeploy +0 -0
  79. package/dist/runner-images/kubernetes/scripts/kubernetesRollback +0 -0
  80. package/dist/skills/catladder-builds/SKILL.md +50 -2
  81. package/dist/skills/catladder-builds/references/build-types.md +10 -2
  82. package/dist/skills/catladder-config/SKILL.md +18 -7
  83. package/dist/skills/catladder-pipelines/SKILL.md +39 -20
  84. package/dist/tsconfig.tsbuildinfo +1 -1
  85. package/package.json +1 -1
@@ -3102,6 +3102,97 @@ exports.makeAggregateCheckJob = makeAggregateCheckJob;
3102
3102
 
3103
3103
  /***/ }),
3104
3104
 
3105
+ /***/ 75125:
3106
+ /***/ ((__unused_webpack_module, exports) => {
3107
+
3108
+ "use strict";
3109
+
3110
+ Object.defineProperty(exports, "__esModule", ({ value: true }));
3111
+ exports.createArtifactDownloadSteps = exports.createArtifactUploadSteps = exports.artifactArchiveFile = void 0;
3112
+ /**
3113
+ * Artifacts are handed between github jobs as ONE tar archive instead
3114
+ * of letting upload-artifact walk the paths itself.
3115
+ *
3116
+ * upload-artifact does not preserve symlinks (it uploads the target's
3117
+ * content) nor file modes, and it strips the paths' common ancestor.
3118
+ * Gitlab artifacts keep all of that, and builds rely on it: next.js
3119
+ * with turbopack writes `.next/node_modules/<pkg>-<hash>` as relative
3120
+ * symlinks into the pnpm virtual store. Copied as plain directories,
3121
+ * node resolves the package's own dependencies from the wrong place
3122
+ * and the app crashes with ERR_MODULE_NOT_FOUND at runtime. tar keeps
3123
+ * symlinks, modes and the paths relative to the repo root (github's
3124
+ * own docs recommend it for exactly this).
3125
+ *
3126
+ * The archive is not hidden (upload-artifact skips hidden files) and
3127
+ * lives in the workspace, the one path run steps and actions agree on
3128
+ * inside job containers too.
3129
+ */
3130
+ const artifactArchiveFile = (jobId) => `catladder-artifacts-${jobId}.tar`;
3131
+ exports.artifactArchiveFile = artifactArchiveFile;
3132
+ const createArtifactUploadSteps = (jobId, paths, condition) => {
3133
+ const archive = (0, exports.artifactArchiveFile)(jobId);
3134
+ const ifCondition = condition ? { if: condition } : {};
3135
+ return [
3136
+ {
3137
+ name: "Archive artifacts",
3138
+ ...ifCondition,
3139
+ run: [
3140
+ "shopt -s nullglob globstar",
3141
+ "paths=()",
3142
+ // unquoted on purpose: artifact paths may be globs. Non-matching
3143
+ // globs vanish (nullglob), missing literal paths are skipped —
3144
+ // gitlab only warns about both, so do we
3145
+ `for path in ${paths.join(" ")}; do`,
3146
+ ' if [ -e "$path" ] || [ -L "$path" ]; then paths+=("$path"); else echo "::warning::artifact path not found: $path"; fi',
3147
+ "done",
3148
+ "if [ ${#paths[@]} -eq 0 ]; then",
3149
+ // an empty archive keeps downstream downloads from failing.
3150
+ // Written by hand: busybox tar (alpine job images) refuses to
3151
+ // create one, and zero blocks are what an empty tar is
3152
+ ` head -c 10240 /dev/zero > ${archive}`,
3153
+ "else",
3154
+ ` tar -cf ${archive} -- "\${paths[@]}"`,
3155
+ "fi",
3156
+ ].join("\n"),
3157
+ shell: "bash",
3158
+ },
3159
+ {
3160
+ name: "Upload artifacts",
3161
+ ...ifCondition,
3162
+ uses: "actions/upload-artifact@v4",
3163
+ with: {
3164
+ name: jobId,
3165
+ path: archive,
3166
+ "if-no-files-found": "error",
3167
+ },
3168
+ },
3169
+ ];
3170
+ };
3171
+ exports.createArtifactUploadSteps = createArtifactUploadSteps;
3172
+ const createArtifactDownloadSteps = (providerIds) => providerIds.length === 0
3173
+ ? []
3174
+ : [
3175
+ ...providerIds.map((id) => ({
3176
+ name: `Download artifacts from ${id}`,
3177
+ uses: "actions/download-artifact@v4",
3178
+ with: { name: id },
3179
+ })),
3180
+ {
3181
+ name: "Extract artifacts",
3182
+ run: providerIds
3183
+ .flatMap((id) => [
3184
+ `tar -xf ${(0, exports.artifactArchiveFile)(id)}`,
3185
+ `rm ${(0, exports.artifactArchiveFile)(id)}`,
3186
+ ])
3187
+ .join("\n"),
3188
+ shell: "bash",
3189
+ },
3190
+ ];
3191
+ exports.createArtifactDownloadSteps = createArtifactDownloadSteps;
3192
+ //# sourceMappingURL=artifactArchive.js.map
3193
+
3194
+ /***/ }),
3195
+
3105
3196
  /***/ 17300:
3106
3197
  /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => {
3107
3198
 
@@ -3171,6 +3262,7 @@ const utils_1 = __nccwpck_require__(49401);
3171
3262
  const gitlab_1 = __nccwpck_require__(86962);
3172
3263
  const scriptFunctions_1 = __nccwpck_require__(85873);
3173
3264
  const ciVariables_1 = __nccwpck_require__(17300);
3265
+ const artifactArchive_1 = __nccwpck_require__(75125);
3174
3266
  /**
3175
3267
  * runner variables that only make sense on gitlab runners and must not
3176
3268
  * leak into github jobs (docker-in-docker daemon config, kubernetes
@@ -3381,7 +3473,7 @@ const getReferencedSecretsEnv = (script, kinds) => Object.fromEntries([...script
3381
3473
  .filter((name) => !ciVariables_1.GITHUB_INJECTED_WORKFLOW_ENV_NAMES.has(name))
3382
3474
  .map((name) => [name, vaultValueExpression(name, kinds)]));
3383
3475
  const makeGithubJob = (context, job, allJobs, uploadProviderIds, images, scripts, secretKinds = new Map()) => {
3384
- var _a, _b, _c, _d, _e, _f, _g, _h, _j;
3476
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l;
3385
3477
  const id = (0, exports.githubJobId)(context, job.name);
3386
3478
  const isComponent = context.type === "component";
3387
3479
  const createsJobEnv = !!job.environment &&
@@ -3526,13 +3618,7 @@ const makeGithubJob = (context, job, allJobs, uploadProviderIds, images, scripts
3526
3618
  ? []
3527
3619
  : [{ name: "Checkout", uses: "actions/checkout@v4" }]),
3528
3620
  ...createCacheSteps(context, job),
3529
- ...needs
3530
- .filter((n) => n.artifacts)
3531
- .map((n) => ({
3532
- name: `Download artifacts from ${n.id}`,
3533
- uses: "actions/download-artifact@v4",
3534
- with: { name: n.id },
3535
- })),
3621
+ ...(0, artifactArchive_1.createArtifactDownloadSteps)(needs.filter((n) => n.artifacts).map((n) => n.id)),
3536
3622
  {
3537
3623
  name: job.name,
3538
3624
  id: "main",
@@ -3553,27 +3639,15 @@ const makeGithubJob = (context, job, allJobs, uploadProviderIds, images, scripts
3553
3639
  },
3554
3640
  ]
3555
3641
  : []),
3556
- ...(pagesPublishDir
3557
- ? []
3558
- : uploadProviderIds.has(id) || artifactPaths.length > 0
3559
- ? artifactPaths.length > 0
3560
- ? [
3561
- {
3562
- name: "Upload artifacts",
3563
- uses: "actions/upload-artifact@v4",
3564
- with: {
3565
- name: id,
3566
- path: artifactPaths.join("\n"),
3567
- "if-no-files-found": "warn",
3568
- // gitlab artifacts include everything; upload-artifact
3569
- // silently drops hidden files by default — which is
3570
- // exactly where next.js builds live (.next)
3571
- "include-hidden-files": true,
3572
- },
3573
- },
3574
- ]
3575
- : []
3576
- : []),
3642
+ ...(!pagesPublishDir && artifactPaths.length > 0
3643
+ ? (0, artifactArchive_1.createArtifactUploadSteps)(id, artifactPaths,
3644
+ // gitlab's `artifacts.when`: steps only run on success by default
3645
+ ((_k = job.artifacts) === null || _k === void 0 ? void 0 : _k.when) === "always"
3646
+ ? "always()"
3647
+ : ((_l = job.artifacts) === null || _l === void 0 ? void 0 : _l.when) === "on_failure"
3648
+ ? "failure()"
3649
+ : undefined)
3650
+ : []),
3577
3651
  ],
3578
3652
  };
3579
3653
  return [id, githubJob];
@@ -4266,7 +4340,6 @@ class GitlabBackend {
4266
4340
  return [mainInclude, ...componentIncludes];
4267
4341
  }
4268
4342
  async createPipeline(config, images) {
4269
- const stages = (0, getPipelineStages_1.getPipelineStages)(config);
4270
4343
  // register the release image first: the per-trigger job creation
4271
4344
  // below emits the build jobs of all images registered so far. The
4272
4345
  // release jobs themselves are created afterwards — they need the
@@ -4285,6 +4358,9 @@ class GitlabBackend {
4285
4358
  jobs: await (0, createGitlabJobs_1.createGitlabJobs)(await (0, createAllJobs_1.createAllJobs)({ config, trigger, pipelineType: this.type }), images, getGitlabRulesForTrigger(trigger)),
4286
4359
  })));
4287
4360
  const allJobsPerTrigger = jobsPerTrigger.flatMap(({ jobs }) => jobs);
4361
+ const stages = (0, getPipelineStages_1.getPipelineStages)(config, new Set(allJobsPerTrigger
4362
+ .map(({ gitlabJob }) => gitlabJob.stage)
4363
+ .filter((stage) => typeof stage === "string")));
4288
4364
  // the jobs the queued-release executor waits for: everything that
4289
4365
  // runs automatically in a main-branch pipeline. Manual jobs (stop,
4290
4366
  // rollback, gates) are excluded — gitlab skips a job whose needs
@@ -4939,14 +5015,23 @@ Object.defineProperty(exports, "__esModule", ({ value: true }));
4939
5015
  exports.getPipelineStages = void 0;
4940
5016
  const config_1 = __nccwpck_require__(11646);
4941
5017
  const jobs_1 = __nccwpck_require__(98521);
5018
+ /**
5019
+ * stages that only appear in the pipeline when a job uses them, so that
5020
+ * projects not using the feature keep their generated files unchanged
5021
+ */
5022
+ const OPTIONAL_STAGES = ["post-build"];
4942
5023
  /**
4943
5024
  * while technically not required, we group different envs in its own stage
4944
5025
  * each job from `createJobs` that is defined as `envMode: "stagePerEnv"` will have `deploy dev`, etc. instead of just `deploy`
4945
5026
  * this is just so that it looks nicer in gitlab and makes running mutliple manual tasks more easy to use
5027
+ *
5028
+ * `usedStages` are the stages of all generated jobs; optional stages are
5029
+ * dropped when none of them is used.
4946
5030
  */
4947
- function getPipelineStages(config) {
5031
+ function getPipelineStages(config, usedStages = new Set()) {
4948
5032
  const allEnvs = (0, config_1.getAllEnvsInAllComponents)(config).filter((e) => e !== "local");
4949
- const stages = jobs_1.BASE_STAGES.reduce((acc, baseStage) => [
5033
+ const isUsed = (baseStage) => [...usedStages].some((stage) => stage === baseStage || stage.startsWith(`${baseStage} `));
5034
+ const stages = jobs_1.BASE_STAGES.filter((baseStage) => !OPTIONAL_STAGES.includes(baseStage) || isUsed(baseStage)).reduce((acc, baseStage) => [
4950
5035
  ...acc,
4951
5036
  baseStage,
4952
5037
  ...allEnvs.map((e) => `${baseStage} ${e}`),
@@ -7395,6 +7480,9 @@ const getEnvType_1 = __nccwpck_require__(46080);
7395
7480
  const getConfiguredAndDefaultEnvs = (config, componentName, envTypes) => {
7396
7481
  var _a, _b;
7397
7482
  const configuredEnvs = (_a = config.components[componentName].env) !== null && _a !== void 0 ? _a : {};
7483
+ if ((0, types_1.isEnvironmentDisabled)(config.environments, "local")) {
7484
+ throw new Error(`environment "local" cannot be disabled (environments.local: false) — it is only used for local development`);
7485
+ }
7398
7486
  // the default envs have the same name as the env types
7399
7487
  // these can be disabled with settimg them to `false`
7400
7488
  // this is the list of all not disabled envs. These are always returned
@@ -7403,6 +7491,7 @@ const getConfiguredAndDefaultEnvs = (config, componentName, envTypes) => {
7403
7491
  // component deploys to them unless it opts out with `env.<name>: false`
7404
7492
  const declaredEnvs = Object.keys((_b = config.environments) !== null && _b !== void 0 ? _b : {})
7405
7493
  .filter((envName) => !(0, types_1.isKnowEnvType)(envName)) // default envs are already handled above
7494
+ .filter((envName) => !(0, types_1.isEnvironmentDisabled)(config.environments, envName))
7406
7495
  .map((envName) => {
7407
7496
  const envType = (0, getEnvType_1.getDeclaredEnvType)(config.environments, envName);
7408
7497
  if (!envType) {
@@ -7419,13 +7508,15 @@ const getConfiguredAndDefaultEnvs = (config, componentName, envTypes) => {
7419
7508
  config.type &&
7420
7509
  envTypes.includes(config.type))
7421
7510
  .map(([envName]) => envName);
7511
+ // envs disabled project-wide (`environments.<name>: false`) exist for
7512
+ // no component — this wins over any per-component config of the env
7422
7513
  return [
7423
7514
  ...new Set([
7424
7515
  ...enabledDefaultEnvs,
7425
7516
  ...declaredEnvs,
7426
7517
  ...configuredCustomEnvs,
7427
7518
  ]),
7428
- ];
7519
+ ].filter((envName) => !(0, types_1.isEnvironmentDisabled)(config.environments, envName));
7429
7520
  };
7430
7521
  const getAllEnvs = (config, componentName) => {
7431
7522
  return getConfiguredAndDefaultEnvs(config, componentName, types_1.DEFAULT_ENV_TYPES);
@@ -7872,6 +7963,9 @@ const assertValidInheritTarget = (environments, env, target) => {
7872
7963
  if (target === "local") {
7873
7964
  throw new Error(`environment "${env}" cannot inherit from "local" — local is not a deployment environment`);
7874
7965
  }
7966
+ if ((0, config_1.isEnvironmentDisabled)(environments, target)) {
7967
+ throw new Error(`environment "${env}" inherits from "${target}", which is disabled (environments.${target}: false)`);
7968
+ }
7875
7969
  if (!(0, config_1.isKnowEnvType)(target) && !(environments === null || environments === void 0 ? void 0 : environments[target])) {
7876
7970
  throw new Error(`environment "${env}" inherits from unknown environment "${target}"`);
7877
7971
  }
@@ -7887,7 +7981,7 @@ const getInheritedConfigChain = (environments, env) => {
7887
7981
  const seen = new Set([env]);
7888
7982
  let current = env;
7889
7983
  for (;;) {
7890
- const target = (0, exports.normalizeEnvInherit)((_a = environments === null || environments === void 0 ? void 0 : environments[current]) === null || _a === void 0 ? void 0 : _a.inherit).config;
7984
+ const target = (0, exports.normalizeEnvInherit)((_a = (0, config_1.getEnvironmentConfig)(environments, current)) === null || _a === void 0 ? void 0 : _a.inherit).config;
7891
7985
  if (!target)
7892
7986
  break;
7893
7987
  assertValidInheritTarget(environments, current, target);
@@ -7911,7 +8005,7 @@ const getSecretsEnv = (environments, env) => {
7911
8005
  const seen = new Set([env]);
7912
8006
  let current = env;
7913
8007
  for (;;) {
7914
- const target = (0, exports.normalizeEnvInherit)((_a = environments === null || environments === void 0 ? void 0 : environments[current]) === null || _a === void 0 ? void 0 : _a.inherit).secrets;
8008
+ const target = (0, exports.normalizeEnvInherit)((_a = (0, config_1.getEnvironmentConfig)(environments, current)) === null || _a === void 0 ? void 0 : _a.inherit).secrets;
7915
8009
  if (!target)
7916
8010
  return current;
7917
8011
  assertValidInheritTarget(environments, current, target);
@@ -7984,7 +8078,7 @@ const getDefaultOnForType = (envType) => {
7984
8078
  */
7985
8079
  const getEnvOn = (env, envConfig, environments) => {
7986
8080
  var _a;
7987
- const on = (_a = environments === null || environments === void 0 ? void 0 : environments[env]) === null || _a === void 0 ? void 0 : _a.on;
8081
+ const on = (_a = (0, types_1.getEnvironmentConfig)(environments, env)) === null || _a === void 0 ? void 0 : _a.on;
7988
8082
  if (on !== undefined)
7989
8083
  return on;
7990
8084
  return getDefaultOnForType((0, getEnvType_1.getEnvType)(env, envConfig, environments));
@@ -8009,7 +8103,7 @@ const getEnvInherit_1 = __nccwpck_require__(52181);
8009
8103
  * the type of the env they inherit their config from
8010
8104
  */
8011
8105
  const getDeclaredEnvType = (environments, env, seen = new Set()) => {
8012
- const declared = environments === null || environments === void 0 ? void 0 : environments[env];
8106
+ const declared = (0, types_1.getEnvironmentConfig)(environments, env);
8013
8107
  if (declared === null || declared === void 0 ? void 0 : declared.type)
8014
8108
  return declared.type;
8015
8109
  if ((0, types_1.isKnowEnvType)(env))
@@ -8076,6 +8170,7 @@ exports.getEnvironment = getEnvironment;
8076
8170
  Object.defineProperty(exports, "__esModule", ({ value: true }));
8077
8171
  exports.getEnvironmentContext = void 0;
8078
8172
  const bash_1 = __nccwpck_require__(94143);
8173
+ const config_1 = __nccwpck_require__(75591);
8079
8174
  const getEnvConfig_1 = __nccwpck_require__(69578);
8080
8175
  const getEnvInstance_1 = __nccwpck_require__(13055);
8081
8176
  const getEnvInherit_1 = __nccwpck_require__(52181);
@@ -8087,7 +8182,7 @@ const getEnvironmentSlugPrefix = (env, instance) => {
8087
8182
  return env;
8088
8183
  };
8089
8184
  const getEnvironmentContext = ({ env, componentName, config, pipelineType, }) => {
8090
- var _a, _b;
8185
+ var _a;
8091
8186
  const envConfigRaw = (0, getEnvConfig_1.getEnvConfig)(config, componentName, env);
8092
8187
  const envType = (0, getEnvType_1.getEnvType)(env, envConfigRaw, config.environments);
8093
8188
  const instance = (0, getEnvInstance_1.getEnvInstance)(envConfigRaw, env, pipelineType, config.environments);
@@ -8095,7 +8190,7 @@ const getEnvironmentContext = ({ env, componentName, config, pipelineType, }) =>
8095
8190
  // config; undefined means the env type's default applies
8096
8191
  const autoStop = envConfigRaw.autoStop !== undefined
8097
8192
  ? envConfigRaw.autoStop
8098
- : (_b = (_a = config.environments) === null || _a === void 0 ? void 0 : _a[env]) === null || _b === void 0 ? void 0 : _b.autoStop;
8193
+ : (_a = (0, config_1.getEnvironmentConfig)(config.environments, env)) === null || _a === void 0 ? void 0 : _a.autoStop;
8099
8194
  const environmentSlugPrefix = getEnvironmentSlugPrefix(env, instance);
8100
8195
  const environmentSlug = environmentSlugPrefix.concat(`-${componentName}`);
8101
8196
  const fullName = (0, bash_1.joinBashExpressions)([config.customerName, config.appName, environmentSlug], "-");
@@ -9020,14 +9115,20 @@ class DeployJob extends jobs_1.CatladderJob {
9020
9115
  : []),
9021
9116
  ];
9022
9117
  // we don't want to deploy when there is a broken test
9023
- const qualityGateRequirement = {
9024
- capability: "qualityGate",
9025
- artifacts: false,
9026
- // use test from workspace build
9027
- from: (0, context_1.componentContextHasWorkspaceBuild)(context)
9028
- ? { workspace: context.build.workspaceName }
9029
- : undefined,
9030
- };
9118
+ const qualityGateRequirements = [
9119
+ // the component's own gates (e.g. post-build tests, custom test jobs)
9120
+ { capability: "qualityGate", artifacts: false },
9121
+ // ...and the gates of the workspace build it is built in
9122
+ ...((0, context_1.componentContextHasWorkspaceBuild)(context)
9123
+ ? [
9124
+ {
9125
+ capability: "qualityGate",
9126
+ artifacts: false,
9127
+ from: { workspace: context.build.workspaceName },
9128
+ },
9129
+ ]
9130
+ : []),
9131
+ ];
9031
9132
  super({
9032
9133
  name: exports.DEPLOY_JOB_NAME,
9033
9134
  script: jobDefinition.script,
@@ -9044,7 +9145,7 @@ class DeployJob extends jobs_1.CatladderJob {
9044
9145
  ...waitForRequirements,
9045
9146
  // if the build is disabled, we don't need to wait for it
9046
9147
  ...(context.build.type !== "disabled"
9047
- ? [...buildRequirements, qualityGateRequirement]
9148
+ ? [...buildRequirements, ...qualityGateRequirements]
9048
9149
  : []),
9049
9150
  ],
9050
9151
  gate: whenDeploy,
@@ -9908,7 +10009,9 @@ const getSchedulerArgs = (scheduler, context) => {
9908
10009
  appFullName: context.environment.fullName,
9909
10010
  projectId,
9910
10011
  region,
9911
- }),
10012
+ // lands in a script line (not an exported variable), so the
10013
+ // raw expression is what the shell has to see
10014
+ }).toString(),
9912
10015
  "message-body": body
9913
10016
  ? '"' + (0, bash_1.bashEscape)(JSON.stringify(body)) + '"'
9914
10017
  : undefined,
@@ -10348,11 +10451,16 @@ exports.GCLOUD_RUN_DEPLOY_TYPE = {
10348
10451
 
10349
10452
  Object.defineProperty(exports, "__esModule", ({ value: true }));
10350
10453
  exports.getCloudRunJobExecuteUrl = void 0;
10454
+ const bash_1 = __nccwpck_require__(94143);
10351
10455
  const jobName_1 = __nccwpck_require__(65576);
10352
10456
  function getCloudRunJobExecuteUrl(jobName, { region, projectId, appFullName, }) {
10353
10457
  const uriBase = `https://${region}-run.googleapis.com/apis/run.googleapis.com/v1/namespaces/${projectId}/jobs`;
10354
10458
  const fullJobName = (0, jobName_1.getCloudRunJobName)(appFullName, jobName);
10355
- return `${uriBase}/${fullJobName}:run`;
10459
+ // joined, not interpolated: on review envs the job name is a bash
10460
+ // expression (the review slug), and a template string would demote
10461
+ // it to a plain string — its quotes then get escaped as literal
10462
+ // characters when the variable is exported
10463
+ return (0, bash_1.joinBashExpressions)([uriBase, "/", fullJobName, ":run"]);
10356
10464
  }
10357
10465
  exports.getCloudRunJobExecuteUrl = getCloudRunJobExecuteUrl;
10358
10466
  //# sourceMappingURL=cloudRunExecutionUrl.js.map
@@ -11813,6 +11921,7 @@ __exportStar(__nccwpck_require__(15707), exports);
11813
11921
  __exportStar(__nccwpck_require__(45492), exports);
11814
11922
  __exportStar(__nccwpck_require__(3659), exports);
11815
11923
  __exportStar(__nccwpck_require__(50713), exports);
11924
+ __exportStar(__nccwpck_require__(28458), exports);
11816
11925
  __exportStar(__nccwpck_require__(26041), exports);
11817
11926
  __exportStar(__nccwpck_require__(47663), exports);
11818
11927
  __exportStar(__nccwpck_require__(91804), exports);
@@ -12410,6 +12519,7 @@ exports.createJobsForComponentContext = void 0;
12410
12519
  const build_1 = __nccwpck_require__(45492);
12411
12520
  const deploy_1 = __nccwpck_require__(3659);
12412
12521
  const jobs_1 = __nccwpck_require__(98521);
12522
+ const createPostBuildTestJobs_1 = __nccwpck_require__(61844);
12413
12523
  const createVerifyJobs_1 = __nccwpck_require__(66900);
12414
12524
  const injectDefaultVarsInCustomJobs = (context, jobs) => jobs.map(({ variables, ...job }) => {
12415
12525
  var _a;
@@ -12429,17 +12539,26 @@ const getCustomJobs = (context) => {
12429
12539
  return injectDefaultVarsInCustomJobs(context, rawJobs);
12430
12540
  };
12431
12541
  const createJobsForComponentContext = async (context) => {
12432
- const [buildJobs, deployJobs, verifyJobs] = await Promise.all([
12542
+ const [buildJobs, postBuildTestJobs, deployJobs, verifyJobs] = await Promise.all([
12433
12543
  context.build.type !== "disabled"
12434
12544
  ? build_1.BUILD_TYPES[context.build.buildType].jobs(context)
12435
12545
  : [],
12546
+ (0, createPostBuildTestJobs_1.createPostBuildTestJobs)(context),
12436
12547
  context.componentConfig.deploy !== false
12437
12548
  ? deploy_1.DEPLOY_TYPES[context.componentConfig.deploy.type].jobs(context)
12438
12549
  : [],
12439
12550
  (0, createVerifyJobs_1.createVerifyJobs)(context),
12440
12551
  ]);
12441
12552
  const customJobs = getCustomJobs(context);
12442
- return [...buildJobs, ...deployJobs, ...verifyJobs, ...customJobs];
12553
+ const otherJobs = [...buildJobs, ...deployJobs, ...verifyJobs, ...customJobs];
12554
+ (0, createPostBuildTestJobs_1.assertUniquePostBuildTestNames)(context, postBuildTestJobs, otherJobs);
12555
+ return [
12556
+ ...buildJobs,
12557
+ ...postBuildTestJobs,
12558
+ ...deployJobs,
12559
+ ...verifyJobs,
12560
+ ...customJobs,
12561
+ ];
12443
12562
  };
12444
12563
  exports.createJobsForComponentContext = createJobsForComponentContext;
12445
12564
  //# sourceMappingURL=createJobsForComponent.js.map
@@ -12454,9 +12573,14 @@ exports.createJobsForComponentContext = createJobsForComponentContext;
12454
12573
  Object.defineProperty(exports, "__esModule", ({ value: true }));
12455
12574
  exports.createJobsForWorkspace = void 0;
12456
12575
  const build_1 = __nccwpck_require__(45492);
12576
+ const createPostBuildTestJobs_1 = __nccwpck_require__(61844);
12457
12577
  const createJobsForWorkspace = async (context) => {
12458
- const buildJobs = await build_1.WORKSPACE_BUILD_TYPES[context.build.buildType].jobs(context);
12459
- return buildJobs;
12578
+ const [buildJobs, postBuildTestJobs] = await Promise.all([
12579
+ build_1.WORKSPACE_BUILD_TYPES[context.build.buildType].jobs(context),
12580
+ (0, createPostBuildTestJobs_1.createPostBuildTestJobs)(context),
12581
+ ]);
12582
+ (0, createPostBuildTestJobs_1.assertUniquePostBuildTestNames)(context, postBuildTestJobs, buildJobs);
12583
+ return [...buildJobs, ...postBuildTestJobs];
12460
12584
  };
12461
12585
  exports.createJobsForWorkspace = createJobsForWorkspace;
12462
12586
  //# sourceMappingURL=createJobsForWorkspace.js.map
@@ -12925,6 +13049,7 @@ exports.resolveRequirements = void 0;
12925
13049
  const DEFAULT_STAGE_CAPABILITIES = {
12926
13050
  build: "build",
12927
13051
  test: "qualityGate",
13052
+ "post-build": "qualityGate",
12928
13053
  deploy: "deployment",
12929
13054
  };
12930
13055
  /**
@@ -13066,6 +13191,216 @@ exports.getWorkspaceDependencies = getWorkspaceDependencies;
13066
13191
 
13067
13192
  /***/ }),
13068
13193
 
13194
+ /***/ 61844:
13195
+ /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => {
13196
+
13197
+ "use strict";
13198
+
13199
+ Object.defineProperty(exports, "__esModule", ({ value: true }));
13200
+ exports.assertUniquePostBuildTestNames = exports.createPostBuildTestJobs = exports.postBuildTestJobName = exports.POST_BUILD_TEST_STAGE = void 0;
13201
+ const createArtifactsConfig_1 = __nccwpck_require__(14149);
13202
+ const cache_1 = __nccwpck_require__(74559);
13203
+ const constants_1 = __nccwpck_require__(5576);
13204
+ const packageManagerInstall_1 = __nccwpck_require__(28419);
13205
+ const types_1 = __nccwpck_require__(35249);
13206
+ const runner_1 = __nccwpck_require__(92146);
13207
+ const jobs_1 = __nccwpck_require__(98521);
13208
+ const utils_1 = __nccwpck_require__(49401);
13209
+ exports.POST_BUILD_TEST_STAGE = "post-build";
13210
+ const postBuildTestJobName = (name) => `🔬 ${name}`;
13211
+ exports.postBuildTestJobName = postBuildTestJobName;
13212
+ const getPostBuildTestsConfig = (context) => {
13213
+ if (context.type === "workspace") {
13214
+ return context.build.config.postBuildTests;
13215
+ }
13216
+ if (context.build.type === "disabled") {
13217
+ return undefined;
13218
+ }
13219
+ return context.build.config.postBuildTests;
13220
+ };
13221
+ /**
13222
+ * whether the build output is a node project, so the job has to install
13223
+ * the dependencies before running the tests
13224
+ */
13225
+ const isNodeBuild = (context) => {
13226
+ if (context.type === "workspace") {
13227
+ return true; // workspace builds are node builds
13228
+ }
13229
+ if (context.build.type === "fromWorkspace") {
13230
+ return true;
13231
+ }
13232
+ if (context.build.type === "standalone") {
13233
+ return (0, types_1.isOfBuildType)(context.build.config, "node", "node-static", "storybook", "meteor");
13234
+ }
13235
+ return false;
13236
+ };
13237
+ const hasBuildCommand = (buildCommand) => buildCommand !== null && buildCommand !== false;
13238
+ /**
13239
+ * whether the build creates an app build job with artifacts. Rails builds
13240
+ * and builds with `buildCommand: false` only build the docker image.
13241
+ */
13242
+ const hasBuildArtifacts = (context) => {
13243
+ if (context.type === "workspace") {
13244
+ return hasBuildCommand(context.build.config.buildCommand);
13245
+ }
13246
+ if (context.build.type === "fromWorkspace") {
13247
+ return hasBuildCommand(context.build.workspaceBuildConfig.buildCommand);
13248
+ }
13249
+ if (context.build.type === "standalone") {
13250
+ return (!(0, types_1.isOfBuildType)(context.build.config, "rails") &&
13251
+ hasBuildCommand(context.build.config.buildCommand));
13252
+ }
13253
+ return false;
13254
+ };
13255
+ const getDefaultImage = (context) => {
13256
+ if (context.type === "component" &&
13257
+ context.build.type === "standalone" &&
13258
+ (0, types_1.isOfBuildType)(context.build.config, "custom")) {
13259
+ // custom builds bring their own toolchain image
13260
+ return context.build.config.jobImage;
13261
+ }
13262
+ return (0, runner_1.getRunnerImage)("jobs-default");
13263
+ };
13264
+ /**
13265
+ * jobs for `postBuildTests`: they run after the build in the `post-build`
13266
+ * stage, against the build artifacts, and act as quality gates, so a
13267
+ * failing post-build test blocks the deploy.
13268
+ */
13269
+ const createPostBuildTestJobs = async (context) => {
13270
+ var _a;
13271
+ // like lint and test: tagged releases deploy what was already tested
13272
+ if (context.trigger === "taggedRelease") {
13273
+ return [];
13274
+ }
13275
+ const tests = Object.entries((_a = getPostBuildTestsConfig(context)) !== null && _a !== void 0 ? _a : {}).filter((entry) => entry[1] !== false);
13276
+ if (tests.length === 0) {
13277
+ return [];
13278
+ }
13279
+ const nodeBuild = isNodeBuild(context);
13280
+ const [packageManagerInstall, nodeCache] = nodeBuild
13281
+ ? await Promise.all([
13282
+ (0, packageManagerInstall_1.getPackageManagerInstall)(context),
13283
+ // pull-only: the build job is the designated cache writer
13284
+ (0, cache_1.getNodeCache)(context, "pull"),
13285
+ ])
13286
+ : [null, null];
13287
+ // components built in a workspace test the workspace build output
13288
+ const from = context.type === "component" && context.build.type === "fromWorkspace"
13289
+ ? { workspace: context.build.workspaceName }
13290
+ : undefined;
13291
+ const buildRequirement = hasBuildArtifacts(context)
13292
+ ? { capability: "buildArtifacts", artifacts: true, strict: true, from }
13293
+ : // no build artifacts (e.g. rails): run after the build (docker image)
13294
+ { capability: "build", artifacts: false, from };
13295
+ return tests.map(([name, test]) => {
13296
+ var _a, _b, _c;
13297
+ const artifactsConfig = (0, createArtifactsConfig_1.createArtifactsConfig)(context.build.dir, test.artifactsReports, test.artifacts);
13298
+ return new jobs_1.CatladderJob({
13299
+ name: (0, exports.postBuildTestJobName)(name),
13300
+ stage: exports.POST_BUILD_TEST_STAGE,
13301
+ envMode: "stagePerEnv",
13302
+ provides: ["qualityGate"],
13303
+ needs: [],
13304
+ requires: [buildRequirement],
13305
+ image: (_a = test.jobImage) !== null && _a !== void 0 ? _a : getDefaultImage(context),
13306
+ services: test.services,
13307
+ // neutral cache declarations, lowered by each backend
13308
+ caches: nodeCache !== null && nodeCache !== void 0 ? nodeCache : undefined,
13309
+ variables: {
13310
+ APP_PATH: context.build.dir,
13311
+ ...(context.type === "component"
13312
+ ? context.environment.jobOnlyVars.build.envVars
13313
+ : {}),
13314
+ ...((_b = test.vars) !== null && _b !== void 0 ? _b : {}),
13315
+ },
13316
+ runnerVariables: {
13317
+ ...(nodeBuild ? constants_1.NODE_RUNNER_BUILD_VARIABLES : {}),
13318
+ ...((_c = test.runnerVariables) !== null && _c !== void 0 ? _c : {}),
13319
+ },
13320
+ script: [
13321
+ ...(nodeBuild ? (0, packageManagerInstall_1.ensureNodeVersion)(context) : []),
13322
+ `cd ${context.build.dir}`,
13323
+ ...(packageManagerInstall !== null && packageManagerInstall !== void 0 ? packageManagerInstall : []),
13324
+ ...(0, utils_1.ensureArray)(test.command),
13325
+ ],
13326
+ allow_failure: test.allowFailure,
13327
+ // reports and screenshots are most needed when the tests fail
13328
+ ...(artifactsConfig
13329
+ ? {
13330
+ artifacts: {
13331
+ when: "always",
13332
+ ...artifactsConfig.artifacts,
13333
+ },
13334
+ }
13335
+ : {}),
13336
+ });
13337
+ });
13338
+ };
13339
+ exports.createPostBuildTestJobs = createPostBuildTestJobs;
13340
+ const jobIdOf = (jobName) => jobName
13341
+ .toLowerCase()
13342
+ .replace(/[^a-z0-9]+/g, "-")
13343
+ .replace(/^-+|-+$/g, "");
13344
+ /**
13345
+ * post-build test names are chosen by the user. Github job ids drop the
13346
+ * emoji of the job name, so e.g. a post-build test named `test` would
13347
+ * clash with the `🧪 test` job.
13348
+ */
13349
+ const assertUniquePostBuildTestNames = (context, postBuildTestJobs, otherJobs) => {
13350
+ const seenIds = new Set(otherJobs.map((job) => jobIdOf(job.name)));
13351
+ // also catches two post-build tests that only differ in case or
13352
+ // punctuation (e.g. `e2e` and `E2E`)
13353
+ const clash = postBuildTestJobs.find((job) => {
13354
+ const id = jobIdOf(job.name);
13355
+ const isClash = seenIds.has(id);
13356
+ seenIds.add(id);
13357
+ return isClash;
13358
+ });
13359
+ if (clash) {
13360
+ throw new Error(`${context.name}: post-build test '${clash.name}' clashes with another job of the same name, please rename it in postBuildTests`);
13361
+ }
13362
+ };
13363
+ exports.assertUniquePostBuildTestNames = assertUniquePostBuildTestNames;
13364
+ //# sourceMappingURL=createPostBuildTestJobs.js.map
13365
+
13366
+ /***/ }),
13367
+
13368
+ /***/ 28458:
13369
+ /***/ (function(__unused_webpack_module, exports, __nccwpck_require__) {
13370
+
13371
+ "use strict";
13372
+
13373
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
13374
+ if (k2 === undefined) k2 = k;
13375
+ var desc = Object.getOwnPropertyDescriptor(m, k);
13376
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
13377
+ desc = { enumerable: true, get: function() { return m[k]; } };
13378
+ }
13379
+ Object.defineProperty(o, k2, desc);
13380
+ }) : (function(o, m, k, k2) {
13381
+ if (k2 === undefined) k2 = k;
13382
+ o[k2] = m[k];
13383
+ }));
13384
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
13385
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
13386
+ };
13387
+ Object.defineProperty(exports, "__esModule", ({ value: true }));
13388
+ __exportStar(__nccwpck_require__(92967), exports);
13389
+ __exportStar(__nccwpck_require__(61844), exports);
13390
+ //# sourceMappingURL=index.js.map
13391
+
13392
+ /***/ }),
13393
+
13394
+ /***/ 92967:
13395
+ /***/ ((__unused_webpack_module, exports) => {
13396
+
13397
+ "use strict";
13398
+
13399
+ Object.defineProperty(exports, "__esModule", ({ value: true }));
13400
+ //# sourceMappingURL=types.js.map
13401
+
13402
+ /***/ }),
13403
+
13069
13404
  /***/ 26041:
13070
13405
  /***/ ((__unused_webpack_module, exports) => {
13071
13406
 
@@ -13262,7 +13597,7 @@ Object.defineProperty(exports, "__esModule", ({ value: true }));
13262
13597
  "use strict";
13263
13598
 
13264
13599
  Object.defineProperty(exports, "__esModule", ({ value: true }));
13265
- exports.isKnowEnvType = exports.DEFAULT_ENV_TYPES = exports.DEFAULT_ENVS = exports.ENV_TYPES = exports.envTriggerEquals = exports.isBranchTrigger = exports.ALL_PIPELINE_TRIGGERS = void 0;
13600
+ exports.isEnvironmentDisabled = exports.getEnvironmentConfig = exports.isKnowEnvType = exports.DEFAULT_ENV_TYPES = exports.DEFAULT_ENVS = exports.ENV_TYPES = exports.envTriggerEquals = exports.isBranchTrigger = exports.ALL_PIPELINE_TRIGGERS = void 0;
13266
13601
  exports.ALL_PIPELINE_TRIGGERS = [
13267
13602
  "mainBranch",
13268
13603
  "mr",
@@ -13305,6 +13640,14 @@ const isKnowEnvType = (env) => {
13305
13640
  return env in exports.ENV_TYPES;
13306
13641
  };
13307
13642
  exports.isKnowEnvType = isKnowEnvType;
13643
+ /**
13644
+ * the project-wide config of `env` (undefined when not declared or
13645
+ * disabled)
13646
+ */
13647
+ const getEnvironmentConfig = (environments, env) => (environments === null || environments === void 0 ? void 0 : environments[env]) || undefined;
13648
+ exports.getEnvironmentConfig = getEnvironmentConfig;
13649
+ const isEnvironmentDisabled = (environments, env) => (environments === null || environments === void 0 ? void 0 : environments[env]) === false;
13650
+ exports.isEnvironmentDisabled = isEnvironmentDisabled;
13308
13651
  //# sourceMappingURL=config.js.map
13309
13652
 
13310
13653
  /***/ }),
@@ -13391,6 +13734,7 @@ exports.BASE_STAGES = [
13391
13734
  "setup",
13392
13735
  "test",
13393
13736
  "build",
13737
+ "post-build",
13394
13738
  "deploy",
13395
13739
  "verify",
13396
13740
  "agents",