feat: 新增 SVG 工具并发布 0.2.0 #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
| # 构建和发布工作流 | |
| # 触发条件: | |
| # 1. 推送到 main 分支且修改了 package.json | |
| # 2. 手动触发 (workflow_dispatch) | |
| # 构建条件: | |
| # - package.json 中的 version 高于 GitHub Releases 已发布的版本 | |
| # - 或手动触发 | |
| name: Build and Release | |
| on: | |
| push: | |
| branches: | |
| - main | |
| paths: | |
| - 'package.json' # 只有 package.json 变化时才触发 | |
| workflow_dispatch: # 支持手动触发 | |
| permissions: | |
| contents: write # 需要写权限来创建 Release | |
| jobs: | |
| # ============================================ | |
| # 第一步:检查版本号是否需要发布 | |
| # ============================================ | |
| check-version: | |
| runs-on: ubuntu-latest | |
| outputs: | |
| should_release: ${{ steps.check.outputs.should_release }} | |
| version: ${{ steps.check.outputs.version }} | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v4 | |
| - name: Check version against latest release | |
| id: check | |
| env: | |
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| run: | | |
| # 获取当前 package.json 中的版本号 | |
| CURRENT_VERSION=$(jq -r '.version' package.json) | |
| echo "Current version in package.json: $CURRENT_VERSION" | |
| # 获取 GitHub Releases 中最新发布的版本号 | |
| LATEST_RELEASE=$(gh release list --limit 1 --json tagName --jq '.[0].tagName // "v0.0.0"' 2>/dev/null || echo "v0.0.0") | |
| # 移除版本号前的 'v' 前缀 | |
| LATEST_VERSION=${LATEST_RELEASE#v} | |
| echo "Latest released version: $LATEST_VERSION" | |
| echo "version=$CURRENT_VERSION" >> $GITHUB_OUTPUT | |
| # 比较版本号,判断是否需要发布 | |
| if [ "$CURRENT_VERSION" != "$LATEST_VERSION" ]; then | |
| echo "Version changed, should release" | |
| echo "should_release=true" >> $GITHUB_OUTPUT | |
| else | |
| echo "Version unchanged, skip release" | |
| echo "should_release=false" >> $GITHUB_OUTPUT | |
| fi | |
| # ============================================ | |
| # 第二步:构建应用(macOS 和 Windows) | |
| # ============================================ | |
| build: | |
| needs: check-version | |
| # 只有版本号高于已发布版本或手动触发时才构建 | |
| if: needs.check-version.outputs.should_release == 'true' || github.event_name == 'workflow_dispatch' | |
| strategy: | |
| fail-fast: false # 一个平台失败不影响其他平台 | |
| matrix: | |
| include: | |
| # macOS ARM64 (Apple Silicon) | |
| - platform: macos-latest | |
| args: --target aarch64-apple-darwin | |
| name: macOS-arm64 | |
| # Windows x64 | |
| - platform: windows-latest | |
| args: '' | |
| name: Windows | |
| runs-on: ${{ matrix.platform }} | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v4 | |
| # 安装 Bun 包管理器 | |
| - name: Setup Bun | |
| uses: oven-sh/setup-bun@v2 | |
| with: | |
| bun-version: latest | |
| # 安装 Rust 工具链 | |
| - name: Setup Rust | |
| uses: dtolnay/rust-toolchain@stable | |
| # Rust 依赖缓存,加速构建 | |
| - name: Rust cache | |
| uses: swatinem/rust-cache@v2 | |
| with: | |
| workspaces: './src-tauri -> target' | |
| # macOS 需要添加 ARM64 目标 | |
| - name: Add macOS arm64 target | |
| if: matrix.platform == 'macos-latest' | |
| run: rustup target add aarch64-apple-darwin | |
| # 安装前端依赖 | |
| - name: Install dependencies | |
| run: bun install | |
| # 同步版本号到 tauri.conf.json | |
| - name: Sync version to tauri.conf.json | |
| shell: bash | |
| run: | | |
| VERSION=$(jq -r '.version' package.json) | |
| jq --arg v "$VERSION" '.version = $v' src-tauri/tauri.conf.json > tmp.json && mv tmp.json src-tauri/tauri.conf.json | |
| # 构建 Tauri 应用 | |
| - name: Build Tauri App | |
| uses: tauri-apps/tauri-action@v0 | |
| env: | |
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| with: | |
| args: ${{ matrix.args }} | |
| # 上传构建产物 | |
| - name: Upload artifacts | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: youran-toolbox-${{ matrix.name }} | |
| path: | | |
| src-tauri/target/*/release/bundle/dmg/*.dmg | |
| src-tauri/target/release/bundle/nsis/*.exe | |
| # ============================================ | |
| # 第三步:创建 GitHub Release | |
| # ============================================ | |
| release: | |
| needs: [check-version, build] | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v4 | |
| # 从 CHANGELOG.md 提取当前版本的更新内容 | |
| - name: Extract changelog | |
| id: changelog | |
| run: | | |
| # 提取第一个 ## 和第二个 ## 之间的内容,跳过标题行 | |
| BODY=$(awk '/^## /{if(seen++){exit} next} seen{print}' CHANGELOG.md) | |
| # 写入多行输出 | |
| echo "body<<EOF" >> $GITHUB_OUTPUT | |
| echo "$BODY" >> $GITHUB_OUTPUT | |
| echo "EOF" >> $GITHUB_OUTPUT | |
| # 下载所有构建产物 | |
| - name: Download all artifacts | |
| uses: actions/download-artifact@v4 | |
| with: | |
| path: artifacts | |
| # 列出所有产物(调试用) | |
| - name: List artifacts | |
| run: find artifacts -type f | |
| # 创建 Release 并上传安装包 | |
| - name: Create Release | |
| uses: softprops/action-gh-release@v2 | |
| with: | |
| tag_name: v${{ needs.check-version.outputs.version }} | |
| name: v${{ needs.check-version.outputs.version }} | |
| body: ${{ steps.changelog.outputs.body }} | |
| files: artifacts/**/* | |
| draft: false | |
| prerelease: false | |
| env: | |
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| # ============================================ | |
| # 第四步:上传安装包到 阿里云 OSS | |
| # ============================================ | |
| upload-oss: | |
| needs: [check-version, build, release] | |
| if: needs.check-version.outputs.should_release == 'true' || github.event_name == 'workflow_dispatch' | |
| runs-on: ubuntu-latest | |
| env: | |
| ALIBABA_CLOUD_ACCESS_KEY_ID: ${{ secrets.ALIYUN_ACCESS_KEY_ID }} | |
| ALIBABA_CLOUD_ACCESS_KEY_SECRET: ${{ secrets.ALIYUN_ACCESS_KEY_SECRET }} | |
| ALIBABA_CLOUD_REGION_ID: ${{ secrets.ALIYUN_REGION_ID }} | |
| OSS_BUCKET: ${{ secrets.ALIYUN_OSS_BUCKET }} | |
| PUBLIC_BASE_URL: ${{ secrets.ALIYUN_OSS_PUBLIC_BASE_URL }} | |
| steps: | |
| - name: Validate OSS secrets | |
| run: | | |
| set -euo pipefail | |
| [ -n "${ALIBABA_CLOUD_ACCESS_KEY_ID}" ] || (echo "Missing secret: ALIYUN_ACCESS_KEY_ID" && exit 1) | |
| [ -n "${ALIBABA_CLOUD_ACCESS_KEY_SECRET}" ] || (echo "Missing secret: ALIYUN_ACCESS_KEY_SECRET" && exit 1) | |
| [ -n "${ALIBABA_CLOUD_REGION_ID}" ] || (echo "Missing secret: ALIYUN_REGION_ID" && exit 1) | |
| [ -n "${OSS_BUCKET}" ] || (echo "Missing secret: ALIYUN_OSS_BUCKET" && exit 1) | |
| - name: Download all artifacts | |
| uses: actions/download-artifact@v4 | |
| with: | |
| path: artifacts | |
| - name: Install Alibaba Cloud CLI | |
| run: | | |
| /bin/bash -c "$(curl -fsSL https://aliyuncli.alicdn.com/install.sh)" | |
| echo "$HOME/.aliyun/bin" >> "$GITHUB_PATH" | |
| - name: Prepare installers | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| mkdir -p dist | |
| find artifacts -type f \( -name "*.dmg" -o -name "*.exe" \) -print0 | while IFS= read -r -d '' file; do | |
| cp "$file" dist/ | |
| done | |
| if ! ls dist/* >/dev/null 2>&1; then | |
| echo "No installer files found in artifacts" | |
| exit 1 | |
| fi | |
| ls -lah dist | |
| - name: Upload installers to OSS | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| VERSION="${{ needs.check-version.outputs.version }}" | |
| # 固定版本目录:oss://<bucket>/vX.Y.Z/ | |
| aliyun ossutil cp dist/ "oss://${OSS_BUCKET}/v${VERSION}/" -r -f --no-progress | |
| # 最新版本目录:oss://<bucket>/latest/ | |
| aliyun ossutil rm "oss://${OSS_BUCKET}/latest/" -r -f || true | |
| aliyun ossutil cp dist/ "oss://${OSS_BUCKET}/latest/" -r -f --no-progress | |
| - name: Print download paths | |
| shell: bash | |
| run: | | |
| VERSION="${{ needs.check-version.outputs.version }}" | |
| echo "OSS version path: oss://${OSS_BUCKET}/v${VERSION}/" | |
| echo "OSS latest path: oss://${OSS_BUCKET}/latest/" | |
| if [ -n "${PUBLIC_BASE_URL:-}" ]; then | |
| echo "Public version URL: ${PUBLIC_BASE_URL}/v${VERSION}/" | |
| echo "Public latest URL: ${PUBLIC_BASE_URL}/latest/" | |
| fi | |
| # ============================================ | |
| # 第五步:同步 Homebrew Cask | |
| # ============================================ | |
| publish-homebrew: | |
| needs: [check-version, release] | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Resolve release asset | |
| id: asset | |
| env: | |
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| run: | | |
| set -euo pipefail | |
| VERSION="${{ needs.check-version.outputs.version }}" | |
| TAG="v${VERSION}" | |
| API_URL="https://api.github.com/repos/${{ github.repository }}/releases/tags/${TAG}" | |
| RELEASE_JSON=$(curl -fsSL \ | |
| -H "Authorization: Bearer ${GH_TOKEN}" \ | |
| -H "Accept: application/vnd.github+json" \ | |
| "${API_URL}") | |
| DMG_NAME=$(echo "${RELEASE_JSON}" | jq -r '.assets[] | select(.name | endswith("_aarch64.dmg")) | .name' | head -n 1) | |
| DMG_URL=$(echo "${RELEASE_JSON}" | jq -r --arg name "${DMG_NAME}" '.assets[] | select(.name == $name) | .browser_download_url') | |
| DMG_DIGEST=$(echo "${RELEASE_JSON}" | jq -r --arg name "${DMG_NAME}" '.assets[] | select(.name == $name) | .digest') | |
| if [ -z "${DMG_NAME}" ] || [ -z "${DMG_URL}" ] || [ "${DMG_NAME}" = "null" ] || [ "${DMG_URL}" = "null" ]; then | |
| echo "Cannot find macOS dmg asset in release ${TAG}" | |
| exit 1 | |
| fi | |
| DMG_SHA="${DMG_DIGEST#sha256:}" | |
| if [ -z "${DMG_SHA}" ] || [ "${DMG_SHA}" = "null" ] || [ "${DMG_SHA}" = "${DMG_DIGEST}" ]; then | |
| DMG_SHA=$(curl -fsSL "${DMG_URL}" | shasum -a 256 | awk '{print $1}') | |
| fi | |
| echo "version=${VERSION}" >> "${GITHUB_OUTPUT}" | |
| echo "dmg_url=${DMG_URL}" >> "${GITHUB_OUTPUT}" | |
| echo "dmg_sha=${DMG_SHA}" >> "${GITHUB_OUTPUT}" | |
| - name: Validate tap token | |
| env: | |
| TAP_TOKEN: ${{ secrets.HOMEBREW_TAP_TOKEN }} | |
| run: | | |
| if [ -z "${TAP_TOKEN}" ]; then | |
| echo "Missing required secret: HOMEBREW_TAP_TOKEN" | |
| exit 1 | |
| fi | |
| - name: Trigger Homebrew tap bump | |
| run: | | |
| set -euo pipefail | |
| VERSION="${{ steps.asset.outputs.version }}" | |
| DMG_SHA="${{ steps.asset.outputs.dmg_sha }}" | |
| PAYLOAD=$(jq -nc \ | |
| --arg version "${VERSION}" \ | |
| --arg sha256 "${DMG_SHA}" \ | |
| '{event_type:"youran_toolbox_release_published", client_payload:{version:$version, sha256:$sha256}}') | |
| curl -fsSL -X POST \ | |
| -H "Accept: application/vnd.github+json" \ | |
| -H "Authorization: Bearer ${{ secrets.HOMEBREW_TAP_TOKEN }}" \ | |
| "https://api.github.com/repos/dufu1991/homebrew-tap/dispatches" \ | |
| -d "${PAYLOAD}" |