hi-client/.github/workflows/build-multiplatform.yml
shanshanzhong 6e189671c7
Some checks failed
Build Android APK / 编译 libcore.aar (push) Failing after 3m16s
Build Android APK / 编译 Android APK (release) (push) Has been skipped
Build Clash Core / Build Android (arm64) (push) Failing after 20s
Build Clash Core / Build Android (armv7) (push) Failing after 6s
Build Clash Core / Build Android (x86_64) (push) Failing after 8s
Build Multi-Platform / 编译 libcore (iOS/tvOS) (push) Failing after 7s
Build Windows / 编译 libcore (Windows) (push) Failing after 6s
Build Windows / build (push) Has been skipped
Build Clash Core / Create Release (push) Has been skipped
Build Android APK / 创建 GitHub Release (push) Has been cancelled
Build Multi-Platform / 编译 libcore (Android) (push) Has been cancelled
Build Multi-Platform / 编译 libcore (Windows) (push) Has been cancelled
Build Multi-Platform / 编译 libcore (macOS) (push) Has been cancelled
Build Multi-Platform / 编译 libcore (Linux) (push) Has been cancelled
Build Multi-Platform / 构建 Android APK (push) Has been cancelled
Build Multi-Platform / 构建 Windows (push) Has been cancelled
Build Multi-Platform / 构建 macOS (push) Has been cancelled
Build Multi-Platform / 构建 Linux (push) Has been cancelled
Build Multi-Platform / 创建 Release (push) Has been cancelled
Build Multi-Platform / 构建 iOS (push) Has been cancelled
ci: 将工作流的运行环境从默认改为client-server
2025-11-05 18:06:45 -08:00

951 lines
32 KiB
YAML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

