name: Release on: push: tags: - 'v*' paths-ignore: - 'docs/**' - '*.md' - '.github/workflows/deploy-docs.yml' - '.github/workflows/update-homebrew.yml' workflow_dispatch: inputs: version: description: 'Version to release (e.g., v0.1.0)' required: true default: 'v0.1.0' permissions: contents: write packages: write actions: read env: CARGO_NET_RETRY: 10 RUSTUP_MAX_RETRIES: 10 CARGO_TERM_COLOR: always jobs: build: strategy: fail-fast: false matrix: include: - platform: macos-latest target: aarch64-apple-darwin name: macOS-arm64 - platform: macos-latest target: x86_64-apple-darwin name: macOS-x64 - platform: windows-latest target: x86_64-pc-windows-msvc name: Windows-x64 - platform: ubuntu-22.04 target: x86_64-unknown-linux-gnu name: Linux-x64 runs-on: ${{ matrix.platform }} steps: - name: Checkout uses: actions/checkout@v4 with: fetch-depth: 0 - name: Setup Node.js uses: actions/setup-node@v4 with: node-version: '20' - name: Setup pnpm uses: pnpm/action-setup@v4 with: version: 9 - name: Install Linux dependencies 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 libssl-dev pkg-config libasound2-dev libxdo-dev - name: Setup Rust uses: dtolnay/rust-toolchain@stable with: targets: ${{ matrix.target }} - name: Setup sccache uses: mozilla-actions/sccache-action@v0.0.9 - name: Setup Rust cache uses: Swatinem/rust-cache@v2 with: workspaces: src-tauri shared-key: "rust-${{ matrix.target }}" cache-on-failure: true cache-all-crates: true save-if: ${{ github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/tags/') }} - name: Sync version from tag to tauri.conf.json shell: bash run: | VERSION="${{ github.event.inputs.version || github.ref_name }}" VERSION="${VERSION#v}" echo "Syncing version to $VERSION" cd src-tauri node -e " const fs = require('fs'); const conf = JSON.parse(fs.readFileSync('tauri.conf.json', 'utf8')); conf.version = '$VERSION'; fs.writeFileSync('tauri.conf.json', JSON.stringify(conf, null, 2) + '\n'); " echo "tauri.conf.json version is now: $(node -e "console.log(JSON.parse(require('fs').readFileSync('tauri.conf.json','utf8')).version)")" - name: Load release notes shell: bash run: | if [ -f RELEASE_NOTES.md ]; then { echo "RELEASE_BODY<> "$GITHUB_ENV" else # fallback: 从 git log 生成简单 changelog TAG="${{ github.event.inputs.version || github.ref_name }}" PREV_TAG=$(git tag --sort=-v:refname | grep -v "^${TAG}$" | head -n 1) if [ -z "$PREV_TAG" ]; then CHANGELOG=$(git log --pretty=format:"- %s (%h)" ${TAG} 2>/dev/null || git log --pretty=format:"- %s (%h)") else CHANGELOG=$(git log --pretty=format:"- %s (%h)" ${PREV_TAG}..${TAG} 2>/dev/null || git log --pretty=format:"- %s (%h)" ${PREV_TAG}..HEAD) fi { echo "RELEASE_BODY<> "$GITHUB_ENV" fi - name: Install frontend dependencies run: pnpm install --no-frozen-lockfile # macOS 签名证书设置 - name: Import Apple Certificate if: matrix.platform == 'macos-latest' env: APPLE_CERTIFICATE: ${{ secrets.APPLE_CERTIFICATE }} APPLE_CERTIFICATE_PASSWORD: ${{ secrets.APPLE_CERTIFICATE_PASSWORD }} run: | # 创建临时 keychain KEYCHAIN_PATH=$RUNNER_TEMP/app-signing.keychain-db KEYCHAIN_PASSWORD=${{ secrets.KEYCHAIN_PASSWORD }} # 解码证书 echo -n "$APPLE_CERTIFICATE" | base64 --decode > $RUNNER_TEMP/certificate.p12 # 创建 keychain security create-keychain -p "$KEYCHAIN_PASSWORD" $KEYCHAIN_PATH security set-keychain-settings -lut 21600 $KEYCHAIN_PATH security unlock-keychain -p "$KEYCHAIN_PASSWORD" $KEYCHAIN_PATH # 导入证书 security import $RUNNER_TEMP/certificate.p12 -P "$APPLE_CERTIFICATE_PASSWORD" -A -t cert -f pkcs12 -k $KEYCHAIN_PATH security set-key-partition-list -S apple-tool:,apple: -k "$KEYCHAIN_PASSWORD" $KEYCHAIN_PATH security list-keychain -d user -s $KEYCHAIN_PATH - name: Build Tauri app (Linux) if: matrix.platform == 'ubuntu-22.04' uses: tauri-apps/tauri-action@v0 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # 禁用 LTO 加速编译(正式发布可改为 thin) CARGO_PROFILE_RELEASE_LTO: "off" # 增加并行编译单元 CARGO_PROFILE_RELEASE_CODEGEN_UNITS: 32 CARGO_INCREMENTAL: 0 SCCACHE_GHA_ENABLED: "true" RUSTC_WRAPPER: sccache OPENSSL_NO_VENDOR: "1" with: tauriScript: npx tauri tagName: ${{ github.event.inputs.version || github.ref_name }} releaseName: 'ProxyCast ${{ github.event.inputs.version || github.ref_name }}' releaseBody: ${{ env.RELEASE_BODY }} releaseDraft: false prerelease: false # 默认不启用 voice feature(包含 whisper-rs,编译很慢) args: --target ${{ matrix.target }} - name: Build Tauri app (Windows) if: matrix.platform == 'windows-latest' uses: tauri-apps/tauri-action@v0 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # 禁用 LTO 加速编译(正式发布可改为 thin) CARGO_PROFILE_RELEASE_LTO: "off" # 增加并行编译单元 CARGO_PROFILE_RELEASE_CODEGEN_UNITS: 32 CARGO_INCREMENTAL: 0 SCCACHE_GHA_ENABLED: "true" RUSTC_WRAPPER: sccache with: tauriScript: npx tauri tagName: ${{ github.event.inputs.version || github.ref_name }} releaseName: 'ProxyCast ${{ github.event.inputs.version || github.ref_name }}' releaseBody: ${{ env.RELEASE_BODY }} releaseDraft: false prerelease: false # 默认不启用 voice feature(包含 whisper-rs,编译很慢) args: --target ${{ matrix.target }} - name: Build Tauri app (macOS with notarization, attempt 1) id: build_macos_primary if: matrix.platform == 'macos-latest' continue-on-error: true uses: tauri-apps/tauri-action@v0 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # 禁用 LTO 加速编译(正式发布可改为 thin) CARGO_PROFILE_RELEASE_LTO: "off" # 增加并行编译单元 CARGO_PROFILE_RELEASE_CODEGEN_UNITS: 32 CARGO_INCREMENTAL: 0 SCCACHE_GHA_ENABLED: "true" RUSTC_WRAPPER: sccache # Apple 签名环境变量 APPLE_CERTIFICATE: ${{ secrets.APPLE_CERTIFICATE }} APPLE_CERTIFICATE_PASSWORD: ${{ secrets.APPLE_CERTIFICATE_PASSWORD }} APPLE_SIGNING_IDENTITY: ${{ secrets.APPLE_SIGNING_IDENTITY }} # Apple 公证环境变量(解决 Gatekeeper 警告) APPLE_ID: ${{ secrets.APPLE_ID }} APPLE_PASSWORD: ${{ secrets.APPLE_PASSWORD }} APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }} with: tauriScript: npx tauri tagName: ${{ github.event.inputs.version || github.ref_name }} releaseName: 'ProxyCast ${{ github.event.inputs.version || github.ref_name }}' releaseBody: ${{ env.RELEASE_BODY }} releaseDraft: false prerelease: false # 默认不启用 voice feature(包含 whisper-rs,编译很慢) args: --target ${{ matrix.target }} - name: Build Tauri app (macOS with notarization, retry) id: build_macos_retry if: matrix.platform == 'macos-latest' && steps.build_macos_primary.outcome == 'failure' continue-on-error: true uses: tauri-apps/tauri-action@v0 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} CARGO_PROFILE_RELEASE_LTO: "off" CARGO_PROFILE_RELEASE_CODEGEN_UNITS: 32 CARGO_INCREMENTAL: 0 SCCACHE_GHA_ENABLED: "true" RUSTC_WRAPPER: sccache APPLE_CERTIFICATE: ${{ secrets.APPLE_CERTIFICATE }} APPLE_CERTIFICATE_PASSWORD: ${{ secrets.APPLE_CERTIFICATE_PASSWORD }} APPLE_SIGNING_IDENTITY: ${{ secrets.APPLE_SIGNING_IDENTITY }} APPLE_ID: ${{ secrets.APPLE_ID }} APPLE_PASSWORD: ${{ secrets.APPLE_PASSWORD }} APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }} with: tauriScript: npx tauri tagName: ${{ github.event.inputs.version || github.ref_name }} releaseName: 'ProxyCast ${{ github.event.inputs.version || github.ref_name }}' releaseBody: ${{ env.RELEASE_BODY }} releaseDraft: false prerelease: false args: --target ${{ matrix.target }} - name: Build Tauri app (macOS fallback without notarization) if: matrix.platform == 'macos-latest' && steps.build_macos_primary.outcome == 'failure' && steps.build_macos_retry.outcome == 'failure' uses: tauri-apps/tauri-action@v0 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} CARGO_PROFILE_RELEASE_LTO: "off" CARGO_PROFILE_RELEASE_CODEGEN_UNITS: 32 CARGO_INCREMENTAL: 0 SCCACHE_GHA_ENABLED: "true" RUSTC_WRAPPER: sccache APPLE_CERTIFICATE: ${{ secrets.APPLE_CERTIFICATE }} APPLE_CERTIFICATE_PASSWORD: ${{ secrets.APPLE_CERTIFICATE_PASSWORD }} APPLE_SIGNING_IDENTITY: ${{ secrets.APPLE_SIGNING_IDENTITY }} with: tauriScript: npx tauri tagName: ${{ github.event.inputs.version || github.ref_name }} releaseName: 'ProxyCast ${{ github.event.inputs.version || github.ref_name }}' releaseBody: ${{ env.RELEASE_BODY }} releaseDraft: false prerelease: false args: --target ${{ matrix.target }} - name: Warn on macOS notarization fallback if: matrix.platform == 'macos-latest' && steps.build_macos_primary.outcome == 'failure' && steps.build_macos_retry.outcome == 'failure' run: | echo "::warning::macOS notarization failed twice; fallback build skipped notarization and produced a signed-only artifact." # 注意:移除了 Post-build cleanup 步骤 # 之前的清理会删除 deps/build/incremental 目录,导致缓存无法复用 # 保留这些文件可以让 rust-cache 更好地工作 - name: Show sccache stats run: sccache --show-stats # 清理 keychain - name: Cleanup Keychain if: matrix.platform == 'macos-latest' && always() run: | security delete-keychain $RUNNER_TEMP/app-signing.keychain-db || true