name: Version Bump and Release
on:
workflow_dispatch:
inputs:
version_type:
description: 'Version bump type'
required: true
default: 'patch'
type: choice
options:
- patch
- minor
- major
custom_version:
description: 'Custom version (optional, overrides version_type)'
required: false
type: string
create_release:
description: 'Create GitHub release after version bump'
required: false
default: true
type: boolean
dry_run:
description: 'Dry run mode (show what would be changed)'
required: false
default: false
type: boolean
permissions:
contents: write pull-requests: write
env:
CARGO_TERM_COLOR: always
jobs:
version-bump:
name: Bump version and create release
runs-on: ubuntu-latest
outputs:
new_version: ${{ steps.version.outputs.new_version }}
release_created: ${{ steps.release.outputs.release_created }}
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
token: ${{ secrets.PAT_TOKEN || secrets.GITHUB_TOKEN }}
fetch-depth: 0
persist-credentials: true
- name: Install Rust toolchain
uses: dtolnay/rust-toolchain@stable
- name: Install cargo-edit
run: cargo install cargo-edit
- name: Get current version
id: current_version
run: |
CURRENT_VERSION=$(cargo metadata --no-deps --format-version 1 | jq -r '.packages[] | select(.name == "purger") | .version')
echo "current_version=$CURRENT_VERSION" >> $GITHUB_OUTPUT
echo "Current version: $CURRENT_VERSION"
- name: Calculate new version
id: version
run: |
CURRENT_VERSION="${{ steps.current_version.outputs.current_version }}"
if [[ -n "${{ inputs.custom_version }}" ]]; then
NEW_VERSION="${{ inputs.custom_version }}"
echo "Using custom version: $NEW_VERSION"
else
# Parse current version
IFS='.' read -ra VERSION_PARTS <<< "$CURRENT_VERSION"
MAJOR=${VERSION_PARTS[0]}
MINOR=${VERSION_PARTS[1]}
PATCH=${VERSION_PARTS[2]}
case "${{ inputs.version_type }}" in
"major")
MAJOR=$((MAJOR + 1))
MINOR=0
PATCH=0
;;
"minor")
MINOR=$((MINOR + 1))
PATCH=0
;;
"patch")
PATCH=$((PATCH + 1))
;;
esac
NEW_VERSION="$MAJOR.$MINOR.$PATCH"
echo "Calculated new version: $NEW_VERSION"
fi
echo "new_version=$NEW_VERSION" >> $GITHUB_OUTPUT
echo "New version will be: $NEW_VERSION"
- name: Update workspace version
if: ${{ !inputs.dry_run }}
run: |
NEW_VERSION="${{ steps.version.outputs.new_version }}"
echo "Updating workspace version to $NEW_VERSION"
# Update workspace version in root Cargo.toml
sed -i "s/^version = \".*\"/version = \"$NEW_VERSION\"/" Cargo.toml
echo "Updated Cargo.toml:"
grep -A 10 "\[workspace.package\]" Cargo.toml
- name: Update CHANGELOG.md
if: ${{ !inputs.dry_run }}
run: |
NEW_VERSION="${{ steps.version.outputs.new_version }}"
CURRENT_DATE=$(date +%Y-%m-%d)
if [ -f "CHANGELOG.md" ]; then
echo "Updating CHANGELOG.md for version $NEW_VERSION"
# 检查是否已经有这个版本的条目
if grep -q "## \[$NEW_VERSION\]" CHANGELOG.md; then
echo "Version $NEW_VERSION already exists in CHANGELOG.md, updating date..."
# 更新现有条目的日期
sed -i "s/^## \[$NEW_VERSION\].*/## [$NEW_VERSION] - $CURRENT_DATE/" CHANGELOG.md
else
echo "Adding new version entry to CHANGELOG.md"
# 使用 Python 来安全地插入新版本条目
python3 << 'EOF'
import sys
import re
new_version = "$NEW_VERSION"
current_date = "$CURRENT_DATE"
with open("CHANGELOG.md", "r") as f:
content = f.read()
# 找到第一个版本条目的位置
pattern = r'^## \['
lines = content.split('\n')
insert_index = -1
for i, line in enumerate(lines):
if re.match(pattern, line):
insert_index = i
break
if insert_index != -1:
# 插入新版本条目
new_entry = [
f"## [{new_version}] - {current_date}",
"",
"### Changed",
f"- Version bump to {new_version}",
""
]
# 在找到的位置插入新条目
lines[insert_index:insert_index] = new_entry
with open("CHANGELOG.md", "w") as f:
f.write('\n'.join(lines))
print(f"Successfully added version {new_version} to CHANGELOG.md")
else:
print("Could not find existing version entries in CHANGELOG.md")
EOF
fi
echo "Updated CHANGELOG.md:"
head -20 CHANGELOG.md
else
echo "CHANGELOG.md not found, skipping update"
fi
- name: Show what would be changed (dry run)
if: ${{ inputs.dry_run }}
run: |
NEW_VERSION="${{ steps.version.outputs.new_version }}"
echo "🧪 DRY RUN: Would update version to $NEW_VERSION"
echo "Current Cargo.toml workspace.package section:"
grep -A 10 "\[workspace.package\]" Cargo.toml
echo ""
echo "Would change version line to: version = \"$NEW_VERSION\""
- name: Verify version update
if: ${{ !inputs.dry_run }}
run: |
# Check that all packages now have the new version
cargo metadata --no-deps --format-version 1 | jq -r '.packages[] | "\(.name): \(.version)"'
- name: Commit version bump
if: ${{ !inputs.dry_run }}
env:
GITHUB_TOKEN: ${{ secrets.PAT_TOKEN || secrets.GITHUB_TOKEN }}
run: |
NEW_VERSION="${{ steps.version.outputs.new_version }}"
# 配置 git 用户信息
git config --local user.email "41898282+github-actions[bot]@users.noreply.github.com"
git config --local user.name "github-actions[bot]"
# 添加并提交更改
git add Cargo.toml CHANGELOG.md
git commit -m "chore: bump version to $NEW_VERSION"
git tag "v$NEW_VERSION"
# 推送更改和标签
echo "Pushing changes to main branch..."
git push origin main
echo "Pushing tag v$NEW_VERSION..."
git push origin "v$NEW_VERSION"
echo "✅ Successfully pushed version bump and tag"
- name: Extract changelog content
id: changelog
if: ${{ !inputs.dry_run && inputs.create_release }}
run: |
NEW_VERSION="${{ steps.version.outputs.new_version }}"
# 从 CHANGELOG.md 中提取当前版本的内容
if [ -f "CHANGELOG.md" ]; then
echo "Extracting changelog content for version $NEW_VERSION..."
# 使用 sed 和 awk 提取版本内容
CHANGELOG_CONTENT=$(sed -n "/^## \[$NEW_VERSION\]/,/^## \[/p" CHANGELOG.md | sed '$d' | tail -n +2)
if [ -n "$CHANGELOG_CONTENT" ]; then
echo "Found changelog content for version $NEW_VERSION"
# 将内容保存到环境变量,处理多行内容
{
echo 'CHANGELOG_CONTENT<<EOF'
echo "$CHANGELOG_CONTENT"
echo 'EOF'
} >> $GITHUB_ENV
echo "changelog_found=true" >> $GITHUB_OUTPUT
else
echo "No changelog content found for version $NEW_VERSION"
echo "changelog_found=false" >> $GITHUB_OUTPUT
fi
else
echo "CHANGELOG.md not found"
echo "changelog_found=false" >> $GITHUB_OUTPUT
fi
- name: Prepare release body
id: release_body
if: ${{ !inputs.dry_run && inputs.create_release }}
run: |
NEW_VERSION="${{ steps.version.outputs.new_version }}"
# 创建基础的安装说明
INSTALLATION_SECTION="
### Installation
\`\`\`bash
# Install CLI tool
cargo install purger-cli
# Install GUI tool
cargo install purger-gui
# Use as library
cargo add purger-core
\`\`\`
### Links
- [purger-core on crates.io](https://crates.io/crates/purger-core)
- [purger-cli on crates.io](https://crates.io/crates/purger-cli)
- [purger-gui on crates.io](https://crates.io/crates/purger-gui)
- [purger on crates.io](https://crates.io/crates/purger)"
if [ "${{ steps.changelog.outputs.changelog_found }}" = "true" ]; then
# 使用 changelog 内容
RELEASE_BODY="## Changes in v$NEW_VERSION
$CHANGELOG_CONTENT
---
$INSTALLATION_SECTION"
else
# 使用默认内容
RELEASE_BODY="## Changes in v$NEW_VERSION
This release was automatically created by the version bump workflow.
$INSTALLATION_SECTION"
fi
# 保存到环境变量
{
echo 'RELEASE_BODY<<EOF'
echo "$RELEASE_BODY"
echo 'EOF'
} >> $GITHUB_ENV
- name: Create GitHub Release
id: release
if: ${{ !inputs.dry_run && inputs.create_release }}
env:
GITHUB_TOKEN: ${{ secrets.PAT_TOKEN || secrets.GITHUB_TOKEN }}
run: |
NEW_VERSION="${{ steps.version.outputs.new_version }}"
TAG_NAME="v$NEW_VERSION"
RELEASE_TITLE="Release v$NEW_VERSION"
# 创建临时文件存储 release body
TEMP_FILE=$(mktemp)
echo "$RELEASE_BODY" > "$TEMP_FILE"
# 使用 GitHub CLI 创建 release
gh release create "$TAG_NAME" \
--title "$RELEASE_TITLE" \
--notes-file "$TEMP_FILE" \
--target main
# 清理临时文件
rm "$TEMP_FILE"
echo "✅ Successfully created GitHub Release: $TAG_NAME"
echo "release_created=true" >> $GITHUB_OUTPUT
- name: Summary
run: |
echo "## 🚀 Version Bump Summary" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "- **Previous version:** ${{ steps.current_version.outputs.current_version }}" >> $GITHUB_STEP_SUMMARY
echo "- **New version:** ${{ steps.version.outputs.new_version }}" >> $GITHUB_STEP_SUMMARY
echo "- **Bump type:** ${{ inputs.version_type }}" >> $GITHUB_STEP_SUMMARY
if [[ "${{ inputs.dry_run }}" == "true" ]]; then
echo "- **Mode:** 🧪 Dry run (no changes made)" >> $GITHUB_STEP_SUMMARY
else
echo "- **Mode:** ✅ Live run (changes committed)" >> $GITHUB_STEP_SUMMARY
if [[ "${{ inputs.create_release }}" == "true" ]]; then
echo "- **GitHub Release:** ✅ Created" >> $GITHUB_STEP_SUMMARY
else
echo "- **GitHub Release:** ⏭️ Skipped" >> $GITHUB_STEP_SUMMARY
fi
fi