@catladder/cli 0.0.0-next-dccc0a36 → 0.0.0-next-89bce6d0
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.
- package/dist/apps/cli/src/catci.js +23 -0
- package/dist/apps/cli/src/catci.js.map +1 -1
- package/dist/apps/cli/src/release/githubQueuedRelease.d.ts +14 -0
- package/dist/apps/cli/src/release/githubQueuedRelease.js +152 -0
- package/dist/apps/cli/src/release/githubQueuedRelease.js.map +1 -0
- package/dist/bundles/catci/index.js +3 -3
- package/dist/bundles/catenv/index.js +340 -109
- package/dist/bundles/cli/index.js +340 -109
- package/dist/bundles/skills/catladder-deploys/references/deploy-types.md +1 -1
- package/dist/bundles/skills/catladder-releases/SKILL.md +13 -2
- package/dist/packages/bash/src/variables/VariableValueContainingReferences.d.ts +6 -0
- package/dist/packages/bash/src/variables/VariableValueContainingReferences.js +8 -0
- package/dist/packages/bash/src/variables/VariableValueContainingReferences.js.map +1 -1
- package/dist/packages/bash/src/variables/resolveAllReferences.d.ts +1 -2
- package/dist/packages/bash/src/variables/resolveAllReferences.js +4 -2
- package/dist/packages/bash/src/variables/resolveAllReferences.js.map +1 -1
- package/dist/packages/bash/src/variables/resolveAllReferencesOnce.d.ts +1 -4
- package/dist/packages/bash/src/variables/resolveAllReferencesOnce.js +4 -2
- package/dist/packages/bash/src/variables/resolveAllReferencesOnce.js.map +1 -1
- package/dist/packages/pipeline/src/backends/github/GithubBackend.js +7 -0
- package/dist/packages/pipeline/src/backends/github/GithubBackend.js.map +1 -1
- package/dist/packages/pipeline/src/backends/github/githubReleaseJobs.d.ts +18 -2
- package/dist/packages/pipeline/src/backends/github/githubReleaseJobs.js +92 -10
- package/dist/packages/pipeline/src/backends/github/githubReleaseJobs.js.map +1 -1
- package/dist/packages/pipeline/src/backends/gitlab/GitlabBackend.js +31 -5
- package/dist/packages/pipeline/src/backends/gitlab/GitlabBackend.js.map +1 -1
- package/dist/packages/pipeline/src/backends/gitlab/gitlabReleaseJobs.d.ts +285 -3
- package/dist/packages/pipeline/src/backends/gitlab/gitlabReleaseJobs.js +142 -54
- package/dist/packages/pipeline/src/backends/gitlab/gitlabReleaseJobs.js.map +1 -1
- package/dist/packages/pipeline/src/context/getEnvironmentVariables.js +17 -9
- package/dist/packages/pipeline/src/context/getEnvironmentVariables.js.map +1 -1
- package/dist/packages/pipeline/src/deploy/cloudRun/index.js +2 -2
- package/dist/packages/pipeline/src/deploy/cloudRun/index.js.map +1 -1
- package/dist/packages/pipeline/src/deploy/cloudRun/utils/database.d.ts +7 -9
- package/dist/packages/pipeline/src/deploy/cloudRun/utils/database.js +32 -24
- package/dist/packages/pipeline/src/deploy/cloudRun/utils/database.js.map +1 -1
- package/dist/packages/pipeline/src/deploy/index.d.ts +2 -2
- package/dist/packages/pipeline/src/types/github-types.d.ts +1 -0
- package/dist/packages/pipeline/src/types/release.d.ts +7 -1
- package/dist/skills/catladder-deploys/references/deploy-types.md +1 -1
- package/dist/skills/catladder-releases/SKILL.md +13 -2
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +1 -1
- package/src/catci.ts +30 -0
- package/src/release/githubQueuedRelease.ts +173 -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(
|
|
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
|
|
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
|
|
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
|
|
8000
|
+
value instanceof VariableValueContainingReferences_1.VariableValueContainingReferences
|
|
7989
8001
|
? (0, resolveReferencesOnce_1.resolveReferencesOnce)(value, ({ componentName, variableName }) => {
|
|
7990
8002
|
return allEnvVarsInComponents[componentName][variableName];
|
|
7991
8003
|
})
|
|
@@ -8456,6 +8468,13 @@ class GithubBackend {
|
|
|
8456
8468
|
manualJobConditions[id] = `inputs.job == '${id}'`;
|
|
8457
8469
|
manualDispatchOptions.push(id);
|
|
8458
8470
|
}
|
|
8471
|
+
// manual release mode: the workflow that runs a queued release
|
|
8472
|
+
// once the main workflow completed green
|
|
8473
|
+
const releaseOnGreen = (0, githubReleaseJobs_1.getGithubReleaseOnGreenWorkflow)(config, images, TRIGGER_WORKFLOWS.mainBranch.name, workflowEnv);
|
|
8474
|
+
if (releaseOnGreen) {
|
|
8475
|
+
workflows[`${GENERATED_FILE_PREFIX}release-on-green.yml`] =
|
|
8476
|
+
releaseOnGreen;
|
|
8477
|
+
}
|
|
8459
8478
|
}
|
|
8460
8479
|
if (Object.keys(jobs).length > 0) {
|
|
8461
8480
|
workflows[`${GENERATED_FILE_PREFIX}${workflowFileName(trigger)}`] = {
|
|
@@ -9000,16 +9019,21 @@ exports.getUploadProviderIds = getUploadProviderIds;
|
|
|
9000
9019
|
"use strict";
|
|
9001
9020
|
|
|
9002
9021
|
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
|
9003
|
-
exports.getGithubReleaseCheckJobs = exports.getGithubReleaseJobs = void 0;
|
|
9022
|
+
exports.getGithubReleaseCheckJobs = exports.getGithubReleaseOnGreenWorkflow = exports.getGithubReleaseJobs = void 0;
|
|
9004
9023
|
const createGithubJobs_1 = __nccwpck_require__(57877);
|
|
9005
9024
|
const release_1 = __nccwpck_require__(38904);
|
|
9006
|
-
const
|
|
9025
|
+
const shippedCatci_1 = __nccwpck_require__(79024);
|
|
9026
|
+
const CATCI = `${shippedCatci_1.GENERATED_CATCI_FOLDER}/index.js`;
|
|
9027
|
+
const createReleaseJob = (config, images, options = {}) => {
|
|
9028
|
+
var _a;
|
|
9007
9029
|
const method = (0, release_1.getReleaseMethod)(config);
|
|
9008
9030
|
const resolved = images.resolveRef({ catladderImage: method.image });
|
|
9009
|
-
const imageNeeds =
|
|
9031
|
+
const imageNeeds = !options.skipImageNeeds &&
|
|
9032
|
+
resolved.need &&
|
|
9033
|
+
typeof resolved.need === "object"
|
|
9010
9034
|
? [(0, createGithubJobs_1.githubGlobalJobId)(resolved.need.job)]
|
|
9011
9035
|
: [];
|
|
9012
|
-
const allNeeds = [...imageNeeds, ...(needs !== null &&
|
|
9036
|
+
const allNeeds = [...imageNeeds, ...((_a = options.needs) !== null && _a !== void 0 ? _a : [])];
|
|
9013
9037
|
return {
|
|
9014
9038
|
name: "create release",
|
|
9015
9039
|
"runs-on": "ubuntu-latest",
|
|
@@ -9038,7 +9062,7 @@ const createReleaseJob = (config, images, needs, extraEnv) => {
|
|
|
9038
9062
|
...(allNeeds.length > 0 ? { needs: allNeeds } : {}),
|
|
9039
9063
|
env: {
|
|
9040
9064
|
GITHUB_TOKEN: "${{ github.token }}",
|
|
9041
|
-
...extraEnv,
|
|
9065
|
+
...options.extraEnv,
|
|
9042
9066
|
},
|
|
9043
9067
|
steps: [
|
|
9044
9068
|
{
|
|
@@ -9048,8 +9072,21 @@ const createReleaseJob = (config, images, needs, extraEnv) => {
|
|
|
9048
9072
|
// tags since the last release) to determine the version
|
|
9049
9073
|
with: { "fetch-depth": 0 },
|
|
9050
9074
|
},
|
|
9075
|
+
...(options.queueCheck
|
|
9076
|
+
? [
|
|
9077
|
+
{
|
|
9078
|
+
name: "check main workflow",
|
|
9079
|
+
id: "queue",
|
|
9080
|
+
run: `node ${CATCI} release github-queue-check`,
|
|
9081
|
+
shell: "bash",
|
|
9082
|
+
},
|
|
9083
|
+
]
|
|
9084
|
+
: []),
|
|
9051
9085
|
{
|
|
9052
9086
|
name: "create release",
|
|
9087
|
+
...(options.queueCheck
|
|
9088
|
+
? { if: "steps.queue.outputs.queued != 'true'" }
|
|
9089
|
+
: {}),
|
|
9053
9090
|
run: method.script,
|
|
9054
9091
|
shell: "bash",
|
|
9055
9092
|
},
|
|
@@ -9063,7 +9100,10 @@ const createReleaseJob = (config, images, needs, extraEnv) => {
|
|
|
9063
9100
|
* workflow after all other jobs; a force variant goes to the manual
|
|
9064
9101
|
* tasks workflow
|
|
9065
9102
|
* - otherwise (manual, the default): the release job goes to the
|
|
9066
|
-
* manual tasks workflow
|
|
9103
|
+
* manual tasks workflow. Its queue-check step releases right away on
|
|
9104
|
+
* a green main run and otherwise queues the release for the
|
|
9105
|
+
* release-on-green workflow (see getGithubReleaseOnGreenWorkflow) —
|
|
9106
|
+
* the counterpart of gitlab's queue button + executor pair.
|
|
9067
9107
|
*/
|
|
9068
9108
|
const getGithubReleaseJobs = (config, mainWorkflowJobIds, images) => {
|
|
9069
9109
|
var _a;
|
|
@@ -9071,28 +9111,89 @@ const getGithubReleaseJobs = (config, mainWorkflowJobIds, images) => {
|
|
|
9071
9111
|
if (((_a = config.releases) === null || _a === void 0 ? void 0 : _a.when) === "auto") {
|
|
9072
9112
|
return {
|
|
9073
9113
|
main: {
|
|
9074
|
-
"create-release": createReleaseJob(config, images,
|
|
9114
|
+
"create-release": createReleaseJob(config, images, {
|
|
9115
|
+
needs: mainWorkflowJobIds,
|
|
9116
|
+
}),
|
|
9075
9117
|
},
|
|
9076
9118
|
manual: {
|
|
9077
|
-
"force-create-release": createReleaseJob(config, images,
|
|
9119
|
+
"force-create-release": createReleaseJob(config, images, {
|
|
9120
|
+
extraEnv: method.forceReleaseVariables,
|
|
9121
|
+
}),
|
|
9078
9122
|
},
|
|
9079
9123
|
};
|
|
9080
9124
|
}
|
|
9081
9125
|
return {
|
|
9082
9126
|
main: {},
|
|
9083
9127
|
manual: {
|
|
9084
|
-
"create-release": createReleaseJob(config, images),
|
|
9128
|
+
"create-release": createReleaseJob(config, images, { queueCheck: true }),
|
|
9085
9129
|
// an explicit force option wherever the method distinguishes it
|
|
9086
9130
|
// (e.g. changesets: release a patch bump without changesets)
|
|
9087
9131
|
...(method.forceReleaseVariables
|
|
9088
9132
|
? {
|
|
9089
|
-
"force-create-release": createReleaseJob(config, images,
|
|
9133
|
+
"force-create-release": createReleaseJob(config, images, {
|
|
9134
|
+
extraEnv: method.forceReleaseVariables,
|
|
9135
|
+
}),
|
|
9090
9136
|
}
|
|
9091
9137
|
: {}),
|
|
9092
9138
|
},
|
|
9093
9139
|
};
|
|
9094
9140
|
};
|
|
9095
9141
|
exports.getGithubReleaseJobs = getGithubReleaseJobs;
|
|
9142
|
+
/**
|
|
9143
|
+
* the release-on-green workflow (manual release mode only): runs on
|
|
9144
|
+
* every completed main workflow run. A lightweight host job checks the
|
|
9145
|
+
* queue marker (written by the create-release task while the main
|
|
9146
|
+
* workflow was still running) — most runs have nothing queued and end
|
|
9147
|
+
* after that single quick job. When the marker matches the completed
|
|
9148
|
+
* run, the containerized release job creates the release.
|
|
9149
|
+
*
|
|
9150
|
+
* No image-build jobs here: the release image was ensured by the main
|
|
9151
|
+
* workflow run that just completed (same commit, same content-hashed
|
|
9152
|
+
* tag).
|
|
9153
|
+
*/
|
|
9154
|
+
const getGithubReleaseOnGreenWorkflow = (config, images, mainWorkflowName, workflowEnv) => {
|
|
9155
|
+
var _a;
|
|
9156
|
+
if (((_a = config.releases) === null || _a === void 0 ? void 0 : _a.when) === "auto") {
|
|
9157
|
+
return null;
|
|
9158
|
+
}
|
|
9159
|
+
return {
|
|
9160
|
+
name: "catladder release on green",
|
|
9161
|
+
on: {
|
|
9162
|
+
workflow_run: {
|
|
9163
|
+
workflows: [mainWorkflowName],
|
|
9164
|
+
types: ["completed"],
|
|
9165
|
+
},
|
|
9166
|
+
},
|
|
9167
|
+
env: workflowEnv,
|
|
9168
|
+
jobs: {
|
|
9169
|
+
guard: {
|
|
9170
|
+
name: "check queued release",
|
|
9171
|
+
"runs-on": "ubuntu-latest",
|
|
9172
|
+
// consuming the queue marker is a ref deletion
|
|
9173
|
+
permissions: { contents: "write" },
|
|
9174
|
+
env: { GITHUB_TOKEN: "${{ github.token }}" },
|
|
9175
|
+
outputs: { release: "${{ steps.guard.outputs.release }}" },
|
|
9176
|
+
steps: [
|
|
9177
|
+
{ name: "Checkout", uses: "actions/checkout@v4" },
|
|
9178
|
+
{
|
|
9179
|
+
name: "check queued release",
|
|
9180
|
+
id: "guard",
|
|
9181
|
+
run: `node ${CATCI} release github-queued-guard "\${{ github.event.workflow_run.head_sha }}" "\${{ github.event.workflow_run.conclusion }}"`,
|
|
9182
|
+
shell: "bash",
|
|
9183
|
+
},
|
|
9184
|
+
],
|
|
9185
|
+
},
|
|
9186
|
+
"create-release": {
|
|
9187
|
+
...createReleaseJob(config, images, {
|
|
9188
|
+
needs: ["guard"],
|
|
9189
|
+
skipImageNeeds: true,
|
|
9190
|
+
}),
|
|
9191
|
+
if: "${{ needs.guard.outputs.release == 'true' }}",
|
|
9192
|
+
},
|
|
9193
|
+
},
|
|
9194
|
+
};
|
|
9195
|
+
};
|
|
9196
|
+
exports.getGithubReleaseOnGreenWorkflow = getGithubReleaseOnGreenWorkflow;
|
|
9096
9197
|
/**
|
|
9097
9198
|
* the informational MR/PR check job of the release method (e.g. the
|
|
9098
9199
|
* changeset check), added to the mr workflow. continue-on-error keeps
|
|
@@ -9287,6 +9388,7 @@ const config_1 = __nccwpck_require__(17860);
|
|
|
9287
9388
|
const createAllJobs_1 = __nccwpck_require__(54114);
|
|
9288
9389
|
const jobImagesPlan_1 = __nccwpck_require__(47864);
|
|
9289
9390
|
const shippedCatci_1 = __nccwpck_require__(79024);
|
|
9391
|
+
const release_1 = __nccwpck_require__(38904);
|
|
9290
9392
|
const index_1 = __nccwpck_require__(68906);
|
|
9291
9393
|
const createGitlabJobs_1 = __nccwpck_require__(41509);
|
|
9292
9394
|
const createGitlabPipeline_1 = __nccwpck_require__(94411);
|
|
@@ -9373,12 +9475,29 @@ class GitlabBackend {
|
|
|
9373
9475
|
}
|
|
9374
9476
|
async createPipeline(config, images) {
|
|
9375
9477
|
const stages = (0, getPipelineStages_1.getPipelineStages)(config);
|
|
9376
|
-
// release
|
|
9377
|
-
//
|
|
9378
|
-
//
|
|
9379
|
-
|
|
9478
|
+
// register the release image first: the per-trigger job creation
|
|
9479
|
+
// below emits the build jobs of all images registered so far. The
|
|
9480
|
+
// release jobs themselves are created afterwards — they need the
|
|
9481
|
+
// main-branch job list for the queued-release executor's needs.
|
|
9482
|
+
images.resolveRef({ catladderImage: (0, release_1.getReleaseMethod)(config).image });
|
|
9380
9483
|
// for all triggers create jobs and add base rules
|
|
9381
|
-
const
|
|
9484
|
+
const jobsPerTrigger = await Promise.all(config_1.ALL_PIPELINE_TRIGGERS.map(async (trigger) => ({
|
|
9485
|
+
trigger,
|
|
9486
|
+
jobs: await (0, createGitlabJobs_1.createGitlabJobs)(await (0, createAllJobs_1.createAllJobs)({ config, trigger, pipelineType: this.type }), images, getGitlabRulesForTrigger(trigger)),
|
|
9487
|
+
})));
|
|
9488
|
+
const allJobsPerTrigger = jobsPerTrigger.flatMap(({ jobs }) => jobs);
|
|
9489
|
+
// the jobs the queued-release executor waits for: everything that
|
|
9490
|
+
// runs automatically in a main-branch pipeline. Manual jobs (stop,
|
|
9491
|
+
// rollback, gates) are excluded — gitlab skips a job whose needs
|
|
9492
|
+
// include an unplayed manual job.
|
|
9493
|
+
const mainBranchJobNames = [
|
|
9494
|
+
...new Set(jobsPerTrigger
|
|
9495
|
+
.filter(({ trigger }) => trigger === "mainBranch")
|
|
9496
|
+
.flatMap(({ jobs }) => jobs)
|
|
9497
|
+
.filter(({ gitlabJob }) => !isManualGitlabJob(gitlabJob))
|
|
9498
|
+
.map(({ name }) => name)),
|
|
9499
|
+
];
|
|
9500
|
+
const releaseJobs = (0, gitlabReleaseJobs_1.getGitlabReleaseJobs)(config, images, mainBranchJobNames);
|
|
9382
9501
|
const allWorkspaceJobs = allJobsPerTrigger
|
|
9383
9502
|
.filter((j) => { var _a; return ((_a = j.context) === null || _a === void 0 ? void 0 : _a.type) === "workspace"; }) // sort by componentName in the same order as they appear in the config
|
|
9384
9503
|
// this is purely for better readability in git diffs when you add new components
|
|
@@ -9444,6 +9563,14 @@ class GitlabBackend {
|
|
|
9444
9563
|
}
|
|
9445
9564
|
}
|
|
9446
9565
|
exports.GitlabBackend = GitlabBackend;
|
|
9566
|
+
/**
|
|
9567
|
+
* whether a job can end up waiting for a human in a main-branch
|
|
9568
|
+
* pipeline (catladder expresses manual-ness only through rules)
|
|
9569
|
+
*/
|
|
9570
|
+
function isManualGitlabJob(job) {
|
|
9571
|
+
var _a;
|
|
9572
|
+
return ((_a = job.rules) !== null && _a !== void 0 ? _a : []).some((rule) => rule.when === "manual");
|
|
9573
|
+
}
|
|
9447
9574
|
function getGitlabRulesForTrigger(trigger) {
|
|
9448
9575
|
// mainBranch: on push to main branch except it's a release commit
|
|
9449
9576
|
// mr: on merge request
|
|
@@ -9976,91 +10103,179 @@ exports.getPipelineStages = getPipelineStages;
|
|
|
9976
10103
|
"use strict";
|
|
9977
10104
|
|
|
9978
10105
|
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
|
9979
|
-
exports.getGitlabReleaseJobs = void 0;
|
|
10106
|
+
exports.getGitlabReleaseJobs = exports.RELEASE_EXECUTOR_JOB_NAME = exports.RELEASE_BUTTON_JOB_NAME = void 0;
|
|
9980
10107
|
const rules_1 = __nccwpck_require__(35086);
|
|
9981
10108
|
const release_1 = __nccwpck_require__(38904);
|
|
9982
10109
|
const EXPIRED_TOKEN_HELP = "echo '👉 If this job failed with access denied, the project access token might be invald - run `project-renew-token` in catladder CLI to fix.'";
|
|
10110
|
+
/**
|
|
10111
|
+
* gitlab rejects jobs with more than 50 `needs:` entries (default
|
|
10112
|
+
* limit). Pipelines beyond that fall back to the legacy manual release
|
|
10113
|
+
* job, which is only playable once all prior stages finished.
|
|
10114
|
+
*/
|
|
10115
|
+
const GITLAB_MAX_NEEDS = 50;
|
|
10116
|
+
exports.RELEASE_BUTTON_JOB_NAME = "create release";
|
|
10117
|
+
exports.RELEASE_EXECUTOR_JOB_NAME = "🚀 release once pipeline succeeds";
|
|
9983
10118
|
const baseReleaseRules = [
|
|
9984
10119
|
rules_1.RULE_NEVER_ON_RELEASE_COMMIT,
|
|
9985
10120
|
rules_1.RULE_NEVER_ON_AGENT_TRIGGER,
|
|
9986
10121
|
rules_1.RULE_NEVER_ON_SCHEDULE,
|
|
9987
10122
|
];
|
|
9988
|
-
|
|
10123
|
+
// NOTE: with `rules:`, when: manual defaults to allow_failure: false,
|
|
10124
|
+
// which turns every unclicked pipeline into status "blocked" — the
|
|
10125
|
+
// manual release jobs therefore carry allow_failure: true so pipelines
|
|
10126
|
+
// end "passed" when nobody releases
|
|
10127
|
+
const manualReleaseRules = [
|
|
10128
|
+
...baseReleaseRules,
|
|
10129
|
+
{
|
|
10130
|
+
if: rules_1.RULE_CONDITION_MAIN_BRANCH,
|
|
10131
|
+
when: "manual",
|
|
10132
|
+
},
|
|
10133
|
+
{
|
|
10134
|
+
if: rules_1.RULE_CONDITION_HOTFIX_BRANCH,
|
|
10135
|
+
when: "manual",
|
|
10136
|
+
},
|
|
10137
|
+
];
|
|
10138
|
+
const getGitlabReleaseJobs = (config, images,
|
|
10139
|
+
/**
|
|
10140
|
+
* names of the jobs that run automatically in main-branch pipelines
|
|
10141
|
+
* (manual jobs excluded: a needed-but-unplayed manual job would make
|
|
10142
|
+
* gitlab skip the release executor)
|
|
10143
|
+
*/
|
|
10144
|
+
mainBranchJobNames) => {
|
|
9989
10145
|
var _a;
|
|
9990
10146
|
const method = (0, release_1.getReleaseMethod)(config);
|
|
9991
10147
|
// NOTE: only the resolved image url is used, not the `need`: release
|
|
9992
|
-
// jobs have no `needs` on purpose —
|
|
9993
|
-
//
|
|
9994
|
-
//
|
|
9995
|
-
// longer wait for the build/deploy stages.
|
|
10148
|
+
// jobs have no `needs` on the whole pipeline on purpose — where they
|
|
10149
|
+
// must wait for it, the wait is expressed by enumerating the jobs
|
|
10150
|
+
// explicitly (see the executor below).
|
|
9996
10151
|
const releaseImage = images.resolveRef({
|
|
9997
10152
|
catladderImage: method.image,
|
|
9998
10153
|
}).image;
|
|
9999
|
-
|
|
10000
|
-
|
|
10001
|
-
|
|
10002
|
-
|
|
10003
|
-
|
|
10004
|
-
|
|
10005
|
-
|
|
10006
|
-
...baseReleaseRules,
|
|
10007
|
-
{
|
|
10008
|
-
if: rules_1.RULE_CONDITION_MAIN_BRANCH,
|
|
10009
|
-
when: ((_a = config.releases) === null || _a === void 0 ? void 0 : _a.when) === "auto" ? "on_success" : "manual",
|
|
10010
|
-
},
|
|
10011
|
-
{
|
|
10012
|
-
if: rules_1.RULE_CONDITION_HOTFIX_BRANCH,
|
|
10013
|
-
when: "manual",
|
|
10014
|
-
},
|
|
10015
|
-
],
|
|
10016
|
-
},
|
|
10154
|
+
const releaseJobBase = {
|
|
10155
|
+
stage: "release",
|
|
10156
|
+
image: releaseImage,
|
|
10157
|
+
script: [method.script],
|
|
10158
|
+
after_script: [EXPIRED_TOKEN_HELP],
|
|
10159
|
+
};
|
|
10160
|
+
const forceReleaseJob = {
|
|
10017
10161
|
["⚠️ force create release"]: {
|
|
10018
|
-
|
|
10019
|
-
image: releaseImage,
|
|
10020
|
-
script: [method.script],
|
|
10021
|
-
after_script: [EXPIRED_TOKEN_HELP],
|
|
10162
|
+
...releaseJobBase,
|
|
10022
10163
|
needs: [],
|
|
10164
|
+
allow_failure: true,
|
|
10023
10165
|
// force semantics of the method's script (e.g. changesets:
|
|
10024
10166
|
// release a patch bump even without pending changesets)
|
|
10025
10167
|
...(method.forceReleaseVariables
|
|
10026
10168
|
? { variables: method.forceReleaseVariables }
|
|
10027
10169
|
: {}),
|
|
10170
|
+
rules: manualReleaseRules,
|
|
10171
|
+
},
|
|
10172
|
+
};
|
|
10173
|
+
// informational MR check of the release method (e.g. the changeset
|
|
10174
|
+
// check): warns via allow_failure, the report lands in the MR
|
|
10175
|
+
// widget as an exposed artifact
|
|
10176
|
+
const checkJob = method.checkScript
|
|
10177
|
+
? {
|
|
10178
|
+
["🦋 changeset check"]: {
|
|
10179
|
+
stage: "test",
|
|
10180
|
+
image: releaseImage,
|
|
10181
|
+
script: [method.checkScript],
|
|
10182
|
+
allow_failure: true,
|
|
10183
|
+
artifacts: {
|
|
10184
|
+
paths: ["changeset-report.md"],
|
|
10185
|
+
expose_as: "changeset report",
|
|
10186
|
+
// the report must survive the warning exit
|
|
10187
|
+
when: "always",
|
|
10188
|
+
},
|
|
10189
|
+
rules: [
|
|
10190
|
+
rules_1.RULE_NEVER_ON_AGENT_TRIGGER,
|
|
10191
|
+
rules_1.RULE_NEVER_ON_SCHEDULE,
|
|
10192
|
+
rules_1.RULE_IS_MERGE_REQUEST,
|
|
10193
|
+
],
|
|
10194
|
+
},
|
|
10195
|
+
}
|
|
10196
|
+
: {};
|
|
10197
|
+
if (((_a = config.releases) === null || _a === void 0 ? void 0 : _a.when) === "auto") {
|
|
10198
|
+
return {
|
|
10199
|
+
[exports.RELEASE_BUTTON_JOB_NAME]: {
|
|
10200
|
+
...releaseJobBase,
|
|
10201
|
+
rules: [
|
|
10202
|
+
...baseReleaseRules,
|
|
10203
|
+
{
|
|
10204
|
+
if: rules_1.RULE_CONDITION_MAIN_BRANCH,
|
|
10205
|
+
when: "on_success",
|
|
10206
|
+
},
|
|
10207
|
+
{
|
|
10208
|
+
if: rules_1.RULE_CONDITION_HOTFIX_BRANCH,
|
|
10209
|
+
when: "manual",
|
|
10210
|
+
// per-rule: only the unclicked hotfix button must not
|
|
10211
|
+
// block — a failed automatic release stays loud
|
|
10212
|
+
allow_failure: true,
|
|
10213
|
+
},
|
|
10214
|
+
],
|
|
10215
|
+
},
|
|
10216
|
+
...forceReleaseJob,
|
|
10217
|
+
...checkJob,
|
|
10218
|
+
};
|
|
10219
|
+
}
|
|
10220
|
+
const executorNeeds = [
|
|
10221
|
+
{ job: exports.RELEASE_BUTTON_JOB_NAME },
|
|
10222
|
+
// optional: hotfix pipelines (and rules-filtered jobs) contain a
|
|
10223
|
+
// different job subset
|
|
10224
|
+
...mainBranchJobNames.map((name) => ({ job: name, optional: true })),
|
|
10225
|
+
];
|
|
10226
|
+
if (executorNeeds.length > GITLAB_MAX_NEEDS) {
|
|
10227
|
+
console.warn(`⚠️ the main-branch pipeline has more than ${GITLAB_MAX_NEEDS} jobs — ` +
|
|
10228
|
+
`the "${exports.RELEASE_BUTTON_JOB_NAME}" job falls back to being playable ` +
|
|
10229
|
+
`only once the pipeline finished (gitlab caps needs: at ${GITLAB_MAX_NEEDS})`);
|
|
10230
|
+
return {
|
|
10231
|
+
[exports.RELEASE_BUTTON_JOB_NAME]: {
|
|
10232
|
+
...releaseJobBase,
|
|
10233
|
+
allow_failure: true,
|
|
10234
|
+
rules: manualReleaseRules,
|
|
10235
|
+
},
|
|
10236
|
+
...forceReleaseJob,
|
|
10237
|
+
...checkJob,
|
|
10238
|
+
};
|
|
10239
|
+
}
|
|
10240
|
+
return {
|
|
10241
|
+
// the queue button: playable from the first second of the pipeline.
|
|
10242
|
+
// It only records the intent to release — the executor below does
|
|
10243
|
+
// the actual work once every other job succeeded. Clicking after the
|
|
10244
|
+
// pipeline finished works the same (the executor starts right away).
|
|
10245
|
+
[exports.RELEASE_BUTTON_JOB_NAME]: {
|
|
10246
|
+
stage: "release",
|
|
10247
|
+
image: releaseImage,
|
|
10248
|
+
script: [
|
|
10249
|
+
`echo "release queued — '${exports.RELEASE_EXECUTOR_JOB_NAME}' runs it as soon as every other job in this pipeline succeeded"`,
|
|
10250
|
+
],
|
|
10251
|
+
needs: [],
|
|
10252
|
+
// allow_failure keeps the unclicked button from blocking the
|
|
10253
|
+
// pipeline AND makes the executor's needs treat it as optional
|
|
10254
|
+
// (skipped executor instead of a forever-waiting pipeline)
|
|
10255
|
+
allow_failure: true,
|
|
10256
|
+
rules: manualReleaseRules,
|
|
10257
|
+
},
|
|
10258
|
+
// the executor: never played manually. Skipped when the button was
|
|
10259
|
+
// not clicked; otherwise starts automatically as soon as the button
|
|
10260
|
+
// and every other (non-manual) job of the pipeline succeeded, and
|
|
10261
|
+
// is skipped when any of them failed.
|
|
10262
|
+
[exports.RELEASE_EXECUTOR_JOB_NAME]: {
|
|
10263
|
+
...releaseJobBase,
|
|
10264
|
+
needs: executorNeeds,
|
|
10028
10265
|
rules: [
|
|
10029
10266
|
...baseReleaseRules,
|
|
10030
10267
|
{
|
|
10031
10268
|
if: rules_1.RULE_CONDITION_MAIN_BRANCH,
|
|
10032
|
-
when: "
|
|
10269
|
+
when: "on_success",
|
|
10033
10270
|
},
|
|
10034
10271
|
{
|
|
10035
10272
|
if: rules_1.RULE_CONDITION_HOTFIX_BRANCH,
|
|
10036
|
-
when: "
|
|
10273
|
+
when: "on_success",
|
|
10037
10274
|
},
|
|
10038
10275
|
],
|
|
10039
10276
|
},
|
|
10040
|
-
|
|
10041
|
-
|
|
10042
|
-
// widget as an exposed artifact
|
|
10043
|
-
...(method.checkScript
|
|
10044
|
-
? {
|
|
10045
|
-
["🦋 changeset check"]: {
|
|
10046
|
-
stage: "test",
|
|
10047
|
-
image: releaseImage,
|
|
10048
|
-
script: [method.checkScript],
|
|
10049
|
-
allow_failure: true,
|
|
10050
|
-
artifacts: {
|
|
10051
|
-
paths: ["changeset-report.md"],
|
|
10052
|
-
expose_as: "changeset report",
|
|
10053
|
-
// the report must survive the warning exit
|
|
10054
|
-
when: "always",
|
|
10055
|
-
},
|
|
10056
|
-
rules: [
|
|
10057
|
-
rules_1.RULE_NEVER_ON_AGENT_TRIGGER,
|
|
10058
|
-
rules_1.RULE_NEVER_ON_SCHEDULE,
|
|
10059
|
-
rules_1.RULE_IS_MERGE_REQUEST,
|
|
10060
|
-
],
|
|
10061
|
-
},
|
|
10062
|
-
}
|
|
10063
|
-
: {}),
|
|
10277
|
+
...forceReleaseJob,
|
|
10278
|
+
...checkJob,
|
|
10064
10279
|
};
|
|
10065
10280
|
};
|
|
10066
10281
|
exports.getGitlabReleaseJobs = getGitlabReleaseJobs;
|
|
@@ -12719,7 +12934,9 @@ const getEnvironmentVariables = async (ctx, options = {}) => {
|
|
|
12719
12934
|
const additionalEnvVars = deployConfigRaw
|
|
12720
12935
|
? deploy_1.DEPLOY_TYPES[deployConfigRaw.type].getAdditionalEnvVars(environmentContext)
|
|
12721
12936
|
: {};
|
|
12722
|
-
|
|
12937
|
+
// deploy types provide HOSTNAME_INTERNAL as a plain string or bash
|
|
12938
|
+
// expression (never containing references)
|
|
12939
|
+
const HOSTNAME_INTERNAL = ((_b = additionalEnvVars.HOSTNAME_INTERNAL) !== null && _b !== void 0 ? _b : "unknown-host.example.com");
|
|
12723
12940
|
host = (_c = envConfigRaw === null || envConfigRaw === void 0 ? void 0 : envConfigRaw.host) !== null && _c !== void 0 ? _c : HOSTNAME_INTERNAL;
|
|
12724
12941
|
url = (0, bash_1.joinBashExpressions)(["https://", host]);
|
|
12725
12942
|
predefinedVariables = {
|
|
@@ -12747,8 +12964,18 @@ const getEnvironmentVariables = async (ctx, options = {}) => {
|
|
|
12747
12964
|
componentName: ctx.componentName,
|
|
12748
12965
|
}),
|
|
12749
12966
|
]));
|
|
12750
|
-
|
|
12751
|
-
|
|
12967
|
+
// predefined variables may contain references too (e.g. the embedded
|
|
12968
|
+
// database connection string references the component's own DB_* vars), so
|
|
12969
|
+
// we resolve references over the merged map. This way public var overrides
|
|
12970
|
+
// (like DB_PASSWORD: "${otherComponent:DB_PASSWORD}") are picked up by the
|
|
12971
|
+
// predefined variables that reference them
|
|
12972
|
+
const mergedEnvVars = {
|
|
12973
|
+
...predefinedVariables,
|
|
12974
|
+
...secretEnvVars,
|
|
12975
|
+
...publicEnvVarsUnresolved,
|
|
12976
|
+
};
|
|
12977
|
+
const resolvedEnvVars = ((_g = options.shouldResolveReferences) !== null && _g !== void 0 ? _g : true)
|
|
12978
|
+
? await (0, bash_3.resolveAllReferences)(mergedEnvVars, async (otherComponentName) => {
|
|
12752
12979
|
const { envVars: otherEnvVars } = await (0, exports.getEnvironmentVariables)({
|
|
12753
12980
|
...ctx,
|
|
12754
12981
|
componentName: otherComponentName,
|
|
@@ -12757,12 +12984,8 @@ const getEnvironmentVariables = async (ctx, options = {}) => {
|
|
|
12757
12984
|
});
|
|
12758
12985
|
return otherEnvVars;
|
|
12759
12986
|
})
|
|
12760
|
-
:
|
|
12761
|
-
const envVars = addIndexVar(
|
|
12762
|
-
...predefinedVariables,
|
|
12763
|
-
...secretEnvVars,
|
|
12764
|
-
...publicEnvVars,
|
|
12765
|
-
});
|
|
12987
|
+
: mergedEnvVars;
|
|
12988
|
+
const envVars = addIndexVar(resolvedEnvVars);
|
|
12766
12989
|
return {
|
|
12767
12990
|
envVars,
|
|
12768
12991
|
secretEnvVarKeys,
|
|
@@ -14753,8 +14976,8 @@ const getCloudSqlVariables = ({ deployConfigRaw, environmentSlugPrefix, env, com
|
|
|
14753
14976
|
};
|
|
14754
14977
|
return {
|
|
14755
14978
|
...dbVars,
|
|
14756
|
-
DATABASE_URL: (0, database_1.getDatabaseConnectionString)(deployConfigRaw.cloudSql, dbVars).concat(additionalQueryParamsString),
|
|
14757
|
-
DATABASE_JDBC_URL: (0, database_1.getDatabaseJdbcUrl)(dbVars, (_c = deployConfigRaw.cloudSql.dbConnectionStringVariablesMode) !== null && _c !== void 0 ? _c : database_1.DEFAULT_DB_VARIABLES_MODE),
|
|
14979
|
+
DATABASE_URL: (0, database_1.getDatabaseConnectionString)(deployConfigRaw.cloudSql, dbVars, componentName).concat(additionalQueryParamsString),
|
|
14980
|
+
DATABASE_JDBC_URL: (0, database_1.getDatabaseJdbcUrl)(dbVars, (_c = deployConfigRaw.cloudSql.dbConnectionStringVariablesMode) !== null && _c !== void 0 ? _c : database_1.DEFAULT_DB_VARIABLES_MODE, componentName),
|
|
14758
14981
|
};
|
|
14759
14982
|
}
|
|
14760
14983
|
return {};
|
|
@@ -14946,64 +15169,72 @@ const getDatabaseCreateScript = (context, deployConfig) => {
|
|
|
14946
15169
|
]);
|
|
14947
15170
|
};
|
|
14948
15171
|
exports.getDatabaseCreateScript = getDatabaseCreateScript;
|
|
14949
|
-
exports.DEFAULT_DB_VARIABLES_MODE = "
|
|
14950
|
-
const getVariableOrValue = (key,
|
|
14951
|
-
|
|
14952
|
-
|
|
14953
|
-
|
|
15172
|
+
exports.DEFAULT_DB_VARIABLES_MODE = "embedded";
|
|
15173
|
+
const getVariableOrValue = (key, mode, componentName) => {
|
|
15174
|
+
// in embedded mode we reference the component's own env var, which gets
|
|
15175
|
+
// resolved to its final value (including vars.public overrides) after all
|
|
15176
|
+
// env vars have been merged
|
|
15177
|
+
return mode === "legacy"
|
|
15178
|
+
? `$${key}`
|
|
15179
|
+
: new bash_1.VariableReference(componentName, key);
|
|
15180
|
+
};
|
|
15181
|
+
const joinDbUrlParts = (parts, mode) => mode === "legacy"
|
|
15182
|
+
? (0, bash_1.joinBashExpressions)(parts)
|
|
15183
|
+
: new bash_1.VariableValueContainingReferences(parts);
|
|
15184
|
+
const getDatabaseJdbcUrl = (variables, mode, componentName) => {
|
|
14954
15185
|
const parts = [
|
|
14955
15186
|
"jdbc:postgresql:///",
|
|
14956
|
-
getVariableOrValue("DB_NAME",
|
|
15187
|
+
getVariableOrValue("DB_NAME", mode, componentName),
|
|
14957
15188
|
"?cloudSqlInstance=",
|
|
14958
|
-
getVariableOrValue("CLOUD_SQL_INSTANCE_CONNECTION_NAME",
|
|
15189
|
+
getVariableOrValue("CLOUD_SQL_INSTANCE_CONNECTION_NAME", mode, componentName),
|
|
14959
15190
|
"&socketFactory=com.google.cloud.sql.postgres.SocketFactory&user=",
|
|
14960
|
-
getVariableOrValue("DB_USER",
|
|
15191
|
+
getVariableOrValue("DB_USER", mode, componentName),
|
|
14961
15192
|
"&password=",
|
|
14962
|
-
getVariableOrValue("DB_PASSWORD",
|
|
15193
|
+
getVariableOrValue("DB_PASSWORD", mode, componentName),
|
|
14963
15194
|
];
|
|
14964
|
-
return (
|
|
15195
|
+
return joinDbUrlParts(parts, mode);
|
|
14965
15196
|
};
|
|
14966
15197
|
exports.getDatabaseJdbcUrl = getDatabaseJdbcUrl;
|
|
14967
|
-
const getRailsDatabaseConnectionString = (variables, mode) => {
|
|
15198
|
+
const getRailsDatabaseConnectionString = (variables, mode, componentName) => {
|
|
14968
15199
|
const parts = [
|
|
14969
15200
|
"postgresql://",
|
|
14970
|
-
getVariableOrValue("DB_USER",
|
|
15201
|
+
getVariableOrValue("DB_USER", mode, componentName),
|
|
14971
15202
|
":",
|
|
14972
|
-
getVariableOrValue("DB_PASSWORD",
|
|
15203
|
+
getVariableOrValue("DB_PASSWORD", mode, componentName),
|
|
14973
15204
|
"@",
|
|
14974
15205
|
encodeURIComponent(`/cloudsql/${variables.CLOUD_SQL_INSTANCE_CONNECTION_NAME}`),
|
|
14975
15206
|
"/",
|
|
14976
|
-
getVariableOrValue("DB_NAME",
|
|
15207
|
+
getVariableOrValue("DB_NAME", mode, componentName),
|
|
14977
15208
|
"?",
|
|
14978
15209
|
];
|
|
14979
|
-
return (
|
|
15210
|
+
return joinDbUrlParts(parts, mode);
|
|
14980
15211
|
};
|
|
14981
15212
|
exports.getRailsDatabaseConnectionString = getRailsDatabaseConnectionString;
|
|
14982
|
-
const getPrismaDatabaseConnectionString = (variables, mode) => {
|
|
15213
|
+
const getPrismaDatabaseConnectionString = (variables, mode, componentName) => {
|
|
14983
15214
|
const parts = [
|
|
14984
15215
|
"postgresql://",
|
|
14985
|
-
getVariableOrValue("DB_USER",
|
|
15216
|
+
getVariableOrValue("DB_USER", mode, componentName),
|
|
14986
15217
|
":",
|
|
14987
|
-
getVariableOrValue("DB_PASSWORD",
|
|
15218
|
+
getVariableOrValue("DB_PASSWORD", mode, componentName),
|
|
14988
15219
|
"@localhost/",
|
|
14989
|
-
getVariableOrValue("DB_NAME",
|
|
15220
|
+
getVariableOrValue("DB_NAME", mode, componentName),
|
|
14990
15221
|
"?host=/cloudsql/",
|
|
14991
|
-
getVariableOrValue("CLOUD_SQL_INSTANCE_CONNECTION_NAME",
|
|
15222
|
+
getVariableOrValue("CLOUD_SQL_INSTANCE_CONNECTION_NAME", mode, componentName),
|
|
14992
15223
|
];
|
|
14993
|
-
return (
|
|
15224
|
+
return joinDbUrlParts(parts, mode);
|
|
14994
15225
|
};
|
|
14995
15226
|
exports.getPrismaDatabaseConnectionString = getPrismaDatabaseConnectionString;
|
|
14996
|
-
const getDatabaseConnectionString = (config, variables) => {
|
|
15227
|
+
const getDatabaseConnectionString = (config, variables, componentName) => {
|
|
14997
15228
|
var _a;
|
|
14998
15229
|
const mode = (_a = config.dbConnectionStringVariablesMode) !== null && _a !== void 0 ? _a : exports.DEFAULT_DB_VARIABLES_MODE;
|
|
14999
15230
|
switch (config.dbConnectionStringFormat) {
|
|
15000
15231
|
case "jdbc":
|
|
15001
|
-
return (0, exports.getDatabaseJdbcUrl)(variables, mode);
|
|
15232
|
+
return (0, exports.getDatabaseJdbcUrl)(variables, mode, componentName);
|
|
15002
15233
|
case "rails":
|
|
15003
|
-
return (0, exports.getRailsDatabaseConnectionString)(variables, mode);
|
|
15234
|
+
return (0, exports.getRailsDatabaseConnectionString)(variables, mode, componentName);
|
|
15004
15235
|
default:
|
|
15005
15236
|
// prisma
|
|
15006
|
-
return (0, exports.getPrismaDatabaseConnectionString)(variables, mode);
|
|
15237
|
+
return (0, exports.getPrismaDatabaseConnectionString)(variables, mode, componentName);
|
|
15007
15238
|
}
|
|
15008
15239
|
};
|
|
15009
15240
|
exports.getDatabaseConnectionString = getDatabaseConnectionString;
|