Skip to content

Prepare release PR from next branch #2

Prepare release PR from next branch

Prepare release PR from next branch #2

name: Prepare release PR from next branch
on:
workflow_dispatch:
inputs:
pr_number:
description: 'PR number to prepare release for'
required: true
type: string
version_type:
description: 'Version bump type'
required: true
type: choice
options:
- patch
- minor
- major
default: patch
env:
REPO_B_TARGET: 'master'
REPO_B_NEXT: 'next'
jobs:
prepare-release:
runs-on: ubuntu-latest
permissions:
contents: write
pull-requests: write
actions: read
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
token: ${{ secrets.GITHUB_TOKEN }}
fetch-depth: 0
- name: Setup Git
run: |
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
- name: Checkout next branch
run: |
git checkout -B ${{ env.REPO_B_NEXT }} origin/${{ env.REPO_B_NEXT }}
- name: Verify PR exists and get info
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
echo "Verifying PR #${{ github.event.inputs.pr_number }} exists..."
PR_STATE=$(gh pr view ${{ github.event.inputs.pr_number }} --json state --jq '.state' 2>/dev/null || echo "NOT_FOUND")
if [ "$PR_STATE" = "NOT_FOUND" ]; then
echo "::error::PR #${{ github.event.inputs.pr_number }} not found"
exit 1
fi
if [ "$PR_STATE" != "OPEN" ]; then
echo "::error::PR #${{ github.event.inputs.pr_number }} is $PR_STATE, expected OPEN"
exit 1
fi
echo "PR #${{ github.event.inputs.pr_number }} is open and ready for release preparation"
- name: Determine version bump
id: version
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
echo "Fetching latest release from GitHub..."
LATEST_RELEASE=$(gh release list --limit 1 --json tagName --jq '.[0].tagName' 2>/dev/null || echo "")
if [ -n "$LATEST_RELEASE" ]; then
CURRENT_VERSION="$LATEST_RELEASE"
echo "Found latest release: $CURRENT_VERSION"
else
echo "No releases found, starting from v0.0.0"
CURRENT_VERSION="v0.0.0"
fi
# Parse version components (remove 'v' prefix if present)
version_number=$(echo $CURRENT_VERSION | sed 's/^v//')
major=$(echo $version_number | cut -d. -f1)
minor=$(echo $version_number | cut -d. -f2)
patch=$(echo $version_number | cut -d. -f3)
echo "Current version components: major=$major, minor=$minor, patch=$patch"
# Calculate new version based on input
case "${{ github.event.inputs.version_type }}" in
major)
major=$((major + 1))
minor=0
patch=0
echo "Major version bump requested"
;;
minor)
minor=$((minor + 1))
patch=0
echo "Minor version bump requested"
;;
patch)
patch=$((patch + 1))
echo "Patch version bump requested"
;;
esac
NEW_VERSION="v${major}.${minor}.${patch}"
echo "Version bump: $CURRENT_VERSION → $NEW_VERSION"
echo "current_version=$CURRENT_VERSION" >> $GITHUB_OUTPUT
echo "new_version=$NEW_VERSION" >> $GITHUB_OUTPUT
- name: Generate changelog entry
run: |
echo "Generating changelog entry for ${{ steps.version.outputs.new_version }}"
# Get all commits in this PR (from target branch to current HEAD)
ALL_PR_COMMITS=$(git rev-list --reverse origin/${{ env.REPO_B_TARGET }}..HEAD)
# Create changelog entry
changelog_entry="## ${{ steps.version.outputs.new_version }} ($(date +%Y-%m-%d))
Full Changelog: [${{ steps.version.outputs.current_version }}...${{ steps.version.outputs.new_version }}](https://github.com/${{ github.repository }}/compare/${{ steps.version.outputs.current_version }}...${{ steps.version.outputs.new_version }})
"
# Add features section
has_feat_commits=false
for commit in $ALL_PR_COMMITS; do
if [ -n "$commit" ]; then
commit_msg=$(git log --format="%s" -1 "$commit")
if echo "$commit_msg" | grep -q "feat:"; then
if [ "$has_feat_commits" = false ]; then
changelog_entry="${changelog_entry}
### Features
"
has_feat_commits=true
fi
# Remove "feat: " prefix and add to changelog with commit link
feature_desc=$(echo "$commit_msg" | sed 's/feat: //')
short_sha=$(echo "$commit" | cut -c1-7)
changelog_entry="${changelog_entry}* ${feature_desc} ([${short_sha}](https://github.com/${{ github.repository }}/commit/${commit}))
"
fi
fi
done
if [ "$has_feat_commits" = true ]; then
changelog_entry="${changelog_entry}
"
fi
# Add fixes section
has_fixes=false
for commit in $ALL_PR_COMMITS; do
if [ -n "$commit" ]; then
commit_msg=$(git log --format="%s" -1 "$commit")
if echo "$commit_msg" | grep -q "fix:"; then
if [ "$has_fixes" = false ]; then
changelog_entry="${changelog_entry}
### Bug Fixes
"
has_fixes=true
fi
# Remove "fix: " prefix and add to changelog with commit link
fix_desc=$(echo "$commit_msg" | sed 's/fix: //')
short_sha=$(echo "$commit" | cut -c1-7)
changelog_entry="${changelog_entry}* ${fix_desc} ([${short_sha}](https://github.com/${{ github.repository }}/commit/${commit}))
"
fi
fi
done
if [ "$has_fixes" = true ]; then
changelog_entry="${changelog_entry}
"
fi
# Add other changes section
has_other=false
for commit in $ALL_PR_COMMITS; do
if [ -n "$commit" ]; then
commit_msg=$(git log --format="%s" -1 "$commit")
if ! echo "$commit_msg" | grep -q -E "^(feat:|fix:|Update sync state)"; then
if [ "$has_other" = false ]; then
changelog_entry="${changelog_entry}
### Other Changes
"
has_other=true
fi
short_sha=$(echo "$commit" | cut -c1-7)
changelog_entry="${changelog_entry}* ${commit_msg} ([${short_sha}](https://github.com/${{ github.repository }}/commit/${commit}))
"
fi
fi
done
# Save changelog entry to temp file
echo "$changelog_entry" > /tmp/changelog_entry.md
echo "Generated changelog entry:"
cat /tmp/changelog_entry.md
- name: Update version files
run: |
# Update pyproject.toml if it exists
if [ -f pyproject.toml ]; then
echo "Updating version in pyproject.toml"
# Remove 'v' prefix for pyproject.toml
VERSION_NUMBER=$(echo "${{ steps.version.outputs.new_version }}" | sed 's/^v//')
# Update version in pyproject.toml using sed
sed -i "s/^version = .*/version = \"$VERSION_NUMBER\"/" pyproject.toml
echo "Updated pyproject.toml version to: $VERSION_NUMBER"
git add pyproject.toml
fi
- name: Update CHANGELOG.md
run: |
echo "Updating CHANGELOG.md"
# Insert new entry at the top (after title if it exists)
if head -1 CHANGELOG.md | grep -q "^# "; then
# Has title, insert after first line
head -1 CHANGELOG.md > /tmp/changelog_new.md
echo "" >> /tmp/changelog_new.md
cat /tmp/changelog_entry.md >> /tmp/changelog_new.md
tail -n +2 CHANGELOG.md >> /tmp/changelog_new.md
else
# No title, insert at beginning
cat /tmp/changelog_entry.md > /tmp/changelog_new.md
cat CHANGELOG.md >> /tmp/changelog_new.md
fi
mv /tmp/changelog_new.md CHANGELOG.md
git add CHANGELOG.md
echo "Updated CHANGELOG.md"
- name: Create release commit
run: |
git commit -m "release: ${{ steps.version.outputs.new_version }}"
echo "Created release commit for ${{ steps.version.outputs.new_version }}"
git push origin ${{ env.REPO_B_NEXT }}
- name: Update PR with release info
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
# Create updated PR body
PR_BODY="## Release ${{ steps.version.outputs.new_version }}
## Changelog
$(cat /tmp/changelog_entry.md)
---
*Release commit added by prepare-release workflow.*
*This PR is now ready to be reviewed and merged.*"
# Update the PR title and body
gh pr edit ${{ github.event.inputs.pr_number }} \
--title "Release ${{ steps.version.outputs.new_version }}" \
--body "$PR_BODY"
echo "Updated PR #${{ github.event.inputs.pr_number }} with release information"
- name: Summary
run: |
echo "::notice::Release preparation completed successfully"
echo "::notice::PR #${{ github.event.inputs.pr_number }} updated with release commit for ${{ steps.version.outputs.new_version }}"
echo "::notice::Review the PR and merge when ready"