@catladder/cli 0.0.0-next-ddc387fe → 0.0.0-next-d13ff46d
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/bundles/skills/catladder-config/SKILL.md +4 -0
- package/dist/bundles/skills/catladder-migrate-ci-backend/SKILL.md +249 -0
- package/dist/bundles/skills/catladder-migrate-release-method/SKILL.md +233 -0
- package/dist/bundles/skills/catladder-pipelines/SKILL.md +2 -0
- package/dist/bundles/skills/catladder-releases/SKILL.md +3 -0
- package/dist/bundles/skills/catladder-secrets/SKILL.md +2 -0
- package/dist/skills/catladder-config/SKILL.md +4 -0
- package/dist/skills/catladder-migrate-ci-backend/SKILL.md +249 -0
- package/dist/skills/catladder-migrate-release-method/SKILL.md +233 -0
- package/dist/skills/catladder-pipelines/SKILL.md +2 -0
- package/dist/skills/catladder-releases/SKILL.md +3 -0
- package/dist/skills/catladder-secrets/SKILL.md +2 -0
- package/package.json +1 -1
|
@@ -117,3 +117,7 @@ escape hatches to shrink over time, not accumulate.
|
|
|
117
117
|
- `catladder-releases` — releasing (semantic-release / changesets)
|
|
118
118
|
- `catladder-pipelines` — how the generated pipelines work, debugging CI
|
|
119
119
|
- `catladder-cli` — non-interactive CLI usage and command reference
|
|
120
|
+
- `catladder-migrate-ci-backend` — moving a project between GitLab CI
|
|
121
|
+
and GitHub Actions
|
|
122
|
+
- `catladder-migrate-release-method` — switching between
|
|
123
|
+
semantic-release and changesets
|
|
@@ -0,0 +1,249 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: catladder-migrate-ci-backend
|
|
3
|
+
description: Guiding a catladder project from one CI backend to the other — GitLab CI → GitHub Actions or GitHub Actions → GitLab CI — using the `pipelines` config to run both in parallel first, then cutting over. Covers the blockers to check up front (unsupported features, secrets vault, release tokens, container registry) and the manual cleanup a cut-over needs. Use when moving a project between GitLab and GitHub, enabling a second CI backend, or decommissioning one. Triggers on "migrate to github", "move to github actions", "switch from gitlab to github", "migrate to gitlab", "enable github pipeline", "run both pipelines", "gitRemote", "secrets-sync-github".
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Migrating between GitLab CI and GitHub Actions
|
|
7
|
+
|
|
8
|
+
catladder generates pipelines for both CI systems from the same
|
|
9
|
+
`catladder.ts`, and **both can be enabled at once**. That is what makes
|
|
10
|
+
this migration safe: run the new backend in parallel with the old one on
|
|
11
|
+
the same commits, compare, and only then cut over.
|
|
12
|
+
|
|
13
|
+
```ts
|
|
14
|
+
pipelines: {
|
|
15
|
+
gitlab: true,
|
|
16
|
+
github: { gitRemote: "github" },
|
|
17
|
+
}
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
(`pipelineType: "gitlab"` is the deprecated single-backend form —
|
|
21
|
+
replace it with `pipelines` as the first step.)
|
|
22
|
+
|
|
23
|
+
Never do this as one big-bang commit. Work through the steps below and
|
|
24
|
+
**stop at each decision point that is the user's to make** — creating
|
|
25
|
+
repositories, pushing to a new host, and moving secrets are all
|
|
26
|
+
outward-facing actions that need their explicit go-ahead.
|
|
27
|
+
|
|
28
|
+
## Step 0 — scope it with the user
|
|
29
|
+
|
|
30
|
+
Ask up front (once, with your reading of the repo attached):
|
|
31
|
+
|
|
32
|
+
- **Direction and end state**: is the old backend going away, or do both
|
|
33
|
+
stay live permanently (some projects keep GitLab for releases and
|
|
34
|
+
GitHub for PR checks)?
|
|
35
|
+
- **Repository**: does the target repo already exist, or must it be
|
|
36
|
+
created and pushed?
|
|
37
|
+
- **Which backend owns releases** during the parallel phase — see step 6.
|
|
38
|
+
- **Where secret values live afterwards** — see step 5. This is the one
|
|
39
|
+
that blocks decommissioning, so raise it early rather than at the end.
|
|
40
|
+
|
|
41
|
+
## Step 1 — check the blockers before changing anything
|
|
42
|
+
|
|
43
|
+
Read the project's `catladder.ts` and report what will *not* carry over.
|
|
44
|
+
Going **GitLab → GitHub**:
|
|
45
|
+
|
|
46
|
+
- **`deploy: { type: "pages" }`** — not supported on the GitHub backend.
|
|
47
|
+
Those components simply generate no deploy jobs there (a warning, not
|
|
48
|
+
a generation error), so the site silently stops being published.
|
|
49
|
+
- **`agents`** (the Claude agent integration) — GitLab-only; agent jobs
|
|
50
|
+
are skipped on GitHub.
|
|
51
|
+
- **Default branch must be `main`** — the generated GitHub main-branch
|
|
52
|
+
workflow triggers on `push: branches: [main]`. A project on `master`
|
|
53
|
+
or `develop` gets a workflow that never runs.
|
|
54
|
+
- **`hooks.transformYamlBeforeWrite` / `transformFileBeforeWrite`** —
|
|
55
|
+
hooks run for the GitHub files too. Any hook that keys on
|
|
56
|
+
`.gitlab-ci.yml` or on GitLab's YAML shape needs revisiting; a hook
|
|
57
|
+
that blindly rewrites `data.include` will corrupt workflow files.
|
|
58
|
+
- **`customJobs`** do carry over (they are platform-neutral job specs),
|
|
59
|
+
but anything GitLab-shaped *inside* them — raw `rules:`, `only:`,
|
|
60
|
+
GitLab-specific job keys — does not.
|
|
61
|
+
- **`secrets.vault: "gitlab"`** (the default) requires a GitLab project.
|
|
62
|
+
See step 5.
|
|
63
|
+
|
|
64
|
+
Going **GitHub → GitLab** the feature list is easier (GitLab supports
|
|
65
|
+
everything the GitHub backend does), but still check the hooks, and note
|
|
66
|
+
that GitLab needs a real access token for releases (step 6).
|
|
67
|
+
|
|
68
|
+
Report this list to the user before touching the config. If a blocker
|
|
69
|
+
has no first-class solution, that is worth an issue on catladder —
|
|
70
|
+
https://git.panter.ch/catladder/catladder/-/issues.
|
|
71
|
+
|
|
72
|
+
## Step 2 — get the repository onto the target host
|
|
73
|
+
|
|
74
|
+
For a new GitHub repo: create it and push `main` plus **all tags**
|
|
75
|
+
(the release job derives the version from the nearest `v*` tag — a
|
|
76
|
+
tagless mirror re-releases `1.0.0`).
|
|
77
|
+
|
|
78
|
+
Add it as a **second remote** rather than replacing `origin`, so the old
|
|
79
|
+
backend keeps working during the parallel phase:
|
|
80
|
+
|
|
81
|
+
```bash
|
|
82
|
+
git remote add github git@github.com:<owner>/<repo>.git
|
|
83
|
+
git push github main --tags
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
Ask before creating the repository or pushing — this publishes the code
|
|
87
|
+
to a new host.
|
|
88
|
+
|
|
89
|
+
## Step 3 — enable both backends
|
|
90
|
+
|
|
91
|
+
```ts
|
|
92
|
+
pipelines: {
|
|
93
|
+
gitlab: true,
|
|
94
|
+
github: { gitRemote: "github" },
|
|
95
|
+
}
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
`gitRemote` tells catladder which git remote points at that CI system's
|
|
99
|
+
repository (default `origin`). Then regenerate and commit:
|
|
100
|
+
|
|
101
|
+
```bash
|
|
102
|
+
yarn catenv
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
The two file sets live side by side and never collide:
|
|
106
|
+
|
|
107
|
+
| backend | generated files |
|
|
108
|
+
|---|---|
|
|
109
|
+
| gitlab | `.gitlab-ci.yml`, `.catladder-generated/gitlab/` |
|
|
110
|
+
| github | `.github/workflows/catladder-*.yml`, `.catladder-generated/github/scripts/` |
|
|
111
|
+
|
|
112
|
+
Shared (generated once for both): `.catladder-generated/images/` and
|
|
113
|
+
`.catladder-generated/catci/`.
|
|
114
|
+
|
|
115
|
+
If the hosted runners of the new backend are smaller than the old ones,
|
|
116
|
+
tune them per backend instead of globally:
|
|
117
|
+
|
|
118
|
+
```ts
|
|
119
|
+
github: { gitRemote: "github", runnerVariables: { TURBO_CONCURRENCY: "2" } },
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
## Step 4 — mirror the secrets to the new backend
|
|
123
|
+
|
|
124
|
+
GitHub Actions cannot read the vault at runtime, so the values must be
|
|
125
|
+
pushed into GitHub:
|
|
126
|
+
|
|
127
|
+
```bash
|
|
128
|
+
gh auth login
|
|
129
|
+
yarn catladder project secrets-sync-github
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
This creates one **GitHub environment per catladder env** and writes
|
|
133
|
+
each secret/variable into the environment(s) whose generated jobs
|
|
134
|
+
actually reference it. (Environments are not cosmetic: GitHub caps a
|
|
135
|
+
repository at 100 secrets, which a multi-env project blows through; the
|
|
136
|
+
cap applies per environment.) Re-run it after every secret change for as
|
|
137
|
+
long as both backends are live.
|
|
138
|
+
|
|
139
|
+
Then verify:
|
|
140
|
+
|
|
141
|
+
```bash
|
|
142
|
+
yarn catladder project doctor
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
It compares the secret/variable *names* the generated workflows
|
|
146
|
+
reference against what is actually set, per environment, and prints the
|
|
147
|
+
command that heals each gap. Values cannot be compared — GitHub secrets
|
|
148
|
+
are write-only by design.
|
|
149
|
+
|
|
150
|
+
## Step 5 — the secrets vault is what actually blocks decommissioning
|
|
151
|
+
|
|
152
|
+
`secrets.vault` defaults to `"gitlab"`: the GitLab project variables
|
|
153
|
+
double as the **readable source of truth**, and the CI backends only
|
|
154
|
+
ever get mirrored copies. GitHub secrets can never serve as that source.
|
|
155
|
+
|
|
156
|
+
So if GitLab is going away, move the vault **before** deleting anything:
|
|
157
|
+
|
|
158
|
+
```ts
|
|
159
|
+
secrets: {
|
|
160
|
+
vault: { type: "bitwarden", collection: "catladder" },
|
|
161
|
+
}
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
A safe order:
|
|
165
|
+
|
|
166
|
+
1. `yarn catladder project secrets-pull <env>: --out /tmp/secrets.yml`
|
|
167
|
+
against the old vault (unset values show as `🚨 FILL ME`)
|
|
168
|
+
2. switch `secrets.vault` in `catladder.ts`, `yarn catenv`
|
|
169
|
+
3. `yarn catladder project secrets-push <env>: --file /tmp/secrets.yml`
|
|
170
|
+
4. `yarn catladder project doctor`, then delete the temp file
|
|
171
|
+
5. only now decommission the GitLab project
|
|
172
|
+
|
|
173
|
+
Never commit the intermediate file, and see the `catladder-secrets`
|
|
174
|
+
skill for the exact command surface.
|
|
175
|
+
|
|
176
|
+
## Step 6 — releases and tokens
|
|
177
|
+
|
|
178
|
+
- **GitLab** needs a `GL_TOKEN` project access token (named
|
|
179
|
+
`semantic-release`) to push the release commit and tag. It is created
|
|
180
|
+
and rotated by `yarn catladder project setup`.
|
|
181
|
+
- **GitHub** needs nothing extra — the release jobs use the built-in
|
|
182
|
+
`github.token`. Tags pushed by that token do not retrigger workflows,
|
|
183
|
+
so the release job dispatches the tagged-release workflow explicitly;
|
|
184
|
+
that is built in and needs no configuration.
|
|
185
|
+
- **Only one backend should own releases at a time.** Both backends
|
|
186
|
+
generate release jobs, and with `releases: { when: "auto" }` both would
|
|
187
|
+
try to tag the same commit. During the parallel phase, agree with the
|
|
188
|
+
user which backend releases; the other one's release job must stay
|
|
189
|
+
unclicked (`when: "manual"`) or the parallel phase must be short.
|
|
190
|
+
|
|
191
|
+
See the `catladder-releases` skill for how the release jobs differ
|
|
192
|
+
(GitLab manual job vs. GitHub dispatch workflow).
|
|
193
|
+
|
|
194
|
+
## Step 7 — registry and the first run
|
|
195
|
+
|
|
196
|
+
The container registry changes with the backend: `$CI_REGISTRY_IMAGE` on
|
|
197
|
+
GitLab, `ghcr.io/<owner>/<repo>` on GitHub. Consequences to state
|
|
198
|
+
explicitly:
|
|
199
|
+
|
|
200
|
+
- The first pipeline on the new backend **rebuilds every job image and
|
|
201
|
+
every app image** — it will be slow, and that is expected, not a bug.
|
|
202
|
+
- The deploy target must be able to **pull from the new registry**.
|
|
203
|
+
catladder does not manage image-pull credentials on the cluster or on
|
|
204
|
+
Cloud Run, so check GHCR package visibility / the pull secret with
|
|
205
|
+
whoever owns the target *before* pointing a real environment at it.
|
|
206
|
+
|
|
207
|
+
## Step 8 — run both, compare, then cut over
|
|
208
|
+
|
|
209
|
+
Let both pipelines run on the same commits until the new backend is
|
|
210
|
+
green for all three triggers: a merge/pull request, the main branch, and
|
|
211
|
+
a tagged release. Only then flip:
|
|
212
|
+
|
|
213
|
+
```ts
|
|
214
|
+
pipelines: { github: true }
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
```bash
|
|
218
|
+
yarn catenv
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
⚠️ **catladder only cleans up backends that are still enabled** — the
|
|
222
|
+
disabled backend's files stay behind and must be deleted by hand:
|
|
223
|
+
|
|
224
|
+
- gitlab: `.gitlab-ci.yml` and `.catladder-generated/gitlab/`
|
|
225
|
+
- github: `.github/workflows/catladder-*.yml` and
|
|
226
|
+
`.catladder-generated/github/scripts/`
|
|
227
|
+
|
|
228
|
+
Then, if the new host becomes `origin`, update the remotes and drop the
|
|
229
|
+
now-wrong `gitRemote`, regenerate once more, and finish with
|
|
230
|
+
`yarn catladder project doctor`.
|
|
231
|
+
|
|
232
|
+
## After the cut-over
|
|
233
|
+
|
|
234
|
+
- Tell the team where the pipeline lives now and how manual actions
|
|
235
|
+
work there (on GitHub, manual jobs are dispatch workflows in the
|
|
236
|
+
Actions sidebar — `🚀 catladder create release`, `▶️ catladder deploy`,
|
|
237
|
+
`⏹️ catladder stop`, `↩️ catladder rollback`).
|
|
238
|
+
- Revoke tokens that are no longer needed (`GL_TOKEN`).
|
|
239
|
+
- Keep the old host readable until the vault migration is verified in
|
|
240
|
+
practice — a rotated secret is much cheaper to recover than a deleted
|
|
241
|
+
one.
|
|
242
|
+
|
|
243
|
+
## Related skills
|
|
244
|
+
|
|
245
|
+
- `catladder-config` — `catladder.ts` structure, `pipelines`, regeneration
|
|
246
|
+
- `catladder-secrets` — vault, `secrets-sync-github`, secret commands
|
|
247
|
+
- `catladder-releases` — release jobs and how they differ per backend
|
|
248
|
+
- `catladder-pipelines` — triggers, generated file layout, debugging CI
|
|
249
|
+
- `catladder-cli` — `project doctor`, `project setup`, non-interactive usage
|
|
@@ -0,0 +1,233 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: catladder-migrate-release-method
|
|
3
|
+
description: Migrating a catladder project from one release method to another — semantic-release (conventional commits) ↔ changesets (`.changeset/*.md` files) — including backfilling changesets for everything merged since the last release tag so the changelog and the version bump stay correct. Use when switching `releases.method`, adopting changesets in a project that has been releasing with semantic-release, or going back. Triggers on "switch to changesets", "migrate to changesets", "adopt changesets", "use changesets instead", "back to semantic-release", "change the release method", "backfill changesets".
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Migrating the release method
|
|
7
|
+
|
|
8
|
+
catladder projects release either with **semantic-release** (version
|
|
9
|
+
derived from conventional commit messages) or with **changesets**
|
|
10
|
+
(version derived from `.changeset/*.md` files that developers write
|
|
11
|
+
deliberately). The method is one field in `catladder.ts`:
|
|
12
|
+
|
|
13
|
+
```ts
|
|
14
|
+
releases: {
|
|
15
|
+
when: "manual",
|
|
16
|
+
method: "changesets", // "semantic-release" (default) | "changesets"
|
|
17
|
+
}
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
Flipping that field is **not** the whole migration. Read
|
|
21
|
+
`catladder-releases` first if you are unsure how either method works.
|
|
22
|
+
|
|
23
|
+
## Why a config flip is not enough
|
|
24
|
+
|
|
25
|
+
The two methods disagree about where "what changed since the last
|
|
26
|
+
release" lives:
|
|
27
|
+
|
|
28
|
+
| | semantic-release | changesets |
|
|
29
|
+
|---|---|---|
|
|
30
|
+
| source of the bump | commit messages since the last `v*` tag | pending `.changeset/*.md` |
|
|
31
|
+
| changelog text | generated from those commits | the changeset summaries |
|
|
32
|
+
|
|
33
|
+
Everything up to and including the **last `v*` tag** is already written
|
|
34
|
+
into `CHANGELOG.md` — that part is settled and must not be touched.
|
|
35
|
+
But everything merged **after** that tag is exactly what semantic-release
|
|
36
|
+
would have released next, and after the switch the release job can no
|
|
37
|
+
longer see it. Without a backfill:
|
|
38
|
+
|
|
39
|
+
- those changes silently disappear from the changelog, and
|
|
40
|
+
- the next version is likely too small (a `feat:` merged since the tag
|
|
41
|
+
releases as a patch, or nothing releases at all).
|
|
42
|
+
|
|
43
|
+
So the migration is: **backfill changesets for the commits since the
|
|
44
|
+
last release tag, then flip the config, in one merge request.**
|
|
45
|
+
|
|
46
|
+
## Step 1 — establish the range
|
|
47
|
+
|
|
48
|
+
Work on a branch (never directly on the main branch).
|
|
49
|
+
|
|
50
|
+
```bash
|
|
51
|
+
git fetch --tags
|
|
52
|
+
git describe --tags --abbrev=0 --match "v[0-9]*"
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
That is the same tag the release job uses. Then list the candidates:
|
|
56
|
+
|
|
57
|
+
```bash
|
|
58
|
+
git log <tag>..HEAD --no-merges --pretty=format:"%h %s"
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
Cases:
|
|
62
|
+
|
|
63
|
+
- **No `v*` tag at all** — the project has never released. There is
|
|
64
|
+
nothing to backfill; the first changesets release will be `1.0.0`
|
|
65
|
+
regardless of the bump levels. Skip to step 5.
|
|
66
|
+
- **The tag is the current HEAD** — nothing merged since the release,
|
|
67
|
+
nothing to backfill. Skip to step 5.
|
|
68
|
+
|
|
69
|
+
## Step 2 — drop what is not user-facing
|
|
70
|
+
|
|
71
|
+
From that list, remove:
|
|
72
|
+
|
|
73
|
+
- `chore(release): X.Y.Z` commits (semantic-release's own commits)
|
|
74
|
+
- pure `chore:` / `ci:` / `build:` / `test:` / internal-refactor commits
|
|
75
|
+
- documentation-only changes, unless the docs *are* the product
|
|
76
|
+
|
|
77
|
+
Keep everything a user of this app would notice. When a commit message
|
|
78
|
+
is too vague to judge, look at the diff (`git show <sha> --stat`) before
|
|
79
|
+
deciding — do not guess from the subject line alone.
|
|
80
|
+
|
|
81
|
+
## Step 3 — group the remaining commits
|
|
82
|
+
|
|
83
|
+
A changeset is **one user-facing change**, not one commit. Several
|
|
84
|
+
commits that built the same feature belong in one changeset; one commit
|
|
85
|
+
that shipped two unrelated things belongs in two.
|
|
86
|
+
|
|
87
|
+
**Decide yourself** when the remaining set is small (roughly ≤ 5
|
|
88
|
+
commits) and the grouping is obvious — then show the user the resulting
|
|
89
|
+
changesets and let them correct.
|
|
90
|
+
|
|
91
|
+
**Ask the user first** when there are many commits, or when the grouping
|
|
92
|
+
is genuinely ambiguous. Ask once, with a concrete proposal attached
|
|
93
|
+
rather than an open question — for example:
|
|
94
|
+
|
|
95
|
+
- the proposed grouping as a list ("these 4 commits → one changeset
|
|
96
|
+
*Export endpoint*"), asking whether to merge or split any of them
|
|
97
|
+
- the commits you could not classify: user-facing or internal?
|
|
98
|
+
- any change they consider **breaking** — that alone decides whether the
|
|
99
|
+
next release is a major
|
|
100
|
+
|
|
101
|
+
Do not ask about things you can answer from the diff.
|
|
102
|
+
|
|
103
|
+
## Step 4 — pick the bump level per changeset
|
|
104
|
+
|
|
105
|
+
- **major** — breaking change (`feat!:`/`fix!:`, or a `BREAKING CHANGE:`
|
|
106
|
+
footer, or anything that forces consumers to adapt)
|
|
107
|
+
- **minor** — a new feature (`feat:`)
|
|
108
|
+
- **patch** — fixes, performance, reverts, and anything else user-facing
|
|
109
|
+
|
|
110
|
+
Only the **highest** pending bump decides the next version, but write
|
|
111
|
+
the honest level on each changeset anyway: the changelog groups by it.
|
|
112
|
+
Note that `0.x` versions get no special treatment — a minor bump on
|
|
113
|
+
`v0.3.1` produces `0.4.0`.
|
|
114
|
+
|
|
115
|
+
## Step 5 — write the changeset files
|
|
116
|
+
|
|
117
|
+
One file per changeset, `.changeset/<kebab-case-slug>.md`:
|
|
118
|
+
|
|
119
|
+
```md
|
|
120
|
+
---
|
|
121
|
+
"<appName>": minor
|
|
122
|
+
---
|
|
123
|
+
|
|
124
|
+
One or two sentences describing the change from the user's perspective.
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
- The package name in the frontmatter is **ignored** (catladder versions
|
|
128
|
+
from git tags, not from package.json) — use the project's `appName`
|
|
129
|
+
for readability.
|
|
130
|
+
- Write the summary for someone reading the changelog, not the commit
|
|
131
|
+
log: what changed and why it matters, not `refactor foo helper`.
|
|
132
|
+
- Reference the MR/PR or issue when it helps the reader.
|
|
133
|
+
|
|
134
|
+
Also add `.changeset/README.md` so the next developer knows what the
|
|
135
|
+
folder is:
|
|
136
|
+
|
|
137
|
+
````md
|
|
138
|
+
# Changesets
|
|
139
|
+
|
|
140
|
+
This repo releases with catladder's `changesets` release method: every
|
|
141
|
+
user-facing change merges together with a changeset file in this
|
|
142
|
+
directory. The release job consumes all pending changesets — the highest
|
|
143
|
+
bump (applied to the last `v*` tag) becomes the new version, the
|
|
144
|
+
summaries become the changelog entries.
|
|
145
|
+
|
|
146
|
+
Write one by hand (`.changeset/<descriptive-name>.md`):
|
|
147
|
+
|
|
148
|
+
```md
|
|
149
|
+
---
|
|
150
|
+
"<appName>": minor
|
|
151
|
+
---
|
|
152
|
+
|
|
153
|
+
One or two sentences describing the change from the user's perspective.
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
Bump levels: `major` (breaking), `minor` (feature), `patch` (fix).
|
|
157
|
+
The package name in the frontmatter is ignored — only the bump counts.
|
|
158
|
+
`yarn changeset` also works if you prefer the interactive prompt.
|
|
159
|
+
|
|
160
|
+
No pending changesets on the main branch means the release job is a
|
|
161
|
+
no-op.
|
|
162
|
+
````
|
|
163
|
+
|
|
164
|
+
## Step 6 — flip the config and regenerate
|
|
165
|
+
|
|
166
|
+
```ts
|
|
167
|
+
releases: {
|
|
168
|
+
when: "manual", // unchanged — `when` is independent of `method`
|
|
169
|
+
method: "changesets",
|
|
170
|
+
}
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
```bash
|
|
174
|
+
yarn catenv
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
Commit the regenerated files (`.catladder-generated/`, `.gitlab-ci.yml`,
|
|
178
|
+
`.github/workflows/catladder-*`) together with the config change and the
|
|
179
|
+
changesets — see the `catladder-config` skill.
|
|
180
|
+
|
|
181
|
+
## Step 7 — verify before merging
|
|
182
|
+
|
|
183
|
+
- State the expected outcome explicitly: *last tag `vX.Y.Z` + highest
|
|
184
|
+
pending bump = next release `vA.B.C`*. If that is not what the user
|
|
185
|
+
expects, the bump levels are wrong.
|
|
186
|
+
- The merge request now runs the **`🦋 changeset check`** job. It should
|
|
187
|
+
report the pending changesets and that same version. A yellow check
|
|
188
|
+
only means "this MR adds no changeset" — for the migration MR itself
|
|
189
|
+
that is not the case, so expect it green.
|
|
190
|
+
- Do **not** hand-write `CHANGELOG.md`. The release job prepends the new
|
|
191
|
+
section; anything you write by hand there gets duplicated.
|
|
192
|
+
- Leave any `.releaserc` / `release.config.js` alone if the project has
|
|
193
|
+
one — it becomes inert, and deleting it is a separate cleanup.
|
|
194
|
+
|
|
195
|
+
## Step 8 — hand over to the team
|
|
196
|
+
|
|
197
|
+
After merging, tell the user what changes for everyone:
|
|
198
|
+
|
|
199
|
+
- every user-facing MR now needs a `.changeset/*.md` file; the
|
|
200
|
+
`🦋 changeset check` job warns (without blocking) when one is missing
|
|
201
|
+
- commit messages no longer decide the version — conventional commits
|
|
202
|
+
are still nice, but purely cosmetic now
|
|
203
|
+
- if something was merged without a changeset and must ship anyway, the
|
|
204
|
+
**force release** escape hatch releases a patch (GitLab:
|
|
205
|
+
`⚠️ force create release`, GitHub: the *force* checkbox of the
|
|
206
|
+
`🚀 catladder create release` workflow)
|
|
207
|
+
|
|
208
|
+
## The other direction: changesets → semantic-release
|
|
209
|
+
|
|
210
|
+
Same shape, mirrored, with one loss to flag up front:
|
|
211
|
+
|
|
212
|
+
1. **Pending changesets are not converted.** Their summaries would
|
|
213
|
+
simply vanish from the changelog. Either cut one last changesets
|
|
214
|
+
release first (draining `.changeset/`), or fold each summary into the
|
|
215
|
+
changelog by hand before removing the files. Tell the user which one
|
|
216
|
+
you are doing.
|
|
217
|
+
2. Commits since the last tag must carry conventional messages for
|
|
218
|
+
semantic-release to see them — history cannot be rewritten after the
|
|
219
|
+
fact, so accept that the first semantic-release version may
|
|
220
|
+
under-report. Say so rather than silently releasing a patch.
|
|
221
|
+
3. Set `method: "semantic-release"` (or drop the field — it is the
|
|
222
|
+
default), run `yarn catenv`, commit the regenerated files.
|
|
223
|
+
4. Delete `.changeset/` including its `README.md`.
|
|
224
|
+
5. From then on the version comes from commit messages again, and the
|
|
225
|
+
`🦋 changeset check` job disappears from MR pipelines.
|
|
226
|
+
|
|
227
|
+
## Related skills
|
|
228
|
+
|
|
229
|
+
- `catladder-releases` — how both methods work, the release jobs and the
|
|
230
|
+
security-audit gate
|
|
231
|
+
- `catladder-config` — catladder.ts structure and regeneration
|
|
232
|
+
- `catladder-migrate-ci-backend` — migrating between GitLab CI and
|
|
233
|
+
GitHub Actions
|
|
@@ -93,3 +93,5 @@ Useful CLI commands (see the `catladder-cli` skill for invocation):
|
|
|
93
93
|
- `catladder-releases` — the `taggedRelease` flow and security-audit gate
|
|
94
94
|
- `catladder-secrets` — env vars and secret management
|
|
95
95
|
- `catladder-cli` — non-interactive CLI usage and command reference
|
|
96
|
+
- `catladder-migrate-ci-backend` — moving between GitLab CI and GitHub
|
|
97
|
+
Actions (running both in parallel, then cutting over)
|
|
@@ -127,3 +127,6 @@ the `security` commands in the `catladder-cli` reference.
|
|
|
127
127
|
- `catladder-config` — catladder.ts structure and regeneration
|
|
128
128
|
- `catladder-pipelines` — the `taggedRelease` trigger and stage/prod deploys
|
|
129
129
|
- `catladder-cli` — `project ci` and `security` commands
|
|
130
|
+
- `catladder-migrate-release-method` — switching an existing project
|
|
131
|
+
between semantic-release and changesets (incl. backfilling changesets
|
|
132
|
+
for everything merged since the last release tag)
|
|
@@ -107,3 +107,5 @@ suspecting anything else.
|
|
|
107
107
|
- `catladder-deploys` — runtime deploy config
|
|
108
108
|
- `catladder-pipelines` — how the generated pipelines work
|
|
109
109
|
- `catladder-cli` — non-interactive CLI usage and command reference
|
|
110
|
+
- `catladder-migrate-ci-backend` — moving between CI backends (the vault
|
|
111
|
+
is what blocks decommissioning the old one)
|
|
@@ -117,3 +117,7 @@ escape hatches to shrink over time, not accumulate.
|
|
|
117
117
|
- `catladder-releases` — releasing (semantic-release / changesets)
|
|
118
118
|
- `catladder-pipelines` — how the generated pipelines work, debugging CI
|
|
119
119
|
- `catladder-cli` — non-interactive CLI usage and command reference
|
|
120
|
+
- `catladder-migrate-ci-backend` — moving a project between GitLab CI
|
|
121
|
+
and GitHub Actions
|
|
122
|
+
- `catladder-migrate-release-method` — switching between
|
|
123
|
+
semantic-release and changesets
|
|
@@ -0,0 +1,249 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: catladder-migrate-ci-backend
|
|
3
|
+
description: Guiding a catladder project from one CI backend to the other — GitLab CI → GitHub Actions or GitHub Actions → GitLab CI — using the `pipelines` config to run both in parallel first, then cutting over. Covers the blockers to check up front (unsupported features, secrets vault, release tokens, container registry) and the manual cleanup a cut-over needs. Use when moving a project between GitLab and GitHub, enabling a second CI backend, or decommissioning one. Triggers on "migrate to github", "move to github actions", "switch from gitlab to github", "migrate to gitlab", "enable github pipeline", "run both pipelines", "gitRemote", "secrets-sync-github".
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Migrating between GitLab CI and GitHub Actions
|
|
7
|
+
|
|
8
|
+
catladder generates pipelines for both CI systems from the same
|
|
9
|
+
`catladder.ts`, and **both can be enabled at once**. That is what makes
|
|
10
|
+
this migration safe: run the new backend in parallel with the old one on
|
|
11
|
+
the same commits, compare, and only then cut over.
|
|
12
|
+
|
|
13
|
+
```ts
|
|
14
|
+
pipelines: {
|
|
15
|
+
gitlab: true,
|
|
16
|
+
github: { gitRemote: "github" },
|
|
17
|
+
}
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
(`pipelineType: "gitlab"` is the deprecated single-backend form —
|
|
21
|
+
replace it with `pipelines` as the first step.)
|
|
22
|
+
|
|
23
|
+
Never do this as one big-bang commit. Work through the steps below and
|
|
24
|
+
**stop at each decision point that is the user's to make** — creating
|
|
25
|
+
repositories, pushing to a new host, and moving secrets are all
|
|
26
|
+
outward-facing actions that need their explicit go-ahead.
|
|
27
|
+
|
|
28
|
+
## Step 0 — scope it with the user
|
|
29
|
+
|
|
30
|
+
Ask up front (once, with your reading of the repo attached):
|
|
31
|
+
|
|
32
|
+
- **Direction and end state**: is the old backend going away, or do both
|
|
33
|
+
stay live permanently (some projects keep GitLab for releases and
|
|
34
|
+
GitHub for PR checks)?
|
|
35
|
+
- **Repository**: does the target repo already exist, or must it be
|
|
36
|
+
created and pushed?
|
|
37
|
+
- **Which backend owns releases** during the parallel phase — see step 6.
|
|
38
|
+
- **Where secret values live afterwards** — see step 5. This is the one
|
|
39
|
+
that blocks decommissioning, so raise it early rather than at the end.
|
|
40
|
+
|
|
41
|
+
## Step 1 — check the blockers before changing anything
|
|
42
|
+
|
|
43
|
+
Read the project's `catladder.ts` and report what will *not* carry over.
|
|
44
|
+
Going **GitLab → GitHub**:
|
|
45
|
+
|
|
46
|
+
- **`deploy: { type: "pages" }`** — not supported on the GitHub backend.
|
|
47
|
+
Those components simply generate no deploy jobs there (a warning, not
|
|
48
|
+
a generation error), so the site silently stops being published.
|
|
49
|
+
- **`agents`** (the Claude agent integration) — GitLab-only; agent jobs
|
|
50
|
+
are skipped on GitHub.
|
|
51
|
+
- **Default branch must be `main`** — the generated GitHub main-branch
|
|
52
|
+
workflow triggers on `push: branches: [main]`. A project on `master`
|
|
53
|
+
or `develop` gets a workflow that never runs.
|
|
54
|
+
- **`hooks.transformYamlBeforeWrite` / `transformFileBeforeWrite`** —
|
|
55
|
+
hooks run for the GitHub files too. Any hook that keys on
|
|
56
|
+
`.gitlab-ci.yml` or on GitLab's YAML shape needs revisiting; a hook
|
|
57
|
+
that blindly rewrites `data.include` will corrupt workflow files.
|
|
58
|
+
- **`customJobs`** do carry over (they are platform-neutral job specs),
|
|
59
|
+
but anything GitLab-shaped *inside* them — raw `rules:`, `only:`,
|
|
60
|
+
GitLab-specific job keys — does not.
|
|
61
|
+
- **`secrets.vault: "gitlab"`** (the default) requires a GitLab project.
|
|
62
|
+
See step 5.
|
|
63
|
+
|
|
64
|
+
Going **GitHub → GitLab** the feature list is easier (GitLab supports
|
|
65
|
+
everything the GitHub backend does), but still check the hooks, and note
|
|
66
|
+
that GitLab needs a real access token for releases (step 6).
|
|
67
|
+
|
|
68
|
+
Report this list to the user before touching the config. If a blocker
|
|
69
|
+
has no first-class solution, that is worth an issue on catladder —
|
|
70
|
+
https://git.panter.ch/catladder/catladder/-/issues.
|
|
71
|
+
|
|
72
|
+
## Step 2 — get the repository onto the target host
|
|
73
|
+
|
|
74
|
+
For a new GitHub repo: create it and push `main` plus **all tags**
|
|
75
|
+
(the release job derives the version from the nearest `v*` tag — a
|
|
76
|
+
tagless mirror re-releases `1.0.0`).
|
|
77
|
+
|
|
78
|
+
Add it as a **second remote** rather than replacing `origin`, so the old
|
|
79
|
+
backend keeps working during the parallel phase:
|
|
80
|
+
|
|
81
|
+
```bash
|
|
82
|
+
git remote add github git@github.com:<owner>/<repo>.git
|
|
83
|
+
git push github main --tags
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
Ask before creating the repository or pushing — this publishes the code
|
|
87
|
+
to a new host.
|
|
88
|
+
|
|
89
|
+
## Step 3 — enable both backends
|
|
90
|
+
|
|
91
|
+
```ts
|
|
92
|
+
pipelines: {
|
|
93
|
+
gitlab: true,
|
|
94
|
+
github: { gitRemote: "github" },
|
|
95
|
+
}
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
`gitRemote` tells catladder which git remote points at that CI system's
|
|
99
|
+
repository (default `origin`). Then regenerate and commit:
|
|
100
|
+
|
|
101
|
+
```bash
|
|
102
|
+
yarn catenv
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
The two file sets live side by side and never collide:
|
|
106
|
+
|
|
107
|
+
| backend | generated files |
|
|
108
|
+
|---|---|
|
|
109
|
+
| gitlab | `.gitlab-ci.yml`, `.catladder-generated/gitlab/` |
|
|
110
|
+
| github | `.github/workflows/catladder-*.yml`, `.catladder-generated/github/scripts/` |
|
|
111
|
+
|
|
112
|
+
Shared (generated once for both): `.catladder-generated/images/` and
|
|
113
|
+
`.catladder-generated/catci/`.
|
|
114
|
+
|
|
115
|
+
If the hosted runners of the new backend are smaller than the old ones,
|
|
116
|
+
tune them per backend instead of globally:
|
|
117
|
+
|
|
118
|
+
```ts
|
|
119
|
+
github: { gitRemote: "github", runnerVariables: { TURBO_CONCURRENCY: "2" } },
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
## Step 4 — mirror the secrets to the new backend
|
|
123
|
+
|
|
124
|
+
GitHub Actions cannot read the vault at runtime, so the values must be
|
|
125
|
+
pushed into GitHub:
|
|
126
|
+
|
|
127
|
+
```bash
|
|
128
|
+
gh auth login
|
|
129
|
+
yarn catladder project secrets-sync-github
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
This creates one **GitHub environment per catladder env** and writes
|
|
133
|
+
each secret/variable into the environment(s) whose generated jobs
|
|
134
|
+
actually reference it. (Environments are not cosmetic: GitHub caps a
|
|
135
|
+
repository at 100 secrets, which a multi-env project blows through; the
|
|
136
|
+
cap applies per environment.) Re-run it after every secret change for as
|
|
137
|
+
long as both backends are live.
|
|
138
|
+
|
|
139
|
+
Then verify:
|
|
140
|
+
|
|
141
|
+
```bash
|
|
142
|
+
yarn catladder project doctor
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
It compares the secret/variable *names* the generated workflows
|
|
146
|
+
reference against what is actually set, per environment, and prints the
|
|
147
|
+
command that heals each gap. Values cannot be compared — GitHub secrets
|
|
148
|
+
are write-only by design.
|
|
149
|
+
|
|
150
|
+
## Step 5 — the secrets vault is what actually blocks decommissioning
|
|
151
|
+
|
|
152
|
+
`secrets.vault` defaults to `"gitlab"`: the GitLab project variables
|
|
153
|
+
double as the **readable source of truth**, and the CI backends only
|
|
154
|
+
ever get mirrored copies. GitHub secrets can never serve as that source.
|
|
155
|
+
|
|
156
|
+
So if GitLab is going away, move the vault **before** deleting anything:
|
|
157
|
+
|
|
158
|
+
```ts
|
|
159
|
+
secrets: {
|
|
160
|
+
vault: { type: "bitwarden", collection: "catladder" },
|
|
161
|
+
}
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
A safe order:
|
|
165
|
+
|
|
166
|
+
1. `yarn catladder project secrets-pull <env>: --out /tmp/secrets.yml`
|
|
167
|
+
against the old vault (unset values show as `🚨 FILL ME`)
|
|
168
|
+
2. switch `secrets.vault` in `catladder.ts`, `yarn catenv`
|
|
169
|
+
3. `yarn catladder project secrets-push <env>: --file /tmp/secrets.yml`
|
|
170
|
+
4. `yarn catladder project doctor`, then delete the temp file
|
|
171
|
+
5. only now decommission the GitLab project
|
|
172
|
+
|
|
173
|
+
Never commit the intermediate file, and see the `catladder-secrets`
|
|
174
|
+
skill for the exact command surface.
|
|
175
|
+
|
|
176
|
+
## Step 6 — releases and tokens
|
|
177
|
+
|
|
178
|
+
- **GitLab** needs a `GL_TOKEN` project access token (named
|
|
179
|
+
`semantic-release`) to push the release commit and tag. It is created
|
|
180
|
+
and rotated by `yarn catladder project setup`.
|
|
181
|
+
- **GitHub** needs nothing extra — the release jobs use the built-in
|
|
182
|
+
`github.token`. Tags pushed by that token do not retrigger workflows,
|
|
183
|
+
so the release job dispatches the tagged-release workflow explicitly;
|
|
184
|
+
that is built in and needs no configuration.
|
|
185
|
+
- **Only one backend should own releases at a time.** Both backends
|
|
186
|
+
generate release jobs, and with `releases: { when: "auto" }` both would
|
|
187
|
+
try to tag the same commit. During the parallel phase, agree with the
|
|
188
|
+
user which backend releases; the other one's release job must stay
|
|
189
|
+
unclicked (`when: "manual"`) or the parallel phase must be short.
|
|
190
|
+
|
|
191
|
+
See the `catladder-releases` skill for how the release jobs differ
|
|
192
|
+
(GitLab manual job vs. GitHub dispatch workflow).
|
|
193
|
+
|
|
194
|
+
## Step 7 — registry and the first run
|
|
195
|
+
|
|
196
|
+
The container registry changes with the backend: `$CI_REGISTRY_IMAGE` on
|
|
197
|
+
GitLab, `ghcr.io/<owner>/<repo>` on GitHub. Consequences to state
|
|
198
|
+
explicitly:
|
|
199
|
+
|
|
200
|
+
- The first pipeline on the new backend **rebuilds every job image and
|
|
201
|
+
every app image** — it will be slow, and that is expected, not a bug.
|
|
202
|
+
- The deploy target must be able to **pull from the new registry**.
|
|
203
|
+
catladder does not manage image-pull credentials on the cluster or on
|
|
204
|
+
Cloud Run, so check GHCR package visibility / the pull secret with
|
|
205
|
+
whoever owns the target *before* pointing a real environment at it.
|
|
206
|
+
|
|
207
|
+
## Step 8 — run both, compare, then cut over
|
|
208
|
+
|
|
209
|
+
Let both pipelines run on the same commits until the new backend is
|
|
210
|
+
green for all three triggers: a merge/pull request, the main branch, and
|
|
211
|
+
a tagged release. Only then flip:
|
|
212
|
+
|
|
213
|
+
```ts
|
|
214
|
+
pipelines: { github: true }
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
```bash
|
|
218
|
+
yarn catenv
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
⚠️ **catladder only cleans up backends that are still enabled** — the
|
|
222
|
+
disabled backend's files stay behind and must be deleted by hand:
|
|
223
|
+
|
|
224
|
+
- gitlab: `.gitlab-ci.yml` and `.catladder-generated/gitlab/`
|
|
225
|
+
- github: `.github/workflows/catladder-*.yml` and
|
|
226
|
+
`.catladder-generated/github/scripts/`
|
|
227
|
+
|
|
228
|
+
Then, if the new host becomes `origin`, update the remotes and drop the
|
|
229
|
+
now-wrong `gitRemote`, regenerate once more, and finish with
|
|
230
|
+
`yarn catladder project doctor`.
|
|
231
|
+
|
|
232
|
+
## After the cut-over
|
|
233
|
+
|
|
234
|
+
- Tell the team where the pipeline lives now and how manual actions
|
|
235
|
+
work there (on GitHub, manual jobs are dispatch workflows in the
|
|
236
|
+
Actions sidebar — `🚀 catladder create release`, `▶️ catladder deploy`,
|
|
237
|
+
`⏹️ catladder stop`, `↩️ catladder rollback`).
|
|
238
|
+
- Revoke tokens that are no longer needed (`GL_TOKEN`).
|
|
239
|
+
- Keep the old host readable until the vault migration is verified in
|
|
240
|
+
practice — a rotated secret is much cheaper to recover than a deleted
|
|
241
|
+
one.
|
|
242
|
+
|
|
243
|
+
## Related skills
|
|
244
|
+
|
|
245
|
+
- `catladder-config` — `catladder.ts` structure, `pipelines`, regeneration
|
|
246
|
+
- `catladder-secrets` — vault, `secrets-sync-github`, secret commands
|
|
247
|
+
- `catladder-releases` — release jobs and how they differ per backend
|
|
248
|
+
- `catladder-pipelines` — triggers, generated file layout, debugging CI
|
|
249
|
+
- `catladder-cli` — `project doctor`, `project setup`, non-interactive usage
|
|
@@ -0,0 +1,233 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: catladder-migrate-release-method
|
|
3
|
+
description: Migrating a catladder project from one release method to another — semantic-release (conventional commits) ↔ changesets (`.changeset/*.md` files) — including backfilling changesets for everything merged since the last release tag so the changelog and the version bump stay correct. Use when switching `releases.method`, adopting changesets in a project that has been releasing with semantic-release, or going back. Triggers on "switch to changesets", "migrate to changesets", "adopt changesets", "use changesets instead", "back to semantic-release", "change the release method", "backfill changesets".
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Migrating the release method
|
|
7
|
+
|
|
8
|
+
catladder projects release either with **semantic-release** (version
|
|
9
|
+
derived from conventional commit messages) or with **changesets**
|
|
10
|
+
(version derived from `.changeset/*.md` files that developers write
|
|
11
|
+
deliberately). The method is one field in `catladder.ts`:
|
|
12
|
+
|
|
13
|
+
```ts
|
|
14
|
+
releases: {
|
|
15
|
+
when: "manual",
|
|
16
|
+
method: "changesets", // "semantic-release" (default) | "changesets"
|
|
17
|
+
}
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
Flipping that field is **not** the whole migration. Read
|
|
21
|
+
`catladder-releases` first if you are unsure how either method works.
|
|
22
|
+
|
|
23
|
+
## Why a config flip is not enough
|
|
24
|
+
|
|
25
|
+
The two methods disagree about where "what changed since the last
|
|
26
|
+
release" lives:
|
|
27
|
+
|
|
28
|
+
| | semantic-release | changesets |
|
|
29
|
+
|---|---|---|
|
|
30
|
+
| source of the bump | commit messages since the last `v*` tag | pending `.changeset/*.md` |
|
|
31
|
+
| changelog text | generated from those commits | the changeset summaries |
|
|
32
|
+
|
|
33
|
+
Everything up to and including the **last `v*` tag** is already written
|
|
34
|
+
into `CHANGELOG.md` — that part is settled and must not be touched.
|
|
35
|
+
But everything merged **after** that tag is exactly what semantic-release
|
|
36
|
+
would have released next, and after the switch the release job can no
|
|
37
|
+
longer see it. Without a backfill:
|
|
38
|
+
|
|
39
|
+
- those changes silently disappear from the changelog, and
|
|
40
|
+
- the next version is likely too small (a `feat:` merged since the tag
|
|
41
|
+
releases as a patch, or nothing releases at all).
|
|
42
|
+
|
|
43
|
+
So the migration is: **backfill changesets for the commits since the
|
|
44
|
+
last release tag, then flip the config, in one merge request.**
|
|
45
|
+
|
|
46
|
+
## Step 1 — establish the range
|
|
47
|
+
|
|
48
|
+
Work on a branch (never directly on the main branch).
|
|
49
|
+
|
|
50
|
+
```bash
|
|
51
|
+
git fetch --tags
|
|
52
|
+
git describe --tags --abbrev=0 --match "v[0-9]*"
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
That is the same tag the release job uses. Then list the candidates:
|
|
56
|
+
|
|
57
|
+
```bash
|
|
58
|
+
git log <tag>..HEAD --no-merges --pretty=format:"%h %s"
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
Cases:
|
|
62
|
+
|
|
63
|
+
- **No `v*` tag at all** — the project has never released. There is
|
|
64
|
+
nothing to backfill; the first changesets release will be `1.0.0`
|
|
65
|
+
regardless of the bump levels. Skip to step 5.
|
|
66
|
+
- **The tag is the current HEAD** — nothing merged since the release,
|
|
67
|
+
nothing to backfill. Skip to step 5.
|
|
68
|
+
|
|
69
|
+
## Step 2 — drop what is not user-facing
|
|
70
|
+
|
|
71
|
+
From that list, remove:
|
|
72
|
+
|
|
73
|
+
- `chore(release): X.Y.Z` commits (semantic-release's own commits)
|
|
74
|
+
- pure `chore:` / `ci:` / `build:` / `test:` / internal-refactor commits
|
|
75
|
+
- documentation-only changes, unless the docs *are* the product
|
|
76
|
+
|
|
77
|
+
Keep everything a user of this app would notice. When a commit message
|
|
78
|
+
is too vague to judge, look at the diff (`git show <sha> --stat`) before
|
|
79
|
+
deciding — do not guess from the subject line alone.
|
|
80
|
+
|
|
81
|
+
## Step 3 — group the remaining commits
|
|
82
|
+
|
|
83
|
+
A changeset is **one user-facing change**, not one commit. Several
|
|
84
|
+
commits that built the same feature belong in one changeset; one commit
|
|
85
|
+
that shipped two unrelated things belongs in two.
|
|
86
|
+
|
|
87
|
+
**Decide yourself** when the remaining set is small (roughly ≤ 5
|
|
88
|
+
commits) and the grouping is obvious — then show the user the resulting
|
|
89
|
+
changesets and let them correct.
|
|
90
|
+
|
|
91
|
+
**Ask the user first** when there are many commits, or when the grouping
|
|
92
|
+
is genuinely ambiguous. Ask once, with a concrete proposal attached
|
|
93
|
+
rather than an open question — for example:
|
|
94
|
+
|
|
95
|
+
- the proposed grouping as a list ("these 4 commits → one changeset
|
|
96
|
+
*Export endpoint*"), asking whether to merge or split any of them
|
|
97
|
+
- the commits you could not classify: user-facing or internal?
|
|
98
|
+
- any change they consider **breaking** — that alone decides whether the
|
|
99
|
+
next release is a major
|
|
100
|
+
|
|
101
|
+
Do not ask about things you can answer from the diff.
|
|
102
|
+
|
|
103
|
+
## Step 4 — pick the bump level per changeset
|
|
104
|
+
|
|
105
|
+
- **major** — breaking change (`feat!:`/`fix!:`, or a `BREAKING CHANGE:`
|
|
106
|
+
footer, or anything that forces consumers to adapt)
|
|
107
|
+
- **minor** — a new feature (`feat:`)
|
|
108
|
+
- **patch** — fixes, performance, reverts, and anything else user-facing
|
|
109
|
+
|
|
110
|
+
Only the **highest** pending bump decides the next version, but write
|
|
111
|
+
the honest level on each changeset anyway: the changelog groups by it.
|
|
112
|
+
Note that `0.x` versions get no special treatment — a minor bump on
|
|
113
|
+
`v0.3.1` produces `0.4.0`.
|
|
114
|
+
|
|
115
|
+
## Step 5 — write the changeset files
|
|
116
|
+
|
|
117
|
+
One file per changeset, `.changeset/<kebab-case-slug>.md`:
|
|
118
|
+
|
|
119
|
+
```md
|
|
120
|
+
---
|
|
121
|
+
"<appName>": minor
|
|
122
|
+
---
|
|
123
|
+
|
|
124
|
+
One or two sentences describing the change from the user's perspective.
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
- The package name in the frontmatter is **ignored** (catladder versions
|
|
128
|
+
from git tags, not from package.json) — use the project's `appName`
|
|
129
|
+
for readability.
|
|
130
|
+
- Write the summary for someone reading the changelog, not the commit
|
|
131
|
+
log: what changed and why it matters, not `refactor foo helper`.
|
|
132
|
+
- Reference the MR/PR or issue when it helps the reader.
|
|
133
|
+
|
|
134
|
+
Also add `.changeset/README.md` so the next developer knows what the
|
|
135
|
+
folder is:
|
|
136
|
+
|
|
137
|
+
````md
|
|
138
|
+
# Changesets
|
|
139
|
+
|
|
140
|
+
This repo releases with catladder's `changesets` release method: every
|
|
141
|
+
user-facing change merges together with a changeset file in this
|
|
142
|
+
directory. The release job consumes all pending changesets — the highest
|
|
143
|
+
bump (applied to the last `v*` tag) becomes the new version, the
|
|
144
|
+
summaries become the changelog entries.
|
|
145
|
+
|
|
146
|
+
Write one by hand (`.changeset/<descriptive-name>.md`):
|
|
147
|
+
|
|
148
|
+
```md
|
|
149
|
+
---
|
|
150
|
+
"<appName>": minor
|
|
151
|
+
---
|
|
152
|
+
|
|
153
|
+
One or two sentences describing the change from the user's perspective.
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
Bump levels: `major` (breaking), `minor` (feature), `patch` (fix).
|
|
157
|
+
The package name in the frontmatter is ignored — only the bump counts.
|
|
158
|
+
`yarn changeset` also works if you prefer the interactive prompt.
|
|
159
|
+
|
|
160
|
+
No pending changesets on the main branch means the release job is a
|
|
161
|
+
no-op.
|
|
162
|
+
````
|
|
163
|
+
|
|
164
|
+
## Step 6 — flip the config and regenerate
|
|
165
|
+
|
|
166
|
+
```ts
|
|
167
|
+
releases: {
|
|
168
|
+
when: "manual", // unchanged — `when` is independent of `method`
|
|
169
|
+
method: "changesets",
|
|
170
|
+
}
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
```bash
|
|
174
|
+
yarn catenv
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
Commit the regenerated files (`.catladder-generated/`, `.gitlab-ci.yml`,
|
|
178
|
+
`.github/workflows/catladder-*`) together with the config change and the
|
|
179
|
+
changesets — see the `catladder-config` skill.
|
|
180
|
+
|
|
181
|
+
## Step 7 — verify before merging
|
|
182
|
+
|
|
183
|
+
- State the expected outcome explicitly: *last tag `vX.Y.Z` + highest
|
|
184
|
+
pending bump = next release `vA.B.C`*. If that is not what the user
|
|
185
|
+
expects, the bump levels are wrong.
|
|
186
|
+
- The merge request now runs the **`🦋 changeset check`** job. It should
|
|
187
|
+
report the pending changesets and that same version. A yellow check
|
|
188
|
+
only means "this MR adds no changeset" — for the migration MR itself
|
|
189
|
+
that is not the case, so expect it green.
|
|
190
|
+
- Do **not** hand-write `CHANGELOG.md`. The release job prepends the new
|
|
191
|
+
section; anything you write by hand there gets duplicated.
|
|
192
|
+
- Leave any `.releaserc` / `release.config.js` alone if the project has
|
|
193
|
+
one — it becomes inert, and deleting it is a separate cleanup.
|
|
194
|
+
|
|
195
|
+
## Step 8 — hand over to the team
|
|
196
|
+
|
|
197
|
+
After merging, tell the user what changes for everyone:
|
|
198
|
+
|
|
199
|
+
- every user-facing MR now needs a `.changeset/*.md` file; the
|
|
200
|
+
`🦋 changeset check` job warns (without blocking) when one is missing
|
|
201
|
+
- commit messages no longer decide the version — conventional commits
|
|
202
|
+
are still nice, but purely cosmetic now
|
|
203
|
+
- if something was merged without a changeset and must ship anyway, the
|
|
204
|
+
**force release** escape hatch releases a patch (GitLab:
|
|
205
|
+
`⚠️ force create release`, GitHub: the *force* checkbox of the
|
|
206
|
+
`🚀 catladder create release` workflow)
|
|
207
|
+
|
|
208
|
+
## The other direction: changesets → semantic-release
|
|
209
|
+
|
|
210
|
+
Same shape, mirrored, with one loss to flag up front:
|
|
211
|
+
|
|
212
|
+
1. **Pending changesets are not converted.** Their summaries would
|
|
213
|
+
simply vanish from the changelog. Either cut one last changesets
|
|
214
|
+
release first (draining `.changeset/`), or fold each summary into the
|
|
215
|
+
changelog by hand before removing the files. Tell the user which one
|
|
216
|
+
you are doing.
|
|
217
|
+
2. Commits since the last tag must carry conventional messages for
|
|
218
|
+
semantic-release to see them — history cannot be rewritten after the
|
|
219
|
+
fact, so accept that the first semantic-release version may
|
|
220
|
+
under-report. Say so rather than silently releasing a patch.
|
|
221
|
+
3. Set `method: "semantic-release"` (or drop the field — it is the
|
|
222
|
+
default), run `yarn catenv`, commit the regenerated files.
|
|
223
|
+
4. Delete `.changeset/` including its `README.md`.
|
|
224
|
+
5. From then on the version comes from commit messages again, and the
|
|
225
|
+
`🦋 changeset check` job disappears from MR pipelines.
|
|
226
|
+
|
|
227
|
+
## Related skills
|
|
228
|
+
|
|
229
|
+
- `catladder-releases` — how both methods work, the release jobs and the
|
|
230
|
+
security-audit gate
|
|
231
|
+
- `catladder-config` — catladder.ts structure and regeneration
|
|
232
|
+
- `catladder-migrate-ci-backend` — migrating between GitLab CI and
|
|
233
|
+
GitHub Actions
|
|
@@ -93,3 +93,5 @@ Useful CLI commands (see the `catladder-cli` skill for invocation):
|
|
|
93
93
|
- `catladder-releases` — the `taggedRelease` flow and security-audit gate
|
|
94
94
|
- `catladder-secrets` — env vars and secret management
|
|
95
95
|
- `catladder-cli` — non-interactive CLI usage and command reference
|
|
96
|
+
- `catladder-migrate-ci-backend` — moving between GitLab CI and GitHub
|
|
97
|
+
Actions (running both in parallel, then cutting over)
|
|
@@ -127,3 +127,6 @@ the `security` commands in the `catladder-cli` reference.
|
|
|
127
127
|
- `catladder-config` — catladder.ts structure and regeneration
|
|
128
128
|
- `catladder-pipelines` — the `taggedRelease` trigger and stage/prod deploys
|
|
129
129
|
- `catladder-cli` — `project ci` and `security` commands
|
|
130
|
+
- `catladder-migrate-release-method` — switching an existing project
|
|
131
|
+
between semantic-release and changesets (incl. backfilling changesets
|
|
132
|
+
for everything merged since the last release tag)
|
|
@@ -107,3 +107,5 @@ suspecting anything else.
|
|
|
107
107
|
- `catladder-deploys` — runtime deploy config
|
|
108
108
|
- `catladder-pipelines` — how the generated pipelines work
|
|
109
109
|
- `catladder-cli` — non-interactive CLI usage and command reference
|
|
110
|
+
- `catladder-migrate-ci-backend` — moving between CI backends (the vault
|
|
111
|
+
is what blocks decommissioning the old one)
|