Skip to content

Commit 4103da3

Browse files
xsahil03xclaude
andauthored
chore(repo): add release-pr skill (#2657)
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent 6b6eb93 commit 4103da3

1 file changed

Lines changed: 218 additions & 0 deletions

File tree

.claude/skills/release-pr/SKILL.md

Lines changed: 218 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,218 @@
1+
---
2+
name: release-pr
3+
description: >
4+
Open a release PR: bumps versions in melos.yaml and pubspecs, finalises CHANGELOGs, opens a PR with auto-generated
5+
release notes against master (stable) or v10.0.0 (beta).
6+
disable-model-invocation: true
7+
argument-hint: "[version]"
8+
arguments: [version]
9+
allowed-tools:
10+
- Bash(git *)
11+
- Bash(gh *)
12+
- Bash(melos *)
13+
- Bash(which *)
14+
- Bash(grep *)
15+
- Read
16+
- Edit
17+
- Write
18+
---
19+
20+
# release-pr
21+
22+
Opens a release PR for stream-chat-flutter. Branch `release/v<X.Y.Z>` → base `master` (stable) or `v10.0.0` (beta) →
23+
title `chore(repo): release v<X.Y.Z>`.
24+
25+
**This skill only opens the PR.** After merge, tagging and pub.dev publishing happen via `release_tag.yml` +
26+
`release_publish.yml` (stable: automatic; beta/named: maintainer pushes the tag manually).
27+
28+
If `$version` is provided (e.g. `/release-pr 9.24.0`), use it. Strip any leading `v`. Otherwise ask the user.
29+
30+
## Release types
31+
32+
| Type | Base | Version shape | Tagging |
33+
|---|---|---|---|
34+
| **Stable** | `master` | `X.Y.Z` | Auto via `release_tag.yml` on merge |
35+
| **Beta** | `v10.0.0` | `X.Y.Z-beta.N` | Manual (`release_tag.yml` only watches master) |
36+
| **Named pre-release** | feature branch | `X.Y.Z-<name>.N` | Manual; same as beta |
37+
38+
`distribute_internal.yml` builds sample apps for branches like `feat/design-refresh`**not** a release. Don't use
39+
this skill for those.
40+
41+
## Inputs
42+
43+
1. **Version** (`X.Y.Z` or `X.Y.Z-suffix`). Use `$version` if supplied, otherwise ask. Don't infer.
44+
2. **Base branch** — auto-derive from `$version`:
45+
- No suffix (`X.Y.Z`) → `master`. Stable release.
46+
- `-beta.N` suffix → `v10.0.0`. Verify it exists: `git ls-remote --heads origin v10.0.0`.
47+
- Any other suffix (`-alpha.N`, `-rc.N`, `-design-refresh.N`, …) → ask the user which feature branch to target.
48+
3. **Previous tag** for the release-notes diff. Run `gh release list --limit 10`; pick the most recent tag of the same
49+
train (stable = no hyphen in tag; beta = matches `-beta.`; named = matches the same suffix prefix).
50+
51+
## Pre-flight
52+
53+
Run these checks. **If any fail, stop the skill, surface the failing check to the user, and do not try to auto-fix**
54+
(no stashing uncommitted work, no force-pulling, no killing processes).
55+
56+
- `git status --short -uno` clean after `git checkout <base>` + `git pull --ff-only`.
57+
- `which melos` succeeds.
58+
- `gh auth status` succeeds.
59+
- `gh pr list --head release/v<version> --state all --json number` returns `[]`.
60+
- Latest CI on the base-branch tip is green: `gh run list --branch <base> --limit 5` — no failures on the most
61+
recent runs.
62+
63+
## Steps
64+
65+
### 1. Branch off the chosen base
66+
67+
Pre-flight already left you on `<base>` with latest. Just create the release branch:
68+
69+
```bash
70+
git checkout -b release/v<version>
71+
```
72+
73+
### 2. Bump versions
74+
75+
Edit two sets of files by hand, then let `melos bs` propagate the rest.
76+
77+
**Edit:**
78+
79+
- `melos.yaml` — in the `command.bootstrap.environment.dependencies` block, bump all five `stream_chat*: ^<version>`
80+
entries. Locate with `grep -n "^ stream_chat" melos.yaml`.
81+
- Each `packages/*/pubspec.yaml` (5 files) — set `version: <version>`. Do **not** touch
82+
`packages/*/example/pubspec.yaml` or `sample_app/pubspec.yaml` `version:` fields; their deps are synced by
83+
`melos bs`.
84+
85+
**Then run:**
86+
87+
```bash
88+
melos bs
89+
```
90+
91+
`melos bs` does the rest:
92+
93+
- Propagates the `melos.yaml` deps block into every workspace pubspec, including each package's intra-monorepo dep
94+
constraints, every `packages/*/example/pubspec.yaml`, and `sample_app/pubspec.yaml`.
95+
- Fires the `command.bootstrap.hooks.post: melos run version:update` hook, which runs `tools/generate_version.dart`
96+
and regenerates `packages/stream_chat/lib/version.dart` from the new pubspec version.
97+
98+
Do **not** run `./tools/version.sh`. It calls `melos version` which leaves `melos.yaml`'s deps block stale — the next
99+
`melos bs` would re-write every pubspec dep constraint back to the old version.
100+
101+
Verify the diff shape matches the previous release PR. Find its number with:
102+
103+
```bash
104+
gh pr list --search "chore(repo): release in:title" --state merged --limit 5 --json number,title
105+
```
106+
107+
Then compare:
108+
109+
```bash
110+
git diff --stat
111+
gh pr diff <prev-release-pr-number> --name-only # for comparison
112+
```
113+
114+
### 3. Finalise the CHANGELOGs
115+
116+
Five files: `packages/{stream_chat, stream_chat_flutter, stream_chat_flutter_core, stream_chat_localizations,
117+
stream_chat_persistence}/CHANGELOG.md`.
118+
119+
For each, **apply the first matching rule below** — it's a decision tree, not a sequence:
120+
121+
1. **Top section is `## Upcoming Changes` or `## Upcoming`** → rename to `## <version>`. Keep bullets.
122+
2. **User-facing changes since `v<prev>`** — new APIs, bug fixes users would notice, deprecations. Check with
123+
`git log v<prev>..HEAD --oneline -- packages/<pkg>`. Add a `## <version>` header with bullets in the existing
124+
emoji-prefixed sections only (`✅ Added`, `🚀 Performance`, `🐞 Fixed`, `🔄 Changed`). Don't invent new
125+
section names.
126+
3. **Only `stream_chat` dep bump** (no in-package changes, but depends on `stream_chat`) → add the dep-bump line:
127+
```
128+
## <version>
129+
130+
- Updated `stream_chat` dependency to [`<version>`](https://pub.dev/packages/stream_chat/changelog).
131+
```
132+
4. **Anything else** (internal-only changes, test fixes, refactors, or truly nothing) → add `## <version>` +
133+
`- Minor bug fixes and improvements`.
134+
135+
**Every package gets a `## <version>` header**, even if it's only a dep-bump line. Empty version sections and
136+
missing headers both fail pana.
137+
138+
### 4. Sanity-check
139+
140+
```bash
141+
melos run analyze
142+
melos run lint:pub
143+
```
144+
145+
If either fails, surface to the user and stop.
146+
147+
### 5. Commit and push
148+
149+
```bash
150+
git add -A
151+
git commit -m "chore(repo): release v<version>"
152+
git push -u origin release/v<version>
153+
```
154+
155+
Single commit. The message format is load-bearing: `release_tag.yml` parses `vX.Y.Z` from it after merge.
156+
157+
### 6. Generate the PR body
158+
159+
The body **must be exactly what GitHub's release UI produces when you click "Generate release notes"** — no template
160+
wrapper, no extra description, no CLA checkboxes. The "New Contributors" block GitHub auto-appends stays in; that's
161+
part of the convention.
162+
163+
```bash
164+
gh api repos/GetStream/stream-chat-flutter/releases/generate-notes \
165+
-f tag_name=v<version> \
166+
-f previous_tag_name=v<previous> \
167+
-f target_commitish=<base> \
168+
--jq .body > /tmp/release-notes.md
169+
```
170+
171+
- `tag_name`: the tag we'll create (need not exist yet).
172+
- `previous_tag_name`: most recent tag of the same train (stable or beta).
173+
- `target_commitish`: the **base branch** (`master` or `v10.0.0`), not the release branch — the notes should cover
174+
every commit between `previous_tag_name` and where the tag will land after merge.
175+
176+
Read the file once to skim. If a PR title looks wrong, fix it on the originating PR upstream and re-run the API
177+
call; don't hand-edit `/tmp/release-notes.md`.
178+
179+
### 7. Open the PR
180+
181+
```bash
182+
gh pr create \
183+
--base <base> \
184+
--head release/v<version> \
185+
--title "chore(repo): release v<version>" \
186+
--body-file /tmp/release-notes.md
187+
```
188+
189+
Return the PR URL.
190+
191+
**If this is a beta or named pre-release**, also include this reminder in your message to the user:
192+
193+
> After this PR merges, manually create and push the tag — `release_tag.yml` only fires on `master`:
194+
> ```bash
195+
> git checkout <base>
196+
> git pull --ff-only
197+
> git tag v<version>
198+
> git push origin v<version>
199+
> ```
200+
201+
For stable releases (base `master`), no reminder needed — `release_tag.yml` handles it on merge.
202+
203+
## After merge (FYI)
204+
205+
Stable (base `master`): `release_tag.yml` extracts `vX.Y.Z` from the commit, creates and pushes the tag.
206+
`release_publish.yml` runs `melos run release:pub` and creates the GitHub release.
207+
208+
Beta / named pre-release: maintainer runs the tag commands surfaced at the end of step 7.
209+
210+
## Don't
211+
212+
- **Never create a GitHub release** (`gh release create`, `POST /repos/.../releases`). Step 6 uses
213+
`generate-notes`, which is read-only. The release itself is created by `release_publish.yml` after the tag is
214+
pushed.
215+
- **Never push a tag.** Stable is automatic on merge; beta/named is the maintainer's manual step.
216+
- **Never run `melos run release:pub`.** That's the publish step, triggered by the workflow on tag push. Even if the
217+
user asks, refuse — running it locally publishes from an unreviewed working tree.
218+
- **Never merge the PR.** Return the URL and stop.

0 commit comments

Comments
 (0)