Skip to content

Commit 3e2d62e

Browse files
[CI] Auto-create release candidate branches for minor/major versions (#4777)
1 parent aaaa0cd commit 3e2d62e

File tree

2 files changed

+222
-0
lines changed

2 files changed

+222
-0
lines changed

.claude/commands/create-frontend-release.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -348,6 +348,14 @@ echo "Workflow triggered. Waiting for PR creation..."
348348
sleep 10
349349
gh run list --workflow=release.yaml --limit=1
350350
```
351+
4. **For Minor/Major Version Releases**: The create-release-candidate-branch workflow will automatically:
352+
- Create a `core/x.yy` branch for the PREVIOUS minor version
353+
- Apply branch protection rules
354+
- Document the feature freeze policy
355+
```bash
356+
# Monitor branch creation (for minor/major releases)
357+
gh run list --workflow=create-release-candidate-branch.yaml --limit=1
358+
```
351359
4. If workflow didn't trigger due to [skip ci]:
352360
```bash
353361
echo "ERROR: Release workflow didn't trigger!"
Lines changed: 214 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,214 @@
1+
name: Create Release Branch
2+
3+
on:
4+
pull_request:
5+
types: [closed]
6+
branches: [main]
7+
paths:
8+
- 'package.json'
9+
10+
jobs:
11+
create-release-branch:
12+
runs-on: ubuntu-latest
13+
if: >
14+
github.event.pull_request.merged == true &&
15+
contains(github.event.pull_request.labels.*.name, 'Release')
16+
permissions:
17+
contents: write
18+
19+
steps:
20+
- name: Checkout repository
21+
uses: actions/checkout@v4
22+
with:
23+
fetch-depth: 0
24+
token: ${{ secrets.PR_GH_TOKEN || secrets.GITHUB_TOKEN }}
25+
26+
- name: Setup Node.js
27+
uses: actions/setup-node@v4
28+
with:
29+
node-version: 'lts/*'
30+
31+
- name: Check version bump type
32+
id: check_version
33+
run: |
34+
# Get current version from main
35+
CURRENT_VERSION=$(node -p "require('./package.json').version")
36+
# Remove 'v' prefix if present (shouldn't be in package.json, but defensive)
37+
CURRENT_VERSION=${CURRENT_VERSION#v}
38+
echo "current_version=$CURRENT_VERSION" >> $GITHUB_OUTPUT
39+
40+
# Validate version format
41+
if ! [[ "$CURRENT_VERSION" =~ ^[0-9]+\.[0-9]+\.[0-9]+ ]]; then
42+
echo "ERROR: Invalid version format: $CURRENT_VERSION"
43+
exit 1
44+
fi
45+
46+
# Extract major and minor versions
47+
MAJOR=$(echo $CURRENT_VERSION | cut -d. -f1)
48+
MINOR=$(echo $CURRENT_VERSION | cut -d. -f2)
49+
PATCH=$(echo $CURRENT_VERSION | cut -d. -f3 | cut -d- -f1)
50+
51+
echo "major=$MAJOR" >> $GITHUB_OUTPUT
52+
echo "minor=$MINOR" >> $GITHUB_OUTPUT
53+
echo "patch=$PATCH" >> $GITHUB_OUTPUT
54+
55+
# Get previous version from the commit before the merge
56+
git checkout HEAD^1
57+
PREV_VERSION=$(node -p "require('./package.json').version" 2>/dev/null || echo "0.0.0")
58+
# Remove 'v' prefix if present
59+
PREV_VERSION=${PREV_VERSION#v}
60+
61+
# Validate previous version format
62+
if ! [[ "$PREV_VERSION" =~ ^[0-9]+\.[0-9]+\.[0-9]+ ]]; then
63+
echo "WARNING: Invalid previous version format: $PREV_VERSION, using 0.0.0"
64+
PREV_VERSION="0.0.0"
65+
fi
66+
67+
PREV_MINOR=$(echo $PREV_VERSION | cut -d. -f2)
68+
69+
echo "prev_version=$PREV_VERSION" >> $GITHUB_OUTPUT
70+
echo "prev_minor=$PREV_MINOR" >> $GITHUB_OUTPUT
71+
72+
# Get previous major version for comparison
73+
PREV_MAJOR=$(echo $PREV_VERSION | cut -d. -f1)
74+
75+
# Check if current version is a pre-release
76+
if [[ "$CURRENT_VERSION" =~ ^[0-9]+\.[0-9]+\.[0-9]+- ]]; then
77+
IS_PRERELEASE=true
78+
else
79+
IS_PRERELEASE=false
80+
fi
81+
82+
# Check if this was a minor version bump or major version bump
83+
# But skip if it's a pre-release version
84+
if [[ "$IS_PRERELEASE" == "true" ]]; then
85+
echo "is_minor_bump=false" >> $GITHUB_OUTPUT
86+
echo "reason=prerelease version" >> $GITHUB_OUTPUT
87+
elif [[ "$MAJOR" -gt "$PREV_MAJOR" && "$MINOR" == "0" && "$PATCH" == "0" ]]; then
88+
# Major version bump (e.g., 1.99.x → 2.0.0)
89+
echo "is_minor_bump=true" >> $GITHUB_OUTPUT
90+
BRANCH_NAME="core/${PREV_MAJOR}.${PREV_MINOR}"
91+
echo "branch_name=$BRANCH_NAME" >> $GITHUB_OUTPUT
92+
elif [[ "$MAJOR" == "$PREV_MAJOR" && "$MINOR" -gt "$PREV_MINOR" && "$PATCH" == "0" ]]; then
93+
# Minor version bump (e.g., 1.23.x → 1.24.0)
94+
echo "is_minor_bump=true" >> $GITHUB_OUTPUT
95+
BRANCH_NAME="core/${MAJOR}.${PREV_MINOR}"
96+
echo "branch_name=$BRANCH_NAME" >> $GITHUB_OUTPUT
97+
else
98+
echo "is_minor_bump=false" >> $GITHUB_OUTPUT
99+
fi
100+
101+
# Return to main branch
102+
git checkout main
103+
104+
- name: Create release branch
105+
if: steps.check_version.outputs.is_minor_bump == 'true'
106+
run: |
107+
BRANCH_NAME="${{ steps.check_version.outputs.branch_name }}"
108+
109+
# Check if branch already exists
110+
if git ls-remote --heads origin "$BRANCH_NAME" | grep -q "$BRANCH_NAME"; then
111+
echo "⚠️ Branch $BRANCH_NAME already exists, skipping creation"
112+
echo "branch_exists=true" >> $GITHUB_ENV
113+
exit 0
114+
else
115+
echo "branch_exists=false" >> $GITHUB_ENV
116+
fi
117+
118+
# Create branch from the commit BEFORE the version bump
119+
# This ensures the release branch has the previous minor version
120+
git checkout -b "$BRANCH_NAME" HEAD^1
121+
122+
# Push the new branch
123+
git push origin "$BRANCH_NAME"
124+
125+
echo "✅ Created release branch: $BRANCH_NAME"
126+
echo "This branch is now in feature freeze and will only receive:"
127+
echo "- Bug fixes"
128+
echo "- Critical security patches"
129+
echo "- Documentation updates"
130+
131+
- name: Create branch protection rules
132+
if: steps.check_version.outputs.is_minor_bump == 'true' && env.branch_exists != 'true'
133+
env:
134+
GITHUB_TOKEN: ${{ secrets.PR_GH_TOKEN || secrets.GITHUB_TOKEN }}
135+
run: |
136+
BRANCH_NAME="${{ steps.check_version.outputs.branch_name }}"
137+
138+
# Create branch protection using GitHub API
139+
echo "Setting up branch protection for $BRANCH_NAME..."
140+
141+
RESPONSE=$(curl -s -w "\n%{http_code}" -X PUT \
142+
-H "Authorization: token $GITHUB_TOKEN" \
143+
-H "Accept: application/vnd.github.v3+json" \
144+
"https://api.github.com/repos/${{ github.repository }}/branches/$BRANCH_NAME/protection" \
145+
-d '{
146+
"required_status_checks": {
147+
"strict": true,
148+
"contexts": ["build", "test"]
149+
},
150+
"enforce_admins": false,
151+
"required_pull_request_reviews": {
152+
"required_approving_review_count": 1,
153+
"dismiss_stale_reviews": true
154+
},
155+
"restrictions": null,
156+
"allow_force_pushes": false,
157+
"allow_deletions": false
158+
}')
159+
160+
HTTP_CODE=$(echo "$RESPONSE" | tail -n 1)
161+
BODY=$(echo "$RESPONSE" | sed '$d')
162+
163+
if [[ "$HTTP_CODE" -eq 200 ]] || [[ "$HTTP_CODE" -eq 201 ]]; then
164+
echo "✅ Branch protection successfully applied"
165+
else
166+
echo "⚠️ Failed to apply branch protection (HTTP $HTTP_CODE)"
167+
echo "Response: $BODY"
168+
# Don't fail the workflow, just warn
169+
fi
170+
171+
- name: Post summary
172+
if: steps.check_version.outputs.is_minor_bump == 'true'
173+
run: |
174+
BRANCH_NAME="${{ steps.check_version.outputs.branch_name }}"
175+
PREV_VERSION="${{ steps.check_version.outputs.prev_version }}"
176+
CURRENT_VERSION="${{ steps.check_version.outputs.current_version }}"
177+
178+
if [[ "${{ env.branch_exists }}" == "true" ]]; then
179+
cat >> $GITHUB_STEP_SUMMARY << EOF
180+
## 🌿 Release Branch Already Exists
181+
182+
The release branch for the previous minor version already exists:
183+
EOF
184+
else
185+
cat >> $GITHUB_STEP_SUMMARY << EOF
186+
## 🌿 Release Branch Created
187+
188+
A new release branch has been created for the previous minor version:
189+
EOF
190+
fi
191+
192+
cat >> $GITHUB_STEP_SUMMARY << EOF
193+
194+
- **Branch**: \`$BRANCH_NAME\`
195+
- **Version**: \`$PREV_VERSION\` (feature frozen)
196+
- **Main branch**: \`$CURRENT_VERSION\` (active development)
197+
198+
### Branch Policy
199+
200+
The \`$BRANCH_NAME\` branch is now in **feature freeze** and will only accept:
201+
- 🐛 Bug fixes
202+
- 🔒 Security patches
203+
- 📚 Documentation updates
204+
205+
All new features should continue to be developed against \`main\`.
206+
207+
### Backporting Changes
208+
209+
To backport a fix to this release branch:
210+
1. Create your fix on \`main\` first
211+
2. Cherry-pick to \`$BRANCH_NAME\`
212+
3. Create a PR targeting \`$BRANCH_NAME\`
213+
4. Use the \`Release\` label on the PR
214+
EOF

0 commit comments

Comments
 (0)