@catladder/cli 5.1.2 → 5.2.0
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/apps/cli/commands/project/doctor/checkGithubReleaseWorkflow.d.ts +30 -0
- package/dist/apps/cli/src/apps/cli/commands/project/doctor/checkGithubReleaseWorkflow.js +89 -0
- package/dist/apps/cli/src/apps/cli/commands/project/doctor/checkGithubReleaseWorkflow.js.map +1 -0
- package/dist/apps/cli/src/apps/cli/commands/project/doctor/index.js +2 -0
- package/dist/apps/cli/src/apps/cli/commands/project/doctor/index.js.map +1 -1
- package/dist/apps/cli/src/apps/cli/commands/project/githubMergeGating.d.ts +4 -1
- package/dist/apps/cli/src/apps/cli/commands/project/githubMergeGating.js +4 -1
- package/dist/apps/cli/src/apps/cli/commands/project/githubMergeGating.js.map +1 -1
- package/dist/apps/cli/src/catci.js +13 -0
- package/dist/apps/cli/src/catci.js.map +1 -1
- package/dist/apps/cli/src/release/changesetsReleaseJob.js +20 -42
- package/dist/apps/cli/src/release/changesetsReleaseJob.js.map +1 -1
- package/dist/apps/cli/src/release/githubDeployKey.d.ts +36 -0
- package/dist/apps/cli/src/release/githubDeployKey.js +126 -0
- package/dist/apps/cli/src/release/githubDeployKey.js.map +1 -0
- package/dist/bundles/catci/index.js +5 -5
- package/dist/bundles/catenv/index.js +41 -3
- package/dist/bundles/cli/index.js +143 -4
- package/dist/bundles/runner-images/semantic-release/Dockerfile +3 -1
- package/dist/bundles/runner-images/semantic-release/scripts/semanticRelease +37 -4
- package/dist/bundles/skills/catladder-migrate-ci-backend/SKILL.md +15 -6
- package/dist/bundles/skills/catladder-pipelines/SKILL.md +32 -0
- package/dist/bundles/skills/catladder-releases/SKILL.md +14 -0
- package/dist/packages/pipeline/src/backends/github/GithubBackend.js +12 -3
- package/dist/packages/pipeline/src/backends/github/GithubBackend.js.map +1 -1
- package/dist/packages/pipeline/src/backends/gitlab/createGitlabPipeline.js +16 -0
- package/dist/packages/pipeline/src/backends/gitlab/createGitlabPipeline.js.map +1 -1
- package/dist/packages/pipeline/src/backends/gitlab/gitlabReleaseJobs.d.ts +17 -0
- package/dist/packages/pipeline/src/backends/gitlab/gitlabReleaseJobs.js +13 -0
- package/dist/packages/pipeline/src/backends/gitlab/gitlabReleaseJobs.js.map +1 -1
- package/dist/packages/pipeline/src/types/gitlab-types.d.ts +29 -1
- package/dist/runner-images/semantic-release/Dockerfile +3 -1
- package/dist/runner-images/semantic-release/scripts/semanticRelease +37 -4
- package/dist/skills/catladder-migrate-ci-backend/SKILL.md +15 -6
- package/dist/skills/catladder-pipelines/SKILL.md +32 -0
- package/dist/skills/catladder-releases/SKILL.md +14 -0
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +1 -1
- package/src/apps/cli/commands/project/__tests__/checkGithubReleaseWorkflow.test.ts +89 -0
- package/src/apps/cli/commands/project/doctor/checkGithubReleaseWorkflow.ts +125 -0
- package/src/apps/cli/commands/project/doctor/index.ts +2 -0
- package/src/apps/cli/commands/project/githubMergeGating.ts +4 -1
- package/src/catci.ts +15 -0
- package/src/release/__tests__/githubDeployKey.test.ts +148 -0
- package/src/release/changesetsReleaseJob.ts +28 -57
- package/src/release/githubDeployKey.ts +141 -0
|
@@ -7,6 +7,7 @@ export declare const getGitlabReleaseJobs: (config: Config, images: JobImagesPla
|
|
|
7
7
|
stage: string;
|
|
8
8
|
image: string;
|
|
9
9
|
script: string[];
|
|
10
|
+
interruptible: true;
|
|
10
11
|
allow_failure: true;
|
|
11
12
|
artifacts: {
|
|
12
13
|
paths: string[];
|
|
@@ -42,6 +43,7 @@ export declare const getGitlabReleaseJobs: (config: Config, images: JobImagesPla
|
|
|
42
43
|
image: string;
|
|
43
44
|
script: string[];
|
|
44
45
|
after_script: string[];
|
|
46
|
+
interruptible: false;
|
|
45
47
|
};
|
|
46
48
|
"create release": {
|
|
47
49
|
rules: {
|
|
@@ -58,6 +60,7 @@ export declare const getGitlabReleaseJobs: (config: Config, images: JobImagesPla
|
|
|
58
60
|
image: string;
|
|
59
61
|
script: string[];
|
|
60
62
|
after_script: string[];
|
|
63
|
+
interruptible: false;
|
|
61
64
|
needs?: undefined;
|
|
62
65
|
allow_failure?: undefined;
|
|
63
66
|
};
|
|
@@ -81,6 +84,7 @@ export declare const getGitlabReleaseJobs: (config: Config, images: JobImagesPla
|
|
|
81
84
|
image: string;
|
|
82
85
|
script: string[];
|
|
83
86
|
after_script: string[];
|
|
87
|
+
interruptible: false;
|
|
84
88
|
};
|
|
85
89
|
"create release": {
|
|
86
90
|
rules: {
|
|
@@ -97,6 +101,7 @@ export declare const getGitlabReleaseJobs: (config: Config, images: JobImagesPla
|
|
|
97
101
|
image: string;
|
|
98
102
|
script: string[];
|
|
99
103
|
after_script: string[];
|
|
104
|
+
interruptible: false;
|
|
100
105
|
needs?: undefined;
|
|
101
106
|
allow_failure?: undefined;
|
|
102
107
|
};
|
|
@@ -105,6 +110,7 @@ export declare const getGitlabReleaseJobs: (config: Config, images: JobImagesPla
|
|
|
105
110
|
stage: string;
|
|
106
111
|
image: string;
|
|
107
112
|
script: string[];
|
|
113
|
+
interruptible: true;
|
|
108
114
|
allow_failure: true;
|
|
109
115
|
artifacts: {
|
|
110
116
|
paths: string[];
|
|
@@ -140,6 +146,7 @@ export declare const getGitlabReleaseJobs: (config: Config, images: JobImagesPla
|
|
|
140
146
|
image: string;
|
|
141
147
|
script: string[];
|
|
142
148
|
after_script: string[];
|
|
149
|
+
interruptible: false;
|
|
143
150
|
};
|
|
144
151
|
"create release": {
|
|
145
152
|
allow_failure: true;
|
|
@@ -157,6 +164,7 @@ export declare const getGitlabReleaseJobs: (config: Config, images: JobImagesPla
|
|
|
157
164
|
image: string;
|
|
158
165
|
script: string[];
|
|
159
166
|
after_script: string[];
|
|
167
|
+
interruptible: false;
|
|
160
168
|
needs?: undefined;
|
|
161
169
|
};
|
|
162
170
|
} | {
|
|
@@ -179,6 +187,7 @@ export declare const getGitlabReleaseJobs: (config: Config, images: JobImagesPla
|
|
|
179
187
|
image: string;
|
|
180
188
|
script: string[];
|
|
181
189
|
after_script: string[];
|
|
190
|
+
interruptible: false;
|
|
182
191
|
};
|
|
183
192
|
"create release": {
|
|
184
193
|
allow_failure: true;
|
|
@@ -196,6 +205,7 @@ export declare const getGitlabReleaseJobs: (config: Config, images: JobImagesPla
|
|
|
196
205
|
image: string;
|
|
197
206
|
script: string[];
|
|
198
207
|
after_script: string[];
|
|
208
|
+
interruptible: false;
|
|
199
209
|
needs?: undefined;
|
|
200
210
|
};
|
|
201
211
|
} | {
|
|
@@ -203,6 +213,7 @@ export declare const getGitlabReleaseJobs: (config: Config, images: JobImagesPla
|
|
|
203
213
|
stage: string;
|
|
204
214
|
image: string;
|
|
205
215
|
script: string[];
|
|
216
|
+
interruptible: true;
|
|
206
217
|
allow_failure: true;
|
|
207
218
|
artifacts: {
|
|
208
219
|
paths: string[];
|
|
@@ -238,11 +249,13 @@ export declare const getGitlabReleaseJobs: (config: Config, images: JobImagesPla
|
|
|
238
249
|
image: string;
|
|
239
250
|
script: string[];
|
|
240
251
|
after_script: string[];
|
|
252
|
+
interruptible: false;
|
|
241
253
|
};
|
|
242
254
|
"create release": {
|
|
243
255
|
stage: string;
|
|
244
256
|
image: string;
|
|
245
257
|
script: string[];
|
|
258
|
+
interruptible: false;
|
|
246
259
|
needs: any[];
|
|
247
260
|
allow_failure: true;
|
|
248
261
|
rules: {
|
|
@@ -277,6 +290,7 @@ export declare const getGitlabReleaseJobs: (config: Config, images: JobImagesPla
|
|
|
277
290
|
image: string;
|
|
278
291
|
script: string[];
|
|
279
292
|
after_script: string[];
|
|
293
|
+
interruptible: false;
|
|
280
294
|
};
|
|
281
295
|
} | {
|
|
282
296
|
"\uD83E\uDD8B changeset check"?: undefined;
|
|
@@ -298,11 +312,13 @@ export declare const getGitlabReleaseJobs: (config: Config, images: JobImagesPla
|
|
|
298
312
|
image: string;
|
|
299
313
|
script: string[];
|
|
300
314
|
after_script: string[];
|
|
315
|
+
interruptible: false;
|
|
301
316
|
};
|
|
302
317
|
"create release": {
|
|
303
318
|
stage: string;
|
|
304
319
|
image: string;
|
|
305
320
|
script: string[];
|
|
321
|
+
interruptible: false;
|
|
306
322
|
needs: any[];
|
|
307
323
|
allow_failure: true;
|
|
308
324
|
rules: {
|
|
@@ -337,5 +353,6 @@ export declare const getGitlabReleaseJobs: (config: Config, images: JobImagesPla
|
|
|
337
353
|
image: string;
|
|
338
354
|
script: string[];
|
|
339
355
|
after_script: string[];
|
|
356
|
+
interruptible: false;
|
|
340
357
|
};
|
|
341
358
|
};
|
|
@@ -53,6 +53,12 @@ mainBranchJobNames) => {
|
|
|
53
53
|
image: releaseImage,
|
|
54
54
|
script: [method.script],
|
|
55
55
|
after_script: [EXPIRED_TOKEN_HELP],
|
|
56
|
+
// a release in flight must never be killed by a new commit — a
|
|
57
|
+
// half-published version (tag pushed, npm publish missing) is not
|
|
58
|
+
// recoverable by re-running. Under gitlab's default "conservative"
|
|
59
|
+
// auto-cancel this also shields the rest of the pipeline, but only
|
|
60
|
+
// once the release actually started, which is late by construction.
|
|
61
|
+
interruptible: false,
|
|
56
62
|
};
|
|
57
63
|
const forceReleaseJob = {
|
|
58
64
|
["⚠️ force create release"]: {
|
|
@@ -76,6 +82,10 @@ mainBranchJobNames) => {
|
|
|
76
82
|
stage: "test",
|
|
77
83
|
image: releaseImage,
|
|
78
84
|
script: [method.checkScript],
|
|
85
|
+
// a redundant report is worthless: without this the job
|
|
86
|
+
// defaults to interruptible: false and, once started, stops
|
|
87
|
+
// gitlab from auto-cancelling the superseded MR pipeline
|
|
88
|
+
interruptible: true,
|
|
79
89
|
allow_failure: true,
|
|
80
90
|
artifacts: {
|
|
81
91
|
paths: ["changeset-report.md"],
|
|
@@ -145,6 +155,9 @@ mainBranchJobNames) => {
|
|
|
145
155
|
script: [
|
|
146
156
|
`echo "release queued — '${exports.RELEASE_EXECUTOR_JOB_NAME}' runs it as soon as every other job in this pipeline succeeded"`,
|
|
147
157
|
],
|
|
158
|
+
// the recorded intent must survive a new commit: cancelling it
|
|
159
|
+
// would silently drop the queued release
|
|
160
|
+
interruptible: false,
|
|
148
161
|
needs: [],
|
|
149
162
|
// allow_failure keeps the unclicked button from blocking the
|
|
150
163
|
// pipeline AND makes the executor's needs treat it as optional
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"gitlabReleaseJobs.js","sourceRoot":"","sources":["../../../../../../../../packages/pipeline/src/backends/gitlab/gitlabReleaseJobs.ts"],"names":[],"mappings":";;;AAAA,uCAOqB;AAErB,2CAAiD;AAIjD,MAAM,kBAAkB,GACtB,gJAAgJ,CAAC;AAEnJ;;;;GAIG;AACH,MAAM,gBAAgB,GAAG,EAAE,CAAC;AAEf,QAAA,uBAAuB,GAAG,gBAAgB,CAAC;AAC3C,QAAA,yBAAyB,GAAG,mCAAmC,CAAC;AAE7E,MAAM,gBAAgB,GAAG;IACvB,oCAA4B;IAC5B,mCAA2B;IAC3B,8BAAsB;CACvB,CAAC;AAEF,sEAAsE;AACtE,mEAAmE;AACnE,uEAAuE;AACvE,oCAAoC;AACpC,MAAM,kBAAkB,GAAG;IACzB,GAAG,gBAAgB;IACnB;QACE,EAAE,EAAE,kCAA0B;QAC9B,IAAI,EAAE,QAAQ;KACf;IACD;QACE,EAAE,EAAE,oCAA4B;QAChC,IAAI,EAAE,QAAQ;KACf;CACqB,CAAC;AAElB,MAAM,oBAAoB,GAAG,CAClC,MAAc,EACd,MAAqB;AACrB;;;;GAIG;AACH,kBAA4B,EAC5B,EAAE;;IACF,MAAM,MAAM,GAAG,IAAA,0BAAgB,EAAC,MAAM,CAAC,CAAC;IACxC,qEAAqE;IACrE,qEAAqE;IACrE,kEAAkE;IAClE,uCAAuC;IACvC,MAAM,YAAY,GAAG,MAAM,CAAC,UAAU,CAAC;QACrC,cAAc,EAAE,MAAM,CAAC,KAAK;KAC7B,CAAC,CAAC,KAAK,CAAC;IAET,MAAM,cAAc,GAAG;QACrB,KAAK,EAAE,SAAS;QAChB,KAAK,EAAE,YAAY;QACnB,MAAM,EAAE,CAAC,MAAM,CAAC,MAAM,CAAC;QACvB,YAAY,EAAE,CAAC,kBAAkB,CAAC;
|
|
1
|
+
{"version":3,"file":"gitlabReleaseJobs.js","sourceRoot":"","sources":["../../../../../../../../packages/pipeline/src/backends/gitlab/gitlabReleaseJobs.ts"],"names":[],"mappings":"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"}
|
|
@@ -4,6 +4,33 @@ export { Retry, Image as GitlabJobImage } from "./gitlab-ci-yml";
|
|
|
4
4
|
export type Artifacts = GitlabCiArtifacts;
|
|
5
5
|
export type { GitlabJobCache };
|
|
6
6
|
export type GitlabRule = Exclude<Exclude<Rules, null>[number], string | string[]>;
|
|
7
|
+
/**
|
|
8
|
+
* gitlab's auto-cancel policy for redundant pipelines. Tunes the
|
|
9
|
+
* project setting "Auto-cancel redundant pipelines" (Settings > CI/CD >
|
|
10
|
+
* General pipelines) — it does NOT enable it: with the setting off,
|
|
11
|
+
* nothing is ever cancelled regardless of this.
|
|
12
|
+
*
|
|
13
|
+
* needs gitlab >= 16.8 (top-level) / >= 16.10 (per workflow rule).
|
|
14
|
+
*/
|
|
15
|
+
export interface GitlabAutoCancel {
|
|
16
|
+
/**
|
|
17
|
+
* - `conservative` (gitlab's default): cancel the whole pipeline, but
|
|
18
|
+
* only while no `interruptible: false` job has *started* — a single
|
|
19
|
+
* such job then shields every other job from cancellation
|
|
20
|
+
* - `interruptible`: cancel every `interruptible: true` job, with no
|
|
21
|
+
* such veto
|
|
22
|
+
* - `none`: never auto-cancel
|
|
23
|
+
*/
|
|
24
|
+
on_new_commit?: "conservative" | "interruptible" | "none";
|
|
25
|
+
on_job_failure?: "all" | "none";
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* a workflow rule may override {@link GitlabAutoCancel} per pipeline
|
|
29
|
+
* kind — job rules may not.
|
|
30
|
+
*/
|
|
31
|
+
export type GitlabWorkflowRule = GitlabRule & {
|
|
32
|
+
auto_cancel?: GitlabAutoCancel;
|
|
33
|
+
};
|
|
7
34
|
export type GitlabEnvironment = Omit<Exclude<JobTemplate["environment"], undefined | string>, "deployment_tier">;
|
|
8
35
|
export interface GitlabJobDef extends Pick<JobTemplate, "after_script" | "allow_failure" | "artifacts" | "before_script" | "coverage" | "dependencies" | "environment" | "except" | "hooks" | "image" | "interruptible" | "needs" | "only" | "parallel" | "release" | "resource_group" | "retry" | "rules" | "script" | "services" | "stage" | "tags" | "trigger" | "variables"> {
|
|
9
36
|
stage: JobTemplate["stage"];
|
|
@@ -23,8 +50,9 @@ export interface GitlabJobDef extends Pick<JobTemplate, "after_script" | "allow_
|
|
|
23
50
|
export interface GitlabPipeline extends Pick<JobTemplate, "variables"> {
|
|
24
51
|
image: string;
|
|
25
52
|
workflow?: {
|
|
26
|
-
rules: GitlabRule[];
|
|
27
53
|
name?: string;
|
|
54
|
+
auto_cancel?: GitlabAutoCancel;
|
|
55
|
+
rules: GitlabWorkflowRule[];
|
|
28
56
|
variables?: Record<string, string>;
|
|
29
57
|
};
|
|
30
58
|
stages: string[];
|
|
@@ -4,7 +4,9 @@
|
|
|
4
4
|
# (@semantic-release/git and @semantic-release/changelog need node >=24.15).
|
|
5
5
|
FROM node:24-alpine
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
# openssh: on github the release job pushes with the release deploy key
|
|
8
|
+
# over ssh (see scripts/semanticRelease)
|
|
9
|
+
RUN apk add --no-cache bash git git-lfs openssh-client
|
|
8
10
|
|
|
9
11
|
# add semantic release
|
|
10
12
|
# NOTE: pin every dependency exactly. These are the semantic-release@25 line
|
|
@@ -39,7 +39,34 @@ fi
|
|
|
39
39
|
|
|
40
40
|
echo "doing semantic release"
|
|
41
41
|
|
|
42
|
+
SEMANTIC_RELEASE_ARGS=()
|
|
43
|
+
# the commit message suffix, see the @semantic-release/git config below
|
|
44
|
+
RELEASE_COMMIT_SUFFIX=""
|
|
42
45
|
if [ "$GITHUB_ACTIONS" = "true" ]; then
|
|
46
|
+
# [skip ci] on github only: a push with the deploy key triggers
|
|
47
|
+
# workflows (unlike a push with GITHUB_TOKEN), and neither the release
|
|
48
|
+
# commit nor the tag needs a push-triggered run — the taggedRelease
|
|
49
|
+
# workflow is dispatched explicitly at the end, the marker keeps it
|
|
50
|
+
# the only run (same as the changesets path). On gitlab the tag
|
|
51
|
+
# pipeline MUST fire natively, so no marker there.
|
|
52
|
+
RELEASE_COMMIT_SUFFIX=" [skip ci]"
|
|
53
|
+
|
|
54
|
+
if [ -n "$CATLADDER_RELEASE_KEY" ]; then
|
|
55
|
+
# merge gating (a ruleset requiring the `catladder ✅` check on the
|
|
56
|
+
# default branch) rejects the release push unless it comes from the
|
|
57
|
+
# release deploy key — the workflow token can never bypass a
|
|
58
|
+
# ruleset. catci writes the key, points git's ssh at it and checks
|
|
59
|
+
# that the key reaches the repository (a clear failure here instead
|
|
60
|
+
# of a GH013 inside @semantic-release/git). The ssh url has to be
|
|
61
|
+
# passed explicitly: semantic-release takes package.json's
|
|
62
|
+
# `repository` over origin, and origin is the token-authenticated
|
|
63
|
+
# https url actions/checkout left behind.
|
|
64
|
+
RELEASE_REPOSITORY_URL=$(node "$CATCI" release github-deploy-key-remote)
|
|
65
|
+
SEMANTIC_RELEASE_ARGS+=(--repository-url "$RELEASE_REPOSITORY_URL")
|
|
66
|
+
else
|
|
67
|
+
echo "CATLADDER_RELEASE_KEY not set — pushing with the workflow token (fails on a repository with merge gating: run 'catladder project setup')"
|
|
68
|
+
fi
|
|
69
|
+
|
|
43
70
|
# the github counterpart of the gitlab plugin below: it creates the
|
|
44
71
|
# entry on the releases page with the generated notes. Everything
|
|
45
72
|
# beyond that is switched off on purpose — commenting on the
|
|
@@ -62,6 +89,10 @@ else
|
|
|
62
89
|
"@semantic-release/gitlab"'
|
|
63
90
|
fi
|
|
64
91
|
|
|
92
|
+
# NOTE: a project bringing its own .releaserc has to keep "[skip ci]" in
|
|
93
|
+
# the git plugin's commit message on github (see RELEASE_COMMIT_SUFFIX),
|
|
94
|
+
# otherwise a deploy-key push starts the main workflow for the release
|
|
95
|
+
# commit and a second taggedRelease run for the tag.
|
|
65
96
|
if [ ! -e .releaserc ]; then
|
|
66
97
|
cat >.releaserc <<EOF
|
|
67
98
|
{
|
|
@@ -98,7 +129,7 @@ if [ ! -e .releaserc ]; then
|
|
|
98
129
|
# loop-prevention rule keys on the literal "chore(release)" prefix, so keep
|
|
99
130
|
# this literal. Don't "fix" it back to \${branch.type}.
|
|
100
131
|
["@semantic-release/git", {
|
|
101
|
-
"message": "chore(release): \${nextRelease.version}\n\n\${nextRelease.notes}"
|
|
132
|
+
"message": "chore(release): \${nextRelease.version}${RELEASE_COMMIT_SUFFIX}\n\n\${nextRelease.notes}"
|
|
102
133
|
}]${PUBLISH_PLUGIN}
|
|
103
134
|
],
|
|
104
135
|
"branches": [
|
|
@@ -124,11 +155,13 @@ if [ "$GITHUB_ACTIONS" = "true" ]; then
|
|
|
124
155
|
TAGS_BEFORE=$(git tag --list 'v[0-9]*')
|
|
125
156
|
fi
|
|
126
157
|
|
|
127
|
-
semantic-release
|
|
158
|
+
semantic-release "${SEMANTIC_RELEASE_ARGS[@]}"
|
|
128
159
|
|
|
129
160
|
# tags pushed with the default GITHUB_TOKEN don't trigger `on: push:
|
|
130
|
-
# tags` workflows (github's recursion guard)
|
|
131
|
-
#
|
|
161
|
+
# tags` workflows (github's recursion guard), and a deploy-key push
|
|
162
|
+
# carries [skip ci] (see above) — dispatch the taggedRelease workflow
|
|
163
|
+
# explicitly for a tag semantic-release created, whichever way it was
|
|
164
|
+
# pushed
|
|
132
165
|
if [ "$GITHUB_ACTIONS" = "true" ] && [ -f "$CATCI" ]; then
|
|
133
166
|
if [ -z "$TAGS_BEFORE" ]; then
|
|
134
167
|
NEW_TAG=$(git tag --list 'v[0-9]*' | tail -n1)
|
|
@@ -183,10 +183,15 @@ skill for the exact command surface.
|
|
|
183
183
|
- **GitLab** needs a `GL_TOKEN` project access token (named
|
|
184
184
|
`semantic-release`) to push the release commit and tag. It is created
|
|
185
185
|
and rotated by `yarn catladder project setup`.
|
|
186
|
-
- **GitHub** needs
|
|
187
|
-
`
|
|
188
|
-
|
|
189
|
-
|
|
186
|
+
- **GitHub** needs the release deploy key that `yarn catladder project
|
|
187
|
+
setup` provisions together with the merge gating (Step 9): the release
|
|
188
|
+
job pushes the release commit and tag over ssh with it, because the
|
|
189
|
+
built-in `github.token` can never bypass the required `catladder ✅`
|
|
190
|
+
check on the default branch (the push fails with GH013 otherwise).
|
|
191
|
+
Without merge gating the job falls back to the token. The release
|
|
192
|
+
commit carries `[skip ci]` and the release job dispatches the
|
|
193
|
+
tagged-release workflow explicitly (one run per tag); that is built
|
|
194
|
+
in and needs no configuration.
|
|
190
195
|
- **Only one backend should own releases at a time.** Both backends
|
|
191
196
|
generate release jobs, and with `releases: { when: "auto" }` both would
|
|
192
197
|
try to tag the same commit. During the parallel phase, agree with the
|
|
@@ -287,9 +292,13 @@ now-wrong `gitRemote`, regenerate once more, and finish with
|
|
|
287
292
|
token bypass rulesets, so setup provisions a write deploy key
|
|
288
293
|
("catladder release", private half in the `CATLADDER_RELEASE_KEY`
|
|
289
294
|
actions secret; deploy keys never expire) that the release job
|
|
290
|
-
pushes with over ssh
|
|
295
|
+
pushes with over ssh — with both release methods, semantic-release
|
|
296
|
+
and changesets. Classic branch protection has no bypass list
|
|
291
297
|
at all, which is why setup uses a ruleset and migrates old
|
|
292
|
-
classic-protection gating away. `project doctor` verifies all of it
|
|
298
|
+
classic-protection gating away. `project doctor` verifies all of it,
|
|
299
|
+
including that the committed release workflow runs the release image
|
|
300
|
+
of the installed catladder (an older semantic-release image pushed
|
|
301
|
+
with the token and ignored the key — regenerate with `catenv`).
|
|
293
302
|
Required reviews stay a human choice.
|
|
294
303
|
- Tell the team where the pipeline lives now and how manual actions
|
|
295
304
|
work there (on GitHub, manual jobs are dispatch workflows in the
|
|
@@ -37,6 +37,38 @@ Generated layout:
|
|
|
37
37
|
Stages: setup → test → build → deploy → verify (post-deploy checks),
|
|
38
38
|
plus stop jobs for review-app teardown.
|
|
39
39
|
|
|
40
|
+
## Superseded pipelines are cancelled
|
|
41
|
+
|
|
42
|
+
Pushing a new commit cancels the pipeline of the previous one, so the
|
|
43
|
+
runners are not busy with results nobody will read.
|
|
44
|
+
|
|
45
|
+
| Pipeline | On a new commit |
|
|
46
|
+
|---|---|
|
|
47
|
+
| `mr` | cancelled |
|
|
48
|
+
| `mainBranch` (dev) | cancelled, except a release already running |
|
|
49
|
+
| `taggedRelease` | **runs through** — never cancelled |
|
|
50
|
+
| agent runs (`trigger`) | **runs through** — never cancelled |
|
|
51
|
+
|
|
52
|
+
- **GitHub**: the generated MR workflow sets `concurrency` with
|
|
53
|
+
`cancel-in-progress: true`.
|
|
54
|
+
- **GitLab**: generated `workflow:auto_cancel:on_new_commit:
|
|
55
|
+
interruptible`, with per-rule `none` for tags and agent runs. Every
|
|
56
|
+
job carries an explicit `interruptible`, jobs that must finish
|
|
57
|
+
(release jobs, agent jobs) carry `interruptible: false`.
|
|
58
|
+
|
|
59
|
+
**GitLab requires the project setting too.** The generated YAML only
|
|
60
|
+
tunes *how* redundant pipelines are cancelled — it does not switch the
|
|
61
|
+
feature on. If old pipelines keep running, check **Settings > CI/CD >
|
|
62
|
+
General pipelines > Auto-cancel redundant pipelines**; with that
|
|
63
|
+
checkbox off nothing is ever cancelled. (`catladder project-doctor`
|
|
64
|
+
does not check this.)
|
|
65
|
+
|
|
66
|
+
**Adding a hand-written GitLab job?** Set `interruptible` explicitly.
|
|
67
|
+
GitLab defaults it to `false`, and a non-interruptible job that has
|
|
68
|
+
started keeps *itself* alive — under gitlab's own default
|
|
69
|
+
(`conservative`, which catladder overrides) it would shield the whole
|
|
70
|
+
pipeline from cancellation.
|
|
71
|
+
|
|
40
72
|
## Review-app auto-stop and pinning (GitLab)
|
|
41
73
|
|
|
42
74
|
GitLab review environments stop automatically after 1 week, dev
|
|
@@ -128,6 +128,20 @@ the `security` commands in the `catladder-cli` reference.
|
|
|
128
128
|
MR adding a changeset describing the accumulated work.
|
|
129
129
|
- Wrong version bump → check commit types (semantic-release) or the bump
|
|
130
130
|
levels in the changeset files (changesets).
|
|
131
|
+
- GitHub: the push is rejected with `GH013 … Required status check
|
|
132
|
+
"catladder ✅" is expected` (or GH006) → the release push has to use
|
|
133
|
+
the release deploy key, the only actor that bypasses the merge-gating
|
|
134
|
+
ruleset. `yarn catladder project setup` provisions it (deploy key
|
|
135
|
+
`catladder release` + secret `CATLADDER_RELEASE_KEY`), `yarn catladder
|
|
136
|
+
project doctor` verifies it — including that the committed release
|
|
137
|
+
workflow runs the current release image; an older semantic-release
|
|
138
|
+
image pushed with the workflow token and ignored the key (regenerate
|
|
139
|
+
with `yarn catenv` and commit).
|
|
140
|
+
- GitHub, semantic-release with a project-provided `.releaserc`: keep
|
|
141
|
+
`[skip ci]` in the `@semantic-release/git` commit message. The
|
|
142
|
+
deploy-key push triggers workflows, and without the marker the release
|
|
143
|
+
commit starts a main run and the tag a second tagged run next to the
|
|
144
|
+
one the release job dispatches.
|
|
131
145
|
- Tag pushed but nothing on the releases page → with `changesets` the
|
|
132
146
|
entry is created via the host api after the push and never fails the
|
|
133
147
|
job (the release itself is done); look for the `could not create the
|