Skip to content

Breeze Multi-Platform Parallel Release #51

Breeze Multi-Platform Parallel Release

Breeze Multi-Platform Parallel Release #51

Workflow file for this run

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