|
| 1 | +--- |
| 2 | +name: generate-changelog |
| 3 | +description: Generate changelog entries from merged PRs since the last release. Fetches PR data, classifies changes as normal or breaking, presents for user review, and creates a PR with the updates. |
| 4 | +disable-model-invocation: true |
| 5 | +--- |
| 6 | + |
| 7 | +# GLSP Changelog Generator |
| 8 | + |
| 9 | +Generate changelog entries by analyzing merged PRs since the last release. |
| 10 | + |
| 11 | +Execute these phases in order: |
| 12 | +1. **Data Gathering** — find last release, fetch PRs, classify, generate entries |
| 13 | +2. **Changelog Update** — create worktree, write entries into CHANGELOG.md |
| 14 | +3. **Review & Approval** — present the actual diff to the user for review |
| 15 | +4. **PR Creation** — push and create PR (only after user approves) |
| 16 | + |
| 17 | +--- |
| 18 | + |
| 19 | +## Phase 1: Data Gathering |
| 20 | + |
| 21 | +### Step 1.1: Determine repository info |
| 22 | + |
| 23 | +Use the current working directory as the repository. Derive: |
| 24 | +- `REPO_DIR`: the repository root (find via `git rev-parse --show-toplevel`) |
| 25 | +- `REPO_NAME`: the directory name (e.g. `glsp-client`) |
| 26 | +- `GH_REPO`: the GitHub identifier `eclipse-glsp/REPO_NAME` |
| 27 | + |
| 28 | +### Step 1.2: Find the latest release tag |
| 29 | + |
| 30 | +Run: |
| 31 | +```bash |
| 32 | +git tag --sort=-creatordate | grep -E '^v[0-9]+\.[0-9]+\.[0-9]+$' | head -1 |
| 33 | +``` |
| 34 | + |
| 35 | +Record as `LAST_TAG`. Get the tag's date: |
| 36 | +```bash |
| 37 | +git log -1 --format=%aI $LAST_TAG |
| 38 | +``` |
| 39 | + |
| 40 | +Record as `TAG_DATE`. |
| 41 | + |
| 42 | +### Step 1.3: Extract category tags from existing CHANGELOG.md |
| 43 | + |
| 44 | +Read `CHANGELOG.md` and extract all unique `[tag]` patterns from changelog entries (pattern: `- [tag] ...`). Build a deduplicated vocabulary list preserving original casing. |
| 45 | + |
| 46 | +### Step 1.4: Fetch merged PRs since the last release |
| 47 | + |
| 48 | +```bash |
| 49 | +gh pr list --repo GH_REPO --state merged --search "merged:>TAG_DATE" --json number,title,author,body,labels,mergedAt --limit 100 |
| 50 | +``` |
| 51 | + |
| 52 | +Use the date portion only from `TAG_DATE` (e.g. `2026-02-09`). |
| 53 | + |
| 54 | +### Step 1.5: Filter PRs |
| 55 | + |
| 56 | +**Skip rules** (in order): |
| 57 | +1. Author login is `dependabot` or `dependabot[bot]` → skip |
| 58 | +2. Title matches version bump patterns → skip: |
| 59 | + - Starts with `Switch to` and contains `-next` |
| 60 | + - Title is a bare version like `vX.Y.Z` |
| 61 | + - Title starts with `Release v` |
| 62 | +3. Title contains `update changelog` (case insensitive) → skip |
| 63 | +4. Title is purely CI/metadata (readme badges, workflow config) AND body does NOT contain `[x] This PR should be mentioned in the changelog` → skip |
| 64 | + |
| 65 | +**Override**: If the PR body contains `[x] This PR should be mentioned in the changelog`, always include it regardless of rules 2-4. Dependabot PRs are always skipped. |
| 66 | + |
| 67 | +### Step 1.6: Classify each PR |
| 68 | + |
| 69 | +1. If body contains `[x] This PR introduces a breaking change` → **breaking** |
| 70 | +2. If body contains `[ ] This PR introduces a breaking change` → **normal** |
| 71 | +3. If no checkbox info, analyze title and body: |
| 72 | + - Breaking keywords: "refactor", "rename", "remove", "replace", "migrate", "breaking", "deprecate" |
| 73 | + - Clearly non-breaking (bug fix, docs, minor enhancement) → **normal** |
| 74 | + - Uncertain → flag for user review |
| 75 | + |
| 76 | +### Step 1.7: Assign category tags |
| 77 | + |
| 78 | +Pick the most fitting tag from the vocabulary (Step 1.3) based on PR title and body. If no tag fits, mark as uncertain: `[???/best-guess]`. |
| 79 | + |
| 80 | +### Step 1.8: Generate changelog entries |
| 81 | + |
| 82 | +#### Entry Format |
| 83 | + |
| 84 | +**Normal changes:** |
| 85 | +``` |
| 86 | +- [tag] Description [#N](https://github.com/eclipse-glsp/REPO_NAME/pull/N) |
| 87 | +``` |
| 88 | + |
| 89 | +**Breaking changes** with migration sub-items: |
| 90 | +``` |
| 91 | +- [tag] Description [#N](https://github.com/eclipse-glsp/REPO_NAME/pull/N) |
| 92 | + - Migration detail 1 |
| 93 | + - Migration detail 2 |
| 94 | +``` |
| 95 | + |
| 96 | +**Multiple PRs for the same change:** |
| 97 | +``` |
| 98 | +- [tag] Description [#N](url) [#M](url) |
| 99 | +``` |
| 100 | + |
| 101 | +#### Style Guide |
| 102 | + |
| 103 | +**Formatting:** |
| 104 | +- Entry prefix: `- ` (dash + exactly 3 spaces) |
| 105 | +- Sub-item indent: 4 spaces + `- ` (4 spaces from parent dash) |
| 106 | +- Sub-sub-item indent: 8 spaces + `- ` |
| 107 | +- Single space between `[tag]` and description text |
| 108 | +- Single space before PR link at end of line |
| 109 | +- PR links are mandatory, full URLs: `[#123](https://github.com/eclipse-glsp/REPO_NAME/pull/123)` |
| 110 | + |
| 111 | +**Wording:** |
| 112 | +- Always start with a **present tense imperative verb** (not past tense) |
| 113 | +- Common verbs: Fix, Improve, Add, Update, Extend, Ensure, Introduce, Remove, Refactor, Rename, Provide, Allow, Support |
| 114 | +- **Bug fixes**: "Fix a bug that caused/prevented...", "Fix X behavior" |
| 115 | +- **Features**: "Introduce...", "Add support for...", "Provide..." |
| 116 | +- **Enhancements**: "Improve...", "Extend...", "Ensure that..." |
| 117 | +- **Refactors**: "Refactor...", "Rework...", "Rename..." |
| 118 | +- Be specific — never "Fix various issues" |
| 119 | + |
| 120 | +**Capitalization:** |
| 121 | +- Tags are always lowercase: `[diagram]`, not `[Diagram]` |
| 122 | +- Description starts lowercase after the tag (unless proper noun or code element) |
| 123 | +- Section headers: Title Case (`### Potentially Breaking Changes`) |
| 124 | + |
| 125 | +**Description cleanup from PR title:** |
| 126 | +- Remove prefixes: `GLSP-1234:`, `GH-123:`, `fix:`, `feat:`, `chore:` |
| 127 | +- Rephrase bug-report style to changelog style: |
| 128 | + - BAD: "Edit label UI does not resize on graph zoom" |
| 129 | + - GOOD: "Fix edit label UI not resizing on graph zoom" |
| 130 | +- Keep concise — one line |
| 131 | + |
| 132 | +**Breaking changes:** |
| 133 | +- Describe what changed, why it's breaking, and how to migrate |
| 134 | +- Extract migration sub-items from the PR body's "What it does" section |
| 135 | + |
| 136 | +--- |
| 137 | + |
| 138 | +## Phase 2: Changelog Update |
| 139 | + |
| 140 | +### Step 2.1: Create a git worktree |
| 141 | + |
| 142 | +Determine the main branch: |
| 143 | +```bash |
| 144 | +git symbolic-ref refs/remotes/origin/HEAD | sed 's|refs/remotes/origin/||' |
| 145 | +``` |
| 146 | + |
| 147 | +Fetch latest and create worktree: |
| 148 | +```bash |
| 149 | +git fetch origin |
| 150 | +git worktree add REPO_DIR-changelog-update origin/MAIN_BRANCH |
| 151 | +``` |
| 152 | + |
| 153 | +All subsequent file operations happen in the worktree. |
| 154 | + |
| 155 | +### Step 2.2: Determine the version section |
| 156 | + |
| 157 | +Read `CHANGELOG.md` in the worktree. |
| 158 | + |
| 159 | +**Active section detection:** |
| 160 | +- An active section has the `- active` suffix (e.g. `## v2.7.0 - active`) |
| 161 | +- If an active section exists → merge new entries into it |
| 162 | +- If the topmost section is a released version (no `- active` suffix) → create a new active section above it |
| 163 | + |
| 164 | +**Creating a new section:** |
| 165 | +- Bump the minor version of `LAST_TAG` (e.g. `v2.6.0` → `v2.7.0`) |
| 166 | +- Insert after the title line, before the first `## ` heading: |
| 167 | + |
| 168 | +```markdown |
| 169 | +## v2.7.0 - active |
| 170 | + |
| 171 | +### Changes |
| 172 | + |
| 173 | +- [tag] Entry [#N](url) |
| 174 | + |
| 175 | +### Potentially Breaking Changes |
| 176 | + |
| 177 | +- [tag] Entry [#N](url) |
| 178 | + - Sub-item detail |
| 179 | +``` |
| 180 | + |
| 181 | +Only include "Potentially Breaking Changes" if there are breaking entries. |
| 182 | + |
| 183 | +**Merging into existing active section:** |
| 184 | +- Check PR numbers against existing entries to avoid duplicates |
| 185 | +- Append new entries to the appropriate subsection |
| 186 | +- Create missing subsections as needed |
| 187 | + |
| 188 | +### Step 2.3: Write the updated CHANGELOG.md |
| 189 | + |
| 190 | +Use the Edit tool. Match existing formatting conventions. |
| 191 | + |
| 192 | +--- |
| 193 | + |
| 194 | +## Phase 3: Review & Approval |
| 195 | + |
| 196 | +### Step 3.1: Show the diff |
| 197 | + |
| 198 | +```bash |
| 199 | +git -C WORKTREE_DIR diff CHANGELOG.md |
| 200 | +``` |
| 201 | + |
| 202 | +Present the diff to the user. |
| 203 | + |
| 204 | +### Step 3.2: Prompt for uncertain items |
| 205 | + |
| 206 | +List any entries flagged as uncertain (category or breaking status) and ask the user to resolve them. |
| 207 | + |
| 208 | +Even if nothing is uncertain, ask: |
| 209 | +> "Does everything look correct, or would you like to adjust any entries?" |
| 210 | +
|
| 211 | +### Step 3.3: Collect user feedback |
| 212 | + |
| 213 | +- **Approve as-is** → proceed to Phase 4 |
| 214 | +- **Request edits** → apply changes, show updated diff, ask again |
| 215 | +- **Resolve uncertain items** → apply, show updated diff |
| 216 | + |
| 217 | +**Do NOT proceed to Phase 4 until the user explicitly approves.** |
| 218 | + |
| 219 | +--- |
| 220 | + |
| 221 | +## Phase 4: PR Creation |
| 222 | + |
| 223 | +### Step 4.1: Determine the branch name |
| 224 | + |
| 225 | +Check if `changelog-update` exists on remote: |
| 226 | +```bash |
| 227 | +git -C WORKTREE_DIR ls-remote --heads origin changelog-update |
| 228 | +``` |
| 229 | + |
| 230 | +If it exists, increment: `changelog-update-2`, `changelog-update-3`, etc. |
| 231 | + |
| 232 | +### Step 4.2: Create branch, commit, and push |
| 233 | + |
| 234 | +```bash |
| 235 | +cd WORKTREE_DIR |
| 236 | +git checkout -b BRANCH_NAME |
| 237 | +git add CHANGELOG.md |
| 238 | +git commit -m "Update changelog" |
| 239 | +git push -u origin BRANCH_NAME |
| 240 | +``` |
| 241 | + |
| 242 | +### Step 4.3: Create the PR |
| 243 | + |
| 244 | +Use the repo's `.github/PULL_REQUEST_TEMPLATE.md` structure: |
| 245 | + |
| 246 | +```bash |
| 247 | +gh pr create --repo GH_REPO --title "Update changelog" --body "$(cat <<'EOF' |
| 248 | +#### What it does |
| 249 | +Updates the changelog with entries for PRs merged since the last release (LAST_TAG). |
| 250 | +
|
| 251 | +#### How to test |
| 252 | +Review the changelog entries for accuracy. |
| 253 | +
|
| 254 | +#### Follow-ups |
| 255 | +None. |
| 256 | +
|
| 257 | +#### Changelog |
| 258 | +
|
| 259 | +- [ ] This PR should be mentioned in the changelog |
| 260 | +- [ ] This PR introduces a breaking change |
| 261 | +EOF |
| 262 | +)" |
| 263 | +``` |
| 264 | + |
| 265 | +Report the PR URL. |
| 266 | + |
| 267 | +### Step 4.4: Label referenced PRs |
| 268 | + |
| 269 | +Add the `changelog` label to every PR mentioned in the new entries: |
| 270 | +```bash |
| 271 | +gh pr edit PR_NUMBER --repo GH_REPO --add-label "changelog" |
| 272 | +``` |
| 273 | + |
| 274 | +Report which PRs were labeled. |
| 275 | + |
| 276 | +### Step 4.5: Clean up the worktree |
| 277 | + |
| 278 | +```bash |
| 279 | +git -C REPO_DIR worktree remove REPO_DIR-changelog-update |
| 280 | +``` |
| 281 | + |
| 282 | +Report completion with PR URL and labeled PRs. |
0 commit comments