@catladder/cli 0.0.0-next-78f04d3f → 0.0.0-next-ddc387fe

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 (46) hide show
  1. package/dist/apps/cli/src/release/changesetsReleaseJob.js +4 -0
  2. package/dist/apps/cli/src/release/changesetsReleaseJob.js.map +1 -1
  3. package/dist/apps/cli/src/release/githubQueuedRelease.js +22 -3
  4. package/dist/apps/cli/src/release/githubQueuedRelease.js.map +1 -1
  5. package/dist/apps/cli/src/release/stepSummary.d.ts +6 -0
  6. package/dist/apps/cli/src/release/stepSummary.js +32 -0
  7. package/dist/apps/cli/src/release/stepSummary.js.map +1 -0
  8. package/dist/bundles/catci/index.js +3 -3
  9. package/dist/bundles/catenv/index.js +284 -149
  10. package/dist/bundles/cli/index.js +284 -149
  11. package/dist/bundles/skills/catladder-deploys/references/deploy-types.md +1 -1
  12. package/dist/bundles/skills/catladder-pipelines/SKILL.md +8 -1
  13. package/dist/bundles/skills/catladder-releases/SKILL.md +17 -10
  14. package/dist/packages/bash/src/variables/VariableValueContainingReferences.d.ts +6 -0
  15. package/dist/packages/bash/src/variables/VariableValueContainingReferences.js +8 -0
  16. package/dist/packages/bash/src/variables/VariableValueContainingReferences.js.map +1 -1
  17. package/dist/packages/bash/src/variables/resolveAllReferences.d.ts +1 -2
  18. package/dist/packages/bash/src/variables/resolveAllReferences.js +4 -2
  19. package/dist/packages/bash/src/variables/resolveAllReferences.js.map +1 -1
  20. package/dist/packages/bash/src/variables/resolveAllReferencesOnce.d.ts +1 -4
  21. package/dist/packages/bash/src/variables/resolveAllReferencesOnce.js +4 -2
  22. package/dist/packages/bash/src/variables/resolveAllReferencesOnce.js.map +1 -1
  23. package/dist/packages/pipeline/src/backends/github/GithubBackend.js +93 -72
  24. package/dist/packages/pipeline/src/backends/github/GithubBackend.js.map +1 -1
  25. package/dist/packages/pipeline/src/backends/github/ensureImageJobs.d.ts +12 -0
  26. package/dist/packages/pipeline/src/backends/github/ensureImageJobs.js +40 -0
  27. package/dist/packages/pipeline/src/backends/github/ensureImageJobs.js.map +1 -0
  28. package/dist/packages/pipeline/src/backends/github/githubReleaseJobs.d.ts +17 -14
  29. package/dist/packages/pipeline/src/backends/github/githubReleaseJobs.js +76 -37
  30. package/dist/packages/pipeline/src/backends/github/githubReleaseJobs.js.map +1 -1
  31. package/dist/packages/pipeline/src/context/getEnvironmentVariables.js +17 -9
  32. package/dist/packages/pipeline/src/context/getEnvironmentVariables.js.map +1 -1
  33. package/dist/packages/pipeline/src/deploy/cloudRun/index.js +2 -2
  34. package/dist/packages/pipeline/src/deploy/cloudRun/index.js.map +1 -1
  35. package/dist/packages/pipeline/src/deploy/cloudRun/utils/database.d.ts +7 -9
  36. package/dist/packages/pipeline/src/deploy/cloudRun/utils/database.js +32 -24
  37. package/dist/packages/pipeline/src/deploy/cloudRun/utils/database.js.map +1 -1
  38. package/dist/packages/pipeline/src/deploy/index.d.ts +2 -2
  39. package/dist/skills/catladder-deploys/references/deploy-types.md +1 -1
  40. package/dist/skills/catladder-pipelines/SKILL.md +8 -1
  41. package/dist/skills/catladder-releases/SKILL.md +17 -10
  42. package/dist/tsconfig.tsbuildinfo +1 -1
  43. package/package.json +1 -1
  44. package/src/release/changesetsReleaseJob.ts +6 -0
  45. package/src/release/githubQueuedRelease.ts +37 -3
  46. package/src/release/stepSummary.ts +27 -0
@@ -7895,6 +7895,14 @@ class VariableValueContainingReferences {
7895
7895
  ? []
7896
7896
  : [part]);
7897
7897
  }
