@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.
Files changed (46) hide show
  1. package/dist/apps/cli/src/apps/cli/commands/project/doctor/checkGithubReleaseWorkflow.d.ts +30 -0
  2. package/dist/apps/cli/src/apps/cli/commands/project/doctor/checkGithubReleaseWorkflow.js +89 -0
  3. package/dist/apps/cli/src/apps/cli/commands/project/doctor/checkGithubReleaseWorkflow.js.map +1 -0
  4. package/dist/apps/cli/src/apps/cli/commands/project/doctor/index.js +2 -0
  5. package/dist/apps/cli/src/apps/cli/commands/project/doctor/index.js.map +1 -1
  6. package/dist/apps/cli/src/apps/cli/commands/project/githubMergeGating.d.ts +4 -1
  7. package/dist/apps/cli/src/apps/cli/commands/project/githubMergeGating.js +4 -1
  8. package/dist/apps/cli/src/apps/cli/commands/project/githubMergeGating.js.map +1 -1
  9. package/dist/apps/cli/src/catci.js +13 -0
  10. package/dist/apps/cli/src/catci.js.map +1 -1
  11. package/dist/apps/cli/src/release/changesetsReleaseJob.js +20 -42
  12. package/dist/apps/cli/src/release/changesetsReleaseJob.js.map +1 -1
  13. package/dist/apps/cli/src/release/githubDeployKey.d.ts +36 -0
  14. package/dist/apps/cli/src/release/githubDeployKey.js +126 -0
  15. package/dist/apps/cli/src/release/githubDeployKey.js.map +1 -0
  16. package/dist/bundles/catci/index.js +5 -5
  17. package/dist/bundles/catenv/index.js +41 -3
  18. package/dist/bundles/cli/index.js +143 -4
  19. package/dist/bundles/runner-images/semantic-release/Dockerfile +3 -1
  20. package/dist/bundles/runner-images/semantic-release/scripts/semanticRelease +37 -4
  21. package/dist/bundles/skills/catladder-migrate-ci-backend/SKILL.md +15 -6
  22. package/dist/bundles/skills/catladder-pipelines/SKILL.md +32 -0
  23. package/dist/bundles/skills/catladder-releases/SKILL.md +14 -0
  24. package/dist/packages/pipeline/src/backends/github/GithubBackend.js +12 -3
  25. package/dist/packages/pipeline/src/backends/github/GithubBackend.js.map +1 -1
  26. package/dist/packages/pipeline/src/backends/gitlab/createGitlabPipeline.js +16 -0
  27. package/dist/packages/pipeline/src/backends/gitlab/createGitlabPipeline.js.map +1 -1
  28. package/dist/packages/pipeline/src/backends/gitlab/gitlabReleaseJobs.d.ts +17 -0
  29. package/dist/packages/pipeline/src/backends/gitlab/gitlabReleaseJobs.js +13 -0
  30. package/dist/packages/pipeline/src/backends/gitlab/gitlabReleaseJobs.js.map +1 -1
  31. package/dist/packages/pipeline/src/types/gitlab-types.d.ts +29 -1
  32. package/dist/runner-images/semantic-release/Dockerfile +3 -1
  33. package/dist/runner-images/semantic-release/scripts/semanticRelease +37 -4
  34. package/dist/skills/catladder-migrate-ci-backend/SKILL.md +15 -6
  35. package/dist/skills/catladder-pipelines/SKILL.md +32 -0
  36. package/dist/skills/catladder-releases/SKILL.md +14 -0
  37. package/dist/tsconfig.tsbuildinfo +1 -1
  38. package/package.json +1 -1
  39. package/src/apps/cli/commands/project/__tests__/checkGithubReleaseWorkflow.test.ts +89 -0
  40. package/src/apps/cli/commands/project/doctor/checkGithubReleaseWorkflow.ts +125 -0
  41. package/src/apps/cli/commands/project/doctor/index.ts +2 -0
  42. package/src/apps/cli/commands/project/githubMergeGating.ts +4 -1
  43. package/src/catci.ts +15 -0
  44. package/src/release/__tests__/githubDeployKey.test.ts +148 -0
  45. package/src/release/changesetsReleaseJob.ts +28 -57
  46. 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":"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"}
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
- RUN apk add --no-cache bash git git-lfs
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) dispatch the
131
- # taggedRelease workflow explicitly for a tag semantic-release created
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 nothing extra the release jobs use the built-in
187
- `github.token`. Tags pushed by that token do not retrigger workflows,
188
- so the release job dispatches the tagged-release workflow explicitly;
189
- that is built in and needs no configuration.
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. Classic branch protection has no bypass list
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