@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
|
@@ -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(
|
|
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
|
|
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
|
|
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
|
|
2007
|
+
value instanceof VariableValueContainingReferences_1.VariableValueContainingReferences
|
|
1996
2008
|
? (0, resolveReferencesOnce_1.resolveReferencesOnce)(value, ({ componentName, variableName }) => {
|
|
1997
2009
|
return allEnvVarsInComponents[componentName][variableName];
|
|
1998
2010
|
})
|
|
@@ -2463,6 +2475,13 @@ class GithubBackend {
|
|
|
2463
2475
|
manualJobConditions[id] = `inputs.job == '${id}'`;
|
|
2464
2476
|
manualDispatchOptions.push(id);
|
|
2465
2477
|
}
|
|
2478
|
+
// manual release mode: the workflow that runs a queued release
|
|
2479
|
+
// once the main workflow completed green
|
|
2480
|
+
const releaseOnGreen = (0, githubReleaseJobs_1.getGithubReleaseOnGreenWorkflow)(config, images, TRIGGER_WORKFLOWS.mainBranch.name, workflowEnv);
|
|
2481
|
+
if (releaseOnGreen) {
|
|
2482
|
+
workflows[`${GENERATED_FILE_PREFIX}release-on-green.yml`] =
|
|
2483
|
+
releaseOnGreen;
|
|
2484
|
+
}
|
|
2466
2485
|
}
|
|
2467
2486
|
if (Object.keys(jobs).length > 0) {
|
|
2468
2487
|
workflows[`${GENERATED_FILE_PREFIX}${workflowFileName(trigger)}`] = {
|
|
@@ -3007,16 +3026,21 @@ exports.getUploadProviderIds = getUploadProviderIds;
|
|
|
3007
3026
|
"use strict";
|
|
3008
3027
|
|
|
3009
3028
|
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
|
3010
|
-
exports.getGithubReleaseCheckJobs = exports.getGithubReleaseJobs = void 0;
|
|
3029
|
+
exports.getGithubReleaseCheckJobs = exports.getGithubReleaseOnGreenWorkflow = exports.getGithubReleaseJobs = void 0;
|
|
3011
3030
|
const createGithubJobs_1 = __nccwpck_require__(57877);
|
|
3012
3031
|
const release_1 = __nccwpck_require__(38904);
|
|
3013
|
-
const
|
|
3032
|
+
const shippedCatci_1 = __nccwpck_require__(79024);
|
|
3033
|
+
const CATCI = `${shippedCatci_1.GENERATED_CATCI_FOLDER}/index.js`;
|
|
3034
|
+
const createReleaseJob = (config, images, options = {}) => {
|
|
3035
|
+
var _a;
|
|
3014
3036
|
const method = (0, release_1.getReleaseMethod)(config);
|
|
3015
3037
|
const resolved = images.resolveRef({ catladderImage: method.image });
|
|
3016
|
-
const imageNeeds =
|
|
3038
|
+
const imageNeeds = !options.skipImageNeeds &&
|
|
3039
|
+
resolved.need &&
|
|
3040
|
+
typeof resolved.need === "object"
|
|
3017
3041
|
? [(0, createGithubJobs_1.githubGlobalJobId)(resolved.need.job)]
|
|
3018
3042
|
: [];
|
|
3019
|
-
const allNeeds = [...imageNeeds, ...(needs !== null &&
|
|
3043
|
+
const allNeeds = [...imageNeeds, ...((_a = options.needs) !== null && _a !== void 0 ? _a : [])];
|
|
3020
3044
|
return {
|
|
3021
3045
|
name: "create release",
|
|
3022
3046
|
"runs-on": "ubuntu-latest",
|
|
@@ -3045,7 +3069,7 @@ const createReleaseJob = (config, images, needs, extraEnv) => {
|
|
|
3045
3069
|
...(allNeeds.length > 0 ? { needs: allNeeds } : {}),
|
|
3046
3070
|
env: {
|
|
3047
3071
|
GITHUB_TOKEN: "${{ github.token }}",
|
|
3048
|
-
...extraEnv,
|
|
3072
|
+
...options.extraEnv,
|
|
3049
3073
|
},
|
|
3050
3074
|
steps: [
|
|
3051
3075
|
{
|
|
@@ -3055,8 +3079,21 @@ const createReleaseJob = (config, images, needs, extraEnv) => {
|
|
|
3055
3079
|
// tags since the last release) to determine the version
|
|
3056
3080
|
with: { "fetch-depth": 0 },
|
|
3057
3081
|
},
|
|
3082
|
+
...(options.queueCheck
|
|
3083
|
+
? [
|
|
3084
|
+
{
|
|
3085
|
+
name: "check main workflow",
|
|
3086
|
+
id: "queue",
|
|
3087
|
+
run: `node ${CATCI} release github-queue-check`,
|
|
3088
|
+
shell: "bash",
|
|
3089
|
+
},
|
|
3090
|
+
]
|
|
3091
|
+
: []),
|
|
3058
3092
|
{
|
|
3059
3093
|
name: "create release",
|
|
3094
|
+
...(options.queueCheck
|
|
3095
|
+
? { if: "steps.queue.outputs.queued != 'true'" }
|
|
3096
|
+
: {}),
|
|
3060
3097
|
run: method.script,
|
|
3061
3098
|
shell: "bash",
|
|
3062
3099
|
},
|
|
@@ -3070,7 +3107,10 @@ const createReleaseJob = (config, images, needs, extraEnv) => {
|
|
|
3070
3107
|
* workflow after all other jobs; a force variant goes to the manual
|
|
3071
3108
|
* tasks workflow
|
|
3072
3109
|
* - otherwise (manual, the default): the release job goes to the
|
|
3073
|
-
* manual tasks workflow
|
|
3110
|
+
* manual tasks workflow. Its queue-check step releases right away on
|
|
3111
|
+
* a green main run and otherwise queues the release for the
|
|
3112
|
+
* release-on-green workflow (see getGithubReleaseOnGreenWorkflow) —
|
|
3113
|
+
* the counterpart of gitlab's queue button + executor pair.
|
|
3074
3114
|
*/
|
|
3075
3115
|
const getGithubReleaseJobs = (config, mainWorkflowJobIds, images) => {
|
|
3076
3116
|
var _a;
|
|
@@ -3078,28 +3118,89 @@ const getGithubReleaseJobs = (config, mainWorkflowJobIds, images) => {
|
|
|
3078
3118
|
if (((_a = config.releases) === null || _a === void 0 ? void 0 : _a.when) === "auto") {
|
|
3079
3119
|
return {
|
|
3080
3120
|
main: {
|
|
3081
|
-
"create-release": createReleaseJob(config, images,
|
|
3121
|
+
"create-release": createReleaseJob(config, images, {
|
|
3122
|
+
needs: mainWorkflowJobIds,
|
|
3123
|
+
}),
|
|
3082
3124
|
},
|
|
3083
3125
|
manual: {
|
|
3084
|
-
"force-create-release": createReleaseJob(config, images,
|
|
3126
|
+
"force-create-release": createReleaseJob(config, images, {
|
|
3127
|
+
extraEnv: method.forceReleaseVariables,
|
|
3128
|
+
}),
|
|
3085
3129
|
},
|
|
3086
3130
|
};
|
|
3087
3131
|
}
|
|
3088
3132
|
return {
|
|
3089
3133
|
main: {},
|
|
3090
3134
|
manual: {
|
|
3091
|
-
"create-release": createReleaseJob(config, images),
|
|
3135
|
+
"create-release": createReleaseJob(config, images, { queueCheck: true }),
|
|
3092
3136
|
// an explicit force option wherever the method distinguishes it
|
|
3093
3137
|
// (e.g. changesets: release a patch bump without changesets)
|
|
3094
3138
|
...(method.forceReleaseVariables
|
|
3095
3139
|
? {
|
|
3096
|
-
"force-create-release": createReleaseJob(config, images,
|
|
3140
|
+
"force-create-release": createReleaseJob(config, images, {
|
|
3141
|
+
extraEnv: method.forceReleaseVariables,
|
|
3142
|
+
}),
|
|
3097
3143
|
}
|
|
3098
3144
|
: {}),
|
|
3099
3145
|
},
|
|
3100
3146
|
};
|
|
3101
3147
|
};
|
|
3102
3148
|
exports.getGithubReleaseJobs = getGithubReleaseJobs;
|
|
3149
|
+
/**
|
|
3150
|
+
* the release-on-green workflow (manual release mode only): runs on
|
|
3151
|
+
* every completed main workflow run. A lightweight host job checks the
|
|
3152
|
+
* queue marker (written by the create-release task while the main
|
|
3153
|
+
* workflow was still running) — most runs have nothing queued and end
|
|
3154
|
+
* after that single quick job. When the marker matches the completed
|
|
3155
|
+
* run, the containerized release job creates the release.
|
|
3156
|
+
*
|
|
3157
|
+
* No image-build jobs here: the release image was ensured by the main
|
|
3158
|
+
* workflow run that just completed (same commit, same content-hashed
|
|
3159
|
+
* tag).
|
|
3160
|
+
*/
|
|
3161
|
+
const getGithubReleaseOnGreenWorkflow = (config, images, mainWorkflowName, workflowEnv) => {
|
|
3162
|
+
var _a;
|
|
3163
|
+
if (((_a = config.releases) === null || _a === void 0 ? void 0 : _a.when) === "auto") {
|
|
3164
|
+
return null;
|
|
3165
|
+
}
|
|
3166
|
+
return {
|
|
3167
|
+
name: "catladder release on green",
|
|
3168
|
+
on: {
|
|
3169
|
+
workflow_run: {
|
|
3170
|
+
workflows: [mainWorkflowName],
|
|
3171
|
+
types: ["completed"],
|
|
3172
|
+
},
|
|
3173
|
+
},
|
|
3174
|
+
env: workflowEnv,
|
|
3175
|
+
jobs: {
|
|
3176
|
+
guard: {
|
|
3177
|
+
name: "check queued release",
|
|
3178
|
+
"runs-on": "ubuntu-latest",
|
|
3179
|
+
// consuming the queue marker is a ref deletion
|
|
3180
|
+
permissions: { contents: "write" },
|
|
3181
|
+
env: { GITHUB_TOKEN: "${{ github.token }}" },
|
|
3182
|
+
outputs: { release: "${{ steps.guard.outputs.release }}" },
|
|
3183
|
+
steps: [
|
|
3184
|
+
{ name: "Checkout", uses: "actions/checkout@v4" },
|
|
3185
|
+
{
|
|
3186
|
+
name: "check queued release",
|
|
3187
|
+
id: "guard",
|
|
3188
|
+
run: `node ${CATCI} release github-queued-guard "\${{ github.event.workflow_run.head_sha }}" "\${{ github.event.workflow_run.conclusion }}"`,
|
|
3189
|
+
shell: "bash",
|
|
3190
|
+
},
|
|
3191
|
+
],
|
|
3192
|
+
},
|
|
3193
|
+
"create-release": {
|
|
3194
|
+
...createReleaseJob(config, images, {
|
|
3195
|
+
needs: ["guard"],
|
|
3196
|
+
skipImageNeeds: true,
|
|
3197
|
+
}),
|
|
3198
|
+
if: "${{ needs.guard.outputs.release == 'true' }}",
|
|
3199
|
+
},
|
|
3200
|
+
},
|
|
3201
|
+
};
|
|
3202
|
+
};
|
|
3203
|
+
exports.getGithubReleaseOnGreenWorkflow = getGithubReleaseOnGreenWorkflow;
|
|
3103
3204
|
/**
|
|
3104
3205
|
* the informational MR/PR check job of the release method (e.g. the
|
|
3105
3206
|
* changeset check), added to the mr workflow. continue-on-error keeps
|
|
@@ -3294,6 +3395,7 @@ const config_1 = __nccwpck_require__(17860);
|
|
|
3294
3395
|
const createAllJobs_1 = __nccwpck_require__(54114);
|
|
3295
3396
|
const jobImagesPlan_1 = __nccwpck_require__(47864);
|
|
3296
3397
|
const shippedCatci_1 = __nccwpck_require__(79024);
|
|
3398
|
+
const release_1 = __nccwpck_require__(38904);
|
|
3297
3399
|
const index_1 = __nccwpck_require__(68906);
|
|
3298
3400
|
const createGitlabJobs_1 = __nccwpck_require__(41509);
|
|
3299
3401
|
const createGitlabPipeline_1 = __nccwpck_require__(94411);
|
|
@@ -3380,12 +3482,29 @@ class GitlabBackend {
|
|
|
3380
3482
|
}
|
|
3381
3483
|
async createPipeline(config, images) {
|
|
3382
3484
|
const stages = (0, getPipelineStages_1.getPipelineStages)(config);
|
|
3383
|
-
// release
|
|
3384
|
-
//
|
|
3385
|
-
//
|
|
3386
|
-
|
|
3485
|
+
// register the release image first: the per-trigger job creation
|
|
3486
|
+
// below emits the build jobs of all images registered so far. The
|
|
3487
|
+
// release jobs themselves are created afterwards — they need the
|
|
3488
|
+
// main-branch job list for the queued-release executor's needs.
|
|
3489
|
+
images.resolveRef({ catladderImage: (0, release_1.getReleaseMethod)(config).image });
|
|
3387
3490
|
// for all triggers create jobs and add base rules
|
|
3388
|
-
const
|
|
3491
|
+
const jobsPerTrigger = await Promise.all(config_1.ALL_PIPELINE_TRIGGERS.map(async (trigger) => ({
|
|
3492
|
+
trigger,
|
|
3493
|
+
jobs: await (0, createGitlabJobs_1.createGitlabJobs)(await (0, createAllJobs_1.createAllJobs)({ config, trigger, pipelineType: this.type }), images, getGitlabRulesForTrigger(trigger)),
|
|
3494
|
+
})));
|
|
3495
|
+
const allJobsPerTrigger = jobsPerTrigger.flatMap(({ jobs }) => jobs);
|
|
3496
|
+
// the jobs the queued-release executor waits for: everything that
|
|
3497
|
+
// runs automatically in a main-branch pipeline. Manual jobs (stop,
|
|
3498
|
+
// rollback, gates) are excluded — gitlab skips a job whose needs
|
|
3499
|
+
// include an unplayed manual job.
|
|
3500
|
+
const mainBranchJobNames = [
|
|
3501
|
+
...new Set(jobsPerTrigger
|
|
3502
|
+
.filter(({ trigger }) => trigger === "mainBranch")
|
|
3503
|
+
.flatMap(({ jobs }) => jobs)
|
|
3504
|
+
.filter(({ gitlabJob }) => !isManualGitlabJob(gitlabJob))
|
|
3505
|
+
.map(({ name }) => name)),
|
|
3506
|
+
];
|
|
3507
|
+
const releaseJobs = (0, gitlabReleaseJobs_1.getGitlabReleaseJobs)(config, images, mainBranchJobNames);
|
|
3389
3508
|
const allWorkspaceJobs = allJobsPerTrigger
|
|
3390
3509
|
.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
|
|
3391
3510
|
// this is purely for better readability in git diffs when you add new components
|
|
@@ -3451,6 +3570,14 @@ class GitlabBackend {
|
|
|
3451
3570
|
}
|
|
3452
3571
|
}
|
|
3453
3572
|
exports.GitlabBackend = GitlabBackend;
|
|
3573
|
+
/**
|
|
3574
|
+
* whether a job can end up waiting for a human in a main-branch
|
|
3575
|
+
* pipeline (catladder expresses manual-ness only through rules)
|
|
3576
|
+
*/
|
|
3577
|
+
function isManualGitlabJob(job) {
|
|
3578
|
+
var _a;
|
|
3579
|
+
return ((_a = job.rules) !== null && _a !== void 0 ? _a : []).some((rule) => rule.when === "manual");
|
|
3580
|
+
}
|
|
3454
3581
|
function getGitlabRulesForTrigger(trigger) {
|
|
3455
3582
|
// mainBranch: on push to main branch except it's a release commit
|
|
3456
3583
|
// mr: on merge request
|
|
@@ -3983,91 +4110,179 @@ exports.getPipelineStages = getPipelineStages;
|
|
|
3983
4110
|
"use strict";
|
|
3984
4111
|
|
|
3985
4112
|
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
|
3986
|
-
exports.getGitlabReleaseJobs = void 0;
|
|
4113
|
+
exports.getGitlabReleaseJobs = exports.RELEASE_EXECUTOR_JOB_NAME = exports.RELEASE_BUTTON_JOB_NAME = void 0;
|
|
3987
4114
|
const rules_1 = __nccwpck_require__(35086);
|
|
3988
4115
|
const release_1 = __nccwpck_require__(38904);
|
|
3989
4116
|
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.'";
|
|
4117
|
+
/**
|
|
4118
|
+
* gitlab rejects jobs with more than 50 `needs:` entries (default
|
|
4119
|
+
* limit). Pipelines beyond that fall back to the legacy manual release
|
|
4120
|
+
* job, which is only playable once all prior stages finished.
|
|
4121
|
+
*/
|
|
4122
|
+
const GITLAB_MAX_NEEDS = 50;
|
|
4123
|
+
exports.RELEASE_BUTTON_JOB_NAME = "create release";
|
|
4124
|
+
exports.RELEASE_EXECUTOR_JOB_NAME = "🚀 release once pipeline succeeds";
|
|
3990
4125
|
const baseReleaseRules = [
|
|
3991
4126
|
rules_1.RULE_NEVER_ON_RELEASE_COMMIT,
|
|
3992
4127
|
rules_1.RULE_NEVER_ON_AGENT_TRIGGER,
|
|
3993
4128
|
rules_1.RULE_NEVER_ON_SCHEDULE,
|
|
3994
4129
|
];
|
|
3995
|
-
|
|
4130
|
+
// NOTE: with `rules:`, when: manual defaults to allow_failure: false,
|
|
4131
|
+
// which turns every unclicked pipeline into status "blocked" — the
|
|
4132
|
+
// manual release jobs therefore carry allow_failure: true so pipelines
|
|
4133
|
+
// end "passed" when nobody releases
|
|
4134
|
+
const manualReleaseRules = [
|
|
4135
|
+
...baseReleaseRules,
|
|
4136
|
+
{
|
|
4137
|
+
if: rules_1.RULE_CONDITION_MAIN_BRANCH,
|
|
4138
|
+
when: "manual",
|
|
4139
|
+
},
|
|
4140
|
+
{
|
|
4141
|
+
if: rules_1.RULE_CONDITION_HOTFIX_BRANCH,
|
|
4142
|
+
when: "manual",
|
|
4143
|
+
},
|
|
4144
|
+
];
|
|
4145
|
+
const getGitlabReleaseJobs = (config, images,
|
|
4146
|
+
/**
|
|
4147
|
+
* names of the jobs that run automatically in main-branch pipelines
|
|
4148
|
+
* (manual jobs excluded: a needed-but-unplayed manual job would make
|
|
4149
|
+
* gitlab skip the release executor)
|
|
4150
|
+
*/
|
|
4151
|
+
mainBranchJobNames) => {
|
|
3996
4152
|
var _a;
|
|
3997
4153
|
const method = (0, release_1.getReleaseMethod)(config);
|
|
3998
4154
|
// NOTE: only the resolved image url is used, not the `need`: release
|
|
3999
|
-
// jobs have no `needs` on purpose —
|
|
4000
|
-
//
|
|
4001
|
-
//
|
|
4002
|
-
// longer wait for the build/deploy stages.
|
|
4155
|
+
// jobs have no `needs` on the whole pipeline on purpose — where they
|
|
4156
|
+
// must wait for it, the wait is expressed by enumerating the jobs
|
|
4157
|
+
// explicitly (see the executor below).
|
|
4003
4158
|
const releaseImage = images.resolveRef({
|
|
4004
4159
|
catladderImage: method.image,
|
|
4005
4160
|
}).image;
|
|
4006
|
-
|
|
4007
|
-
|
|
4008
|
-
|
|
4009
|
-
|
|
4010
|
-
|
|
4011
|
-
|
|
4012
|
-
|
|
4013
|
-
...baseReleaseRules,
|
|
4014
|
-
{
|
|
4015
|
-
if: rules_1.RULE_CONDITION_MAIN_BRANCH,
|
|
4016
|
-
when: ((_a = config.releases) === null || _a === void 0 ? void 0 : _a.when) === "auto" ? "on_success" : "manual",
|
|
4017
|
-
},
|
|
4018
|
-
{
|
|
4019
|
-
if: rules_1.RULE_CONDITION_HOTFIX_BRANCH,
|
|
4020
|
-
when: "manual",
|
|
4021
|
-
},
|
|
4022
|
-
],
|
|
4023
|
-
},
|
|
4161
|
+
const releaseJobBase = {
|
|
4162
|
+
stage: "release",
|
|
4163
|
+
image: releaseImage,
|
|
4164
|
+
script: [method.script],
|
|
4165
|
+
after_script: [EXPIRED_TOKEN_HELP],
|
|
4166
|
+
};
|
|
4167
|
+
const forceReleaseJob = {
|
|
4024
4168
|
["⚠️ force create release"]: {
|
|
4025
|
-
|
|
4026
|
-
image: releaseImage,
|
|
4027
|
-
script: [method.script],
|
|
4028
|
-
after_script: [EXPIRED_TOKEN_HELP],
|
|
4169
|
+
...releaseJobBase,
|
|
4029
4170
|
needs: [],
|
|
4171
|
+
allow_failure: true,
|
|
4030
4172
|
// force semantics of the method's script (e.g. changesets:
|
|
4031
4173
|
// release a patch bump even without pending changesets)
|
|
4032
4174
|
...(method.forceReleaseVariables
|
|
4033
4175
|
? { variables: method.forceReleaseVariables }
|
|
4034
4176
|
: {}),
|
|
4177
|
+
rules: manualReleaseRules,
|
|
4178
|
+
},
|
|
4179
|
+
};
|
|
4180
|
+
// informational MR check of the release method (e.g. the changeset
|
|
4181
|
+
// check): warns via allow_failure, the report lands in the MR
|
|
4182
|
+
// widget as an exposed artifact
|
|
4183
|
+
const checkJob = method.checkScript
|
|
4184
|
+
? {
|
|
4185
|
+
["🦋 changeset check"]: {
|
|
4186
|
+
stage: "test",
|
|
4187
|
+
image: releaseImage,
|
|
4188
|
+
script: [method.checkScript],
|
|
4189
|
+
allow_failure: true,
|
|
4190
|
+
artifacts: {
|
|
4191
|
+
paths: ["changeset-report.md"],
|
|
4192
|
+
expose_as: "changeset report",
|
|
4193
|
+
// the report must survive the warning exit
|
|
4194
|
+
when: "always",
|
|
4195
|
+
},
|
|
4196
|
+
rules: [
|
|
4197
|
+
rules_1.RULE_NEVER_ON_AGENT_TRIGGER,
|
|
4198
|
+
rules_1.RULE_NEVER_ON_SCHEDULE,
|
|
4199
|
+
rules_1.RULE_IS_MERGE_REQUEST,
|
|
4200
|
+
],
|
|
4201
|
+
},
|
|
4202
|
+
}
|
|
4203
|
+
: {};
|
|
4204
|
+
if (((_a = config.releases) === null || _a === void 0 ? void 0 : _a.when) === "auto") {
|
|
4205
|
+
return {
|
|
4206
|
+
[exports.RELEASE_BUTTON_JOB_NAME]: {
|
|
4207
|
+
...releaseJobBase,
|
|
4208
|
+
rules: [
|
|
4209
|
+
...baseReleaseRules,
|
|
4210
|
+
{
|
|
4211
|
+
if: rules_1.RULE_CONDITION_MAIN_BRANCH,
|
|
4212
|
+
when: "on_success",
|
|
4213
|
+
},
|
|
4214
|
+
{
|
|
4215
|
+
if: rules_1.RULE_CONDITION_HOTFIX_BRANCH,
|
|
4216
|
+
when: "manual",
|
|
4217
|
+
// per-rule: only the unclicked hotfix button must not
|
|
4218
|
+
// block — a failed automatic release stays loud
|
|
4219
|
+
allow_failure: true,
|
|
4220
|
+
},
|
|
4221
|
+
],
|
|
4222
|
+
},
|
|
4223
|
+
...forceReleaseJob,
|
|
4224
|
+
...checkJob,
|
|
4225
|
+
};
|
|
4226
|
+
}
|
|
4227
|
+
const executorNeeds = [
|
|
4228
|
+
{ job: exports.RELEASE_BUTTON_JOB_NAME },
|
|
4229
|
+
// optional: hotfix pipelines (and rules-filtered jobs) contain a
|
|
4230
|
+
// different job subset
|
|
4231
|
+
...mainBranchJobNames.map((name) => ({ job: name, optional: true })),
|
|
4232
|
+
];
|
|
4233
|
+
if (executorNeeds.length > GITLAB_MAX_NEEDS) {
|
|
4234
|
+
console.warn(`⚠️ the main-branch pipeline has more than ${GITLAB_MAX_NEEDS} jobs — ` +
|
|
4235
|
+
`the "${exports.RELEASE_BUTTON_JOB_NAME}" job falls back to being playable ` +
|
|
4236
|
+
`only once the pipeline finished (gitlab caps needs: at ${GITLAB_MAX_NEEDS})`);
|
|
4237
|
+
return {
|
|
4238
|
+
[exports.RELEASE_BUTTON_JOB_NAME]: {
|
|
4239
|
+
...releaseJobBase,
|
|
4240
|
+
allow_failure: true,
|
|
4241
|
+
rules: manualReleaseRules,
|
|
4242
|
+
},
|
|
4243
|
+
...forceReleaseJob,
|
|
4244
|
+
...checkJob,
|
|
4245
|
+
};
|
|
4246
|
+
}
|
|
4247
|
+
return {
|
|
4248
|
+
// the queue button: playable from the first second of the pipeline.
|
|
4249
|
+
// It only records the intent to release — the executor below does
|
|
4250
|
+
// the actual work once every other job succeeded. Clicking after the
|
|
4251
|
+
// pipeline finished works the same (the executor starts right away).
|
|
4252
|
+
[exports.RELEASE_BUTTON_JOB_NAME]: {
|
|
4253
|
+
stage: "release",
|
|
4254
|
+
image: releaseImage,
|
|
4255
|
+
script: [
|
|
4256
|
+
`echo "release queued — '${exports.RELEASE_EXECUTOR_JOB_NAME}' runs it as soon as every other job in this pipeline succeeded"`,
|
|
4257
|
+
],
|
|
4258
|
+
needs: [],
|
|
4259
|
+
// allow_failure keeps the unclicked button from blocking the
|
|
4260
|
+
// pipeline AND makes the executor's needs treat it as optional
|
|
4261
|
+
// (skipped executor instead of a forever-waiting pipeline)
|
|
4262
|
+
allow_failure: true,
|
|
4263
|
+
rules: manualReleaseRules,
|
|
4264
|
+
},
|
|
4265
|
+
// the executor: never played manually. Skipped when the button was
|
|
4266
|
+
// not clicked; otherwise starts automatically as soon as the button
|
|
4267
|
+
// and every other (non-manual) job of the pipeline succeeded, and
|
|
4268
|
+
// is skipped when any of them failed.
|
|
4269
|
+
[exports.RELEASE_EXECUTOR_JOB_NAME]: {
|
|
4270
|
+
...releaseJobBase,
|
|
4271
|
+
needs: executorNeeds,
|
|
4035
4272
|
rules: [
|
|
4036
4273
|
...baseReleaseRules,
|
|
4037
4274
|
{
|
|
4038
4275
|
if: rules_1.RULE_CONDITION_MAIN_BRANCH,
|
|
4039
|
-
when: "
|
|
4276
|
+
when: "on_success",
|
|
4040
4277
|
},
|
|
4041
4278
|
{
|
|
4042
4279
|
if: rules_1.RULE_CONDITION_HOTFIX_BRANCH,
|
|
4043
|
-
when: "
|
|
4280
|
+
when: "on_success",
|
|
4044
4281
|
},
|
|
4045
4282
|
],
|
|
4046
4283
|
},
|
|
4047
|
-
|
|
4048
|
-
|
|
4049
|
-
// widget as an exposed artifact
|
|
4050
|
-
...(method.checkScript
|
|
4051
|
-
? {
|
|
4052
|
-
["🦋 changeset check"]: {
|
|
4053
|
-
stage: "test",
|
|
4054
|
-
image: releaseImage,
|
|
4055
|
-
script: [method.checkScript],
|
|
4056
|
-
allow_failure: true,
|
|
4057
|
-
artifacts: {
|
|
4058
|
-
paths: ["changeset-report.md"],
|
|
4059
|
-
expose_as: "changeset report",
|
|
4060
|
-
// the report must survive the warning exit
|
|
4061
|
-
when: "always",
|
|
4062
|
-
},
|
|
4063
|
-
rules: [
|
|
4064
|
-
rules_1.RULE_NEVER_ON_AGENT_TRIGGER,
|
|
4065
|
-
rules_1.RULE_NEVER_ON_SCHEDULE,
|
|
4066
|
-
rules_1.RULE_IS_MERGE_REQUEST,
|
|
4067
|
-
],
|
|
4068
|
-
},
|
|
4069
|
-
}
|
|
4070
|
-
: {}),
|
|
4284
|
+
...forceReleaseJob,
|
|
4285
|
+
...checkJob,
|
|
4071
4286
|
};
|
|
4072
4287
|
};
|
|
4073
4288
|
exports.getGitlabReleaseJobs = getGitlabReleaseJobs;
|
|
@@ -6726,7 +6941,9 @@ const getEnvironmentVariables = async (ctx, options = {}) => {
|
|
|
6726
6941
|
const additionalEnvVars = deployConfigRaw
|
|
6727
6942
|
? deploy_1.DEPLOY_TYPES[deployConfigRaw.type].getAdditionalEnvVars(environmentContext)
|
|
6728
6943
|
: {};
|
|
6729
|
-
|
|
6944
|
+
// deploy types provide HOSTNAME_INTERNAL as a plain string or bash
|
|
6945
|
+
// expression (never containing references)
|
|
6946
|
+
const HOSTNAME_INTERNAL = ((_b = additionalEnvVars.HOSTNAME_INTERNAL) !== null && _b !== void 0 ? _b : "unknown-host.example.com");
|
|
6730
6947
|
host = (_c = envConfigRaw === null || envConfigRaw === void 0 ? void 0 : envConfigRaw.host) !== null && _c !== void 0 ? _c : HOSTNAME_INTERNAL;
|
|
6731
6948
|
url = (0, bash_1.joinBashExpressions)(["https://", host]);
|
|
6732
6949
|
predefinedVariables = {
|
|
@@ -6754,8 +6971,18 @@ const getEnvironmentVariables = async (ctx, options = {}) => {
|
|
|
6754
6971
|
componentName: ctx.componentName,
|
|
6755
6972
|
}),
|
|
6756
6973
|
]));
|
|
6757
|
-
|
|
6758
|
-
|
|
6974
|
+
// predefined variables may contain references too (e.g. the embedded
|
|
6975
|
+
// database connection string references the component's own DB_* vars), so
|
|
6976
|
+
// we resolve references over the merged map. This way public var overrides
|
|
6977
|
+
// (like DB_PASSWORD: "${otherComponent:DB_PASSWORD}") are picked up by the
|
|
6978
|
+
// predefined variables that reference them
|
|
6979
|
+
const mergedEnvVars = {
|
|
6980
|
+
...predefinedVariables,
|
|
6981
|
+
...secretEnvVars,
|
|
6982
|
+
...publicEnvVarsUnresolved,
|
|
6983
|
+
};
|
|
6984
|
+
const resolvedEnvVars = ((_g = options.shouldResolveReferences) !== null && _g !== void 0 ? _g : true)
|
|
6985
|
+
? await (0, bash_3.resolveAllReferences)(mergedEnvVars, async (otherComponentName) => {
|
|
6759
6986
|
const { envVars: otherEnvVars } = await (0, exports.getEnvironmentVariables)({
|
|
6760
6987
|
...ctx,
|
|
6761
6988
|
componentName: otherComponentName,
|
|
@@ -6764,12 +6991,8 @@ const getEnvironmentVariables = async (ctx, options = {}) => {
|
|
|
6764
6991
|
});
|
|
6765
6992
|
return otherEnvVars;
|
|
6766
6993
|
})
|
|
6767
|
-
:
|
|
6768
|
-
const envVars = addIndexVar(
|
|
6769
|
-
...predefinedVariables,
|
|
6770
|
-
...secretEnvVars,
|
|
6771
|
-
...publicEnvVars,
|
|
6772
|
-
});
|
|
6994
|
+
: mergedEnvVars;
|
|
6995
|
+
const envVars = addIndexVar(resolvedEnvVars);
|
|
6773
6996
|
return {
|
|
6774
6997
|
envVars,
|
|
6775
6998
|
secretEnvVarKeys,
|
|
@@ -8760,8 +8983,8 @@ const getCloudSqlVariables = ({ deployConfigRaw, environmentSlugPrefix, env, com
|
|
|
8760
8983
|
};
|
|
8761
8984
|
return {
|
|
8762
8985
|
...dbVars,
|
|
8763
|
-
DATABASE_URL: (0, database_1.getDatabaseConnectionString)(deployConfigRaw.cloudSql, dbVars).concat(additionalQueryParamsString),
|
|
8764
|
-
DATABASE_JDBC_URL: (0, database_1.getDatabaseJdbcUrl)(dbVars, (_c = deployConfigRaw.cloudSql.dbConnectionStringVariablesMode) !== null && _c !== void 0 ? _c : database_1.DEFAULT_DB_VARIABLES_MODE),
|
|
8986
|
+
DATABASE_URL: (0, database_1.getDatabaseConnectionString)(deployConfigRaw.cloudSql, dbVars, componentName).concat(additionalQueryParamsString),
|
|
8987
|
+
DATABASE_JDBC_URL: (0, database_1.getDatabaseJdbcUrl)(dbVars, (_c = deployConfigRaw.cloudSql.dbConnectionStringVariablesMode) !== null && _c !== void 0 ? _c : database_1.DEFAULT_DB_VARIABLES_MODE, componentName),
|
|
8765
8988
|
};
|
|
8766
8989
|
}
|
|
8767
8990
|
return {};
|
|
@@ -8953,64 +9176,72 @@ const getDatabaseCreateScript = (context, deployConfig) => {
|
|
|
8953
9176
|
]);
|
|
8954
9177
|
};
|
|
8955
9178
|
exports.getDatabaseCreateScript = getDatabaseCreateScript;
|
|
8956
|
-
exports.DEFAULT_DB_VARIABLES_MODE = "
|
|
8957
|
-
const getVariableOrValue = (key,
|
|
8958
|
-
|
|
8959
|
-
|
|
8960
|
-
|
|
9179
|
+
exports.DEFAULT_DB_VARIABLES_MODE = "embedded";
|
|
9180
|
+
const getVariableOrValue = (key, mode, componentName) => {
|
|
9181
|
+
// in embedded mode we reference the component's own env var, which gets
|
|
9182
|
+
// resolved to its final value (including vars.public overrides) after all
|
|
9183
|
+
// env vars have been merged
|
|
9184
|
+
return mode === "legacy"
|
|
9185
|
+
? `$${key}`
|
|
9186
|
+
: new bash_1.VariableReference(componentName, key);
|
|
9187
|
+
};
|
|
9188
|
+
const joinDbUrlParts = (parts, mode) => mode === "legacy"
|
|
9189
|
+
? (0, bash_1.joinBashExpressions)(parts)
|
|
9190
|
+
: new bash_1.VariableValueContainingReferences(parts);
|
|
9191
|
+
const getDatabaseJdbcUrl = (variables, mode, componentName) => {
|
|
8961
9192
|
const parts = [
|
|
8962
9193
|
"jdbc:postgresql:///",
|
|
8963
|
-
getVariableOrValue("DB_NAME",
|
|
9194
|
+
getVariableOrValue("DB_NAME", mode, componentName),
|
|
8964
9195
|
"?cloudSqlInstance=",
|
|
8965
|
-
getVariableOrValue("CLOUD_SQL_INSTANCE_CONNECTION_NAME",
|
|
9196
|
+
getVariableOrValue("CLOUD_SQL_INSTANCE_CONNECTION_NAME", mode, componentName),
|
|
8966
9197
|
"&socketFactory=com.google.cloud.sql.postgres.SocketFactory&user=",
|
|
8967
|
-
getVariableOrValue("DB_USER",
|
|
9198
|
+
getVariableOrValue("DB_USER", mode, componentName),
|
|
8968
9199
|
"&password=",
|
|
8969
|
-
getVariableOrValue("DB_PASSWORD",
|
|
9200
|
+
getVariableOrValue("DB_PASSWORD", mode, componentName),
|
|
8970
9201
|
];
|
|
8971
|
-
return (
|
|
9202
|
+
return joinDbUrlParts(parts, mode);
|
|
8972
9203
|
};
|
|
8973
9204
|
exports.getDatabaseJdbcUrl = getDatabaseJdbcUrl;
|
|
8974
|
-
const getRailsDatabaseConnectionString = (variables, mode) => {
|
|
9205
|
+
const getRailsDatabaseConnectionString = (variables, mode, componentName) => {
|
|
8975
9206
|
const parts = [
|
|
8976
9207
|
"postgresql://",
|
|
8977
|
-
getVariableOrValue("DB_USER",
|
|
9208
|
+
getVariableOrValue("DB_USER", mode, componentName),
|
|
8978
9209
|
":",
|
|
8979
|
-
getVariableOrValue("DB_PASSWORD",
|
|
9210
|
+
getVariableOrValue("DB_PASSWORD", mode, componentName),
|
|
8980
9211
|
"@",
|
|
8981
9212
|
encodeURIComponent(`/cloudsql/${variables.CLOUD_SQL_INSTANCE_CONNECTION_NAME}`),
|
|
8982
9213
|
"/",
|
|
8983
|
-
getVariableOrValue("DB_NAME",
|
|
9214
|
+
getVariableOrValue("DB_NAME", mode, componentName),
|
|
8984
9215
|
"?",
|
|
8985
9216
|
];
|
|
8986
|
-
return (
|
|
9217
|
+
return joinDbUrlParts(parts, mode);
|
|
8987
9218
|
};
|
|
8988
9219
|
exports.getRailsDatabaseConnectionString = getRailsDatabaseConnectionString;
|
|
8989
|
-
const getPrismaDatabaseConnectionString = (variables, mode) => {
|
|
9220
|
+
const getPrismaDatabaseConnectionString = (variables, mode, componentName) => {
|
|
8990
9221
|
const parts = [
|
|
8991
9222
|
"postgresql://",
|
|
8992
|
-
getVariableOrValue("DB_USER",
|
|
9223
|
+
getVariableOrValue("DB_USER", mode, componentName),
|
|
8993
9224
|
":",
|
|
8994
|
-
getVariableOrValue("DB_PASSWORD",
|
|
9225
|
+
getVariableOrValue("DB_PASSWORD", mode, componentName),
|
|
8995
9226
|
"@localhost/",
|
|
8996
|
-
getVariableOrValue("DB_NAME",
|
|
9227
|
+
getVariableOrValue("DB_NAME", mode, componentName),
|
|
8997
9228
|
"?host=/cloudsql/",
|
|
8998
|
-
getVariableOrValue("CLOUD_SQL_INSTANCE_CONNECTION_NAME",
|
|
9229
|
+
getVariableOrValue("CLOUD_SQL_INSTANCE_CONNECTION_NAME", mode, componentName),
|
|
8999
9230
|
];
|
|
9000
|
-
return (
|
|
9231
|
+
return joinDbUrlParts(parts, mode);
|
|
9001
9232
|
};
|
|
9002
9233
|
exports.getPrismaDatabaseConnectionString = getPrismaDatabaseConnectionString;
|
|
9003
|
-
const getDatabaseConnectionString = (config, variables) => {
|
|
9234
|
+
const getDatabaseConnectionString = (config, variables, componentName) => {
|
|
9004
9235
|
var _a;
|
|
9005
9236
|
const mode = (_a = config.dbConnectionStringVariablesMode) !== null && _a !== void 0 ? _a : exports.DEFAULT_DB_VARIABLES_MODE;
|
|
9006
9237
|
switch (config.dbConnectionStringFormat) {
|
|
9007
9238
|
case "jdbc":
|
|
9008
|
-
return (0, exports.getDatabaseJdbcUrl)(variables, mode);
|
|
9239
|
+
return (0, exports.getDatabaseJdbcUrl)(variables, mode, componentName);
|
|
9009
9240
|
case "rails":
|
|
9010
|
-
return (0, exports.getRailsDatabaseConnectionString)(variables, mode);
|
|
9241
|
+
return (0, exports.getRailsDatabaseConnectionString)(variables, mode, componentName);
|
|
9011
9242
|
default:
|
|
9012
9243
|
// prisma
|
|
9013
|
-
return (0, exports.getPrismaDatabaseConnectionString)(variables, mode);
|
|
9244
|
+
return (0, exports.getPrismaDatabaseConnectionString)(variables, mode, componentName);
|
|
9014
9245
|
}
|
|
9015
9246
|
};
|
|
9016
9247
|
exports.getDatabaseConnectionString = getDatabaseConnectionString;
|