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<> $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"