@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
@@ -1902,6 +1902,14 @@ class VariableValueContainingReferences {
1902
1902
  ? []
1903
1903
  : [part]);
1904
1904
  }
1905
+ /**
1906
+ * concats values to this one and returns a new VariableValueContainingReferences.
1907
+ * mirrors String.prototype.concat / BashExpression.concat so a VariableValue
1908
+ * can be extended regardless of its concrete type
1909
+ */
1910
+ concat(...values) {
1911
+ return new VariableValueContainingReferences([...this.parts, ...values]);
1912
+ }
1905
1913
  toString(options = {
1906
1914
  quotes: false,
1907
1915
  }) {
@@ -1951,16 +1959,18 @@ Object.defineProperty(exports, "__esModule", ({ value: true }));
1951
1959
  exports.resolveAllReferences = void 0;
1952
1960
  const VariableValueContainingReferences_1 = __nccwpck_require__(70795);
1953
1961
  const resolveAllReferencesOnce_1 = __nccwpck_require__(98690);
1962
+ const hasUnresolvedReferences = (value) => value instanceof VariableValueContainingReferences_1.VariableValueContainingReferences &&
1963
+ value.parts.some((part) => part instanceof VariableValueContainingReferences_1.VariableReference);
1954
1964
  const resolveAllReferences = async (values, getEnvVars) => {
1955
1965
  // replace until there aren't any references left
1956
1966
  let result = values;
1957
1967
  let i = 0;
1958
- while (Object.values(result).some((value) => value.parts.some((part) => part instanceof VariableValueContainingReferences_1.VariableReference))) {
1968
+ while (Object.values(result).some(hasUnresolvedReferences)) {
1959
1969
  const replaced = await (0, resolveAllReferencesOnce_1.resolveAllReferencesOnce)(result, getEnvVars);
1960
1970
  result = replaced;
1961
1971
  i++;
1962
1972
  if (i > 1000) {
1963
- const unresolved = Object.entries(result).filter(([key, value]) => value.parts.some((part) => part instanceof VariableValueContainingReferences_1.VariableReference));
1973
+ const unresolved = Object.entries(result).filter(([key, value]) => hasUnresolvedReferences(value));
1964
1974
  throw new Error("Infinite loop detected in these variables: " +
1965
1975
  unresolved
1966
1976
  .map(([key, value]) => `${key} (last reference: ${value.parts.find((part) => part instanceof VariableValueContainingReferences_1.VariableReference)})`)
@@ -1984,7 +1994,9 @@ exports.resolveAllReferencesOnce = void 0;
1984
1994
  const VariableValueContainingReferences_1 = __nccwpck_require__(70795);
1985
1995
  const resolveReferencesOnce_1 = __nccwpck_require__(29767);
1986
1996
  const resolveAllReferencesOnce = async (values, getEnvVars) => {
1987
- const allReferences = Object.values(values).flatMap((value) => value === null || value === void 0 ? void 0 : value.parts.filter((part) => part instanceof VariableValueContainingReferences_1.VariableReference));
1997
+ const allReferences = Object.values(values).flatMap((value) => value instanceof VariableValueContainingReferences_1.VariableValueContainingReferences
1998
+ ? value.parts.filter((part) => part instanceof VariableValueContainingReferences_1.VariableReference)
1999
+ : []);
1988
2000
  const allComponentsUnique = Array.from(new Set(allReferences.map((reference) => reference.componentName)));
1989
2001
  const allEnvVarsInComponents = Object.fromEntries(await Promise.all(allComponentsUnique.map(async (componentName) => [
1990
2002
  componentName,
@@ -1992,7 +2004,7 @@ const resolveAllReferencesOnce = async (values, getEnvVars) => {
1992
2004
  ])));
1993
2005
  return Object.fromEntries(Object.entries(values).map(([key, value]) => [
1994
2006
  key,
1995
- value !== null && value !== undefined
2007
+ value instanceof VariableValueContainingReferences_1.VariableValueContainingReferences
1996
2008
  ? (0, resolveReferencesOnce_1.resolveReferencesOnce)(value, ({ componentName, variableName }) => {
1997
2009
  return allEnvVarsInComponents[componentName][variableName];
1998
2010
  })
@@ -2276,9 +2288,8 @@ const ciVariables_1 = __nccwpck_require__(88393);
2276
2288
  const index_1 = __nccwpck_require__(68906);
2277
2289
  const createGithubJobs_1 = __nccwpck_require__(57877);
2278
2290
  const jobImagesPlan_1 = __nccwpck_require__(47864);
2279
- const scriptFunctions_1 = __nccwpck_require__(79252);
2291
+ const ensureImageJobs_1 = __nccwpck_require__(70051);
2280
2292
  const shippedCatci_1 = __nccwpck_require__(79024);
2281
- const utils_1 = __nccwpck_require__(24516);
2282
2293
  const githubReleaseJobs_1 = __nccwpck_require__(89800);
2283
2294
  const scriptFiles_1 = __nccwpck_require__(30966);
2284
2295
  const WORKFLOWS_FOLDER = ".github/workflows";
@@ -2287,9 +2298,13 @@ const WORKFLOWS_FOLDER = ".github/workflows";
2287
2298
  * user-maintained workflows in the same folder
2288
2299
  */
2289
2300
  const GENERATED_FILE_PREFIX = "catladder-";
2301
+ // NOTE on the emoji prefixes: github sorts the Actions sidebar by raw
2302
+ // code point, so the 🛠️ (U+1F6E0) prefix makes the pipeline workflows
2303
+ // sort BELOW the triggerable action workflows (↩️ ⏹️ ▶️ 🚀, all lower
2304
+ // code points) — the things a human clicks come first.
2290
2305
  const TRIGGER_WORKFLOWS = {
2291
2306
  mr: {
2292
- name: "catladder review",
2307
+ name: "🛠️ catladder review",
2293
2308
  on: { pull_request: { types: ["opened", "synchronize", "reopened"] } },
2294
2309
  concurrency: {
2295
2310
  group: "catladder-review-${{ github.event.number }}",
@@ -2297,12 +2312,12 @@ const TRIGGER_WORKFLOWS = {
2297
2312
  },
2298
2313
  },
2299
2314
  mainBranch: {
2300
- name: "catladder main",
2315
+ name: "🛠️ catladder main",
2301
2316
  // TODO: make the default branch configurable
2302
2317
  on: { push: { branches: ["main"] } },
2303
2318
  },
2304
2319
  taggedRelease: {
2305
- name: "catladder release",
2320
+ name: "🛠️ catladder release",
2306
2321
  // tags pushed with the default GITHUB_TOKEN (as the release job
2307
2322
  // does) don't trigger `on: push: tags` — the release job therefore
2308
2323
  // also dispatches this workflow explicitly for the new tag
@@ -2352,15 +2367,18 @@ class GithubBackend {
2352
2367
  ...(0, index_1.getPipelineOptions)(config, this.type).runnerVariables,
2353
2368
  };
2354
2369
  const reviewStopJobs = {};
2355
- // jobs routed to the workflow_dispatch "manual tasks" workflow. A
2356
- // manual job and its build closure live here instead of an automatic
2357
- // workflow; `manualJobConditions` is the `if` that selects each one
2358
- // from the dispatch input, `manualDispatchOptions` the leaf jobs that
2359
- // become dropdown choices (closure jobs run because their leaf was
2360
- // chosen, not on their own).
2361
- const manualJobs = {};
2362
- const manualJobConditions = {};
2363
- const manualDispatchOptions = [];
2370
+ // jobs routed to the per-kind workflow_dispatch workflows (deploy /
2371
+ // stop / rollback). A manual job and its build closure live there
2372
+ // instead of an automatic workflow; `conditions` is the `if` that
2373
+ // selects each one from the dispatch input, `options` the leaf jobs
2374
+ // that become dropdown choices (closure jobs run because their leaf
2375
+ // was chosen, not on their own). A closure job serving leaves of
2376
+ // several kinds is duplicated into each kind's workflow.
2377
+ const manualKindBuckets = {
2378
+ deploy: { jobs: {}, conditions: {}, options: [], envTypes: [] },
2379
+ stop: { jobs: {}, conditions: {}, options: [], envTypes: [] },
2380
+ rollback: { jobs: {}, conditions: {}, options: [], envTypes: [] },
2381
+ };
2364
2382
  for (const trigger of config_1.ALL_PIPELINE_TRIGGERS) {
2365
2383
  const allJobs = await (0, createAllJobs_1.createAllJobs)({
2366
2384
  config,
@@ -2437,32 +2455,46 @@ class GithubBackend {
2437
2455
  }
2438
2456
  }
2439
2457
  }
2458
+ const kindOfLeaf = new Map(leafIds.map((id) => [id, manualKind(triggerJobs.get(id).job)]));
2440
2459
  const jobs = {};
2441
2460
  for (const [id, { githubJob }] of triggerJobs) {
2442
2461
  const leaves = servesLeaves.get(id);
2443
2462
  if (leaves) {
2444
- manualJobs[id] = githubJob;
2445
- manualJobConditions[id] = [...leaves]
2446
- .sort()
2447
- .map((leaf) => `inputs.job == '${leaf}'`)
2448
- .join(" || ");
2463
+ // one entry per kind the job serves — restricted to that
2464
+ // kind's leaves, so the `if` only references its own inputs
2465
+ for (const kind of new Set([...leaves].map((leaf) => kindOfLeaf.get(leaf)))) {
2466
+ const bucket = manualKindBuckets[kind];
2467
+ bucket.jobs[id] = githubJob;
2468
+ bucket.conditions[id] = [...leaves]
2469
+ .filter((leaf) => kindOfLeaf.get(leaf) === kind)
2470
+ .sort()
2471
+ .map((leaf) => `inputs.job == '${leaf}'`)
2472
+ .join(" || ");
2473
+ }
2449
2474
  }
2450
2475
  else {
2451
2476
  jobs[id] = githubJob;
2452
2477
  }
2453
2478
  }
2454
- manualDispatchOptions.push(...leafIds);
2479
+ for (const id of leafIds) {
2480
+ const bucket = manualKindBuckets[kindOfLeaf.get(id)];
2481
+ bucket.options.push(id);
2482
+ const context = triggerJobs.get(id).context;
2483
+ if (context.type === "component") {
2484
+ bucket.envTypes.push(context.environment.envType);
2485
+ }
2486
+ }
2455
2487
  if (trigger === "mr") {
2456
2488
  Object.assign(jobs, (0, githubReleaseJobs_1.getGithubReleaseCheckJobs)(config, images));
2457
2489
  }
2458
2490
  if (trigger === "mainBranch") {
2459
- const releaseJobs = (0, githubReleaseJobs_1.getGithubReleaseJobs)(config, Object.keys(jobs), images);
2460
- Object.assign(jobs, releaseJobs.main);
2461
- for (const [id, job] of Object.entries(releaseJobs.manual)) {
2462
- manualJobs[id] = job;
2463
- manualJobConditions[id] = `inputs.job == '${id}'`;
2464
- manualDispatchOptions.push(id);
2465
- }
2491
+ // auto release mode: the release job runs at the end of the
2492
+ // main workflow itself
2493
+ Object.assign(jobs, (0, githubReleaseJobs_1.getGithubReleaseJobs)(config, Object.keys(jobs), images));
2494
+ // the dispatch workflow to trigger a release by hand (with a
2495
+ // force checkbox where the method distinguishes it)
2496
+ workflows[`${GENERATED_FILE_PREFIX}create-release.yml`] =
2497
+ (0, githubReleaseJobs_1.getGithubCreateReleaseWorkflow)(config, images, workflowEnv);
2466
2498
  // manual release mode: the workflow that runs a queued release
2467
2499
  // once the main workflow completed green
2468
2500
  const releaseOnGreen = (0, githubReleaseJobs_1.getGithubReleaseOnGreenWorkflow)(config, images, TRIGGER_WORKFLOWS.mainBranch.name, workflowEnv);
@@ -2476,41 +2508,59 @@ class GithubBackend {
2476
2508
  ...TRIGGER_WORKFLOWS[trigger],
2477
2509
  ...workflowPermissions(images),
2478
2510
  env: workflowEnv,
2479
- jobs: { ...makeEnsureImageGithubJobs(images), ...jobs },
2511
+ jobs: { ...(0, ensureImageJobs_1.makeEnsureImageGithubJobs)(images), ...jobs },
2480
2512
  };
2481
2513
  }
2482
2514
  }
2483
2515
  if (Object.keys(reviewStopJobs).length > 0) {
2484
2516
  workflows[`${GENERATED_FILE_PREFIX}review-stop.yml`] = {
2485
- name: "catladder review stop",
2517
+ name: "🛠️ catladder review stop",
2486
2518
  on: { pull_request: { types: ["closed"] } },
2487
2519
  ...workflowPermissions(images),
2488
2520
  env: workflowEnv,
2489
- jobs: { ...makeEnsureImageGithubJobs(images), ...reviewStopJobs },
2521
+ jobs: { ...(0, ensureImageJobs_1.makeEnsureImageGithubJobs)(images), ...reviewStopJobs },
2490
2522
  };
2491
2523
  }
2492
- if (Object.keys(manualJobs).length > 0) {
2493
- workflows[`${GENERATED_FILE_PREFIX}manual.yml`] = {
2494
- name: "catladder manual tasks",
2524
+ for (const [kind, bucket] of Object.entries(manualKindBuckets)) {
2525
+ if (Object.keys(bucket.jobs).length === 0) {
2526
+ continue;
2527
+ }
2528
+ const options = [...new Set(bucket.options)];
2529
+ // a single candidate needs no dropdown — the dispatch just runs it
2530
+ const singleTask = options.length === 1;
2531
+ // one common env across the kind's tasks → carry it in the name
2532
+ // ("▶️ catladder deploy prod"); mixed envs keep the generic name
2533
+ // and the dropdown distinguishes
2534
+ const envTypes = [...new Set(bucket.envTypes)];
2535
+ const envSuffix = envTypes.length === 1 &&
2536
+ bucket.envTypes.length === bucket.options.length
2537
+ ? ` ${envTypes[0]}`
2538
+ : "";
2539
+ workflows[`${GENERATED_FILE_PREFIX}${kind}.yml`] = {
2540
+ name: `${MANUAL_KIND_WORKFLOW_NAMES[kind]}${envSuffix}`,
2495
2541
  on: {
2496
- workflow_dispatch: {
2497
- inputs: {
2498
- job: {
2499
- description: "the task to run",
2500
- required: true,
2501
- type: "choice",
2502
- options: [...new Set(manualDispatchOptions)],
2542
+ workflow_dispatch: singleTask
2543
+ ? {}
2544
+ : {
2545
+ inputs: {
2546
+ job: {
2547
+ description: "the task to run",
2548
+ required: true,
2549
+ type: "choice",
2550
+ options,
2551
+ },
2503
2552
  },
2504
2553
  },
2505
- },
2506
2554
  },
2507
2555
  ...workflowPermissions(images),
2508
2556
  env: workflowEnv,
2509
2557
  jobs: {
2510
- ...makeEnsureImageGithubJobs(images),
2511
- ...Object.fromEntries(Object.entries(manualJobs).map(([id, job]) => [
2558
+ ...(0, ensureImageJobs_1.makeEnsureImageGithubJobs)(images),
2559
+ ...Object.fromEntries(Object.entries(bucket.jobs).map(([id, job]) => [
2512
2560
  id,
2513
- { ...job, if: `\${{ ${manualJobConditions[id]} }}` },
2561
+ singleTask
2562
+ ? job
2563
+ : { ...job, if: `\${{ ${bucket.conditions[id]} }}` },
2514
2564
  ])),
2515
2565
  },
2516
2566
  };
@@ -2525,33 +2575,6 @@ exports.GithubBackend = GithubBackend;
2525
2575
  const workflowPermissions = (_images) => ({
2526
2576
  permissions: { contents: "read", packages: "write" },
2527
2577
  });
2528
- /**
2529
- * the image build jobs: always run (github has no server-side
2530
- * changes-filter), the existence check makes unchanged runs fast.
2531
- * Docker is native on github runners — no container, no dind.
2532
- */
2533
- const makeEnsureImageGithubJobs = (images) => Object.fromEntries(images.getEnsureJobs().map((job) => {
2534
- var _a, _b;
2535
- return [
2536
- (0, createGithubJobs_1.githubGlobalJobId)(job.name),
2537
- {
2538
- name: job.name,
2539
- "runs-on": "ubuntu-latest",
2540
- steps: [
2541
- { name: "Checkout", uses: "actions/checkout@v4" },
2542
- {
2543
- name: job.name,
2544
- id: "main",
2545
- run: [
2546
- ...(0, scriptFunctions_1.getGithubScriptFunctionDefinitions)(),
2547
- ...((_b = (_a = job.script) === null || _a === void 0 ? void 0 : _a.filter(utils_1.notNil)) !== null && _b !== void 0 ? _b : []),
2548
- ].join("\n"),
2549
- shell: "bash",
2550
- },
2551
- ],
2552
- },
2553
- ];
2554
- }));
2555
2578
  const workflowFileName = (trigger) => {
2556
2579
  switch (trigger) {
2557
2580
  case "mr":
@@ -2575,10 +2598,20 @@ const isReviewStopJob = (context, job) => {
2575
2598
  };
2576
2599
  /**
2577
2600
  * stop/rollback jobs of non-review environments have no automatic
2578
- * trigger; they are collected in a workflow_dispatch workflow
2601
+ * trigger; they are collected in workflow_dispatch workflows
2579
2602
  * (gitlab runs them as manual jobs inside the pipeline)
2580
2603
  */
2581
2604
  const isManualTaskJob = (job) => job.stage === "stop" || job.stage === "rollback";
2605
+ const MANUAL_KIND_WORKFLOW_NAMES = {
2606
+ deploy: "▶️ catladder deploy",
2607
+ stop: "⏹️ catladder stop",
2608
+ rollback: "↩️ catladder rollback",
2609
+ };
2610
+ const manualKind = (job) => job.stage === "stop"
2611
+ ? "stop"
2612
+ : job.stage === "rollback"
2613
+ ? "rollback"
2614
+ : "deploy";
2582
2615
  //# sourceMappingURL=GithubBackend.js.map
2583
2616
 
2584
2617
  /***/ }),
@@ -3008,17 +3041,78 @@ exports.getUploadProviderIds = getUploadProviderIds;
3008
3041
 
3009
3042
  /***/ }),
3010
3043
 
3044
+ /***/ 70051:
3045
+ /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => {
3046
+
3047
+ "use strict";
3048
+
3049
+ Object.defineProperty(exports, "__esModule", ({ value: true }));
3050
+ exports.makeEnsureImageGithubJobs = void 0;
3051
+ const createGithubJobs_1 = __nccwpck_require__(57877);
3052
+ const scriptFunctions_1 = __nccwpck_require__(79252);
3053
+ const utils_1 = __nccwpck_require__(24516);
3054
+ /**
3055
+ * the image build jobs: always run (github has no server-side
3056
+ * changes-filter), the existence check makes unchanged runs fast.
3057
+ * Docker is native on github runners — no container, no dind.
3058
+ * `only` restricts to the named images (e.g. the create-release
3059
+ * workflow needs just the release image).
3060
+ */
3061
+ const makeEnsureImageGithubJobs = (images, options = {}) => Object.fromEntries(images
3062
+ .getEnsureJobs()
3063
+ .filter((job) => !options.only || options.only.includes(job.name))
3064
+ .map((job) => {
3065
+ var _a, _b;
3066
+ return [
3067
+ (0, createGithubJobs_1.githubGlobalJobId)(job.name),
3068
+ {
3069
+ name: job.name,
3070
+ "runs-on": "ubuntu-latest",
3071
+ steps: [
3072
+ { name: "Checkout", uses: "actions/checkout@v4" },
3073
+ {
3074
+ name: job.name,
3075
+ id: "main",
3076
+ run: [
3077
+ ...(0, scriptFunctions_1.getGithubScriptFunctionDefinitions)(),
3078
+ ...((_b = (_a = job.script) === null || _a === void 0 ? void 0 : _a.filter(utils_1.notNil)) !== null && _b !== void 0 ? _b : []),
3079
+ ].join("\n"),
3080
+ shell: "bash",
3081
+ },
3082
+ ],
3083
+ },
3084
+ ];
3085
+ }));
3086
+ exports.makeEnsureImageGithubJobs = makeEnsureImageGithubJobs;
3087
+ //# sourceMappingURL=ensureImageJobs.js.map
3088
+
3089
+ /***/ }),
3090
+
3011
3091
  /***/ 89800:
3012
3092
  /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => {
3013
3093
 
3014
3094
  "use strict";
3015
3095
 
3016
3096
  Object.defineProperty(exports, "__esModule", ({ value: true }));
3017
- exports.getGithubReleaseCheckJobs = exports.getGithubReleaseOnGreenWorkflow = exports.getGithubReleaseJobs = void 0;
3097
+ exports.getGithubReleaseCheckJobs = exports.getGithubReleaseOnGreenWorkflow = exports.getGithubCreateReleaseWorkflow = exports.getGithubReleaseJobs = void 0;
3018
3098
  const createGithubJobs_1 = __nccwpck_require__(57877);
3099
+ const ensureImageJobs_1 = __nccwpck_require__(70051);
3019
3100
  const release_1 = __nccwpck_require__(38904);
3020
3101
  const shippedCatci_1 = __nccwpck_require__(79024);
3021
3102
  const CATCI = `${shippedCatci_1.GENERATED_CATCI_FOLDER}/index.js`;
3103
+ /**
3104
+ * the build job of the release image only — the create-release
3105
+ * dispatch workflow needs no other image (empty for external images)
3106
+ */
3107
+ const makeReleaseImageEnsureJobs = (config, images) => {
3108
+ const resolved = images.resolveRef({
3109
+ catladderImage: (0, release_1.getReleaseMethod)(config).image,
3110
+ });
3111
+ if (!resolved.need || typeof resolved.need !== "object") {
3112
+ return {};
3113
+ }
3114
+ return (0, ensureImageJobs_1.makeEnsureImageGithubJobs)(images, { only: [resolved.need.job] });
3115
+ };
3022
3116
  const createReleaseJob = (config, images, options = {}) => {
3023
3117
  var _a;
3024
3118
  const method = (0, release_1.getReleaseMethod)(config);
@@ -3072,6 +3166,7 @@ const createReleaseJob = (config, images, options = {}) => {
3072
3166
  {
3073
3167
  name: "check main workflow",
3074
3168
  id: "queue",
3169
+ ...(options.queueCheckIf ? { if: options.queueCheckIf } : {}),
3075
3170
  run: `node ${CATCI} release github-queue-check`,
3076
3171
  shell: "bash",
3077
3172
  },
@@ -3089,51 +3184,75 @@ const createReleaseJob = (config, images, options = {}) => {
3089
3184
  };
3090
3185
  };
3091
3186
  /**
3092
- * the release jobs for the github backend (the counterpart of the
3093
- * gitlab release jobs):
3094
- * - releases.when === "auto": the release job runs in the main-branch
3095
- * workflow after all other jobs; a force variant goes to the manual
3096
- * tasks workflow
3097
- * - otherwise (manual, the default): the release job goes to the
3098
- * manual tasks workflow. Its queue-check step releases right away on
3099
- * a green main run and otherwise queues the release for the
3100
- * release-on-green workflow (see getGithubReleaseOnGreenWorkflow) —
3101
- * the counterpart of gitlab's queue button + executor pair.
3187
+ * the release jobs of the main-branch workflow: with releases.when ===
3188
+ * "auto" the release runs at the end of every main workflow run.
3189
+ * Manual releasing happens via the create-release dispatch workflow
3190
+ * (see getGithubCreateReleaseWorkflow) instead.
3102
3191
  */
3103
3192
  const getGithubReleaseJobs = (config, mainWorkflowJobIds, images) => {
3104
3193
  var _a;
3105
- const method = (0, release_1.getReleaseMethod)(config);
3106
- if (((_a = config.releases) === null || _a === void 0 ? void 0 : _a.when) === "auto") {
3107
- return {
3108
- main: {
3109
- "create-release": createReleaseJob(config, images, {
3110
- needs: mainWorkflowJobIds,
3111
- }),
3112
- },
3113
- manual: {
3114
- "force-create-release": createReleaseJob(config, images, {
3115
- extraEnv: method.forceReleaseVariables,
3116
- }),
3117
- },
3118
- };
3194
+ if (((_a = config.releases) === null || _a === void 0 ? void 0 : _a.when) !== "auto") {
3195
+ return {};
3119
3196
  }
3120
3197
  return {
3121
- main: {},
3122
- manual: {
3123
- "create-release": createReleaseJob(config, images, { queueCheck: true }),
3124
- // an explicit force option wherever the method distinguishes it
3125
- // (e.g. changesets: release a patch bump without changesets)
3126
- ...(method.forceReleaseVariables
3198
+ "create-release": createReleaseJob(config, images, {
3199
+ needs: mainWorkflowJobIds,
3200
+ }),
3201
+ };
3202
+ };
3203
+ exports.getGithubReleaseJobs = getGithubReleaseJobs;
3204
+ /**
3205
+ * the dispatch workflow to trigger a release by hand — a first-class
3206
+ * entry in the Actions sidebar (the counterpart of gitlab's `create
3207
+ * release` button). Its queue-check step releases right away when the
3208
+ * main workflow run for HEAD is green and otherwise queues the release
3209
+ * for the release-on-green workflow. Where the release method
3210
+ * distinguishes a force mode (e.g. changesets: release a patch bump
3211
+ * without pending changesets), forcing is a checkbox input — a forced
3212
+ * release skips the queue-check and releases immediately, whatever the
3213
+ * pipeline state.
3214
+ */
3215
+ const getGithubCreateReleaseWorkflow = (config, images, workflowEnv) => {
3216
+ const method = (0, release_1.getReleaseMethod)(config);
3217
+ const forceVariables = method.forceReleaseVariables;
3218
+ const job = createReleaseJob(config, images, {
3219
+ queueCheck: true,
3220
+ ...(forceVariables
3221
+ ? {
3222
+ queueCheckIf: "${{ !inputs.force }}",
3223
+ // only effective when the force checkbox is ticked
3224
+ extraEnv: Object.fromEntries(Object.entries(forceVariables).map(([key, value]) => [
3225
+ key,
3226
+ `\${{ inputs.force && '${value}' || '' }}`,
3227
+ ])),
3228
+ }
3229
+ : {}),
3230
+ });
3231
+ return {
3232
+ name: "🚀 catladder create release",
3233
+ on: {
3234
+ workflow_dispatch: forceVariables
3127
3235
  ? {
3128
- "force-create-release": createReleaseJob(config, images, {
3129
- extraEnv: method.forceReleaseVariables,
3130
- }),
3236
+ inputs: {
3237
+ force: {
3238
+ description: "force: release even without pending changes (patch bump), ignoring the pipeline state",
3239
+ required: false,
3240
+ type: "boolean",
3241
+ default: false,
3242
+ },
3243
+ },
3131
3244
  }
3132
- : {}),
3245
+ : {},
3246
+ },
3247
+ permissions: { contents: "read", packages: "write" },
3248
+ env: workflowEnv,
3249
+ jobs: {
3250
+ ...makeReleaseImageEnsureJobs(config, images),
3251
+ "create-release": job,
3133
3252
  },
3134
3253
  };
3135
3254
  };
3136
- exports.getGithubReleaseJobs = getGithubReleaseJobs;
3255
+ exports.getGithubCreateReleaseWorkflow = getGithubCreateReleaseWorkflow;
3137
3256
  /**
3138
3257
  * the release-on-green workflow (manual release mode only): runs on
3139
3258
  * every completed main workflow run. A lightweight host job checks the
@@ -3152,7 +3271,7 @@ const getGithubReleaseOnGreenWorkflow = (config, images, mainWorkflowName, workf
3152
3271
  return null;
3153
3272
  }
3154
3273
  return {
3155
- name: "catladder release on green",
3274
+ name: "🛠️ catladder release on green",
3156
3275
  on: {
3157
3276
  workflow_run: {
3158
3277
  workflows: [mainWorkflowName],
@@ -6929,7 +7048,9 @@ const getEnvironmentVariables = async (ctx, options = {}) => {
6929
7048
  const additionalEnvVars = deployConfigRaw
6930
7049
  ? deploy_1.DEPLOY_TYPES[deployConfigRaw.type].getAdditionalEnvVars(environmentContext)
6931
7050
  : {};
6932
- const HOSTNAME_INTERNAL = (_b = additionalEnvVars.HOSTNAME_INTERNAL) !== null && _b !== void 0 ? _b : "unknown-host.example.com";
7051
+ // deploy types provide HOSTNAME_INTERNAL as a plain string or bash
7052
+ // expression (never containing references)
7053
+ const HOSTNAME_INTERNAL = ((_b = additionalEnvVars.HOSTNAME_INTERNAL) !== null && _b !== void 0 ? _b : "unknown-host.example.com");
6933
7054
  host = (_c = envConfigRaw === null || envConfigRaw === void 0 ? void 0 : envConfigRaw.host) !== null && _c !== void 0 ? _c : HOSTNAME_INTERNAL;
6934
7055
  url = (0, bash_1.joinBashExpressions)(["https://", host]);
6935
7056
  predefinedVariables = {
@@ -6957,8 +7078,18 @@ const getEnvironmentVariables = async (ctx, options = {}) => {
6957
7078
  componentName: ctx.componentName,
6958
7079
  }),
6959
7080
  ]));
6960
- const publicEnvVars = ((_g = options.shouldResolveReferences) !== null && _g !== void 0 ? _g : true)
6961
- ? await (0, bash_3.resolveAllReferences)(publicEnvVarsUnresolved, async (otherComponentName) => {
7081
+ // predefined variables may contain references too (e.g. the embedded
7082
+ // database connection string references the component's own DB_* vars), so
7083
+ // we resolve references over the merged map. This way public var overrides
7084
+ // (like DB_PASSWORD: "${otherComponent:DB_PASSWORD}") are picked up by the
7085
+ // predefined variables that reference them
7086
+ const mergedEnvVars = {
7087
+ ...predefinedVariables,
7088
+ ...secretEnvVars,
7089
+ ...publicEnvVarsUnresolved,
7090
+ };
7091
+ const resolvedEnvVars = ((_g = options.shouldResolveReferences) !== null && _g !== void 0 ? _g : true)
7092
+ ? await (0, bash_3.resolveAllReferences)(mergedEnvVars, async (otherComponentName) => {
6962
7093
  const { envVars: otherEnvVars } = await (0, exports.getEnvironmentVariables)({
6963
7094
  ...ctx,
6964
7095
  componentName: otherComponentName,
@@ -6967,12 +7098,8 @@ const getEnvironmentVariables = async (ctx, options = {}) => {
6967
7098
  });
6968
7099
  return otherEnvVars;
6969
7100
  })
6970
- : publicEnvVarsUnresolved;
6971
- const envVars = addIndexVar({
6972
- ...predefinedVariables,
6973
- ...secretEnvVars,
6974
- ...publicEnvVars,
6975
- });
7101
+ : mergedEnvVars;
7102
+ const envVars = addIndexVar(resolvedEnvVars);
6976
7103
  return {
6977
7104
  envVars,
6978
7105
  secretEnvVarKeys,
@@ -8963,8 +9090,8 @@ const getCloudSqlVariables = ({ deployConfigRaw, environmentSlugPrefix, env, com
8963
9090
  };
8964
9091
  return {
8965
9092
  ...dbVars,
8966
- DATABASE_URL: (0, database_1.getDatabaseConnectionString)(deployConfigRaw.cloudSql, dbVars).concat(additionalQueryParamsString),
8967
- DATABASE_JDBC_URL: (0, database_1.getDatabaseJdbcUrl)(dbVars, (_c = deployConfigRaw.cloudSql.dbConnectionStringVariablesMode) !== null && _c !== void 0 ? _c : database_1.DEFAULT_DB_VARIABLES_MODE),
9093
+ DATABASE_URL: (0, database_1.getDatabaseConnectionString)(deployConfigRaw.cloudSql, dbVars, componentName).concat(additionalQueryParamsString),
9094
+ DATABASE_JDBC_URL: (0, database_1.getDatabaseJdbcUrl)(dbVars, (_c = deployConfigRaw.cloudSql.dbConnectionStringVariablesMode) !== null && _c !== void 0 ? _c : database_1.DEFAULT_DB_VARIABLES_MODE, componentName),
8968
9095
  };
8969
9096
  }
8970
9097
  return {};
@@ -9156,64 +9283,72 @@ const getDatabaseCreateScript = (context, deployConfig) => {
9156
9283
  ]);
9157
9284
  };
9158
9285
  exports.getDatabaseCreateScript = getDatabaseCreateScript;
9159
- exports.DEFAULT_DB_VARIABLES_MODE = "legacy";
9160
- const getVariableOrValue = (key, variables, mode) => {
9161
- return mode === "legacy" ? `$${key}` : variables[key];
9162
- };
9163
- const getDatabaseJdbcUrl = (variables, mode) => {
9286
+ exports.DEFAULT_DB_VARIABLES_MODE = "embedded";
9287
+ const getVariableOrValue = (key, mode, componentName) => {
9288
+ // in embedded mode we reference the component's own env var, which gets
9289
+ // resolved to its final value (including vars.public overrides) after all
9290
+ // env vars have been merged
9291
+ return mode === "legacy"
9292
+ ? `$${key}`
9293
+ : new bash_1.VariableReference(componentName, key);
9294
+ };
9295
+ const joinDbUrlParts = (parts, mode) => mode === "legacy"
9296
+ ? (0, bash_1.joinBashExpressions)(parts)
9297
+ : new bash_1.VariableValueContainingReferences(parts);
9298
+ const getDatabaseJdbcUrl = (variables, mode, componentName) => {
9164
9299
  const parts = [
9165
9300
  "jdbc:postgresql:///",
9166
- getVariableOrValue("DB_NAME", variables, mode),
9301
+ getVariableOrValue("DB_NAME", mode, componentName),
9167
9302
  "?cloudSqlInstance=",
9168
- getVariableOrValue("CLOUD_SQL_INSTANCE_CONNECTION_NAME", variables, mode),
9303
+ getVariableOrValue("CLOUD_SQL_INSTANCE_CONNECTION_NAME", mode, componentName),
9169
9304
  "&socketFactory=com.google.cloud.sql.postgres.SocketFactory&user=",
9170
- getVariableOrValue("DB_USER", variables, mode),
9305
+ getVariableOrValue("DB_USER", mode, componentName),
9171
9306
  "&password=",
9172
- getVariableOrValue("DB_PASSWORD", variables, mode),
9307
+ getVariableOrValue("DB_PASSWORD", mode, componentName),
9173
9308
  ];
9174
- return (0, bash_1.joinBashExpressions)(parts);
9309
+ return joinDbUrlParts(parts, mode);
9175
9310
  };
9176
9311
  exports.getDatabaseJdbcUrl = getDatabaseJdbcUrl;
9177
- const getRailsDatabaseConnectionString = (variables, mode) => {
9312
+ const getRailsDatabaseConnectionString = (variables, mode, componentName) => {
9178
9313
  const parts = [
9179
9314
  "postgresql://",
9180
- getVariableOrValue("DB_USER", variables, mode),
9315
+ getVariableOrValue("DB_USER", mode, componentName),
9181
9316
  ":",
9182
- getVariableOrValue("DB_PASSWORD", variables, mode),
9317
+ getVariableOrValue("DB_PASSWORD", mode, componentName),
9183
9318
  "@",
9184
9319
  encodeURIComponent(`/cloudsql/${variables.CLOUD_SQL_INSTANCE_CONNECTION_NAME}`),
9185
9320
  "/",
9186
- getVariableOrValue("DB_NAME", variables, mode),
9321
+ getVariableOrValue("DB_NAME", mode, componentName),
9187
9322
  "?",
9188
9323
  ];
9189
- return (0, bash_1.joinBashExpressions)(parts);
9324
+ return joinDbUrlParts(parts, mode);
9190
9325
  };
9191
9326
  exports.getRailsDatabaseConnectionString = getRailsDatabaseConnectionString;
9192
- const getPrismaDatabaseConnectionString = (variables, mode) => {
9327
+ const getPrismaDatabaseConnectionString = (variables, mode, componentName) => {
9193
9328
  const parts = [
9194
9329
  "postgresql://",
9195
- getVariableOrValue("DB_USER", variables, mode),
9330
+ getVariableOrValue("DB_USER", mode, componentName),
9196
9331
  ":",
9197
- getVariableOrValue("DB_PASSWORD", variables, mode),
9332
+ getVariableOrValue("DB_PASSWORD", mode, componentName),
9198
9333
  "@localhost/",
9199
- getVariableOrValue("DB_NAME", variables, mode),
9334
+ getVariableOrValue("DB_NAME", mode, componentName),
9200
9335
  "?host=/cloudsql/",
9201
- getVariableOrValue("CLOUD_SQL_INSTANCE_CONNECTION_NAME", variables, mode),
9336
+ getVariableOrValue("CLOUD_SQL_INSTANCE_CONNECTION_NAME", mode, componentName),
9202
9337
  ];
9203
- return (0, bash_1.joinBashExpressions)(parts);
9338
+ return joinDbUrlParts(parts, mode);
9204
9339
  };
9205
9340
  exports.getPrismaDatabaseConnectionString = getPrismaDatabaseConnectionString;
9206
- const getDatabaseConnectionString = (config, variables) => {
9341
+ const getDatabaseConnectionString = (config, variables, componentName) => {
9207
9342
  var _a;
9208
9343
  const mode = (_a = config.dbConnectionStringVariablesMode) !== null && _a !== void 0 ? _a : exports.DEFAULT_DB_VARIABLES_MODE;
9209
9344
  switch (config.dbConnectionStringFormat) {
9210
9345
  case "jdbc":
9211
- return (0, exports.getDatabaseJdbcUrl)(variables, mode);
9346
+ return (0, exports.getDatabaseJdbcUrl)(variables, mode, componentName);
9212
9347
  case "rails":
9213
- return (0, exports.getRailsDatabaseConnectionString)(variables, mode);
9348
+ return (0, exports.getRailsDatabaseConnectionString)(variables, mode, componentName);
9214
9349
  default:
9215
9350
  // prisma
9216
- return (0, exports.getPrismaDatabaseConnectionString)(variables, mode);
9351
+ return (0, exports.getPrismaDatabaseConnectionString)(variables, mode, componentName);
9217
9352
  }
9218
9353
  };
9219
9354
  exports.getDatabaseConnectionString = getDatabaseConnectionString;