Breeze Multi-Platform Parallel Release #51
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: Breeze Multi-Platform Parallel Release | |
| on: | |
| workflow_dispatch: | |
| inputs: | |
| version: | |
| description: "发布版本号 (例如: v2.12.6)" | |
| required: true | |
| default: "v2.12.6" | |
| permissions: | |
| contents: write | |
| env: | |
| MY_GITHUB_TOKEN: ${{ secrets.MY_GITHUB_TOKEN }} | |
| BREEZE_PLUGIN_GITHUB_TOKEN: ${{ secrets.BREEZE_PLUGIN_GITHUB_TOKEN != '' && | |
| secrets.BREEZE_PLUGIN_GITHUB_TOKEN || github.token }} | |
| jobs: | |
| # ══════════════════════════════════════════ | |
| # 1. Android 构建 (含 git-crypt & Sentry) | |
| # ══════════════════════════════════════════ | |
| build-android: | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v5 | |
| with: | |
| fetch-depth: 0 | |
| # - name: 克隆 rquickjs_playground | |
| # run: git clone https://github.com/deretame/rquickjs_playground.git | |
| - name: 释放空间 (Nuclear Mode) | |
| run: | | |
| sudo rm -rf /opt/hostedtoolcache /usr/local/lib/android /usr/share/dotnet /opt/codeql | |
| sudo mkdir -p /usr/local/lib/android/sdk | |
| sudo chown -R $USER:$USER /usr/local/lib/android | |
| docker system prune -a -f | |
| sudo apt-get clean | |
| - name: 解密敏感文件 (git-crypt) | |
| env: | |
| GIT_CRYPT_KEY: ${{ secrets.GIT_CRYPT_KEY }} | |
| run: | | |
| sudo apt-get update && sudo apt-get install -y git-crypt | |
| echo "$GIT_CRYPT_KEY" | base64 -d > ./git-crypt-key | |
| git-crypt unlock ./git-crypt-key && rm ./git-crypt-key | |
| - name: 设置 Java 21 & SDK | |
| uses: actions/setup-java@v5 | |
| with: | |
| distribution: "temurin" | |
| java-version: "21" | |
| cache: "gradle" | |
| - name: 设置 Android SDK | |
| uses: android-actions/setup-android@v4 | |
| - name: 设置 Dart SDK | |
| uses: dart-lang/setup-dart@v1 | |
| - name: 设置 Flutter (FVM) | |
| run: | | |
| dart pub global activate fvm | |
| echo "$HOME/.pub-cache/bin" >> $GITHUB_PATH | |
| export PATH="$PATH:$HOME/.pub-cache/bin" | |
| fvm install && fvm use --force | |
| echo "$GITHUB_WORKSPACE/.fvm/flutter_sdk/bin" >> $GITHUB_PATH | |
| - name: 设置 pnpm | |
| uses: pnpm/action-setup@v6 | |
| with: | |
| version: 10 | |
| - name: 设置 Node.js | |
| uses: actions/setup-node@v5 | |
| with: | |
| node-version: 22 | |
| - name: 安装 Rust (Android 目标) | |
| uses: dtolnay/rust-toolchain@stable | |
| with: | |
| targets: aarch64-linux-android, armv7-linux-androideabi, x86_64-linux-android | |
| - name: 缓存 Rust 构建产物 | |
| uses: Swatinem/rust-cache@v2 | |
| with: | |
| workspaces: | | |
| rust | |
| - name: 读取 NDK 版本配置 | |
| id: ndk-config | |
| run: | | |
| NDK_VER=$(grep "ndkVersion" android/app/build.gradle.kts | awk -F'"' '{print $2}') | |
| echo "ndk_version=$NDK_VER" >> $GITHUB_OUTPUT | |
| - name: 缓存 Android NDK | |
| uses: actions/cache@v5 | |
| with: | |
| path: /usr/local/lib/android/sdk/ndk/${{ steps.ndk-config.outputs.ndk_version }} | |
| key: ${{ runner.os }}-android-ndk-${{ steps.ndk-config.outputs.ndk_version }} | |
| - name: 安装 Android NDK | |
| run: yes | sdkmanager "ndk;${{ steps.ndk-config.outputs.ndk_version }}" | |
| - name: 设置 NDK 环境变量 | |
| run: | | |
| NDK_VER=${{ steps.ndk-config.outputs.ndk_version }} | |
| ANDROID_HOME=${ANDROID_HOME:-$ANDROID_SDK_ROOT} | |
| NDK_PATH="$ANDROID_HOME/ndk/$NDK_VER" | |
| echo "ANDROID_NDK_HOME=$NDK_PATH" >> $GITHUB_ENV | |
| echo "ANDROID_NDK_ROOT=$NDK_PATH" >> $GITHUB_ENV | |
| echo "$NDK_PATH/toolchains/llvm/prebuilt/linux-x86_64/bin" >> $GITHUB_PATH | |
| - name: 接受 Android Licenses | |
| run: yes | fvm flutter doctor --android-licenses || true | |
| - name: 运行 Android 构建脚本 | |
| env: | |
| SENTRY_DSN: ${{ secrets.SENTRY_DSN }} | |
| SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }} | |
| SENTRY_ORG: ${{ secrets.SENTRY_ORG }} | |
| SENTRY_PROJECT: ${{ secrets.SENTRY_PROJECT }} | |
| run: | | |
| fvm dart ./script/build_apk.dart | |
| - name: 上传 Android 符号表到 Sentry(可选) | |
| continue-on-error: true | |
| working-directory: ${{ github.workspace }} | |
| env: | |
| SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }} | |
| SENTRY_ORG: ${{ secrets.SENTRY_ORG }} | |
| SENTRY_PROJECT: ${{ secrets.SENTRY_PROJECT }} | |
| run: | | |
| if [ -z "$SENTRY_AUTH_TOKEN" ]; then | |
| echo "未配置 SENTRY_AUTH_TOKEN,跳过 Android 符号表上传。" | |
| exit 0 | |
| fi | |
| fvm dart run sentry_dart_plugin --sentry-define=auth_token="$SENTRY_AUTH_TOKEN" | |
| - name: 上传 Android 产物 | |
| uses: actions/upload-artifact@v6 | |
| with: | |
| name: android-apks | |
| path: | | |
| build/app/outputs/flutter-apk/*.apk | |
| build/app/outputs/apk/skia/*.apk | |
| # ══════════════════════════════════════════ | |
| # 2. Linux 构建 (Sentry Plugin 上传) | |
| # ══════════════════════════════════════════ | |
| build-linux: | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v5 | |
| with: | |
| fetch-depth: 0 | |
| # - name: 克隆 rquickjs_playground | |
| # run: git clone https://github.com/deretame/rquickjs_playground.git | |
| - name: 设置 Java 21 | |
| uses: actions/setup-java@v5 | |
| with: | |
| distribution: "temurin" | |
| java-version: "21" | |
| - name: 安装 Flatpak 构建环境 | |
| run: | | |
| sudo apt-get update | |
| sudo apt-get install -y flatpak flatpak-builder ninja-build libgtk-3-dev liblzma-dev libgcrypt20-dev libayatana-appindicator3-dev libwebkit2gtk-4.1-dev libcurl4-openssl-dev | |
| flatpak remote-add --user --if-not-exists flathub https://flathub.org/repo/flathub.flatpakrepo | |
| flatpak install --user -y flathub org.freedesktop.Platform//24.08 org.freedesktop.Sdk//24.08 | |
| - name: 设置 Dart SDK | |
| uses: dart-lang/setup-dart@v1 | |
| - name: 设置 Flutter (FVM) | |
| run: | | |
| dart pub global activate fvm | |
| echo "$HOME/.pub-cache/bin" >> $GITHUB_PATH | |
| export PATH="$PATH:$HOME/.pub-cache/bin" | |
| fvm install && fvm use --force | |
| echo "$GITHUB_WORKSPACE/.fvm/flutter_sdk/bin" >> $GITHUB_PATH | |
| - name: 设置 pnpm | |
| uses: pnpm/action-setup@v6 | |
| with: | |
| version: 10 | |
| - name: 设置 Node.js | |
| uses: actions/setup-node@v5 | |
| with: | |
| node-version: 22 | |
| - name: 安装 Rust 工具链 | |
| uses: dtolnay/rust-toolchain@stable | |
| with: | |
| toolchain: stable | |
| targets: x86_64-unknown-linux-gnu | |
| - name: 缓存 Rust 构建产物 | |
| uses: Swatinem/rust-cache@v2 | |
| with: | |
| workspaces: | | |
| rust | |
| - name: 构建 Linux + 可选上传符号 + 构建 Flatpak | |
| env: | |
| SENTRY_DSN: ${{ secrets.SENTRY_DSN }} | |
| SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }} | |
| SENTRY_ORG: ${{ secrets.SENTRY_ORG }} | |
| SENTRY_PROJECT: ${{ secrets.SENTRY_PROJECT }} | |
| run: | | |
| fvm dart ./script/build_linux_flatpak.dart | |
| - name: 上传 Linux 产物 | |
| uses: actions/upload-artifact@v6 | |
| with: | |
| name: linux-flatpak | |
| path: breeze.flatpak | |
| # ══════════════════════════════════════════ | |
| # 3. Windows 构建 (Sentry Plugin 上传) | |
| # ══════════════════════════════════════════ | |
| build-windows: | |
| runs-on: windows-2025-vs2026 | |
| steps: | |
| - name: 关闭 Git CRLF 转换 | |
| run: git config --global core.autocrlf false | |
| - uses: actions/checkout@v5 | |
| with: | |
| fetch-depth: 0 | |
| # - name: 克隆 rquickjs_playground | |
| # run: git clone https://github.com/deretame/rquickjs_playground.git | |
| - name: 设置 Java 21 | |
| uses: actions/setup-java@v5 | |
| with: | |
| distribution: "temurin" | |
| java-version: "21" | |
| - name: 设置 Dart SDK | |
| uses: dart-lang/setup-dart@v1 | |
| - name: 设置 Flutter (FVM) | |
| run: | | |
| dart pub global activate fvm | |
| fvm install && fvm use --force | |
| echo "$((Get-Item .).FullName)\.fvm\flutter_sdk\bin" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append | |
| - name: 安装 Rust 工具链 | |
| uses: dtolnay/rust-toolchain@stable | |
| - name: 缓存 Rust/Tauri 构建产物 | |
| uses: Swatinem/rust-cache@v2 | |
| with: | |
| workspaces: | | |
| rust | |
| windows-installer/src-tauri | |
| - name: 设置 Node.js & pnpm | |
| uses: pnpm/action-setup@v6 | |
| with: | |
| version: 10 | |
| - uses: actions/setup-node@v5 | |
| with: | |
| node-version: 22 | |
| - name: 运行 Windows 构建脚本 | |
| env: | |
| SENTRY_DSN: ${{ secrets.SENTRY_DSN }} | |
| SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }} | |
| SENTRY_ORG: ${{ secrets.SENTRY_ORG }} | |
| SENTRY_PROJECT: ${{ secrets.SENTRY_PROJECT }} | |
| run: | | |
| fvm flutter pub get | |
| fvm dart ./script/build_windows.dart | |
| - name: 上传 Windows 符号表到 Sentry(可选) | |
| continue-on-error: true | |
| working-directory: ${{ github.workspace }} | |
| env: | |
| SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }} | |
| SENTRY_ORG: ${{ secrets.SENTRY_ORG }} | |
| SENTRY_PROJECT: ${{ secrets.SENTRY_PROJECT }} | |
| run: | | |
| if ([string]::IsNullOrWhiteSpace($env:SENTRY_AUTH_TOKEN)) { | |
| Write-Host "未配置 SENTRY_AUTH_TOKEN,跳过 Windows 符号表上传。" | |
| exit 0 | |
| } | |
| fvm dart run sentry_dart_plugin --sentry-define=auth_token="$env:SENTRY_AUTH_TOKEN" | |
| - name: 上传 Windows 产物 | |
| uses: actions/upload-artifact@v6 | |
| with: | |
| name: windows-exe | |
| path: windows-installer/src-tauri/target/release/windows-installer.exe | |
| # ══════════════════════════════════════════ | |
| # 4. macOS 构建 (Sentry Plugin 上传) | |
| # ══════════════════════════════════════════ | |
| build-macos: | |
| runs-on: macos-latest | |
| env: | |
| CARGO_HOME: ${{ github.workspace }}/.cargo | |
| RUSTUP_HOME: ${{ github.workspace }}/.rustup | |
| steps: | |
| - uses: actions/checkout@v5 | |
| with: | |
| fetch-depth: 0 | |
| - name: Set up Xcode | |
| uses: maxim-lobanov/setup-xcode@v1 | |
| with: | |
| xcode-version: latest-stable | |
| # - name: 克隆 rquickjs_playground | |
| # run: git clone https://github.com/deretame/rquickjs_playground.git | |
| - name: 安装打包依赖 (create-dmg) | |
| run: brew install create-dmg | |
| - name: 设置 Dart SDK | |
| uses: dart-lang/setup-dart@v1 | |
| - name: 设置 Flutter (FVM) | |
| run: | | |
| dart pub global activate fvm | |
| echo "$HOME/.pub-cache/bin" >> $GITHUB_PATH | |
| export PATH="$PATH:$HOME/.pub-cache/bin" | |
| fvm install && fvm use --force | |
| echo "$GITHUB_WORKSPACE/.fvm/flutter_sdk/bin" >> $GITHUB_PATH | |
| - name: 安装 Rust 工具链 | |
| uses: dtolnay/rust-toolchain@stable | |
| with: | |
| targets: aarch64-apple-darwin, x86_64-apple-darwin | |
| - name: 校验并修复 rustup | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| mkdir -p "$CARGO_HOME" "$RUSTUP_HOME" | |
| echo "$CARGO_HOME/bin" >> "$GITHUB_PATH" | |
| export PATH="$CARGO_HOME/bin:$PATH" | |
| echo "which rustup: $(which rustup || true)" | |
| ls -l "$CARGO_HOME/bin"/rustup* || true | |
| if ! rustup --version || ! rustup toolchain list; then | |
| echo "Detected invalid rustup binary, reinstalling..." | |
| rm -f "$CARGO_HOME/bin/rustup" "$CARGO_HOME/bin/rustup-init" | |
| curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --default-toolchain stable --no-modify-path | |
| export PATH="$CARGO_HOME/bin:$PATH" | |
| rustup --version | |
| rustup toolchain list | |
| fi | |
| - name: 缓存 Rust 构建产物 | |
| uses: Swatinem/rust-cache@v2 | |
| with: | |
| workspaces: | | |
| rust | |
| - name: 设置 pnpm | |
| uses: pnpm/action-setup@v6 | |
| with: | |
| version: 10 | |
| - name: 设置 Node.js | |
| uses: actions/setup-node@v5 | |
| with: | |
| node-version: 22 | |
| - name: 拉取依赖 & 修复 Podfile | |
| run: | | |
| fvm flutter pub get | |
| if [ -f "macos/Podfile" ]; then | |
| sed -i '' "s/platform :osx, '[0-9.]*'/platform :osx, '11.0'/g" macos/Podfile | |
| ruby -i -pe ' | |
| if /flutter_additional_macos_build_settings\(target\)/ | |
| $_ += " target.build_configurations.each do |config|\n config.build_settings[\"MACOSX_DEPLOYMENT_TARGET\"] = \"11.0\"\n end\n" | |
| end | |
| ' macos/Podfile | |
| fi | |
| - name: 编译 macOS 产物 | |
| env: | |
| SENTRY_DSN: ${{ secrets.SENTRY_DSN }} | |
| SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }} | |
| SENTRY_ORG: ${{ secrets.SENTRY_ORG }} | |
| SENTRY_PROJECT: ${{ secrets.SENTRY_PROJECT }} | |
| run: | | |
| export PATH="$CARGO_HOME/bin:$PATH" | |
| rustup toolchain list | |
| fvm flutter build macos --release --dart-define=sentry_dsn=$SENTRY_DSN --split-debug-info=build/symbols | |
| - name: 上传 macOS 符号表到 Sentry(可选) | |
| continue-on-error: true | |
| working-directory: ${{ github.workspace }} | |
| env: | |
| SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }} | |
| SENTRY_ORG: ${{ secrets.SENTRY_ORG }} | |
| SENTRY_PROJECT: ${{ secrets.SENTRY_PROJECT }} | |
| run: | | |
| if [ -z "$SENTRY_AUTH_TOKEN" ]; then | |
| echo "未配置 SENTRY_AUTH_TOKEN,跳过 macOS 符号表上传。" | |
| exit 0 | |
| fi | |
| fvm dart run sentry_dart_plugin --sentry-define=auth_token="$SENTRY_AUTH_TOKEN" | |
| - name: 制作 DMG | |
| run: | | |
| APP_PATH=$(find build/macos/Build/Products/Release -name "*.app" -maxdepth 1 | head -n 1) | |
| APP_NAME=$(basename "$APP_PATH") | |
| rm -f "Breeze-macOS.dmg" | |
| create-dmg \ | |
| --volname "Breeze Installer" \ | |
| --window-pos 200 120 \ | |
| --window-size 600 400 \ | |
| --icon-size 100 \ | |
| --icon "$APP_NAME" 150 190 \ | |
| --hide-extension "$APP_NAME" \ | |
| --app-drop-link 450 190 \ | |
| "Breeze-macOS.dmg" \ | |
| "$APP_PATH" | |
| - name: 上传 macOS DMG | |
| uses: actions/upload-artifact@v6 | |
| with: | |
| name: macos-dmg | |
| path: Breeze-macOS.dmg | |
| # ══════════════════════════════════════════ | |
| # 5. iOS 构建 (免签 + Sentry Plugin 上传) | |
| # ══════════════════════════════════════════ | |
| build-ios: | |
| runs-on: macos-latest | |
| env: | |
| CARGO_HOME: ${{ github.workspace }}/.cargo | |
| RUSTUP_HOME: ${{ github.workspace }}/.rustup | |
| steps: | |
| - uses: actions/checkout@v5 | |
| with: | |
| fetch-depth: 0 | |
| - name: Set up Xcode | |
| uses: maxim-lobanov/setup-xcode@v1 | |
| with: | |
| xcode-version: latest-stable | |
| # - name: 克隆 rquickjs_playground | |
| # run: git clone https://github.com/deretame/rquickjs_playground.git | |
| - name: 设置 Dart SDK | |
| uses: dart-lang/setup-dart@v1 | |
| - name: 设置 Flutter (FVM) | |
| run: | | |
| dart pub global activate fvm | |
| echo "$HOME/.pub-cache/bin" >> $GITHUB_PATH | |
| export PATH="$PATH:$HOME/.pub-cache/bin" | |
| fvm install && fvm use --force | |
| echo "$GITHUB_WORKSPACE/.fvm/flutter_sdk/bin" >> $GITHUB_PATH | |
| - name: 安装 Rust 工具链 | |
| uses: dtolnay/rust-toolchain@stable | |
| with: | |
| targets: aarch64-apple-ios | |
| - name: 校验并修复 rustup | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| mkdir -p "$CARGO_HOME" "$RUSTUP_HOME" | |
| echo "$CARGO_HOME/bin" >> "$GITHUB_PATH" | |
| export PATH="$CARGO_HOME/bin:$PATH" | |
| echo "which rustup: $(which rustup || true)" | |
| ls -l "$CARGO_HOME/bin"/rustup* || true | |
| if ! rustup --version || ! rustup toolchain list; then | |
| echo "Detected invalid rustup binary, reinstalling..." | |
| rm -f "$CARGO_HOME/bin/rustup" "$CARGO_HOME/bin/rustup-init" | |
| curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --default-toolchain stable --no-modify-path | |
| export PATH="$CARGO_HOME/bin:$PATH" | |
| rustup --version | |
| rustup toolchain list | |
| fi | |
| - name: 缓存 Rust 构建产物 | |
| uses: Swatinem/rust-cache@v2 | |
| with: | |
| workspaces: | | |
| rust | |
| - name: 设置 pnpm | |
| uses: pnpm/action-setup@v6 | |
| with: | |
| version: 10 | |
| - name: 设置 Node.js | |
| uses: actions/setup-node@v5 | |
| with: | |
| node-version: 22 | |
| - name: 编译 iOS (无签) | |
| env: | |
| SENTRY_DSN: ${{ secrets.SENTRY_DSN }} | |
| SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }} | |
| SENTRY_ORG: ${{ secrets.SENTRY_ORG }} | |
| SENTRY_PROJECT: ${{ secrets.SENTRY_PROJECT }} | |
| run: | | |
| export PATH="$CARGO_HOME/bin:$PATH" | |
| export SDKROOT="$(xcrun --sdk iphoneos --show-sdk-path)" | |
| export CARGO_TARGET_AARCH64_APPLE_IOS_LINKER="$(xcrun --sdk iphoneos --find clang)" | |
| export IPHONEOS_DEPLOYMENT_TARGET="15.0" | |
| export CFLAGS_aarch64_apple_ios="-miphoneos-version-min=$IPHONEOS_DEPLOYMENT_TARGET -isysroot $SDKROOT" | |
| export CXXFLAGS_aarch64_apple_ios="$CFLAGS_aarch64_apple_ios" | |
| export CARGO_TARGET_AARCH64_APPLE_IOS_RUSTFLAGS="-C link-arg=-miphoneos-version-min=$IPHONEOS_DEPLOYMENT_TARGET" | |
| export RUSTFLAGS="$CARGO_TARGET_AARCH64_APPLE_IOS_RUSTFLAGS" | |
| rm -f rust/.cargo/config.toml | |
| echo "--- CARGO_TARGET_AARCH64_APPLE_IOS_LINKER ---" | |
| printf '%s\n' "$CARGO_TARGET_AARCH64_APPLE_IOS_LINKER" | |
| echo "--- SDKROOT ---" | |
| printf '%s\n' "$SDKROOT" | |
| echo "--- IPHONEOS_DEPLOYMENT_TARGET ---" | |
| printf '%s\n' "${IPHONEOS_DEPLOYMENT_TARGET:-<unset>}" | |
| echo "--- RUSTFLAGS ---" | |
| printf '%s\n' "${RUSTFLAGS:-<unset>}" | |
| echo "--- CFLAGS_aarch64_apple_ios ---" | |
| printf '%s\n' "${CFLAGS_aarch64_apple_ios:-<unset>}" | |
| rustup toolchain list | |
| fvm flutter precache --ios | |
| fvm flutter pub get | |
| sed -i '' 's/CODE_SIGNING_REQUIRED = YES;/CODE_SIGNING_REQUIRED = NO;/g' ios/Runner.xcodeproj/project.pbxproj | |
| sed -i '' 's/CODE_SIGNING_ALLOWED = YES;/CODE_SIGNING_ALLOWED = NO;/g' ios/Runner.xcodeproj/project.pbxproj | |
| fvm flutter build ios --release --no-codesign --dart-define=sentry_dsn=$SENTRY_DSN --split-debug-info=build/symbols | |
| - name: 上传 iOS 符号表到 Sentry(可选) | |
| continue-on-error: true | |
| working-directory: ${{ github.workspace }} | |
| env: | |
| SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }} | |
| SENTRY_ORG: ${{ secrets.SENTRY_ORG }} | |
| SENTRY_PROJECT: ${{ secrets.SENTRY_PROJECT }} | |
| run: | | |
| if [ -z "$SENTRY_AUTH_TOKEN" ]; then | |
| echo "未配置 SENTRY_AUTH_TOKEN,跳过 iOS 符号表上传。" | |
| exit 0 | |
| fi | |
| fvm dart run sentry_dart_plugin --sentry-define=auth_token="$SENTRY_AUTH_TOKEN" | |
| - name: 封装 IPA | |
| run: | | |
| mkdir -p Payload | |
| cp -r build/ios/iphoneos/Runner.app Payload/ | |
| zip -r "Breeze-iOS.ipa" Payload | |
| - name: 上传 iOS IPA | |
| uses: actions/upload-artifact@v6 | |
| with: | |
| name: ios-ipa | |
| path: Breeze-iOS.ipa | |
| # ══════════════════════════════════════════ | |
| # 6. 汇总发布至 GitHub Release | |
| # ══════════════════════════════════════════ | |
| publish: | |
| needs: [ build-android, build-linux, build-windows, build-macos, build-ios ] | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v5 | |
| with: | |
| fetch-depth: 0 | |
| - name: 下载所有构建产物 | |
| uses: actions/download-artifact@v4 | |
| with: | |
| path: release-assets | |
| - name: 重命名产物文件(追加版本后缀) | |
| run: | | |
| VERSION="${{ github.event.inputs.version }}" | |
| find release-assets -type f | while IFS= read -r file; do | |
| dir="$(dirname "$file")" | |
| name="$(basename "$file")" | |
| ext="${name##*.}" | |
| base="${name%.*}" | |
| newname="${base}-${VERSION}.${ext}" | |
| mv "$file" "$dir/$newname" | |
| echo "Renamed: $name -> $newname" | |
| done | |
| - name: 提取日志并准备 Release 内容 | |
| id: prepare | |
| run: | | |
| VERSION="${{ github.event.inputs.version }}" | |
| REPO="${{ github.repository }}" | |
| awk -v ver="$VERSION" ' | |
| index($0, "## [" ver "]") == 1 {flag=1; print; next} | |
| flag && /^## \[[^]]+\]/ {exit} | |
| flag | |
| ' CHANGELOG.md > changelog_section.txt | |
| echo "## 📝 更新日志" > release_body.md | |
| cat changelog_section.txt >> release_body.md | |
| echo -e "\n---\n" >> release_body.md | |
| echo "### 📦 推荐下载" >> release_body.md | |
| echo "| 平台 | 下载链接 |" >> release_body.md | |
| echo "| :--- | :--- |" >> release_body.md | |
| echo "| **Windows** | [windows-installer-${VERSION}.exe](https://github.com/$REPO/releases/download/$VERSION/windows-installer-${VERSION}.exe) |" >> release_body.md | |
| echo "| **macOS** | [Breeze-macOS-${VERSION}.dmg](https://github.com/$REPO/releases/download/$VERSION/Breeze-macOS-${VERSION}.dmg) |" >> release_body.md | |
| echo "| **Android** | [app-arm64-v8a-release-${VERSION}.apk](https://github.com/$REPO/releases/download/$VERSION/app-arm64-v8a-release-${VERSION}.apk) |" >> release_body.md | |
| echo "| **iOS** | [Breeze-iOS-${VERSION}.ipa](https://github.com/$REPO/releases/download/$VERSION/Breeze-iOS-${VERSION}.ipa) |" >> release_body.md | |
| echo "| **Linux** | [breeze-${VERSION}.flatpak](https://github.com/$REPO/releases/download/$VERSION/breeze-${VERSION}.flatpak) |" >> release_body.md | |
| echo -e "\n> 💡 **不知道该下载哪个文件?**\n> 请查看 [详细安装指南](https://github.com/$REPO#-%E5%A6%82%E4%BD%95%E5%AE%89%E8%A3%85)。" >> release_body.md | |
| - name: 创建 GitHub Release | |
| uses: softprops/action-gh-release@v2 | |
| with: | |
| tag_name: ${{ github.event.inputs.version }} | |
| name: Breeze ${{ github.event.inputs.version }} | |
| body_path: release_body.md | |
| files: | | |
| release-assets/android-apks/**/*.apk | |
| release-assets/linux-flatpak/*.flatpak | |
| release-assets/windows-exe/*.exe | |
| release-assets/macos-dmg/*.dmg | |
| release-assets/ios-ipa/*.ipa | |
| - name: 发送 Release 到 Telegram | |
| env: | |
| BOT_TOKEN: ${{ secrets.TELEGRAM_BOT_TOKEN }} | |
| CHAT_ID: ${{ secrets.TELEGRAM_CHAT_ID }} | |
| REPO: ${{ github.repository }} | |
| VERSION: ${{ github.event.inputs.version }} | |
| run: | | |
| if [ -z "$BOT_TOKEN" ] || [ -z "$CHAT_ID" ]; then | |
| echo "未配置 TELEGRAM_BOT_TOKEN 或 TELEGRAM_CHAT_ID,跳过 Telegram 通知。" | |
| exit 0 | |
| fi | |
| RELEASE_URL="https://github.com/${REPO}/releases/tag/${VERSION}" | |
| BODY="$(head -n 60 release_body.md)" | |
| CAPTION="$(cat <<EOF | |
| 🚀 ${REPO} 发布新版本 | |
| 版本: ${VERSION} | |
| 链接: ${RELEASE_URL} | |
| ${BODY} | |
| EOF | |
| )" | |
| CAPTION="$(printf '%s' "$CAPTION" | head -c 900)" | |
| mapfile -t files < <(find release-assets -type f \( -name "*.apk" -o -name "*.flatpak" -o -name "*.exe" -o -name "*.dmg" -o -name "*.ipa" \) | sort) | |
| if [ "${#files[@]}" -eq 0 ]; then | |
| echo "未找到附件,发送纯文本消息。" | |
| curl -sS -X POST "https://api.telegram.org/bot${BOT_TOKEN}/sendMessage" \ | |
| --data-urlencode "chat_id=${CHAT_ID}" \ | |
| --data-urlencode "text=${CAPTION}" \ | |
| --data-urlencode "disable_web_page_preview=true" | |
| exit 0 | |
| fi | |
| if [ "${#files[@]}" -eq 1 ]; then | |
| file="${files[0]}" | |
| name="$(basename "$file")" | |
| echo "Uploading single document ${name}" | |
| curl -sS -X POST "https://api.telegram.org/bot${BOT_TOKEN}/sendDocument" \ | |
| -F "chat_id=${CHAT_ID}" \ | |
| -F "caption=${CAPTION}" \ | |
| -F "document=@${file}" | |
| exit 0 | |
| fi | |
| build_media_json() { | |
| local start="$1" | |
| local end="$2" | |
| local include_caption="$3" | |
| local filter='[' | |
| local comma='' | |
| local local_index=0 | |
| local args=() | |
| for ((i=start; i<end; i++)); do | |
| args+=(--arg "media_${local_index}" "attach://file${local_index}") | |
| if [ "$include_caption" = "1" ] && [ "$local_index" -eq 0 ]; then | |
| args+=(--arg caption "$CAPTION") | |
| filter+="${comma}{\"type\":\"document\",\"media\":\$media_${local_index},\"caption\":\$caption}" | |
| else | |
| filter+="${comma}{\"type\":\"document\",\"media\":\$media_${local_index}}" | |
| fi | |
| comma=',' | |
| local_index=$((local_index + 1)) | |
| done | |
| filter+=']' | |
| jq -nc "${args[@]}" "$filter" | |
| } | |
| batch_size=10 | |
| first_batch=1 | |
| total="${#files[@]}" | |
| for ((start=0; start<total; start+=batch_size)); do | |
| end=$((start + batch_size)) | |
| if [ "$end" -gt "$total" ]; then | |
| end="$total" | |
| fi | |
| if [ $((end - start)) -eq 1 ]; then | |
| file="${files[$start]}" | |
| name="$(basename "$file")" | |
| echo "Uploading trailing single document ${name}" | |
| if [ "$first_batch" -eq 1 ]; then | |
| curl -sS -X POST "https://api.telegram.org/bot${BOT_TOKEN}/sendDocument" \ | |
| -F "chat_id=${CHAT_ID}" \ | |
| -F "caption=${CAPTION}" \ | |
| -F "document=@${file}" | |
| first_batch=0 | |
| else | |
| curl -sS -X POST "https://api.telegram.org/bot${BOT_TOKEN}/sendDocument" \ | |
| -F "chat_id=${CHAT_ID}" \ | |
| -F "document=@${file}" | |
| fi | |
| continue | |
| fi | |
| media_json="$(build_media_json "$start" "$end" "$first_batch")" | |
| curl_args=(-sS -X POST "https://api.telegram.org/bot${BOT_TOKEN}/sendMediaGroup" -F "chat_id=${CHAT_ID}" -F "media=${media_json}") | |
| local_index=0 | |
| for ((i=start; i<end; i++)); do | |
| curl_args+=(-F "file${local_index}=@${files[$i]}") | |
| local_index=$((local_index + 1)) | |
| done | |
| echo "Uploading media group with $((end - start)) documents" | |
| curl "${curl_args[@]}" | |
| first_batch=0 | |
| done | |
| # ══════════════════════════════════════════ | |
| # 7. 自动更新 Homebrew Tap (仅限 macOS Cask) | |
| # ══════════════════════════════════════════ | |
| update-homebrew: | |
| needs: [ publish ] | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: 检出 Homebrew Tap 仓库 | |
| uses: actions/checkout@v5 | |
| with: | |
| repository: deretame/homebrew-Breeze | |
| token: ${{ secrets.HOMEBREW_GITHUB_TOKEN }} | |
| path: homebrew-Breeze | |
| - name: 下载 macOS 产物并计算 SHA256 | |
| env: | |
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| VERSION: ${{ github.event.inputs.version }} | |
| run: | | |
| CLEAN_VERSION="${VERSION#v}" | |
| echo "CLEAN_VERSION=$CLEAN_VERSION" >> $GITHUB_ENV | |
| gh release download "$VERSION" --repo "${{ github.repository }}" --pattern "Breeze-macOS-${VERSION}.dmg" | |
| DMG_SHA256=$(sha256sum Breeze-macOS-${VERSION}.dmg | awk '{ print $1 }') | |
| echo "DMG_SHA256: $DMG_SHA256" | |
| echo "DMG_SHA256=$DMG_SHA256" >> $GITHUB_ENV | |
| - name: 更新 Homebrew Cask 文件并清理无用 Formula | |
| working-directory: ./homebrew-Breeze | |
| run: | | |
| # 1. 精准更新 macOS 的 Cask 文件 | |
| CASK_FILE="Casks/breeze.rb" | |
| if [ -f "$CASK_FILE" ]; then | |
| sed -i "s/version \".*\"/version \"$CLEAN_VERSION\"/" "$CASK_FILE" | |
| sed -i "s/sha256 \".*\"/sha256 \"$DMG_SHA256\"/" "$CASK_FILE" | |
| echo "已成功更新 $CASK_FILE" | |
| else | |
| echo "错误:未找到真正的 Cask 文件 $CASK_FILE" | |
| exit 1 | |
| fi | |
| # 2. 彻底删除那个毫无意义的 Android APK 占位 Formula | |
| FORMULA_FILE="Formula/breeze.rb" | |
| if [ -f "$FORMULA_FILE" ]; then | |
| git rm "$FORMULA_FILE" | |
| echo "已自动删除无用的占位文件: $FORMULA_FILE" | |
| fi | |
| - name: 提交并推送到 Homebrew | |
| working-directory: ./homebrew-Breeze | |
| run: | | |
| git config user.name "github-actions[bot]" | |
| git config user.email "github-actions[bot]@users.noreply.github.com" | |
| git add . | |
| if git diff --cached --quiet; then | |
| echo "没有需要提交的变更,跳过推送。" | |
| exit 0 | |
| fi | |
| git commit -m "Update macOS Cask to ${{ github.event.inputs.version }} and clean up placeholders" | |
| git push |