name: Build Multi-Platform
on:
push:
branches:
- main
- develop
tags:
- 'v*'
pull_request:
branches:
- main
workflow_dispatch:
inputs:
build_type:
description: '构建类型'
required: true
default: 'release'
type: choice
options:
- debug
- release
api_domain:
description: 'API 域名'
required: true
default: 'api.maodag.top'
type: string
oss_url_1:
description: 'OSS 配置地址 1'
required: true
default: 'https://ppp2.oss-cn-hongkong.aliyuncs.com/bear1.txt'
type: string
oss_url_2:
description: 'OSS 配置地址 2'
required: true
default: 'https://xgp3.oss-ap-northeast-1.aliyuncs.com/bear1.txt'
type: string
oss_url_3:
description: 'OSS 配置地址 3'
required: true
default: 'https://xpp4.oss-ap-northeast-2.aliyuncs.com/bear1.txt'
type: string
oss_url_4:
description: 'OSS 配置地址 4'
required: true
default: 'https://xpp5.oss-ap-southeast-1.aliyuncs.com/bear1.txt'
type: string
encryption_key:
description: '加密密钥'
required: true
default: 'c0qhq99a-nq8h-ropg-wrlc-ezj4dlkxqpzx'
type: string
platforms:
description: '构建平台 (多选: android,windows,macos,linux,ios)'
required: true
default: 'android,windows,macos,linux'
type: string
jobs:
# ==================== 编译 libcore (iOS/tvOS) ====================
build-libcore-ios:
name: 编译 libcore (iOS/tvOS)
runs-on: client-server
if: contains(inputs.platforms || 'ios', 'ios')
steps:
- name: 📥 Checkout 代码
uses: actions/checkout@v4
with:
submodules: recursive
fetch-depth: 0
- name: 🔧 设置 Go 环境
uses: actions/setup-go@v5
with:
go-version: '1.23'
cache: true
cache-dependency-path: libcore/go.sum
- name: 🔧 设置 Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
- name: 📦 编译 libcore.xcframework (支持 iOS/tvOS)
working-directory: libcore
run: |
echo "🚀 开始编译 iOS/tvOS libcore..."
make ios-full
if [ -d "bin/Libcore.xcframework" ]; then
echo "✅ iOS/tvOS libcore 编译成功"
ls -lh bin/
else
echo "❌ iOS/tvOS libcore 编译失败"
exit 1
fi
- name: 📤 上传 iOS libcore
uses: actions/upload-artifact@v4
with:
name: libcore-ios
path: libcore/bin/Libcore.xcframework
retention-days: 7
# ==================== 编译 libcore (Android) ====================
build-libcore-android:
name: 编译 libcore (Android)
runs-on: ubuntu-latest
if: contains(inputs.platforms || 'android', 'android')
steps:
- name: 📥 Checkout 代码
uses: actions/checkout@v4
with:
submodules: recursive
fetch-depth: 0
- name: 🔧 设置 Go 环境
uses: actions/setup-go@v5
with:
go-version: '1.23'
cache: true
cache-dependency-path: libcore/go.sum
- name: 🔧 设置 Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
- name: 🔧 设置 Java
uses: actions/setup-java@v4
with:
distribution: 'zulu'
java-version: '17'
- name: 🔧 安装 gomobile
run: |
go install golang.org/x/mobile/cmd/gomobile@latest
gomobile init
- name: 📦 编译 libcore.aar
working-directory: libcore
run: |
echo "🚀 开始编译 Android libcore..."
make android
if [ -f "bin/libcore.aar" ]; then
echo "✅ libcore.aar 生成成功"
ls -lh bin/libcore.aar
else
echo "❌ libcore.aar 未找到"
exit 1
fi
- name: 📤 上传 libcore.aar
uses: actions/upload-artifact@v4
with:
name: libcore-android
path: libcore/bin/libcore.aar
retention-days: 7
# ==================== 编译 libcore (Windows) ====================
build-libcore-windows:
name: 编译 libcore (Windows)
runs-on: ubuntu-latest
if: contains(inputs.platforms || 'windows', 'windows')
steps:
- name: 📥 Checkout 代码
uses: actions/checkout@v4
with:
submodules: recursive
fetch-depth: 0
- name: 🔧 设置 Go 环境
uses: actions/setup-go@v5
with:
go-version: '1.23'
cache: true
cache-dependency-path: libcore/go.sum
- name: 🔧 设置 Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
- name: 🔧 安装 MinGW (交叉编译工具)
run: |
sudo apt-get update
sudo apt-get install -y mingw-w64
- name: 📦 编译 libcore.dll
working-directory: libcore
run: |
echo "🚀 开始编译 Windows libcore..."
make windows-amd64
if [ -f "bin/libcore.dll" ] && [ -f "bin/HiddifyCli.exe" ]; then
echo "✅ Windows libcore 编译成功"
ls -lh bin/libcore.dll bin/HiddifyCli.exe
else
echo "❌ Windows libcore 编译失败"
exit 1
fi
- name: 📤 上传 Windows libcore
uses: actions/upload-artifact@v4
with:
name: libcore-windows
path: |
libcore/bin/libcore.dll
libcore/bin/HiddifyCli.exe
libcore/bin/webui/**
retention-days: 7
# ==================== 编译 libcore (macOS) ====================
build-libcore-macos:
name: 编译 libcore (macOS)
runs-on: macos-latest
if: contains(inputs.platforms || 'macos', 'macos')
steps:
- name: 📥 Checkout 代码
uses: actions/checkout@v4
with:
submodules: recursive
fetch-depth: 0
- name: 🔧 设置 Go 环境
uses: actions/setup-go@v5
with:
go-version: '1.23'
cache: true
cache-dependency-path: libcore/go.sum
- name: 🔧 设置 Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
- name: 📦 编译 libcore.dylib
working-directory: libcore
run: |
echo "🚀 开始编译 macOS libcore..."
make macos-universal
if [ -f "bin/libcore.dylib" ] && [ -f "bin/HiddifyCli" ]; then
echo "✅ macOS libcore 编译成功"
ls -lh bin/libcore.dylib bin/HiddifyCli
else
echo "❌ macOS libcore 编译失败"
exit 1
fi
- name: 📤 上传 macOS libcore
uses: actions/upload-artifact@v4
with:
name: libcore-macos
path: |
libcore/bin/libcore.dylib
libcore/bin/HiddifyCli
retention-days: 7
# ==================== 编译 libcore (Linux) ====================
build-libcore-linux:
name: 编译 libcore (Linux)
runs-on: ubuntu-latest
if: contains(inputs.platforms || 'linux', 'linux')
steps:
- name: 📥 Checkout 代码
uses: actions/checkout@v4
with:
submodules: recursive
fetch-depth: 0
- name: 🔧 设置 Go 环境
uses: actions/setup-go@v5
with:
go-version: '1.23'
cache: true
cache-dependency-path: libcore/go.sum
- name: 🔧 设置 Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
- name: 📦 编译 libcore.so
working-directory: libcore
run: |
echo "🚀 开始编译 Linux libcore..."
make linux-amd64
if [ -f "bin/lib/libcore.so" ] && [ -f "bin/HiddifyCli" ]; then
echo "✅ Linux libcore 编译成功"
ls -lh bin/lib/libcore.so bin/HiddifyCli
else
echo "❌ Linux libcore 编译失败"
exit 1
fi
- name: 📤 上传 Linux libcore
uses: actions/upload-artifact@v4
with:
name: libcore-linux
path: |
libcore/bin/lib/libcore.so
libcore/bin/HiddifyCli
libcore/bin/webui/**
retention-days: 7
# ==================== Android 构建 ====================
build-android:
name: 构建 Android APK
needs: build-libcore-android
runs-on: ubuntu-latest
if: contains(inputs.platforms || 'android', 'android')
steps:
- name: 📥 Checkout 代码
uses: actions/checkout@v4
with:
submodules: recursive
fetch-depth: 0
- name: 🔧 设置 Java
uses: actions/setup-java@v4
with:
distribution: 'zulu'
java-version: '17'
- name: 🔧 设置 Flutter
uses: subosito/flutter-action@v2
with:
flutter-version: '3.24.5'
channel: 'stable'
cache: true
- name: 📥 下载 libcore.aar
uses: actions/download-artifact@v4
with:
name: libcore-android
path: android/app/libs/
- name: ⚙️ 配置 API、OSS 和加密密钥
run: |
CONFIG_FILE="lib/app/common/app_config.dart"
API_DOMAIN="${{ inputs.api_domain || 'api.maodag.top' }}"
OSS_URL_1="${{ inputs.oss_url_1 || 'https://ppp2.oss-cn-hongkong.aliyuncs.com/bear1.txt' }}"
OSS_URL_2="${{ inputs.oss_url_2 || 'https://xgp3.oss-ap-northeast-1.aliyuncs.com/bear1.txt' }}"
OSS_URL_3="${{ inputs.oss_url_3 || 'https://xpp4.oss-ap-northeast-2.aliyuncs.com/bear1.txt' }}"
OSS_URL_4="${{ inputs.oss_url_4 || 'https://xpp5.oss-ap-southeast-1.aliyuncs.com/bear1.txt' }}"
ENCRYPTION_KEY="${{ inputs.encryption_key || 'c0qhq99a-nq8h-ropg-wrlc-ezj4dlkxqpzx' }}"
echo "🔧 配置参数:"
echo " API: $API_DOMAIN"
echo " OSS: $OSS_URL_1"
echo " 密钥: ${ENCRYPTION_KEY:0:10}..."
# 转义密钥中的特殊字符(使用 perl 兼容所有平台)
ENCRYPTION_KEY_ESCAPED=$(echo "$ENCRYPTION_KEY" | perl -pe 's/([\[\].*^$()+?{|\\])/\\$1/g')
sed -i "s|api\.maodag\.top|$API_DOMAIN|g" "$CONFIG_FILE"
sed -i "s|https://ppp2\.oss-cn-hongkong\.aliyuncs\.com/bear1\.txt|$OSS_URL_1|g" "$CONFIG_FILE"
sed -i "s|https://xgp3\.oss-ap-northeast-1\.aliyuncs\.com/bear1\.txt|$OSS_URL_2|g" "$CONFIG_FILE"
sed -i "s|https://xpp4\.oss-ap-northeast-2\.aliyuncs\.com/bear1\.txt|$OSS_URL_3|g" "$CONFIG_FILE"
sed -i "s|https://xpp5\.oss-ap-southeast-1\.aliyuncs\.com/bear1\.txt|$OSS_URL_4|g" "$CONFIG_FILE"
sed -i "s|c0qhq99a-nq8h-ropg-wrlc-ezj4dlkxqpzx|$ENCRYPTION_KEY_ESCAPED|g" "$CONFIG_FILE"
echo "✅ 配置完成"
- name: 📦 安装 Flutter 依赖
run: |
flutter pub get
flutter pub run build_runner build --delete-conflicting-outputs
- name: 🔨 构建 APK (Release)
run: |
flutter build apk --release --split-per-abi
- name: 📦 重命名 APK
run: |
COMMIT_SHA=${GITHUB_SHA::7}
DATE=$(date '+%Y%m%d')
cd build/app/outputs/flutter-apk/
for file in app-*-release.apk; do
if [ -f "$file" ]; then
ARCH=$(echo "$file" | sed "s/app-\(.*\)-release.apk/\1/")
NEW_NAME="BearVPN-android-${ARCH}-release-${DATE}-${COMMIT_SHA}.apk"
mv "$file" "$NEW_NAME"
echo "✅ $NEW_NAME"
fi
done
- name: 📤 上传 APK
uses: actions/upload-artifact@v4
with:
name: android-apk
path: build/app/outputs/flutter-apk/*.apk
retention-days: 30
# ==================== Windows 构建 ====================
build-windows:
name: 构建 Windows
needs: build-libcore-windows
runs-on: windows-latest
if: contains(inputs.platforms || 'windows', 'windows')
steps:
- name: 📥 Checkout 代码
uses: actions/checkout@v4
with:
submodules: recursive
fetch-depth: 0
- name: 🔧 设置 Flutter
uses: subosito/flutter-action@v2
with:
flutter-version: '3.24.5'
channel: 'stable'
cache: true
- name: 📥 下载 Windows libcore
uses: actions/download-artifact@v4
with:
name: libcore-windows
path: libcore_windows_temp
- name: 🔧 复制 libcore 文件到正确位置并重命名
run: |
Write-Host "📋 开始复制 libcore 文件..."
# 显示下载的文件结构
Write-Host "🔍 检查下载的文件结构:"
Get-ChildItem -Recurse libcore_windows_temp -ErrorAction SilentlyContinue | Format-Table Name, FullName
# 确保目标目录存在
New-Item -ItemType Directory -Force -Path "libcore\bin" | Out-Null
# 查找 libcore.dll可能在 libcore_windows_temp/bin/ 或 libcore_windows_temp/libcore/bin/
$dllFiles = Get-ChildItem -Path libcore_windows_temp -Recurse -Filter "libcore.dll" -ErrorAction SilentlyContinue
if ($dllFiles) {
$sourceDll = $dllFiles[0].FullName
Write-Host "✅ 找到 libcore.dll: $sourceDll"
Copy-Item $sourceDll "libcore\bin\libcore.dll" -Force
} else {
Write-Host "❌ 未找到 libcore.dll"
Write-Host "当前目录内容:"
Get-ChildItem -Path . -Recurse | Select-Object -First 20 | Format-Table Name, FullName
exit 1
}
# 查找并复制 HiddifyCli.exe重命名为 BearVPNCli.exe
$exeFiles = Get-ChildItem -Path libcore_windows_temp -Recurse -Filter "HiddifyCli.exe" -ErrorAction SilentlyContinue
if ($exeFiles) {
$sourceExe = $exeFiles[0].FullName
Write-Host "✅ 找到 HiddifyCli.exe: $sourceExe"
Write-Host "📝 复制并重命名为 BearVPNCli.exe"
Copy-Item $sourceExe "libcore\bin\BearVPNCli.exe" -Force
Write-Host "✅ 重命名完成HiddifyCli.exe → BearVPNCli.exe"
} else {
Write-Host "⚠️ 未找到 HiddifyCli.exe这不是致命错误"
}
# 复制 webui 目录
$webuiDir = Get-ChildItem -Path libcore_windows_temp -Recurse -Filter "webui" -Directory -ErrorAction SilentlyContinue
if ($webuiDir) {
Write-Host "✅ 找到 webui 目录: $($webuiDir[0].FullName)"
Copy-Item -Path $webuiDir[0].FullName -Destination "libcore\bin\webui" -Recurse -Force
} else {
Write-Host "⚠️ 未找到 webui 目录(这不是致命错误)"
}
Write-Host ""
Write-Host "📄 验证复制后的文件结构:"
if (Test-Path "libcore\bin") {
Get-ChildItem libcore\bin\ -Recurse | Format-Table Name, FullName, Length
} else {
Write-Host "❌ libcore\bin 目录不存在"
}
if (-not (Test-Path "libcore\bin\libcore.dll")) {
Write-Host "❌ libcore.dll 未正确复制到 libcore\bin\"
exit 1
}
Write-Host "✅ libcore 文件复制完成"
shell: pwsh
- name: ⚙️ 配置 API、OSS 和加密密钥
shell: bash
run: |
CONFIG_FILE="lib/app/common/app_config.dart"
API_DOMAIN="${{ inputs.api_domain || 'api.maodag.top' }}"
OSS_URL_1="${{ inputs.oss_url_1 || 'https://ppp2.oss-cn-hongkong.aliyuncs.com/bear1.txt' }}"
OSS_URL_2="${{ inputs.oss_url_2 || 'https://xgp3.oss-ap-northeast-1.aliyuncs.com/bear1.txt' }}"
OSS_URL_3="${{ inputs.oss_url_3 || 'https://xpp4.oss-ap-northeast-2.aliyuncs.com/bear1.txt' }}"
OSS_URL_4="${{ inputs.oss_url_4 || 'https://xpp5.oss-ap-southeast-1.aliyuncs.com/bear1.txt' }}"
ENCRYPTION_KEY="${{ inputs.encryption_key || 'c0qhq99a-nq8h-ropg-wrlc-ezj4dlkxqpzx' }}"
ENCRYPTION_KEY_ESCAPED=$(echo "$ENCRYPTION_KEY" | perl -pe 's/([\[\].*^$()+?{|\\])/\\$1/g')
sed -i "s|api\.maodag\.top|$API_DOMAIN|g" "$CONFIG_FILE"
sed -i "s|https://ppp2\.oss-cn-hongkong\.aliyuncs\.com/bear1\.txt|$OSS_URL_1|g" "$CONFIG_FILE"
sed -i "s|https://xgp3\.oss-ap-northeast-1\.aliyuncs\.com/bear1\.txt|$OSS_URL_2|g" "$CONFIG_FILE"
sed -i "s|https://xpp4\.oss-ap-northeast-2\.aliyuncs\.com/bear1\.txt|$OSS_URL_3|g" "$CONFIG_FILE"
sed -i "s|https://xpp5\.oss-ap-southeast-1\.aliyuncs\.com/bear1\.txt|$OSS_URL_4|g" "$CONFIG_FILE"
sed -i "s|c0qhq99a-nq8h-ropg-wrlc-ezj4dlkxqpzx|$ENCRYPTION_KEY_ESCAPED|g" "$CONFIG_FILE"
- name: 📦 安装 Flutter 依赖
run: |
flutter pub get
- name: 🔧 生成代码文件 (build_runner)
run: |
echo "🔧 开始运行 build_runner..."
flutter pub run build_runner build --delete-conflicting-outputs
echo ""
echo "✅ build_runner 完成,检查生成的文件..."
if (Test-Path "lib\singbox\model\singbox_status.freezed.dart") {
echo "✅ singbox_status.freezed.dart 已生成"
} else {
echo "❌ singbox_status.freezed.dart 未生成"
exit 1
}
if (Test-Path "lib\singbox\service\singbox_service_provider.g.dart") {
echo "✅ singbox_service_provider.g.dart 已生成"
} else {
echo "❌ singbox_service_provider.g.dart 未生成"
exit 1
}
shell: pwsh
- name: 🔧 验证 libcore 文件存在
run: |
Write-Host "📋 验证 libcore 文件是否存在..."
if (Test-Path "libcore\bin\libcore.dll") {
$dllInfo = Get-Item "libcore\bin\libcore.dll"
Write-Host "✅ libcore.dll 存在: $($dllInfo.FullName) - 大小: $($dllInfo.Length) bytes"
} else {
Write-Host "❌ libcore.dll 不存在"
Write-Host "当前 libcore\bin 目录内容:"
if (Test-Path "libcore\bin") {
Get-ChildItem "libcore\bin" | Format-Table Name, FullName, Length
} else {
Write-Host "libcore\bin 目录不存在"
}
exit 1
}
if (Test-Path "libcore\bin\BearVPNCli.exe") {
$exeInfo = Get-Item "libcore\bin\BearVPNCli.exe"
Write-Host "✅ BearVPNCli.exe 存在: $($exeInfo.FullName) - 大小: $($exeInfo.Length) bytes"
} else {
Write-Host "⚠️ BearVPNCli.exe 不存在"
}
shell: pwsh
- name: 🔨 构建 Windows (Release)
run: |
flutter build windows --release
- name: 🔍 验证 Windows 文件结构
run: |
Write-Host "📋 检查 Release 目录文件结构..."
$releaseDir = "build\windows\x64\runner\Release"
if (Test-Path $releaseDir) {
Write-Host "✅ Release 目录存在"
Write-Host ""
Write-Host "📄 文件列表:"
Get-ChildItem $releaseDir | Format-Table Name, Length, LastWriteTime
# 检查关键文件
if (Test-Path "$releaseDir\BearVPN.exe") {
Write-Host "✅ BearVPN.exe 存在"
} else {
Write-Host "❌ BearVPN.exe 不存在"
}
if (Test-Path "$releaseDir\BearVPNCli.exe") {
Write-Host "✅ BearVPNCli.exe 存在"
} else {
Write-Host "⚠️ BearVPNCli.exe 不存在"
}
if (Test-Path "$releaseDir\libcore.dll") {
Write-Host "✅ libcore.dll 存在"
} else {
Write-Host "❌ libcore.dll 不存在"
}
} else {
Write-Host "❌ Release 目录不存在"
}
shell: pwsh
- name: 📦 打包 Windows
shell: bash
run: |
COMMIT_SHA=${GITHUB_SHA::7}
DATE=$(date '+%Y%m%d')
cd build/windows/x64/runner/Release
7z a -tzip "../../../../../BearVPN-windows-x64-release-${DATE}-${COMMIT_SHA}.zip" ./*
- name: 📤 上传 Windows
uses: actions/upload-artifact@v4
with:
name: windows-x64
path: BearVPN-windows-*.zip
retention-days: 30
# ==================== macOS 构建 ====================
build-macos:
name: 构建 macOS
needs: build-libcore-macos
runs-on: macos-latest
if: contains(inputs.platforms || 'macos', 'macos')
steps:
- name: 📥 Checkout 代码
uses: actions/checkout@v4
with:
submodules: recursive
fetch-depth: 0
- name: 🔧 设置 Flutter
uses: subosito/flutter-action@v2
with:
flutter-version: '3.24.5'
channel: 'stable'
cache: true
- name: 📥 下载 macOS libcore
uses: actions/download-artifact@v4
with:
name: libcore-macos
path: libcore/bin/
- name: 🔧 设置 libcore 执行权限
run: |
chmod +x libcore/bin/HiddifyCli
- name: ⚙️ 配置 API、OSS 和加密密钥
run: |
CONFIG_FILE="lib/app/common/app_config.dart"
API_DOMAIN="${{ inputs.api_domain || 'api.maodag.top' }}"
OSS_URL_1="${{ inputs.oss_url_1 || 'https://ppp2.oss-cn-hongkong.aliyuncs.com/bear1.txt' }}"
OSS_URL_2="${{ inputs.oss_url_2 || 'https://xgp3.oss-ap-northeast-1.aliyuncs.com/bear1.txt' }}"
OSS_URL_3="${{ inputs.oss_url_3 || 'https://xpp4.oss-ap-northeast-2.aliyuncs.com/bear1.txt' }}"
OSS_URL_4="${{ inputs.oss_url_4 || 'https://xpp5.oss-ap-southeast-1.aliyuncs.com/bear1.txt' }}"
ENCRYPTION_KEY="${{ inputs.encryption_key || 'c0qhq99a-nq8h-ropg-wrlc-ezj4dlkxqpzx' }}"
# macOS 使用 BSD sed转义特殊字符
# 使用 perl 来转义特殊字符,更可靠
ENCRYPTION_KEY_ESCAPED=$(echo "$ENCRYPTION_KEY" | perl -pe 's/([\[\].*^$()+?{|\\])/\\$1/g')
echo "🔧 配置参数:"
echo " API: $API_DOMAIN"
echo " 密钥: ${ENCRYPTION_KEY:0:10}..."
# macOS sed 需要使用 -i '' 和正确的语法
sed -i '' "s|api\.maodag\.top|$API_DOMAIN|g" "$CONFIG_FILE"
sed -i '' "s|https://ppp2\.oss-cn-hongkong\.aliyuncs\.com/bear1\.txt|$OSS_URL_1|g" "$CONFIG_FILE"
sed -i '' "s|https://xgp3\.oss-ap-northeast-1\.aliyuncs\.com/bear1\.txt|$OSS_URL_2|g" "$CONFIG_FILE"
sed -i '' "s|https://xpp4\.oss-ap-northeast-2\.aliyuncs\.com/bear1\.txt|$OSS_URL_3|g" "$CONFIG_FILE"
sed -i '' "s|https://xpp5\.oss-ap-southeast-1\.aliyuncs\.com/bear1\.txt|$OSS_URL_4|g" "$CONFIG_FILE"
# 使用不同的分隔符避免转义问题
sed -i '' "s|c0qhq99a-nq8h-ropg-wrlc-ezj4dlkxqpzx|$ENCRYPTION_KEY_ESCAPED|g" "$CONFIG_FILE"
echo "✅ 配置完成"
- name: 📦 安装 Flutter 依赖
run: |
flutter pub get
flutter pub run build_runner build --delete-conflicting-outputs
- name: 🔨 构建 macOS (Release)
run: |
flutter build macos --release
- name: 📦 打包 macOS
run: |
COMMIT_SHA=${GITHUB_SHA::7}
DATE=$(date '+%Y%m%d')
cd build/macos/Build/Products/Release
zip -r -y "../../../../../BearVPN-macos-release-${DATE}-${COMMIT_SHA}.zip" BearVPN.app
- name: 📤 上传 macOS
uses: actions/upload-artifact@v4
with:
name: macos-app
path: BearVPN-macos-*.zip
retention-days: 30
# ==================== Linux 构建 ====================
build-linux:
name: 构建 Linux
needs: build-libcore-linux
runs-on: ubuntu-latest
if: contains(inputs.platforms || 'linux', 'linux')
steps:
- name: 📥 Checkout 代码
uses: actions/checkout@v4
with:
submodules: recursive
fetch-depth: 0
- name: 🔧 安装 Linux 依赖
run: |
sudo apt-get update
sudo apt-get install -y \
clang \
cmake \
ninja-build \
pkg-config \
libgtk-3-dev \
liblzma-dev \
libstdc++-12-dev \
libayatana-appindicator3-dev
- name: 🔧 设置 Flutter
uses: subosito/flutter-action@v2
with:
flutter-version: '3.24.5'
channel: 'stable'
cache: true
- name: 📥 下载 Linux libcore
uses: actions/download-artifact@v4
with:
name: libcore-linux
path: libcore/bin/
- name: 🔧 设置 libcore 执行权限
run: |
chmod +x libcore/bin/HiddifyCli
- name: ⚙️ 配置 API、OSS 和加密密钥
run: |
CONFIG_FILE="lib/app/common/app_config.dart"
API_DOMAIN="${{ inputs.api_domain || 'api.maodag.top' }}"
OSS_URL_1="${{ inputs.oss_url_1 || 'https://ppp2.oss-cn-hongkong.aliyuncs.com/bear1.txt' }}"
OSS_URL_2="${{ inputs.oss_url_2 || 'https://xgp3.oss-ap-northeast-1.aliyuncs.com/bear1.txt' }}"
OSS_URL_3="${{ inputs.oss_url_3 || 'https://xpp4.oss-ap-northeast-2.aliyuncs.com/bear1.txt' }}"
OSS_URL_4="${{ inputs.oss_url_4 || 'https://xpp5.oss-ap-southeast-1.aliyuncs.com/bear1.txt' }}"
ENCRYPTION_KEY="${{ inputs.encryption_key || 'c0qhq99a-nq8h-ropg-wrlc-ezj4dlkxqpzx' }}"
ENCRYPTION_KEY_ESCAPED=$(echo "$ENCRYPTION_KEY" | perl -pe 's/([\[\].*^$()+?{|\\])/\\$1/g')
sed -i "s|api\.maodag\.top|$API_DOMAIN|g" "$CONFIG_FILE"
sed -i "s|https://ppp2\.oss-cn-hongkong\.aliyuncs\.com/bear1\.txt|$OSS_URL_1|g" "$CONFIG_FILE"
sed -i "s|https://xgp3\.oss-ap-northeast-1\.aliyuncs\.com/bear1\.txt|$OSS_URL_2|g" "$CONFIG_FILE"
sed -i "s|https://xpp4\.oss-ap-northeast-2\.aliyuncs\.com/bear1\.txt|$OSS_URL_3|g" "$CONFIG_FILE"
sed -i "s|https://xpp5\.oss-ap-southeast-1\.aliyuncs\.com/bear1\.txt|$OSS_URL_4|g" "$CONFIG_FILE"
sed -i "s|c0qhq99a-nq8h-ropg-wrlc-ezj4dlkxqpzx|$ENCRYPTION_KEY_ESCAPED|g" "$CONFIG_FILE"
- name: 📦 安装 Flutter 依赖
run: |
flutter pub get
flutter pub run build_runner build --delete-conflicting-outputs
- name: 🔨 构建 Linux (Release)
run: |
flutter build linux --release
- name: 📦 打包 Linux
run: |
COMMIT_SHA=${GITHUB_SHA::7}
DATE=$(date '+%Y%m%d')
cd build/linux/x64/release/bundle
tar -czf "../../../../../BearVPN-linux-x64-release-${DATE}-${COMMIT_SHA}.tar.gz" ./*
- name: 📤 上传 Linux
uses: actions/upload-artifact@v4
with:
name: linux-x64
path: BearVPN-linux-*.tar.gz
retention-days: 30
# ==================== iOS 构建 ====================
build-ios:
name: 构建 iOS
needs: build-libcore-ios
runs-on: macos-latest
if: contains(inputs.platforms || 'ios', 'ios')
steps:
- name: 📥 Checkout 代码
uses: actions/checkout@v4
with:
submodules: recursive
fetch-depth: 0
- name: 🔧 设置 Flutter
uses: subosito/flutter-action@v2
with:
flutter-version: '3.24.5'
channel: 'stable'
cache: true
- name: 📥 下载 iOS libcore
uses: actions/download-artifact@v4
with:
name: libcore-ios
path: ios/Frameworks/
- name: ⚙️ 配置 API、OSS 和加密密钥
run: |
CONFIG_FILE="lib/app/common/app_config.dart"
API_DOMAIN="${{ inputs.api_domain || 'api.maodag.top' }}"
OSS_URL_1="${{ inputs.oss_url_1 || 'https://ppp2.oss-cn-hongkong.aliyuncs.com/bear1.txt' }}"
OSS_URL_2="${{ inputs.oss_url_2 || 'https://xgp3.oss-ap-northeast-1.aliyuncs.com/bear1.txt' }}"
OSS_URL_3="${{ inputs.oss_url_3 || 'https://xpp4.oss-ap-northeast-2.aliyuncs.com/bear1.txt' }}"
OSS_URL_4="${{ inputs.oss_url_4 || 'https://xpp5.oss-ap-southeast-1.aliyuncs.com/bear1.txt' }}"
ENCRYPTION_KEY="${{ inputs.encryption_key || 'c0qhq99a-nq8h-ropg-wrlc-ezj4dlkxqpzx' }}"
# 使用 perl 转义特殊字符
ENCRYPTION_KEY_ESCAPED=$(echo "$ENCRYPTION_KEY" | perl -pe 's/([\[\].*^$()+?{|\\])/\\$1/g')
sed -i '' "s|api\.maodag\.top|$API_DOMAIN|g" "$CONFIG_FILE"
sed -i '' "s|https://ppp2\.oss-cn-hongkong\.aliyuncs\.com/bear1\.txt|$OSS_URL_1|g" "$CONFIG_FILE"
sed -i '' "s|https://xgp3\.oss-ap-northeast-1\.aliyuncs\.com/bear1\.txt|$OSS_URL_2|g" "$CONFIG_FILE"
sed -i '' "s|https://xpp4\.oss-ap-northeast-2\.aliyuncs\.com/bear1\.txt|$OSS_URL_3|g" "$CONFIG_FILE"
sed -i '' "s|https://xpp5\.oss-ap-southeast-1\.aliyuncs\.com/bear1\.txt|$OSS_URL_4|g" "$CONFIG_FILE"
sed -i '' "s|c0qhq99a-nq8h-ropg-wrlc-ezj4dlkxqpzx|$ENCRYPTION_KEY_ESCAPED|g" "$CONFIG_FILE"
- name: 📦 安装 Flutter 依赖
run: |
flutter pub get
flutter pub run build_runner build --delete-conflicting-outputs
- name: 🔨 构建 iOS (Release)
run: |
flutter build ios --release --no-codesign
- name: 📦 打包 iOS
run: |
COMMIT_SHA=${GITHUB_SHA::7}
DATE=$(date '+%Y%m%d')
cd build/ios/iphoneos
# 创建 Payload 目录
mkdir -p Payload
cp -r Runner.app Payload/
# 创建 IPA 文件
zip -r "../../../../BearVPN-ios-release-${DATE}-${COMMIT_SHA}.ipa" Payload
echo "✅ iOS IPA 创建完成"
ls -lh BearVPN-ios-*.ipa
- name: 📤 上传 iOS
uses: actions/upload-artifact@v4
with:
name: ios-app
path: BearVPN-ios-*.ipa
retention-days: 30
# ==================== 创建 Release ====================
create-release:
name: 创建 Release
needs: [build-android, build-windows, build-macos, build-linux, build-ios]
runs-on: ubuntu-latest
if: startsWith(github.ref, 'refs/tags/v')
steps:
- name: 📥 下载所有构建产物
uses: actions/download-artifact@v4
with:
path: artifacts
- name: 📋 生成 Release 说明
id: release_notes
run: |
cat > release_notes.md << 'EOF'
## 🎉 BearVPN 多平台版本发布
### 📦 平台支持
| 平台 | 架构 | 文件 |
|-----|------|-----|
| **Android** | arm64-v8a | BearVPN-android-arm64-v8a-*.apk |
| **Android** | armeabi-v7a | BearVPN-android-armeabi-v7a-*.apk |
| **Android** | x86_64 | BearVPN-android-x86_64-*.apk |
| **iOS** | Universal | BearVPN-ios-*.ipa |
| **Windows** | x64 | BearVPN-windows-x64-*.zip |
| **macOS** | Universal | BearVPN-macos-*.zip |
| **Linux** | x64 | BearVPN-linux-x64-*.tar.gz |
### ✨ 主要特性
- ✅ 支持 Shadowsocks/VLESS/Trojan/Hysteria2
- ✅ 内置 sing-box 核心 v3.1.7
- ✅ 跨平台支持
- ✅ 自定义路由规则
### 📥 安装指南
**Android:** 下载 APK 直接安装
**iOS:** 下载 IPA 使用 AltStore/Sideloadly 安装
**Windows:** 解压 ZIP 运行 BearVPN.exe
**macOS:** 解压 ZIP 拖拽到应用程序
**Linux:** 解压 tar.gz 运行可执行文件
---
**构建信息:**
- 提交: ${GITHUB_SHA::7}
- 时间: $(date '+%Y-%m-%d %H:%M:%S UTC')
- Flutter: 3.24.5
- sing-box: v3.1.7
EOF
echo "release_notes<<EOF" >> $GITHUB_OUTPUT
cat release_notes.md >> $GITHUB_OUTPUT
echo "EOF" >> $GITHUB_OUTPUT
- name: 🚀 创建 Release
uses: softprops/action-gh-release@v2
with:
files: |
artifacts/android-apk/*.apk
artifacts/ios-app/*.ipa
artifacts/windows-x64/*.zip
artifacts/macos-app/*.zip
artifacts/linux-x64/*.tar.gz
body: ${{ steps.release_notes.outputs.release_notes }}
draft: false
prerelease: false
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: 📢 构建完成
run: |
echo "✅ 所有平台构建完成!"
echo "📦 Release: ${{ github.ref }}"
echo "🔗 https://github.com/${{ github.repository }}/releases"