purger 0.4.1

A tool for cleaning Rust project build directories
Documentation
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  # 允许创建和修改PR(如果需要)

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:
          # 优先使用 PAT,如果没有则使用默认的 GITHUB_TOKEN
          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:
          # 使用与 checkout 相同的 token
          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