Skip to content

Commit 012e073

Browse files
authored
Merge pull request RooCodeInc#1641 from cline/ocasta181/ENG-182
ENG-182 | Add Changesets
2 parents 01b1c8c + 6937823 commit 012e073

File tree

8 files changed

+407
-6
lines changed

8 files changed

+407
-6
lines changed

.changeset/twelve-deers-search.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"claude-dev": minor
3+
---
4+
5+
Adding changesets for automating version bumping and release notes

.github/pull_request_template.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121

2222
- [ ] Changes are limited to a single feature, bugfix or chore (split larger changes into separate PRs)
2323
- [ ] Tests are passing (`npm test`) and code is formatted and linted (`npm run format && npm run lint`)
24+
- [ ] I have created a changeset using `npm run changeset` (required for user-facing changes)
2425
- [ ] I have reviewed [contributor guidelines](https://github.com/cline/cline/blob/main/CONTRIBUTING.md)
2526

2627
### Screenshots
Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
"""
2+
This script updates a specific version's release notes section in CHANGELOG.md with new content
3+
or reformats existing content.
4+
5+
The script:
6+
1. Takes a version number, changelog path, and optionally new content as input from environment variables
7+
2. Finds the section in the changelog for the specified version
8+
3. Either:
9+
a) Replaces the content with new content if provided, or
10+
b) Reformats existing content by:
11+
- Removing the first two lines of the changeset format
12+
- Ensuring version numbers are wrapped in square brackets
13+
4. Writes the updated changelog back to the file
14+
15+
Environment Variables:
16+
CHANGELOG_PATH: Path to the changelog file (defaults to 'CHANGELOG.md')
17+
VERSION: The version number to update/format
18+
PREV_VERSION: The previous version number (used to locate section boundaries)
19+
NEW_CONTENT: Optional new content to insert for this version
20+
"""
21+
22+
#!/usr/bin/env python3
23+
24+
import os
25+
import sys
26+
27+
CHANGELOG_PATH = os.environ.get("CHANGELOG_PATH", "CHANGELOG.md")
28+
VERSION = os.environ['VERSION']
29+
PREV_VERSION = os.environ.get("PREV_VERSION", "")
30+
NEW_CONTENT = os.environ.get("NEW_CONTENT", "")
31+
32+
def overwrite_changelog_section(changelog_text: str, new_content: str):
33+
# Find the section for the specified version
34+
version_pattern = f"## {VERSION}\n"
35+
bracketed_version_pattern = f"## [{VERSION}]\n"
36+
prev_version_pattern = f"## [{PREV_VERSION}]\n"
37+
print(f"latest version: {VERSION}")
38+
print(f"prev_version: {PREV_VERSION}")
39+
40+
# Try both unbracketed and bracketed version patterns
41+
version_index = changelog_text.find(version_pattern)
42+
if version_index == -1:
43+
version_index = changelog_text.find(bracketed_version_pattern)
44+
if version_index == -1:
45+
# If version not found, add it at the top (after the first line)
46+
first_newline = changelog_text.find('\n')
47+
if first_newline == -1:
48+
# If no newline found, just prepend
49+
return f"## [{VERSION}]\n\n{changelog_text}"
50+
return f"{changelog_text[:first_newline + 1]}## [{VERSION}]\n\n{changelog_text[first_newline + 1:]}"
51+
else:
52+
# Using bracketed version
53+
version_pattern = bracketed_version_pattern
54+
55+
notes_start_index = version_index + len(version_pattern)
56+
notes_end_index = changelog_text.find(prev_version_pattern, notes_start_index) if PREV_VERSION and prev_version_pattern in changelog_text else len(changelog_text)
57+
58+
if new_content:
59+
return changelog_text[:notes_start_index] + f"{new_content}\n" + changelog_text[notes_end_index:]
60+
else:
61+
changeset_lines = changelog_text[notes_start_index:notes_end_index].split("\n")
62+
# Ensure we have at least 2 lines before removing them
63+
if len(changeset_lines) < 2:
64+
print("Warning: Changeset content has fewer than 2 lines")
65+
parsed_lines = "\n".join(changeset_lines)
66+
else:
67+
# Remove the first two lines from the regular changeset format, ex: \n### Patch Changes
68+
parsed_lines = "\n".join(changeset_lines[2:])
69+
updated_changelog = changelog_text[:notes_start_index] + parsed_lines + changelog_text[notes_end_index:]
70+
# Ensure version number is bracketed
71+
updated_changelog = updated_changelog.replace(f"## {VERSION}", f"## [{VERSION}]")
72+
return updated_changelog
73+
74+
try:
75+
print(f"Reading changelog from: {CHANGELOG_PATH}")
76+
with open(CHANGELOG_PATH, 'r') as f:
77+
changelog_content = f.read()
78+
79+
print(f"Changelog content length: {len(changelog_content)} characters")
80+
print("First 200 characters of changelog:")
81+
print(changelog_content[:200])
82+
print("----------------------------------------------------------------------------------")
83+
84+
new_changelog = overwrite_changelog_section(changelog_content, NEW_CONTENT)
85+
86+
print("New changelog content:")
87+
print("----------------------------------------------------------------------------------")
88+
print(new_changelog)
89+
print("----------------------------------------------------------------------------------")
90+
91+
print(f"Writing updated changelog back to: {CHANGELOG_PATH}")
92+
with open(CHANGELOG_PATH, 'w') as f:
93+
f.write(new_changelog)
94+
95+
print(f"{CHANGELOG_PATH} updated successfully!")
96+
97+
except FileNotFoundError:
98+
print(f"Error: Changelog file not found at {CHANGELOG_PATH}")
99+
sys.exit(1)
100+
except Exception as e:
101+
print(f"Error updating changelog: {str(e)}")
102+
print(f"Current working directory: {os.getcwd()}")
103+
sys.exit(1)
Lines changed: 162 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,162 @@
1+
name: Changeset Release
2+
run-name: Changeset Release ${{ github.actor != 'cline-bot' && '- Create PR' || '- Update Changelog' }}
3+
4+
permissions:
5+
contents: write
6+
pull-requests: write
7+
8+
on:
9+
workflow_dispatch:
10+
pull_request:
11+
types: [closed, opened, labeled]
12+
13+
env:
14+
REPO_PATH: ${{ github.repository }}
15+
GIT_REF: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.sha || 'main' }}
16+
17+
jobs:
18+
# Job 1: Create version bump PR when changesets are merged to main
19+
changeset-pr-version-bump:
20+
if: >
21+
( github.event_name == 'pull_request' &&
22+
github.event.pull_request.merged == true &&
23+
github.event.pull_request.base.ref == 'main' &&
24+
github.actor != 'cline-bot' ) ||
25+
github.event_name == 'workflow_dispatch'
26+
runs-on: ubuntu-latest
27+
permissions:
28+
contents: write
29+
pull-requests: write
30+
steps:
31+
- name: Git Checkout
32+
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4
33+
with:
34+
fetch-depth: 0
35+
ref: ${{ env.GIT_REF }}
36+
37+
- name: Setup Node.js
38+
uses: actions/setup-node@b39b52d1213e96004bfcb1c61a8a6fa8ab84f3e8 # v4
39+
with:
40+
node-version: 20
41+
cache: "npm"
42+
43+
- name: Install Dependencies
44+
run: npm run install:all
45+
46+
# Check if there are any new changesets to process
47+
- name: Check for changesets
48+
id: check-changesets
49+
run: |
50+
NEW_CHANGESETS=$(find .changeset -name "*.md" ! -name "README.md" | wc -l | tr -d ' ')
51+
echo "Changesets diff with previous version: $NEW_CHANGESETS"
52+
echo "new_changesets=$NEW_CHANGESETS" >> $GITHUB_OUTPUT
53+
54+
# Create version bump PR using changesets/action if there are new changesets
55+
- name: Changeset Pull Request
56+
if: steps.check-changesets.outputs.new_changesets != '0'
57+
id: changesets
58+
uses: changesets/action@e9cc34b540dd3ad1b030c57fd97269e8f6ad905a # v1
59+
with:
60+
commit: "changeset version bump"
61+
title: "Changeset version bump"
62+
version: npm run version-packages # This performs the changeset version bump
63+
env:
64+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
65+
66+
# Job 2: Process version bump PR created by cline-bot
67+
changeset-pr-edit-approve:
68+
name: Auto approve and merge Bump version PRs
69+
runs-on: ubuntu-latest
70+
permissions:
71+
contents: write
72+
pull-requests: write
73+
if: >
74+
github.event_name == 'pull_request' &&
75+
github.event.pull_request.base.ref == 'main' &&
76+
github.actor == 'cline-bot' &&
77+
contains(github.event.pull_request.title, 'Changeset version bump')
78+
steps:
79+
- name: Determine checkout ref
80+
id: checkout-ref
81+
run: |
82+
echo "Event action: ${{ github.event.action }}"
83+
echo "Actor: ${{ github.actor }}"
84+
echo "Head ref: ${{ github.head_ref }}"
85+
echo "PR SHA: ${{ github.event.pull_request.head.sha }}"
86+
87+
if [[ "${{ github.event.action }}" == "opened" && "${{ github.actor }}" == "cline-bot" ]]; then
88+
echo "Using branch ref: ${{ github.head_ref }}"
89+
echo "git_ref=${{ github.head_ref }}" >> $GITHUB_OUTPUT
90+
else
91+
echo "Using SHA ref: ${{ github.event.pull_request.head.sha }}"
92+
echo "git_ref=${{ github.event.pull_request.head.sha }}" >> $GITHUB_OUTPUT
93+
fi
94+
95+
- name: Checkout Repo
96+
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4
97+
with:
98+
token: ${{ secrets.GITHUB_TOKEN }}
99+
fetch-depth: 0
100+
ref: ${{ steps.checkout-ref.outputs.git_ref }}
101+
102+
# Get current and previous versions to edit changelog entry
103+
- name: Get version
104+
id: get_version
105+
run: |
106+
VERSION=$(git show HEAD:package.json | jq -r '.version')
107+
echo "version=$VERSION" >> $GITHUB_OUTPUT
108+
PREV_VERSION=$(git show origin/main:package.json | jq -r '.version')
109+
echo "prev_version=$PREV_VERSION" >> $GITHUB_OUTPUT
110+
echo "version=$VERSION"
111+
echo "prev_version=$PREV_VERSION"
112+
113+
# Update CHANGELOG.md with proper format
114+
- name: Update Changelog Format
115+
if: ${{ !contains(github.event.pull_request.labels.*.name, 'changelog-ready') }}
116+
env:
117+
VERSION: ${{ steps.get_version.outputs.version }}
118+
PREV_VERSION: ${{ steps.get_version.outputs.prev_version }}
119+
run: python .github/scripts/overwrite_changeset_changelog.py
120+
121+
# Commit and push changelog updates
122+
- name: Push Changelog updates
123+
if: ${{ !contains(github.event.pull_request.labels.*.name, 'changelog-ready') }}
124+
run: |
125+
git config user.name "cline-bot"
126+
git config user.email [email protected]
127+
echo "Running git add and commit..."
128+
git add CHANGELOG.md
129+
git commit -m "Updating CHANGELOG.md format"
130+
git status
131+
echo "--------------------------------------------------------------------------------"
132+
echo "Pushing to remote..."
133+
echo "--------------------------------------------------------------------------------"
134+
git push
135+
136+
# Add label to indicate changelog has been formatted
137+
- name: Add changelog-ready label
138+
if: ${{ !contains(github.event.pull_request.labels.*.name, 'changelog-ready') }}
139+
uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7
140+
with:
141+
github-token: ${{ secrets.GITHUB_TOKEN }}
142+
script: |
143+
await github.rest.issues.addLabels({
144+
owner: context.repo.owner,
145+
repo: context.repo.repo,
146+
issue_number: context.issue.number,
147+
labels: ['changelog-ready']
148+
});
149+
150+
# Auto-approve PR only after it has been labeled
151+
- name: Auto approve PR
152+
if: contains(github.event.pull_request.labels.*.name, 'changelog-ready')
153+
uses: hmarr/auto-approve-action@de8bf34d0402c38aa2c8346973342b2cb02c4435 # v4
154+
with:
155+
review-message: "I'm approving since it's a bump version PR"
156+
157+
# Auto-merge PR
158+
- name: Automerge on PR
159+
if: false # Needs enablePullRequestAutoMerge in repo settings to work contains(github.event.pull_request.labels.*.name, 'changelog-ready')
160+
run: gh pr merge --auto --merge ${{ github.event.pull_request.number }}
161+
env:
162+
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}

0 commit comments

Comments
 (0)