-
Notifications
You must be signed in to change notification settings - Fork 55
237 lines (197 loc) · 9.77 KB
/
prepare-release.yaml
File metadata and controls
237 lines (197 loc) · 9.77 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
# Prepare Release Workflow
# This workflow automates the release preparation process:
# 1. Updates the version in eng/targets/Release.props
# 2. Generates changelog from git diff between main and last release tag
# 3. Updates CHANGELOG.md with the new version section
# 4. Creates a release branch and tag
# After the workflow completes, manually create a PR from the release branch to main.
name: Prepare Release
on:
workflow_dispatch:
inputs:
version:
description: 'Release version (e.g., 1.24.0 or 1.24.0-preview.1). Leave empty to auto-increment (patch for stable, pre-release number for pre-release).'
required: false
type: string
permissions:
contents: write
jobs:
prepare-release:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
fetch-depth: 0 # Full history needed for git diff and tags
- name: Get current version
id: get-current-version
run: |
# Extract VersionPrefix and VersionSuffix from Release.props
VERSION_PREFIX=$(grep -oP '(?<=<VersionPrefix>)[^<]+' eng/targets/Release.props)
VERSION_SUFFIX=$(grep -oP '(?<=<VersionSuffix>)[^<]*' eng/targets/Release.props)
if [ -n "$VERSION_SUFFIX" ]; then
CURRENT_VERSION="${VERSION_PREFIX}-${VERSION_SUFFIX}"
else
CURRENT_VERSION="$VERSION_PREFIX"
fi
echo "current_version=$CURRENT_VERSION" >> $GITHUB_OUTPUT
echo "Current version: $CURRENT_VERSION"
- name: Calculate next version
id: calc-version
run: |
INPUT_VERSION="${{ github.event.inputs.version }}"
CURRENT_VERSION="${{ steps.get-current-version.outputs.current_version }}"
if [ -n "$INPUT_VERSION" ]; then
NEW_VERSION="$INPUT_VERSION"
else
# Auto-increment: parse current version and bump appropriately
# Handle pre-release versions like 1.23.0-preview.1 -> 1.23.0-preview.2
# Handle stable versions like 1.23.0 -> 1.23.1
NEW_VERSION=$(python3 -c "
import re, sys
v = '$CURRENT_VERSION'
m = re.match(r'^(\d+)\.(\d+)\.(\d+)(?:-([a-zA-Z]+)\.(\d+))?$', v)
if not m:
print(v)
sys.exit(0)
major, minor, patch, pre_type, pre_num = m.groups()
if pre_type and pre_num:
print(f'{major}.{minor}.{patch}-{pre_type}.{int(pre_num) + 1}')
else:
print(f'{major}.{minor}.{int(patch) + 1}')
")
fi
echo "new_version=$NEW_VERSION" >> $GITHUB_OUTPUT
echo "New version: $NEW_VERSION"
- name: Get latest release tag
id: get-latest-tag
run: |
NEW_VERSION="${{ steps.calc-version.outputs.new_version }}"
# Get the latest tag that looks like a version (v*), excluding the
# tag being created so that re-runs don't use it as the baseline.
LATEST_TAG=$(git tag -l 'v*' --sort=-v:refname | \
grep -v "^v${NEW_VERSION}$" | head -n 1)
if [ -z "$LATEST_TAG" ]; then
echo "No previous release tag found, using initial commit"
LATEST_TAG=$(git rev-list --max-parents=0 HEAD)
fi
echo "latest_tag=$LATEST_TAG" >> $GITHUB_OUTPUT
echo "Latest tag: $LATEST_TAG"
- name: Generate changelog diff
id: changelog-diff
run: |
LATEST_TAG="${{ steps.get-latest-tag.outputs.latest_tag }}"
NEW_VERSION="${{ steps.calc-version.outputs.new_version }}"
echo "Generating changelog for changes between $LATEST_TAG and HEAD..."
# Get commits between last tag and HEAD.
# GitHub squash-merges put the PR number in parentheses, e.g. "Some change (#123)".
# We convert "(#N)" to a markdown link.
CHANGELOG_CONTENT=$(git log "$LATEST_TAG"..HEAD --pretty=format:"%s" --no-merges | \
sed 's/(#\([0-9]*\))/([#\1](https:\/\/github.com\/microsoft\/durabletask-dotnet\/pull\/\1))/' | \
sed 's/^/- /' | \
grep -v '^- $' || echo "")
# Save to file for multi-line output
echo "$CHANGELOG_CONTENT" > /tmp/changelog_content.txt
echo "changelog_file=/tmp/changelog_content.txt" >> $GITHUB_OUTPUT
echo "Generated changelog:"
cat /tmp/changelog_content.txt
- name: Update Release.props version
run: |
NEW_VERSION="${{ steps.calc-version.outputs.new_version }}"
# Parse version into prefix and suffix
if [[ "$NEW_VERSION" == *-* ]]; then
VERSION_PREFIX="${NEW_VERSION%%-*}"
VERSION_SUFFIX="${NEW_VERSION#*-}"
else
VERSION_PREFIX="$NEW_VERSION"
VERSION_SUFFIX=""
fi
echo "Updating Release.props: VersionPrefix=$VERSION_PREFIX, VersionSuffix=$VERSION_SUFFIX"
# Update VersionPrefix
sed -i "s|<VersionPrefix>[^<]*</VersionPrefix>|<VersionPrefix>$VERSION_PREFIX</VersionPrefix>|" eng/targets/Release.props
# Update VersionSuffix
sed -i "s|<VersionSuffix>[^<]*</VersionSuffix>|<VersionSuffix>$VERSION_SUFFIX</VersionSuffix>|" eng/targets/Release.props
echo "Updated eng/targets/Release.props:"
cat eng/targets/Release.props
- name: Update CHANGELOG.md
run: |
NEW_VERSION="${{ steps.calc-version.outputs.new_version }}"
CHANGELOG_FILE="/tmp/changelog_content.txt"
python3 -c "
import sys
version = '${NEW_VERSION}'
changelog_file = '${CHANGELOG_FILE}'
with open(changelog_file, 'r') as f:
changes = f.read().strip()
# Read with newline='' to preserve original line endings (CRLF or LF)
with open('CHANGELOG.md', 'r', newline='') as f:
content = f.read()
# Detect line ending style used in the file
eol = '\r\n' if '\r\n' in content else '\n'
changes = changes.replace('\r\n', '\n').replace('\n', eol)
new_section = f'{eol}## v{version}{eol}{changes}{eol}'
# Insert the new version section after '## Unreleased'
marker = '## Unreleased'
idx = content.find(marker)
if idx != -1:
insert_pos = idx + len(marker)
# Skip any whitespace/newlines after the marker
while insert_pos < len(content) and content[insert_pos] in (' ', '\t', '\n', '\r'):
# Stop if we hit another section header
next_char = insert_pos + 1 if content[insert_pos] == '\r' else insert_pos
if content[insert_pos] in ('\n', '\r') and next_char + 1 < len(content) and content[next_char + 1] == '#':
break
insert_pos += 1
content = content[:insert_pos] + eol + new_section + content[insert_pos:]
else:
# No Unreleased section found, insert at top after title
title_end = content.find(eol, content.find('# Changelog'))
if title_end != -1:
content = content[:title_end + len(eol)] + eol + '## Unreleased' + eol + new_section + content[title_end + len(eol):]
# Write with newline='' to preserve the detected line endings
with open('CHANGELOG.md', 'w', newline='') as f:
f.write(content)
print(f'Updated CHANGELOG.md with v{version}')
"
- name: Create release branch and commit
id: create-branch
run: |
NEW_VERSION="${{ steps.calc-version.outputs.new_version }}"
BRANCH_NAME="release/v${NEW_VERSION}"
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
# Create and checkout release branch (idempotent)
git checkout -B "$BRANCH_NAME"
# Stage and commit changes
git add eng/targets/Release.props
git add CHANGELOG.md
if ! git diff --cached --quiet; then
git commit -m "Release v${NEW_VERSION}"
else
echo "No changes to commit (re-run with same version)"
fi
# Create release tag (idempotent)
git tag -f "v${NEW_VERSION}"
# Push branch and tag (force to handle re-runs)
git push -f origin "$BRANCH_NAME"
git push -f origin "v${NEW_VERSION}"
echo "branch_name=$BRANCH_NAME" >> $GITHUB_OUTPUT
echo "Created branch $BRANCH_NAME and tag v${NEW_VERSION}"
- name: Summary
run: |
NEW_VERSION="${{ steps.calc-version.outputs.new_version }}"
BRANCH_NAME="${{ steps.create-branch.outputs.branch_name }}"
echo "## Release Preparation Complete! :rocket:" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "- **Version**: v${NEW_VERSION}" >> $GITHUB_STEP_SUMMARY
echo "- **Branch**: ${BRANCH_NAME}" >> $GITHUB_STEP_SUMMARY
echo "- **Tag**: v${NEW_VERSION}" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "### Next Steps" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "1. **Create a Pull Request** from **${BRANCH_NAME}** → **main** with title: **Release v${NEW_VERSION}**" >> $GITHUB_STEP_SUMMARY
echo "2. Review the version bump in \`eng/targets/Release.props\` and changelog updates" >> $GITHUB_STEP_SUMMARY
echo "3. Update per-package \`RELEASENOTES.md\` files if needed" >> $GITHUB_STEP_SUMMARY
echo "4. Merge the PR after CI passes" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "[Create PR](https://github.com/microsoft/durabletask-dotnet/compare/main...${BRANCH_NAME}?expand=1&title=Release+v${NEW_VERSION})" >> $GITHUB_STEP_SUMMARY