发布流程 (Release Pipeline) #21
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: 发布流程 (Release Pipeline) | |
| on: | |
| # 允许手动触发:用于发布新版本(包含自动版本升级、打标签、构建和发布) | |
| workflow_dispatch: | |
| inputs: | |
| version_type: | |
| description: '版本类型 (patch=修复, minor=功能, major=重大变更)' | |
| required: true | |
| default: patch | |
| type: choice | |
| options: | |
| - patch | |
| - minor | |
| - major | |
| - custom | |
| custom_version: | |
| description: '自定义版本号 (格式: x.y.z, 仅当类型选为 custom 时有效)' | |
| required: false | |
| type: string | |
| # 允许 Tag 触发:用于重新构建或手动打 Tag 后的发布 | |
| push: | |
| tags: ['v*'] | |
| jobs: | |
| # ----------------------------------------------------------------------------- | |
| # 阶段 1: 准备发布 (版本管理 & 标签) | |
| # ----------------------------------------------------------------------------- | |
| prepare: | |
| name: 准备发布 | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: write # 需要写入权限来推送 Tag | |
| outputs: | |
| version: ${{ steps.determine_version.outputs.version }} | |
| tag_name: ${{ steps.determine_version.outputs.tag_name }} | |
| should_build: ${{ steps.check_trigger.outputs.should_build }} | |
| steps: | |
| - name: 检出代码 | |
| uses: actions/checkout@v4 | |
| with: | |
| fetch-depth: 0 | |
| token: ${{ secrets.GITHUB_TOKEN }} | |
| - name: 配置 Git 用户 | |
| if: github.event_name == 'workflow_dispatch' | |
| run: | | |
| git config user.name "github-actions[bot]" | |
| git config user.email "github-actions[bot]@users.noreply.github.com" | |
| - name: 确定版本号 | |
| id: determine_version | |
| run: | | |
| if [ "${{ github.event_name }}" == "workflow_dispatch" ]; then | |
| # --- 手动触发逻辑:计算新版本 --- | |
| # 1. 获取当前版本 | |
| if [ -f "Cargo.toml" ]; then | |
| CURRENT_VERSION=$(grep '^version = ' Cargo.toml | head -1 | cut -d'"' -f2) | |
| else | |
| CURRENT_VERSION="0.0.0" | |
| fi | |
| echo "Current version: $CURRENT_VERSION" | |
| # 2. 计算新版本 | |
| TYPE="${{ inputs.version_type }}" | |
| CUSTOM="${{ inputs.custom_version }}" | |
| if [ "$TYPE" = "custom" ]; then | |
| if [ -z "$CUSTOM" ]; then | |
| echo "Error: Custom version is required when type is 'custom'" | |
| exit 1 | |
| fi | |
| NEW_VERSION="$CUSTOM" | |
| else | |
| IFS='.' read -r major minor patch <<< "$CURRENT_VERSION" | |
| case $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" | |
| fi | |
| echo "New version: $NEW_VERSION" | |
| echo "version=$NEW_VERSION" >> $GITHUB_OUTPUT | |
| echo "tag_name=v$NEW_VERSION" >> $GITHUB_OUTPUT | |
| echo "is_new_release=true" >> $GITHUB_OUTPUT | |
| else | |
| # --- Tag 触发逻辑:使用现有 Tag --- | |
| TAG_NAME="${{ github.ref_name }}" | |
| VERSION=${TAG_NAME#v} | |
| echo "Triggered by tag: $TAG_NAME" | |
| echo "version=$VERSION" >> $GITHUB_OUTPUT | |
| echo "tag_name=$TAG_NAME" >> $GITHUB_OUTPUT | |
| echo "is_new_release=false" >> $GITHUB_OUTPUT | |
| fi | |
| - name: 更新版本文件并推送 (仅手动触发) | |
| if: github.event_name == 'workflow_dispatch' | |
| run: | | |
| NEW_VERSION="${{ steps.determine_version.outputs.version }}" | |
| TAG_NAME="${{ steps.determine_version.outputs.tag_name }}" | |
| CURRENT_DATE=$(date +"%Y-%m-%d") | |
| echo "Updating files to version $NEW_VERSION..." | |
| # 更新 Cargo.toml | |
| if [ -f "Cargo.toml" ]; then | |
| sed -i.bak "s/^version = \"[^\"]*\"/version = \"$NEW_VERSION\"/" Cargo.toml && rm Cargo.toml.bak | |
| fi | |
| # 更新 package.json | |
| if [ -f "package.json" ]; then | |
| sed -i.bak "s/\"version\"[[:space:]]*:[[:space:]]*\"[^\"]*\"/\"version\": \"$NEW_VERSION\"/" package.json && rm package.json.bak | |
| fi | |
| # 更新 tauri.conf.json | |
| if [ -f "tauri.conf.json" ]; then | |
| sed -i.bak "s/\"version\"[[:space:]]*:[[:space:]]*\"[^\"]*\"/\"version\": \"$NEW_VERSION\"/" tauri.conf.json && rm tauri.conf.json.bak | |
| fi | |
| # 更新 version.json | |
| if [ -f "version.json" ]; then | |
| sed -i.bak "s/\"version\"[[:space:]]*:[[:space:]]*\"[^\"]*\"/\"version\": \"$NEW_VERSION\"/" version.json && rm version.json.bak | |
| sed -i.bak "s/\"build_date\"[[:space:]]*:[[:space:]]*\"[^\"]*\"/\"build_date\": \"$CURRENT_DATE\"/" version.json && rm version.json.bak | |
| fi | |
| # 提交并推送 | |
| git add . | |
| git commit -m "release: Release $NEW_VERSION" | |
| git tag -a "$TAG_NAME" -m "Release $NEW_VERSION" | |
| echo "Pushing changes and tag..." | |
| git push origin main | |
| git push origin "$TAG_NAME" | |
| # ----------------------------------------------------------------------------- | |
| # 阶段 2: 多平台构建 (Build) | |
| # ----------------------------------------------------------------------------- | |
| build: | |
| name: 构建 CLI (${{ matrix.name }}) | |
| needs: prepare | |
| runs-on: ${{ matrix.platform }} | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| include: | |
| - platform: macos-latest | |
| args: --target aarch64-apple-darwin | |
| name: macos-aarch64 | |
| - platform: macos-latest | |
| args: --target x86_64-apple-darwin | |
| name: macos-x86_64 | |
| - platform: ubuntu-22.04 | |
| args: '' | |
| name: linux-x86_64 | |
| - platform: windows-latest | |
| args: '' | |
| name: windows-x86_64 | |
| steps: | |
| - name: 检出代码 | |
| uses: actions/checkout@v4 | |
| with: | |
| # 关键:检出特定的 Tag,确保所有构建机器使用相同的代码状态 | |
| ref: ${{ needs.prepare.outputs.tag_name }} | |
| - name: 安装 Linux 系统依赖 | |
| if: matrix.platform == 'ubuntu-22.04' | |
| run: | | |
| sudo apt-get update | |
| sudo apt-get install -y \ | |
| libwebkit2gtk-4.1-dev libappindicator3-dev librsvg2-dev patchelf pkg-config \ | |
| libglib2.0-dev libgtk-3-dev libgdk-pixbuf2.0-dev libpango1.0-dev libatk1.0-dev \ | |
| libcairo-gobject2 libjavascriptcoregtk-4.1-dev libasound2-dev libpulse-dev libjack-dev | |
| - name: 安装 Node.js | |
| uses: actions/setup-node@v4 | |
| with: | |
| node-version: lts/* | |
| - name: 安装 pnpm | |
| uses: pnpm/action-setup@v4 | |
| - name: 安装 Rust stable | |
| uses: dtolnay/rust-toolchain@stable | |
| with: | |
| # macOS 需要同时安装 x86_64 和 aarch64 target | |
| targets: ${{ matrix.platform == 'macos-latest' && 'aarch64-apple-darwin,x86_64-apple-darwin' || '' }} | |
| - name: 配置 Rust 缓存 | |
| uses: swatinem/rust-cache@v2 | |
| - name: 安装前端依赖 | |
| run: pnpm install | |
| - name: 安装 Tauri CLI | |
| run: cargo install tauri-cli --version "^2.0" --locked | |
| - name: 构建二进制文件 | |
| shell: bash | |
| run: | | |
| echo "Building for ${{ matrix.name }}..." | |
| if [[ "${{ matrix.platform }}" == "macos-latest" ]]; then | |
| cargo tauri build ${{ matrix.args }} --no-bundle | |
| else | |
| cargo tauri build --no-bundle | |
| fi | |
| - name: 打包产物 | |
| shell: bash | |
| run: | | |
| mkdir -p cli-package | |
| TAG_NAME="${{ needs.prepare.outputs.tag_name }}" | |
| # 确定产物路径 | |
| if [[ "${{ matrix.platform }}" == "macos-latest" ]]; then | |
| if [[ "${{ matrix.args }}" == *"aarch64"* ]]; then | |
| TARGET_DIR="target/aarch64-apple-darwin/release" | |
| else | |
| TARGET_DIR="target/x86_64-apple-darwin/release" | |
| fi | |
| else | |
| TARGET_DIR="target/release" | |
| fi | |
| # 复制并压缩 | |
| if [[ "${{ matrix.platform }}" == "windows-latest" ]]; then | |
| cp "$TARGET_DIR/等一下.exe" cli-package/ | |
| cp "$TARGET_DIR/三术.exe" cli-package/ | |
| cd cli-package | |
| 7z a ../sanshu-cli-${TAG_NAME}-${{ matrix.name }}.zip * | |
| else | |
| cp "$TARGET_DIR/等一下" cli-package/ | |
| cp "$TARGET_DIR/三术" cli-package/ | |
| cd cli-package | |
| tar -czf ../sanshu-cli-${TAG_NAME}-${{ matrix.name }}.tar.gz * | |
| fi | |
| - name: 上传构建产物 | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: sanshu-cli-${{ matrix.name }} | |
| path: | | |
| sanshu-cli-*.tar.gz | |
| sanshu-cli-*.zip | |
| if-no-files-found: error | |
| # ----------------------------------------------------------------------------- | |
| # 阶段 3: 发布 (Publish) | |
| # ----------------------------------------------------------------------------- | |
| release: | |
| name: 发布 Release | |
| needs: [prepare, build] | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: write | |
| steps: | |
| - name: 检出代码 | |
| uses: actions/checkout@v4 | |
| with: | |
| fetch-depth: 0 | |
| ref: ${{ needs.prepare.outputs.tag_name }} | |
| - name: 下载所有构建产物 | |
| uses: actions/download-artifact@v4 | |
| with: | |
| path: artifacts | |
| - name: 安装 git-cliff (用于生成 Changelog) | |
| uses: taiki-e/install-action@git-cliff | |
| - name: 生成 Changelog | |
| id: changelog | |
| run: | | |
| TAG_NAME="${{ needs.prepare.outputs.tag_name }}" | |
| # 尝试获取上一个 Tag | |
| PREVIOUS_TAG=$(git tag --sort=-version:refname | grep -E '^v[0-9]+' | grep -A 1 "^$TAG_NAME$" | tail -n 1 || echo "") | |
| echo "Generating changelog for $TAG_NAME (Previous: $PREVIOUS_TAG)" | |
| if [ -z "$PREVIOUS_TAG" ] || [ "$PREVIOUS_TAG" == "$TAG_NAME" ]; then | |
| git-cliff --tag "$TAG_NAME" --output changelog.md | |
| else | |
| git-cliff "$PREVIOUS_TAG..$TAG_NAME" --output changelog.md | |
| fi | |
| # 生成发布标题 (提取主要特性) | |
| VERSION=${TAG_NAME#v} | |
| if [ -n "$PREVIOUS_TAG" ]; then | |
| MAIN_FEATURE=$(git log --oneline "$PREVIOUS_TAG..$TAG_NAME" | grep -E "^[a-f0-9]+ (feat|fix)" | head -1 | sed 's/^[a-f0-9]* //' | sed 's/^feat: /✨ /' | sed 's/^fix: /🐞 /') | |
| fi | |
| if [ -z "$MAIN_FEATURE" ]; then | |
| echo "release_title=$VERSION 📦 版本更新" >> $GITHUB_OUTPUT | |
| else | |
| echo "release_title=$VERSION $MAIN_FEATURE" >> $GITHUB_OUTPUT | |
| fi | |
| - name: 创建 GitHub Release | |
| uses: softprops/action-gh-release@v1 | |
| with: | |
| tag_name: ${{ needs.prepare.outputs.tag_name }} | |
| name: ${{ steps.changelog.outputs.release_title }} | |
| body_path: changelog.md | |
| draft: false | |
| prerelease: false | |
| files: | | |
| artifacts/*/sanshu-cli-*.tar.gz | |
| artifacts/*/sanshu-cli-*.zip | |
| env: | |
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| - name: 更新 README 中的版本信息 | |
| run: | | |
| TAG_NAME="${{ needs.prepare.outputs.tag_name }}" | |
| VERSION_NUMBER="${{ needs.prepare.outputs.version }}" | |
| DATE=$(date +%Y-%m-%d) | |
| echo "📝 Updating README with version: $VERSION_NUMBER (tag: $TAG_NAME)" | |
| # 使用不冲突分隔符并匹配可选 v 前缀,避免 sed 解析错误 | |
| sed -i -E "s#^> \\*\\*📋 版本信息\\*\\*:当前最新版本 v?[0-9.]+ \\| 上次更新:.*#> **📋 版本信息**:当前最新版本 ${VERSION_NUMBER} | 上次更新:${DATE}#g" README.md | |
| # 下载链接中同时更新 tag 路径和文件名里的版本号 | |
| sed -i -E "s#v[0-9.]+/sanshu-cli-v[0-9.]+-#${TAG_NAME}/sanshu-cli-${TAG_NAME}-#g" README.md | |
| # 显示更改 | |
| echo "=== README changes ===" | |
| git diff README.md || true | |
| - name: 提交 README 更新 | |
| run: | | |
| TAG_NAME="${{ needs.prepare.outputs.tag_name }}" | |
| VERSION_NUMBER="${{ needs.prepare.outputs.version }}" | |
| if git diff --quiet README.md; then | |
| echo "No changes to README, skipping commit" | |
| exit 0 | |
| fi | |
| git config user.name "github-actions[bot]" | |
| git config user.email "github-actions[bot]@users.noreply.github.com" | |
| git add README.md | |
| git commit -m "docs: update README to version ${VERSION_NUMBER} | |
| - Update download links to ${TAG_NAME} | |
| - Update version info and last updated date | |
| - Auto-generated by release workflow" | |
| git push origin HEAD:main | |
| echo "✅ README updated and pushed successfully" | |
| - name: 触发 Homebrew 更新 | |
| uses: actions/github-script@v7 | |
| continue-on-error: true | |
| with: | |
| script: | | |
| console.log('Triggering Homebrew update...'); | |
| try { | |
| await github.rest.actions.createWorkflowDispatch({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| workflow_id: 'update-homebrew.yml', | |
| ref: 'main', | |
| inputs: { tag_name: '${{ needs.prepare.outputs.tag_name }}' } | |
| }); | |
| console.log('✅ Homebrew update triggered'); | |
| } catch (error) { | |
| console.error('⚠️ Could not trigger Homebrew update:', error); | |
| } |