7898
+ /**
7899
+ * concats values to this one and returns a new VariableValueContainingReferences.
7900
+ * mirrors String.prototype.concat / BashExpression.concat so a VariableValue
7901
+ * can be extended regardless of its concrete type
7902
+ */
7903
+ concat(...values) {
7904
+ return new VariableValueContainingReferences([...this.parts, ...values]);
7905
+ }
7898
7906
  toString(options = {
7899
7907
  quotes: false,
7900
7908
  }) {
@@ -7944,16 +7952,18 @@ Object.defineProperty(exports, "__esModule", ({ value: true }));
7944
7952
  exports.resolveAllReferences = void 0;
7945
7953
  const VariableValueContainingReferences_1 = __nccwpck_require__(70795);
7946
7954
  const resolveAllReferencesOnce_1 = __nccwpck_require__(98690);
7955
+ const hasUnresolvedReferences = (value) => value instanceof VariableValueContainingReferences_1.VariableValueContainingReferences &&
7956
+ value.parts.some((part) => part instanceof VariableValueContainingReferences_1.VariableReference);
7947
7957
  const resolveAllReferences = async (values, getEnvVars) => {
7948
7958
  // replace until there aren't any references left
7949
7959
  let result = values;
7950
7960
  let i = 0;
7951
- while (Object.values(result).some((value) => value.parts.some((part) => part instanceof VariableValueContainingReferences_1.VariableReference))) {
7961
+ while (Object.values(result).some(hasUnresolvedReferences)) {
7952
7962
  const replaced = await (0, resolveAllReferencesOnce_1.resolveAllReferencesOnce)(result, getEnvVars);
7953
7963
  result = replaced;
7954
7964
  i++;
7955
7965
  if (i > 1000) {
7956
- const unresolved = Object.entries(result).filter(([key, value]) => value.parts.some((part) => part instanceof VariableValueContainingReferences_1.VariableReference));
7966
+ const unresolved = Object.entries(result).filter(([key, value]) => hasUnresolvedReferences(value));
7957
7967
  throw new Error("Infinite loop detected in these variables: " +
7958
7968
  unresolved
7959
7969
  .map(([key, value]) => `${key} (last reference: ${value.parts.find((part) => part instanceof VariableValueContainingReferences_1.VariableReference)})`)
@@ -7977,7 +7987,9 @@ exports.resolveAllReferencesOnce = void 0;
7977
7987
  const VariableValueContainingReferences_1 = __nccwpck_require__(70795);
7978
7988
  const resolveReferencesOnce_1 = __nccwpck_require__(29767);
7979
7989
  const resolveAllReferencesOnce = async (values, getEnvVars) => {
7980
- const allReferences = Object.values(values).flatMap((value) => value === null || value === void 0 ? void 0 : value.parts.filter((part) => part instanceof VariableValueContainingReferences_1.VariableReference));
7990
+ const allReferences = Object.values(values).flatMap((value) => value instanceof VariableValueContainingReferences_1.VariableValueContainingReferences
7991
+ ? value.parts.filter((part) => part instanceof VariableValueContainingReferences_1.VariableReference)
7992
+ : []);
7981
7993
  const allComponentsUnique = Array.from(new Set(allReferences.map((reference) => reference.componentName)));
7982
7994
  const allEnvVarsInComponents = Object.fromEntries(await Promise.all(allComponentsUnique.map(async (componentName) => [
7983
7995
  componentName,
@@ -7985,7 +7997,7 @@ const resolveAllReferencesOnce = async (values, getEnvVars) => {
7985
7997
  ])));
7986
7998
  return Object.fromEntries(Object.entries(values).map(([key, value]) => [
7987
7999
  key,
7988
- value !== null && value !== undefined
8000
+ value instanceof VariableValueContainingReferences_1.VariableValueContainingReferences
7989
8001
  ? (0, resolveReferencesOnce_1.resolveReferencesOnce)(value, ({ componentName, variableName }) => {
7990
8002
  return allEnvVarsInComponents[componentName][variableName];
7991
8003
  })
@@ -8269,9 +8281,8 @@ const ciVariables_1 = __nccwpck_require__(88393);
8269
8281
  const index_1 = __nccwpck_require__(68906);
8270
8282
  const createGithubJobs_1 = __nccwpck_require__(57877);
8271
8283
  const jobImagesPlan_1 = __nccwpck_require__(47864);
8272
- const scriptFunctions_1 = __nccwpck_require__(79252);
8284
+ const ensureImageJobs_1 = __nccwpck_require__(70051);
8273
8285
  const shippedCatci_1 = __nccwpck_require__(79024);
8274
- const utils_1 = __nccwpck_require__(24516);
8275
8286
  const githubReleaseJobs_1 = __nccwpck_require__(89800);
8276
8287
  const scriptFiles_1 = __nccwpck_require__(30966);
8277
8288
  const WORKFLOWS_FOLDER = ".github/workflows";
@@ -8280,9 +8291,13 @@ const WORKFLOWS_FOLDER = ".github/workflows";
8280
8291
  * user-maintained workflows in the same folder
8281
8292
  */
8282
8293
  const GENERATED_FILE_PREFIX = "catladder-";
8294
+ // NOTE on the emoji prefixes: github sorts the Actions sidebar by raw
8295
+ // code point, so the 🛠️ (U+1F6E0) prefix makes the pipeline workflows
8296
+ // sort BELOW the triggerable action workflows (↩️ ⏹️ ▶️ 🚀, all lower
8297
+ // code points) — the things a human clicks come first.
8283
8298
  const TRIGGER_WORKFLOWS = {
8284
8299
  mr: {
8285
- name: "catladder review",
8300
+ name: "🛠️ catladder review",
8286
8301
  on: { pull_request: { types: ["opened", "synchronize", "reopened"] } },
8287
8302
  concurrency: {
8288
8303
  group: "catladder-review-${{ github.event.number }}",
@@ -8290,12 +8305,12 @@ const TRIGGER_WORKFLOWS = {
8290
8305
  },
8291
8306
  },
8292
8307
  mainBranch: {
8293
- name: "catladder main",
8308
+ name: "🛠️ catladder main",
8294
8309
  // TODO: make the default branch configurable
8295
8310
  on: { push: { branches: ["main"] } },
8296
8311
  },
8297
8312
  taggedRelease: {
8298
- name: "catladder release",
8313
+ name: "🛠️ catladder release",
8299
8314
  // tags pushed with the default GITHUB_TOKEN (as the release job
8300
8315
  // does) don't trigger `on: push: tags` — the release job therefore
8301
8316
  // also dispatches this workflow explicitly for the new tag
@@ -8345,15 +8360,18 @@ class GithubBackend {
8345
8360
  ...(0, index_1.getPipelineOptions)(config, this.type).runnerVariables,
8346
8361
  };
8347
8362
  const reviewStopJobs = {};
8348
- // jobs routed to the workflow_dispatch "manual tasks" workflow. A
8349
- // manual job and its build closure live here instead of an automatic
8350
- // workflow; `manualJobConditions` is the `if` that selects each one
8351
- // from the dispatch input, `manualDispatchOptions` the leaf jobs that
8352
- // become dropdown choices (closure jobs run because their leaf was
8353
- // chosen, not on their own).
8354
- const manualJobs = {};
8355
- const manualJobConditions = {};
8356
- const manualDispatchOptions = [];
8363
+ // jobs routed to the per-kind workflow_dispatch workflows (deploy /
8364
+ // stop / rollback). A manual job and its build closure live there
8365
+ // instead of an automatic workflow; `conditions` is the `if` that
8366
+ // selects each one from the dispatch input, `options` the leaf jobs
8367
+ // that become dropdown choices (closure jobs run because their leaf
8368
+ // was chosen, not on their own). A closure job serving leaves of
8369
+ // several kinds is duplicated into each kind's workflow.
8370
+ const manualKindBuckets = {
8371
+ deploy: { jobs: {}, conditions: {}, options: [], envTypes: [] },
8372
+ stop: { jobs: {}, conditions: {}, options: [], envTypes: [] },
8373
+ rollback: { jobs: {}, conditions: {}, options: [], envTypes: [] },
8374
+ };
8357
8375
  for (const trigger of config_1.ALL_PIPELINE_TRIGGERS) {
8358
8376
  const allJobs = await (0, createAllJobs_1.createAllJobs)({
8359
8377
  config,
@@ -8430,32 +8448,46 @@ class GithubBackend {
8430
8448
  }
8431
8449
  }
8432
8450
  }
8451
+ const kindOfLeaf = new Map(leafIds.map((id) => [id, manualKind(triggerJobs.get(id).job)]));
8433
8452
  const jobs = {};
8434
8453
  for (const [id, { githubJob }] of triggerJobs) {
8435
8454
  const leaves = servesLeaves.get(id);
8436
8455
  if (leaves) {
8437
- manualJobs[id] = githubJob;
8438
- manualJobConditions[id] = [...leaves]
8439
- .sort()
8440
- .map((leaf) => `inputs.job == '${leaf}'`)
8441
- .join(" || ");
8456
+ // one entry per kind the job serves — restricted to that
8457
+ // kind's leaves, so the `if` only references its own inputs
8458
+ for (const kind of new Set([...leaves].map((leaf) => kindOfLeaf.get(leaf)))) {
8459
+ const bucket = manualKindBuckets[kind];
8460
+ bucket.jobs[id] = githubJob;
8461
+ bucket.conditions[id] = [...leaves]
8462
+ .filter((leaf) => kindOfLeaf.get(leaf) === kind)
8463
+ .sort()
8464
+ .map((leaf) => `inputs.job == '${leaf}'`)
8465
+ .join(" || ");
8466
+ }
8442
8467
  }
8443
8468
  else {
8444
8469
  jobs[id] = githubJob;
8445
8470
  }
8446
8471
  }
8447
- manualDispatchOptions.push(...leafIds);
8472
+ for (const id of leafIds) {
8473
+ const bucket = manualKindBuckets[kindOfLeaf.get(id)];
8474
+ bucket.options.push(id);
8475
+ const context = triggerJobs.get(id).context;
8476
+ if (context.type === "component") {
8477
+ bucket.envTypes.push(context.environment.envType);
8478
+ }
8479
+ }
8448
8480
  if (trigger === "mr") {
8449
8481
  Object.assign(jobs, (0, githubReleaseJobs_1.getGithubReleaseCheckJobs)(config, images));
8450
8482
  }
8451
8483
  if (trigger === "mainBranch") {
8452
- const releaseJobs = (0, githubReleaseJobs_1.getGithubReleaseJobs)(config, Object.keys(jobs), images);
8453
- Object.assign(jobs, releaseJobs.main);
8454
- for (const [id, job] of Object.entries(releaseJobs.manual)) {
8455
- manualJobs[id] = job;
8456
- manualJobConditions[id] = `inputs.job == '${id}'`;
8457
- manualDispatchOptions.push(id);
8458
- }
8484
+ // auto release mode: the release job runs at the end of the
8485
+ // main workflow itself
8486
+ Object.assign(jobs, (0, githubReleaseJobs_1.getGithubReleaseJobs)(config, Object.keys(jobs), images));
8487
+ // the dispatch workflow to trigger a release by hand (with a
8488
+ // force checkbox where the method distinguishes it)
8489
+ workflows[`${GENERATED_FILE_PREFIX}create-release.yml`] =
8490
+ (0, githubReleaseJobs_1.getGithubCreateReleaseWorkflow)(config, images, workflowEnv);
8459
8491
  // manual release mode: the workflow that runs a queued release
8460
8492
  // once the main workflow completed green
8461
8493
  const releaseOnGreen = (0, githubReleaseJobs_1.getGithubReleaseOnGreenWorkflow)(config, images, TRIGGER_WORKFLOWS.mainBranch.name, workflowEnv);
@@ -8469,41 +8501,59 @@ class GithubBackend {
8469
8501
  ...TRIGGER_WORKFLOWS[trigger],
8470
8502
  ...workflowPermissions(images),
8471
8503
  env: workflowEnv,
8472
- jobs: { ...makeEnsureImageGithubJobs(images), ...jobs },
8504
+ jobs: { ...(0, ensureImageJobs_1.makeEnsureImageGithubJobs)(images), ...jobs },
8473
8505
  };
8474
8506
  }
8475
8507
  }
8476
8508
  if (Object.keys(reviewStopJobs).length > 0) {
8477
8509
  workflows[`${GENERATED_FILE_PREFIX}review-stop.yml`] = {
8478
- name: "catladder review stop",
8510
+ name: "🛠️ catladder review stop",
8479
8511
  on: { pull_request: { types: ["closed"] } },
8480
8512
  ...workflowPermissions(images),
8481
8513
  env: workflowEnv,
8482
- jobs: { ...makeEnsureImageGithubJobs(images), ...reviewStopJobs },
8514
+ jobs: { ...(0, ensureImageJobs_1.makeEnsureImageGithubJobs)(images), ...reviewStopJobs },
8483
8515
  };
8484
8516
  }
8485
- if (Object.keys(manualJobs).length > 0) {
8486
- workflows[`${GENERATED_FILE_PREFIX}manual.yml`] = {
8487
- name: "catladder manual tasks",
8517
+ for (const [kind, bucket] of Object.entries(manualKindBuckets)) {
8518
+ if (Object.keys(bucket.jobs).length === 0) {
8519
+ continue;
8520
+ }
8521
+ const options = [...new Set(bucket.options)];
8522
+ // a single candidate needs no dropdown — the dispatch just runs it
8523
+ const singleTask = options.length === 1;
8524
+ // one common env across the kind's tasks → carry it in the name
8525
+ // ("▶️ catladder deploy prod"); mixed envs keep the generic name
8526
+ // and the dropdown distinguishes
8527
+ const envTypes = [...new Set(bucket.envTypes)];
8528
+ const envSuffix = envTypes.length === 1 &&
8529
+ bucket.envTypes.length === bucket.options.length
8530
+ ? ` ${envTypes[0]}`
8531
+ : "";
8532
+ workflows[`${GENERATED_FILE_PREFIX}${kind}.yml`] = {
8533
+ name: `${MANUAL_KIND_WORKFLOW_NAMES[kind]}${envSuffix}`,
8488
8534
  on: {
8489
- workflow_dispatch: {
8490
- inputs: {
8491
- job: {
8492
- description: "the task to run",
8493
- required: true,
8494
- type: "choice",
8495
- options: [...new Set(manualDispatchOptions)],
8535
+ workflow_dispatch: singleTask
8536
+ ? {}
8537
+ : {
8538
+ inputs: {
8539
+ job: {
8540
+ description: "the task to run",
8541
+ required: true,
8542
+ type: "choice",
8543
+ options,
8544
+ },
8496
8545
  },
8497
8546
  },
8498
- },
8499
8547
  },
8500
8548
  ...workflowPermissions(images),
8501
8549
  env: workflowEnv,
8502
8550
  jobs: {
8503
- ...makeEnsureImageGithubJobs(images),
8504
- ...Object.fromEntries(Object.entries(manualJobs).map(([id, job]) => [
8551
+ ...(0, ensureImageJobs_1.makeEnsureImageGithubJobs)(images),
8552
+ ...Object.fromEntries(Object.entries(bucket.jobs).map(([id, job]) => [
8505
8553
  id,
8506
- { ...job, if: `\${{ ${manualJobConditions[id]} }}` },
8554
+ singleTask
8555
+ ? job
8556
+ : { ...job, if: `\${{ ${bucket.conditions[id]} }}` },
8507
8557
  ])),
8508
8558
  },
8509
8559
  };
@@ -8518,33 +8568,6 @@ exports.GithubBackend = GithubBackend;
8518
8568
  const workflowPermissions = (_images) => ({
8519
8569
  permissions: { contents: "read", packages: "write" },
8520
8570
  });
8521
- /**
8522
- * the image build jobs: always run (github has no server-side
8523
- * changes-filter), the existence check makes unchanged runs fast.
8524
- * Docker is native on github runners — no container, no dind.
8525
- */
8526
- const makeEnsureImageGithubJobs = (images) => Object.fromEntries(images.getEnsureJobs().map((job) => {
8527
- var _a, _b;
8528
- return [
8529
- (0, createGithubJobs_1.githubGlobalJobId)(job.name),
8530
- {
8531
- name: job.name,
8532
- "runs-on": "ubuntu-latest",
8533
- steps: [
8534
- { name: "Checkout", uses: "actions/checkout@v4" },
8535
- {
8536
- name: job.name,
8537
- id: "main",
8538
- run: [
8539
- ...(0, scriptFunctions_1.getGithubScriptFunctionDefinitions)(),
8540
- ...((_b = (_a = job.script) === null || _a === void 0 ? void 0 : _a.filter(utils_1.notNil)) !== null && _b !== void 0 ? _b : []),
8541
- ].join("\n"),
8542
- shell: "bash",
8543
- },
8544
- ],
8545
- },
8546
- ];
8547
- }));
8548
8571
  const workflowFileName = (trigger) => {
8549
8572
  switch (trigger) {
8550
8573
  case "mr":
@@ -8568,10 +8591,20 @@ const isReviewStopJob = (context, job) => {
8568
8591
  };
8569
8592
  /**
8570
8593
  * stop/rollback jobs of non-review environments have no automatic
8571
- * trigger; they are collected in a workflow_dispatch workflow
8594
+ * trigger; they are collected in workflow_dispatch workflows
8572
8595
  * (gitlab runs them as manual jobs inside the pipeline)
8573
8596
  */
8574
8597
  const isManualTaskJob = (job) => job.stage === "stop" || job.stage === "rollback";
8598
+ const MANUAL_KIND_WORKFLOW_NAMES = {
8599
+ deploy: "▶️ catladder deploy",
8600
+ stop: "⏹️ catladder stop",
8601
+ rollback: "↩️ catladder rollback",
8602
+ };
8603
+ const manualKind = (job) => job.stage === "stop"
8604
+ ? "stop"
8605
+ : job.stage === "rollback"
8606
+ ? "rollback"
8607
+ : "deploy";
8575
8608
  //# sourceMappingURL=GithubBackend.js.map
8576
8609
 
8577
8610
  /***/ }),
@@ -9001,17 +9034,78 @@ exports.getUploadProviderIds = getUploadProviderIds;
9001
9034
 
9002
9035
  /***/ }),
9003
9036
 
9037
+ /***/ 70051:
9038
+ /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => {
9039
+
9040
+ "use strict";
9041
+
9042
+ Object.defineProperty(exports, "__esModule", ({ value: true }));
9043
+ exports.makeEnsureImageGithubJobs = void 0;
9044
+ const createGithubJobs_1 = __nccwpck_require__(57877);
9045
+ const scriptFunctions_1 = __nccwpck_require__(79252);
9046
+ const utils_1 = __nccwpck_require__(24516);
9047
+ /**
9048
+ * the image build jobs: always run (github has no server-side
9049
+ * changes-filter), the existence check makes unchanged runs fast.
9050
+ * Docker is native on github runners — no container, no dind.
9051
+ * `only` restricts to the named images (e.g. the create-release
9052
+ * workflow needs just the release image).
9053
+ */
9054
+ const makeEnsureImageGithubJobs = (images, options = {}) => Object.fromEntries(images
9055
+ .getEnsureJobs()
9056
+ .filter((job) => !options.only || options.only.includes(job.name))
9057
+ .map((job) => {
9058
+ var _a, _b;
9059
+ return [
9060
+ (0, createGithubJobs_1.githubGlobalJobId)(job.name),
9061
+ {
9062
+ name: job.name,
9063
+ "runs-on": "ubuntu-latest",
9064
+ steps: [
9065
+ { name: "Checkout", uses: "actions/checkout@v4" },
9066
+ {
9067
+ name: job.name,
9068
+ id: "main",
9069
+ run: [
9070
+ ...(0, scriptFunctions_1.getGithubScriptFunctionDefinitions)(),
9071
+ ...((_b = (_a = job.script) === null || _a === void 0 ? void 0 : _a.filter(utils_1.notNil)) !== null && _b !== void 0 ? _b : []),
9072
+ ].join("\n"),
9073
+ shell: "bash",
9074
+ },
9075
+ ],
9076
+ },
9077
+ ];
9078
+ }));
9079
+ exports.makeEnsureImageGithubJobs = makeEnsureImageGithubJobs;
9080
+ //# sourceMappingURL=ensureImageJobs.js.map
9081
+
9082
+ /***/ }),
9083
+
9004
9084
  /***/ 89800:
9005
9085
  /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => {
9006
9086
 
9007
9087
  "use strict";
9008
9088
 
9009
9089
  Object.defineProperty(exports, "__esModule", ({ value: true }));
9010
- exports.getGithubReleaseCheckJobs = exports.getGithubReleaseOnGreenWorkflow = exports.getGithubReleaseJobs = void 0;
9090
+ exports.getGithubReleaseCheckJobs = exports.getGithubReleaseOnGreenWorkflow = exports.getGithubCreateReleaseWorkflow = exports.getGithubReleaseJobs = void 0;
9011
9091
  const createGithubJobs_1 = __nccwpck_require__(57877);
9092
+ const ensureImageJobs_1 = __nccwpck_require__(70051);
9012
9093
  const release_1 = __nccwpck_require__(38904);
9013
9094
  const shippedCatci_1 = __nccwpck_require__(79024);
9014
9095
  const CATCI = `${shippedCatci_1.GENERATED_CATCI_FOLDER}/index.js`;
9096
+ /**
9097
+ * the build job of the release image only — the create-release
9098
+ * dispatch workflow needs no other image (empty for external images)
9099
+ */
9100
+ const makeReleaseImageEnsureJobs = (config, images) => {
9101
+ const resolved = images.resolveRef({
9102
+ catladderImage: (0, release_1.getReleaseMethod)(config).image,
9103
+ });
9104
+ if (!resolved.need || typeof resolved.need !== "object") {
9105
+ return {};
9106
+ }
9107
+ return (0, ensureImageJobs_1.makeEnsureImageGithubJobs)(images, { only: [resolved.need.job] });
9108
+ };
9015
9109
  const createReleaseJob = (config, images, options = {}) => {
9016
9110
  var _a;
9017
9111
  const method = (0, release_1.getReleaseMethod)(config);
@@ -9065,6 +9159,7 @@ const createReleaseJob = (config, images, options = {}) => {
9065
9159
  {
9066
9160
  name: "check main workflow",
9067
9161
  id: "queue",
9162
+ ...(options.queueCheckIf ? { if: options.queueCheckIf } : {}),
9068
9163
  run: `node ${CATCI} release github-queue-check`,
9069
9164
  shell: "bash",
9070
9165
  },
@@ -9082,51 +9177,75 @@ const createReleaseJob = (config, images, options = {}) => {
9082
9177
  };
9083
9178
  };
9084
9179
  /**
9085
- * the release jobs for the github backend (the counterpart of the
9086
- * gitlab release jobs):
9087
- * - releases.when === "auto": the release job runs in the main-branch
9088
- * workflow after all other jobs; a force variant goes to the manual
9089
- * tasks workflow
9090
- * - otherwise (manual, the default): the release job goes to the
9091
- * manual tasks workflow. Its queue-check step releases right away on
9092
- * a green main run and otherwise queues the release for the
9093
- * release-on-green workflow (see getGithubReleaseOnGreenWorkflow) —
9094
- * the counterpart of gitlab's queue button + executor pair.
9180
+ * the release jobs of the main-branch workflow: with releases.when ===
9181
+ * "auto" the release runs at the end of every main workflow run.
9182
+ * Manual releasing happens via the create-release dispatch workflow
9183
+ * (see getGithubCreateReleaseWorkflow) instead.
9095
9184
  */
9096
9185
  const getGithubReleaseJobs = (config, mainWorkflowJobIds, images) => {
9097
9186
  var _a;
9098
- const method = (0, release_1.getReleaseMethod)(config);
9099
- if (((_a = config.releases) === null || _a === void 0 ? void 0 : _a.when) === "auto") {
9100
- return {
9101
- main: {
9102
- "create-release": createReleaseJob(config, images, {
9103
- needs: mainWorkflowJobIds,
9104
- }),
9105
- },
9106
- manual: {
9107
- "force-create-release": createReleaseJob(config, images, {
9108
- extraEnv: method.forceReleaseVariables,
9109
- }),
9110
- },
9111
- };
9187
+ if (((_a = config.releases) === null || _a === void 0 ? void 0 : _a.when) !== "auto") {
9188
+ return {};
9112
9189
  }
9113
9190
  return {
9114
- main: {},
9115
- manual: {
9116
- "create-release": createReleaseJob(config, images, { queueCheck: true }),
9117
- // an explicit force option wherever the method distinguishes it
9118
- // (e.g. changesets: release a patch bump without changesets)
9119
- ...(method.forceReleaseVariables
9191
+ "create-release": createReleaseJob(config, images, {
9192
+ needs: mainWorkflowJobIds,
9193
+ }),
9194
+ };
9195
+ };
9196
+ exports.getGithubReleaseJobs = getGithubReleaseJobs;
9197
+ /**
9198
+ * the dispatch workflow to trigger a release by hand — a first-class
9199
+ * entry in the Actions sidebar (the counterpart of gitlab's `create
9200
+ * release` button). Its queue-check step releases right away when the
9201
+ * main workflow run for HEAD is green and otherwise queues the release
9202
+ * for the release-on-green workflow. Where the release method
9203
+ * distinguishes a force mode (e.g. changesets: release a patch bump
9204
+ * without pending changesets), forcing is a checkbox input — a forced
9205
+ * release skips the queue-check and releases immediately, whatever the
9206
+ * pipeline state.
9207
+ */
9208
+ const getGithubCreateReleaseWorkflow = (config, images, workflowEnv) => {
9209
+ const method = (0, release_1.getReleaseMethod)(config);
9210
+ const forceVariables = method.forceReleaseVariables;
9211
+ const job = createReleaseJob(config, images, {
9212
+ queueCheck: true,
9213
+ ...(forceVariables
9214
+ ? {
9215
+ queueCheckIf: "${{ !inputs.force }}",
9216
+ // only effective when the force checkbox is ticked
9217
+ extraEnv: Object.fromEntries(Object.entries(forceVariables).map(([key, value]) => [
9218
+ key,
9219
+ `\${{ inputs.force && '${value}' || '' }}`,
9220
+ ])),
9221
+ }
9222
+ : {}),
9223
+ });
9224
+ return {
9225
+ name: "🚀 catladder create release",
9226
+ on: {
9227
+ workflow_dispatch: forceVariables
9120
9228
  ? {
9121
- "force-create-release": createReleaseJob(config, images, {
9122
- extraEnv: method.forceReleaseVariables,
9123
- }),
9229
+ inputs: {
9230
+ force: {
9231
+ description: "force: release even without pending changes (patch bump), ignoring the pipeline state",
9232
+ required: false,
9233
+ type: "boolean",
9234
+ default: false,
9235
+ },
9236
+ },
9124
9237
  }
9125
- : {}),
9238
+ : {},
9239
+ },
9240
+ permissions: { contents: "read", packages: "write" },
9241
+ env: workflowEnv,
9242
+ jobs: {
9243
+ ...makeReleaseImageEnsureJobs(config, images),
9244
+ "create-release": job,
9126
9245
  },
9127
9246
  };
9128
9247
  };
9129
- exports.getGithubReleaseJobs = getGithubReleaseJobs;
9248
+ exports.getGithubCreateReleaseWorkflow = getGithubCreateReleaseWorkflow;
9130
9249
  /**
9131
9250
  * the release-on-green workflow (manual release mode only): runs on
9132
9251
  * every completed main workflow run. A lightweight host job checks the
@@ -9145,7 +9264,7 @@ const getGithubReleaseOnGreenWorkflow = (config, images, mainWorkflowName, workf
9145
9264
  return null;
9146
9265
  }
9147
9266
  return {
9148
- name: "catladder release on green",
9267
+ name: "🛠️ catladder release on green",
9149
9268
  on: {
9150
9269
  workflow_run: {
9151
9270
  workflows: [mainWorkflowName],
@@ -12922,7 +13041,9 @@ const getEnvironmentVariables = async (ctx, options = {}) => {
12922
13041
  const additionalEnvVars = deployConfigRaw
12923
13042
  ? deploy_1.DEPLOY_TYPES[deployConfigRaw.type].getAdditionalEnvVars(environmentContext)
12924
13043
  : {};
12925
- const HOSTNAME_INTERNAL = (_b = additionalEnvVars.HOSTNAME_INTERNAL) !== null && _b !== void 0 ? _b : "unknown-host.example.com";
13044
+ // deploy types provide HOSTNAME_INTERNAL as a plain string or bash
13045
+ // expression (never containing references)
13046
+ const HOSTNAME_INTERNAL = ((_b = additionalEnvVars.HOSTNAME_INTERNAL) !== null && _b !== void 0 ? _b : "unknown-host.example.com");
12926
13047
  host = (_c = envConfigRaw === null || envConfigRaw === void 0 ? void 0 : envConfigRaw.host) !== null && _c !== void 0 ? _c : HOSTNAME_INTERNAL;
12927
13048
  url = (0, bash_1.joinBashExpressions)(["https://", host]);
12928
13049
  predefinedVariables = {
@@ -12950,8 +13071,18 @@ const getEnvironmentVariables = async (ctx, options = {}) => {
12950
13071
  componentName: ctx.componentName,
12951
13072
  }),
12952
13073
  ]));
12953
- const publicEnvVars = ((_g = options.shouldResolveReferences) !== null && _g !== void 0 ? _g : true)
12954
- ? await (0, bash_3.resolveAllReferences)(publicEnvVarsUnresolved, async (otherComponentName) => {
13074
+ // predefined variables may contain references too (e.g. the embedded
13075
+ // database connection string references the component's own DB_* vars), so
13076
+ // we resolve references over the merged map. This way public var overrides
13077
+ // (like DB_PASSWORD: "${otherComponent:DB_PASSWORD}") are picked up by the
13078
+ // predefined variables that reference them
13079
+ const mergedEnvVars = {
13080
+ ...predefinedVariables,
13081
+ ...secretEnvVars,
13082
+ ...publicEnvVarsUnresolved,
13083
+ };
13084
+ const resolvedEnvVars = ((_g = options.shouldResolveReferences) !== null && _g !== void 0 ? _g : true)
13085
+ ? await (0, bash_3.resolveAllReferences)(mergedEnvVars, async (otherComponentName) => {
12955
13086
  const { envVars: otherEnvVars } = await (0, exports.getEnvironmentVariables)({
12956
13087
  ...ctx,
12957
13088
  componentName: otherComponentName,
@@ -12960,12 +13091,8 @@ const getEnvironmentVariables = async (ctx, options = {}) => {
12960
13091
  });
12961
13092
  return otherEnvVars;
12962
13093
  })
12963
- : publicEnvVarsUnresolved;
12964
- const envVars = addIndexVar({
12965
- ...predefinedVariables,
12966
- ...secretEnvVars,
12967
- ...publicEnvVars,
12968
- });
13094
+ : mergedEnvVars;
13095
+ const envVars = addIndexVar(resolvedEnvVars);
12969
13096
  return {
12970
13097
  envVars,
12971
13098
  secretEnvVarKeys,
@@ -14956,8 +15083,8 @@ const getCloudSqlVariables = ({ deployConfigRaw, environmentSlugPrefix, env, com
14956
15083
  };
14957
15084
  return {
14958
15085
  ...dbVars,
14959
- DATABASE_URL: (0, database_1.getDatabaseConnectionString)(deployConfigRaw.cloudSql, dbVars).concat(additionalQueryParamsString),
14960
- DATABASE_JDBC_URL: (0, database_1.getDatabaseJdbcUrl)(dbVars, (_c = deployConfigRaw.cloudSql.dbConnectionStringVariablesMode) !== null && _c !== void 0 ? _c : database_1.DEFAULT_DB_VARIABLES_MODE),
15086
+ DATABASE_URL: (0, database_1.getDatabaseConnectionString)(deployConfigRaw.cloudSql, dbVars, componentName).concat(additionalQueryParamsString),
15087
+ DATABASE_JDBC_URL: (0, database_1.getDatabaseJdbcUrl)(dbVars, (_c = deployConfigRaw.cloudSql.dbConnectionStringVariablesMode) !== null && _c !== void 0 ? _c : database_1.DEFAULT_DB_VARIABLES_MODE, componentName),
14961
15088
  };
14962
15089
  }
14963
15090
  return {};
@@ -15149,64 +15276,72 @@ const getDatabaseCreateScript = (context, deployConfig) => {
15149
15276
  ]);
15150
15277
  };
15151
15278
  exports.getDatabaseCreateScript = getDatabaseCreateScript;
15152
- exports.DEFAULT_DB_VARIABLES_MODE = "legacy";
15153
- const getVariableOrValue = (key, variables, mode) => {
15154
- return mode === "legacy" ? `$${key}` : variables[key];
15155
- };
15156
- const getDatabaseJdbcUrl = (variables, mode) => {
15279
+ exports.DEFAULT_DB_VARIABLES_MODE = "embedded";
15280
+ const getVariableOrValue = (key, mode, componentName) => {
15281
+ // in embedded mode we reference the component's own env var, which gets
15282
+ // resolved to its final value (including vars.public overrides) after all
15283
+ // env vars have been merged
15284
+ return mode === "legacy"
15285
+ ? `$${key}`
15286
+ : new bash_1.VariableReference(componentName, key);
15287
+ };
15288
+ const joinDbUrlParts = (parts, mode) => mode === "legacy"
15289
+ ? (0, bash_1.joinBashExpressions)(parts)
15290
+ : new bash_1.VariableValueContainingReferences(parts);
15291
+ const getDatabaseJdbcUrl = (variables, mode, componentName) => {
15157
15292
  const parts = [
15158
15293
  "jdbc:postgresql:///",
15159
- getVariableOrValue("DB_NAME", variables, mode),
15294
+ getVariableOrValue("DB_NAME", mode, componentName),
15160
15295
  "?cloudSqlInstance=",
15161
- getVariableOrValue("CLOUD_SQL_INSTANCE_CONNECTION_NAME", variables, mode),
15296
+ getVariableOrValue("CLOUD_SQL_INSTANCE_CONNECTION_NAME", mode, componentName),
15162
15297
  "&socketFactory=com.google.cloud.sql.postgres.SocketFactory&user=",
15163
- getVariableOrValue("DB_USER", variables, mode),
15298
+ getVariableOrValue("DB_USER", mode, componentName),
15164
15299
  "&password=",
15165
- getVariableOrValue("DB_PASSWORD", variables, mode),
15300
+ getVariableOrValue("DB_PASSWORD", mode, componentName),
15166
15301
  ];
15167
- return (0, bash_1.joinBashExpressions)(parts);
15302
+ return joinDbUrlParts(parts, mode);
15168
15303
  };
15169
15304
  exports.getDatabaseJdbcUrl = getDatabaseJdbcUrl;
15170
- const getRailsDatabaseConnectionString = (variables, mode) => {
15305
+ const getRailsDatabaseConnectionString = (variables, mode, componentName) => {
15171
15306
  const parts = [
15172
15307
  "postgresql://",
15173
- getVariableOrValue("DB_USER", variables, mode),
15308
+ getVariableOrValue("DB_USER", mode, componentName),
15174
15309
  ":",
15175
- getVariableOrValue("DB_PASSWORD", variables, mode),
15310
+ getVariableOrValue("DB_PASSWORD", mode, componentName),
15176
15311
  "@",
15177
15312
  encodeURIComponent(`/cloudsql/${variables.CLOUD_SQL_INSTANCE_CONNECTION_NAME}`),
15178
15313
  "/",
15179
- getVariableOrValue("DB_NAME", variables, mode),
15314
+ getVariableOrValue("DB_NAME", mode, componentName),
15180
15315
  "?",
15181
15316
  ];
15182
- return (0, bash_1.joinBashExpressions)(parts);
15317
+ return joinDbUrlParts(parts, mode);
15183
15318
  };
15184
15319
  exports.getRailsDatabaseConnectionString = getRailsDatabaseConnectionString;
15185
- const getPrismaDatabaseConnectionString = (variables, mode) => {
15320
+ const getPrismaDatabaseConnectionString = (variables, mode, componentName) => {
15186
15321
  const parts = [
15187
15322
  "postgresql://",
15188
- getVariableOrValue("DB_USER", variables, mode),
15323
+ getVariableOrValue("DB_USER", mode, componentName),
15189
15324
  ":",
15190
- getVariableOrValue("DB_PASSWORD", variables, mode),
15325
+ getVariableOrValue("DB_PASSWORD", mode, componentName),
15191
15326
  "@localhost/",
15192
- getVariableOrValue("DB_NAME", variables, mode),
15327
+ getVariableOrValue("DB_NAME", mode, componentName),
15193
15328
  "?host=/cloudsql/",
15194
- getVariableOrValue("CLOUD_SQL_INSTANCE_CONNECTION_NAME", variables, mode),
15329
+ getVariableOrValue("CLOUD_SQL_INSTANCE_CONNECTION_NAME", mode, componentName),
15195
15330
  ];
15196
- return (0, bash_1.joinBashExpressions)(parts);
15331
+ return joinDbUrlParts(parts, mode);
15197
15332
  };
15198
15333
  exports.getPrismaDatabaseConnectionString = getPrismaDatabaseConnectionString;
15199
- const getDatabaseConnectionString = (config, variables) => {
15334
+ const getDatabaseConnectionString = (config, variables, componentName) => {
15200
15335
  var _a;
15201
15336
  const mode = (_a = config.dbConnectionStringVariablesMode) !== null && _a !== void 0 ? _a : exports.DEFAULT_DB_VARIABLES_MODE;
15202
15337
  switch (config.dbConnectionStringFormat) {
15203
15338
  case "jdbc":
15204
- return (0, exports.getDatabaseJdbcUrl)(variables, mode);
15339
+ return (0, exports.getDatabaseJdbcUrl)(variables, mode, componentName);
15205
15340
  case "rails":
15206
- return (0, exports.getRailsDatabaseConnectionString)(variables, mode);
15341
+ return (0, exports.getRailsDatabaseConnectionString)(variables, mode, componentName);
15207
15342
  default:
15208
15343
  // prisma
15209
- return (0, exports.getPrismaDatabaseConnectionString)(variables, mode);
15344
+ return (0, exports.getPrismaDatabaseConnectionString)(variables, mode, componentName);
15210
15345
  }
15211
15346
  };
15212
15347
  exports.getDatabaseConnectionString = getDatabaseConnectionString;