fix: 精简脚本
This commit is contained in:
parent
b90d451bcc
commit
c9a6ad2046
@ -1,343 +0,0 @@
|
||||
# ✅ 旧数据清理修复总结
|
||||
|
||||
## 🎯 修复完成
|
||||
|
||||
**修复日期**: 2025-10-31
|
||||
**修复对象**: 每次安装APP时个人中心显示旧邮箱账号 `calvin.duke@hotmail.com` 的问题
|
||||
**修复状态**: ✅ **完成并通过验证**
|
||||
|
||||
---
|
||||
|
||||
## 📊 修复内容概览
|
||||
|
||||
本次修复包含**三层防护机制**,确保不会出现旧数据残留问题。
|
||||
|
||||
| 层级 | 文件 | 修复方法 | 优先级 |
|
||||
|-----|------|--------|------|
|
||||
| 1️⃣ 应用启动层 | `kr_splash_controller.dart` | DEBUG模式自动清理 | 最高 |
|
||||
| 2️⃣ 数据验证层 | `app_run_data.dart` | Token合法性检查 | 高 |
|
||||
| 3️⃣ 打包预防层 | `clean_build_cache.sh` | 打包前清理脚本 | 中 |
|
||||
|
||||
---
|
||||
|
||||
## 🔧 详细修改清单
|
||||
|
||||
### 修改1: kr_splash_controller.dart
|
||||
|
||||
**新增文件导入** (第7, 10行):
|
||||
```dart
|
||||
import 'package:flutter/foundation.dart' show kDebugMode;
|
||||
import 'package:kaer_with_panels/app/utils/kr_secure_storage.dart';
|
||||
```
|
||||
|
||||
**修改onInit方法** (第50-55行):
|
||||
```dart
|
||||
// 🔧 修复1.0:新增 - DEBUG模式下清理旧数据
|
||||
if (kDebugMode) {
|
||||
KRLogUtil.kr_i('🧹 DEBUG模式:准备清理旧本地存储数据', tag: 'SplashController');
|
||||
_kr_clearOldLocalData();
|
||||
}
|
||||
```
|
||||
|
||||
**新增清理方法** (第396-415行):
|
||||
```dart
|
||||
/// 🔧 修复1.1:清理旧的本地存储数据(DEBUG模式专用)
|
||||
Future<void> _kr_clearOldLocalData() async {
|
||||
// 清理USER_INFO和DEVICE_INFO
|
||||
}
|
||||
```
|
||||
|
||||
**影响**: ✅ 无编译错误, ✅ 无性能影响, ✅ 100%解决旧数据问题
|
||||
|
||||
---
|
||||
|
||||
### 修改2: app_run_data.dart
|
||||
|
||||
**新增文件导入** (第2行):
|
||||
```dart
|
||||
import 'dart:math' show min;
|
||||
```
|
||||
|
||||
**新增Token验证方法** (第68-121行):
|
||||
```dart
|
||||
/// 🔧 修复2.1:验证Token格式是否有效
|
||||
bool _kr_isValidToken(String token) {
|
||||
// 检查JWT格式: header.payload.signature
|
||||
// 验证base64编码
|
||||
// 验证JSON有效性
|
||||
}
|
||||
```
|
||||
|
||||
**修改初始化逻辑** (第294-315行):
|
||||
```dart
|
||||
// 🔧 修复2:验证token有效性和账号信息完整性
|
||||
if (kr_token != null && kr_token!.isNotEmpty && _kr_isValidToken(kr_token!)) {
|
||||
if (kr_account.value != null && kr_account.value!.isNotEmpty) {
|
||||
// ✅ 通过验证,恢复登录
|
||||
} else {
|
||||
// ❌ 账号为空,清理数据
|
||||
}
|
||||
} else {
|
||||
// ❌ Token无效,清理数据
|
||||
}
|
||||
```
|
||||
|
||||
**影响**: ✅ 无编译错误, ✅ 性能影响微乎其微(<1ms), ✅ 检测到任何异常数据立即清理
|
||||
|
||||
---
|
||||
|
||||
### 修改3: clean_build_cache.sh
|
||||
|
||||
**新增文件**: `scripts/clean_build_cache.sh`
|
||||
|
||||
**功能**:
|
||||
- ✅ 清理macOS应用数据
|
||||
- ✅ 清理Hive数据库文件
|
||||
- ✅ 清理Flutter构建缓存
|
||||
- ✅ 清理构建产物
|
||||
|
||||
**使用方法**:
|
||||
```bash
|
||||
cd scripts/
|
||||
./clean_build_cache.sh
|
||||
flutter pub get
|
||||
./build_android.sh # 或其他平台脚本
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 修改4: DATA_CLEANUP_README.md
|
||||
|
||||
**新增文件**: `scripts/DATA_CLEANUP_README.md`
|
||||
|
||||
**内容**:
|
||||
- 📋 详细的修复说明
|
||||
- 🧪 测试验证方法
|
||||
- 🔍 日志信息参考
|
||||
- ⚠️ 注意事项和故障排查
|
||||
|
||||
---
|
||||
|
||||
## ✅ 代码验证结果
|
||||
|
||||
```
|
||||
🧪 测试Token验证逻辑
|
||||
|
||||
✅ 测试1:有效的JWT token - 通过
|
||||
✅ 测试2:格式错误 - 分段不足 - 正确拒绝
|
||||
✅ 测试3:格式错误 - 空payload - 正确拒绝
|
||||
✅ 测试4:格式错误 - 无效base64 - 正确拒绝
|
||||
|
||||
📝 代码分析: 0个错误, 0个与修复相关的警告
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🔄 修复流程图
|
||||
|
||||
```
|
||||
APP启动
|
||||
↓
|
||||
onInit() 执行
|
||||
↓
|
||||
if (kDebugMode)
|
||||
├─ YES → 清理旧数据 ✅
|
||||
└─ NO → 跳过清理 (生产环境)
|
||||
↓
|
||||
初始化用户信息 kr_initializeUserInfo()
|
||||
↓
|
||||
Token合法性检查 _kr_isValidToken()
|
||||
├─ ✅ 有效 → 恢复登录
|
||||
└─ ❌ 无效 → 自动清理 kr_loginOut()
|
||||
↓
|
||||
进入主页
|
||||
├─ 已登录: 显示账号
|
||||
└─ 未登录: 显示未登录提示
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📈 修复效果
|
||||
|
||||
### 修复前
|
||||
- ❌ 显示旧邮箱账号 `calvin.duke@hotmail.com`
|
||||
- ❌ 无法追踪数据来源
|
||||
- ❌ 用户困惑
|
||||
|
||||
### 修复后
|
||||
- ✅ 新安装时显示未登录
|
||||
- ✅ 自动检测和清理异常数据
|
||||
- ✅ 完整的日志追踪
|
||||
- ✅ 用户体验改善
|
||||
|
||||
---
|
||||
|
||||
## 📊 性能影响
|
||||
|
||||
| 操作 | 耗时 | 影响 |
|
||||
|-----|------|------|
|
||||
| DEBUG清理 | ~10ms | 可忽略 |
|
||||
| Token验证 | <1ms | 无影响 |
|
||||
| 总体启动 | 无明显变化 | ✅ 无影响 |
|
||||
|
||||
---
|
||||
|
||||
## 🚀 部署步骤
|
||||
|
||||
### 步骤1: 验证代码
|
||||
```bash
|
||||
# 已完成 ✅
|
||||
flutter analyze lib/app/modules/kr_splash/controllers/kr_splash_controller.dart
|
||||
flutter analyze lib/app/common/app_run_data.dart
|
||||
# 结果:0个相关错误
|
||||
```
|
||||
|
||||
### 步骤2: 打包前清理
|
||||
```bash
|
||||
cd scripts/
|
||||
./clean_build_cache.sh
|
||||
flutter pub get
|
||||
```
|
||||
|
||||
### 步骤3: 构建APP
|
||||
```bash
|
||||
# Android
|
||||
./build_android.sh
|
||||
|
||||
# iOS
|
||||
./build_ios.sh
|
||||
|
||||
# macOS
|
||||
./build_macos.sh
|
||||
|
||||
# Linux
|
||||
./build_linux.sh
|
||||
```
|
||||
|
||||
### 步骤4: 测试验证
|
||||
1. 安装新构建的APP
|
||||
2. 打开个人中心
|
||||
3. 验证不显示旧账号
|
||||
4. 查看日志确认清理信息
|
||||
|
||||
---
|
||||
|
||||
## 🔍 日志验证
|
||||
|
||||
### 成功清理的日志(DEBUG模式)
|
||||
```
|
||||
🧹 DEBUG模式:准备清理旧本地存储数据
|
||||
🧹 开始清理旧本地存储数据...
|
||||
✅ 已清理USER_INFO
|
||||
✅ 已清理DEVICE_INFO
|
||||
✅ 旧本地存储数据已全部清理
|
||||
```
|
||||
|
||||
### Token验证通过
|
||||
```
|
||||
✅ Token格式验证通过
|
||||
✅ Token和账号验证通过,设置登录状态为true
|
||||
📊 恢复账号: user@example.com
|
||||
```
|
||||
|
||||
### Token验证失败
|
||||
```
|
||||
❌ Token格式无效:分段数不对 (2 != 3)
|
||||
⚠️ Token验证失败或格式错误,清理该条用户数据
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📁 文件清单
|
||||
|
||||
### 修改的文件
|
||||
- ✅ `lib/app/modules/kr_splash/controllers/kr_splash_controller.dart` (+22行)
|
||||
- ✅ `lib/app/common/app_run_data.dart` (+98行)
|
||||
|
||||
### 新增的文件
|
||||
- ✅ `scripts/clean_build_cache.sh` (新增)
|
||||
- ✅ `scripts/DATA_CLEANUP_README.md` (新增)
|
||||
- ✅ `FIX_DATA_CLEANUP_SUMMARY.md` (本文件)
|
||||
|
||||
### 总计变更
|
||||
- 新增: 3个文件
|
||||
- 修改: 2个文件
|
||||
- 删除: 0个文件
|
||||
- 总代码行数: +120行
|
||||
|
||||
---
|
||||
|
||||
## ⚡ 关键特性
|
||||
|
||||
### 🛡️ 多层防护
|
||||
1. **应用启动层**: DEBUG模式自动清理
|
||||
2. **数据验证层**: Token格式检查
|
||||
3. **打包预防层**: 打包前清理脚本
|
||||
|
||||
### 🎯 精准定位
|
||||
- ✅ 检测被污染的Token
|
||||
- ✅ 检测空的账号信息
|
||||
- ✅ 检测格式错误的数据
|
||||
|
||||
### 🔒 安全保障
|
||||
- ✅ 生产环境不受影响(仅DEBUG清理)
|
||||
- ✅ 用户有效数据不会被误删
|
||||
- ✅ 完整的日志审计
|
||||
|
||||
### 📝 易于维护
|
||||
- ✅ 清晰的代码注释
|
||||
- ✅ 完整的文档说明
|
||||
- ✅ 多种调试日志
|
||||
|
||||
---
|
||||
|
||||
## 📞 故障排查
|
||||
|
||||
### Q: 修复后还是显示旧账号?
|
||||
A: 检查以下几点:
|
||||
1. 是否完全卸载了旧APP?
|
||||
2. 是否运行了 `clean_build_cache.sh`?
|
||||
3. 查看启动日志是否有清理消息
|
||||
|
||||
### Q: 正常登录的用户数据会丢失吗?
|
||||
A: **不会!** 只有以下情况才会清理:
|
||||
- Token格式错误
|
||||
- 账号信息为空
|
||||
- JSON无法解析
|
||||
|
||||
### Q: 是否影响性能?
|
||||
A: **影响微乎其微**:
|
||||
- DEBUG清理: ~10ms
|
||||
- Token验证: <1ms
|
||||
- 对用户无感知
|
||||
|
||||
---
|
||||
|
||||
## ✨ 总结
|
||||
|
||||
✅ **问题已彻底解决**
|
||||
|
||||
通过三层防护机制:
|
||||
1. 应用启动自动清理 (DEBUG)
|
||||
2. 数据恢复时验证
|
||||
3. 打包前预防清理
|
||||
|
||||
确保不会再出现旧数据残留问题。
|
||||
|
||||
**修复完全向后兼容,不影响现有用户!**
|
||||
|
||||
---
|
||||
|
||||
## 📋 审核清单
|
||||
|
||||
- ✅ 代码修改完成
|
||||
- ✅ 代码无语法错误
|
||||
- ✅ 逻辑经过验证
|
||||
- ✅ 文档已编写
|
||||
- ✅ 清理脚本已测试
|
||||
- ✅ 日志信息完整
|
||||
- ✅ 向后兼容性检查
|
||||
- ✅ 性能影响评估
|
||||
|
||||
**所有项目均已通过!** ✅
|
||||
|
||||
@ -1,82 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Android 多架构构建脚本
|
||||
# 支持构建不同架构的 APK
|
||||
|
||||
set -e
|
||||
|
||||
# 颜色输出
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
BLUE='\033[0;34m'
|
||||
NC='\033[0m' # No Color
|
||||
|
||||
echo -e "${GREEN}🚀 开始构建 Android APK...${NC}"
|
||||
|
||||
# 清理之前的构建
|
||||
echo -e "${YELLOW}🧹 清理之前的构建...${NC}"
|
||||
flutter clean
|
||||
flutter pub get
|
||||
|
||||
# 构建发布版本 APK
|
||||
echo -e "${YELLOW}🔨 构建 Android APK(所有架构)...${NC}"
|
||||
flutter build apk --release
|
||||
|
||||
# 显示构建结果
|
||||
echo -e "${GREEN}✅ Android APK 构建完成!${NC}"
|
||||
echo ""
|
||||
echo -e "${BLUE}📦 构建产物:${NC}"
|
||||
echo ""
|
||||
|
||||
# Universal APK (包含所有架构)
|
||||
if [ -f "build/app/outputs/flutter-apk/app-release.apk" ]; then
|
||||
SIZE=$(du -h "build/app/outputs/flutter-apk/app-release.apk" | cut -f1)
|
||||
echo -e "${GREEN}✓ Universal APK (所有架构): app-release.apk${NC}"
|
||||
echo -e " 大小: $SIZE"
|
||||
echo -e " 路径: build/app/outputs/flutter-apk/app-release.apk"
|
||||
echo ""
|
||||
fi
|
||||
|
||||
# 32位 ARM (armeabi-v7a)
|
||||
if [ -f "build/app/outputs/flutter-apk/app-armeabi-v7a-release.apk" ]; then
|
||||
SIZE=$(du -h "build/app/outputs/flutter-apk/app-armeabi-v7a-release.apk" | cut -f1)
|
||||
echo -e "${GREEN}✓ 32位 ARM (armeabi-v7a): app-armeabi-v7a-release.apk${NC}"
|
||||
echo -e " 大小: $SIZE"
|
||||
echo -e " 路径: build/app/outputs/flutter-apk/app-armeabi-v7a-release.apk"
|
||||
echo ""
|
||||
fi
|
||||
|
||||
# 64位 ARM (arm64-v8a)
|
||||
if [ -f "build/app/outputs/flutter-apk/app-arm64-v8a-release.apk" ]; then
|
||||
SIZE=$(du -h "build/app/outputs/flutter-apk/app-arm64-v8a-release.apk" | cut -f1)
|
||||
echo -e "${GREEN}✓ 64位 ARM (arm64-v8a): app-arm64-v8a-release.apk${NC}"
|
||||
echo -e " 大小: $SIZE"
|
||||
echo -e " 路径: build/app/outputs/flutter-apk/app-arm64-v8a-release.apk"
|
||||
echo ""
|
||||
fi
|
||||
|
||||
# x86 (32位)
|
||||
if [ -f "build/app/outputs/flutter-apk/app-x86-release.apk" ]; then
|
||||
SIZE=$(du -h "build/app/outputs/flutter-apk/app-x86-release.apk" | cut -f1)
|
||||
echo -e "${GREEN}✓ 32位 x86: app-x86-release.apk${NC}"
|
||||
echo -e " 大小: $SIZE"
|
||||
echo -e " 路径: build/app/outputs/flutter-apk/app-x86-release.apk"
|
||||
echo ""
|
||||
fi
|
||||
|
||||
# x86_64 (64位)
|
||||
if [ -f "build/app/outputs/flutter-apk/app-x86_64-release.apk" ]; then
|
||||
SIZE=$(du -h "build/app/outputs/flutter-apk/app-x86_64-release.apk" | cut -f1)
|
||||
echo -e "${GREEN}✓ 64位 x86_64: app-x86_64-release.apk${NC}"
|
||||
echo -e " 大小: $SIZE"
|
||||
echo -e " 路径: build/app/outputs/flutter-apk/app-x86_64-release.apk"
|
||||
echo ""
|
||||
fi
|
||||
|
||||
echo -e "${BLUE}📝 说明:${NC}"
|
||||
echo " • Universal APK: 适用于所有设备,但体积最大"
|
||||
echo " • armeabi-v7a: 适用于 32位 ARM 设备(较旧的设备)"
|
||||
echo " • arm64-v8a: 适用于 64位 ARM 设备(现代设备,推荐)"
|
||||
echo ""
|
||||
echo -e "${GREEN}🎉 构建完成!${NC}"
|
||||
327
build_ios.sh
327
build_ios.sh
@ -1,327 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
# iOS 自动化构建脚本
|
||||
# 支持开发版本和分发版本的构建
|
||||
|
||||
set -e
|
||||
|
||||
# 颜色输出
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
BLUE='\033[0;34m'
|
||||
NC='\033[0m' # No Color
|
||||
|
||||
# 日志函数
|
||||
log_info() {
|
||||
echo -e "${BLUE}[INFO]${NC} $1"
|
||||
}
|
||||
|
||||
log_success() {
|
||||
echo -e "${GREEN}[SUCCESS]${NC} $1"
|
||||
}
|
||||
|
||||
log_warning() {
|
||||
echo -e "${YELLOW}[WARNING]${NC} $1"
|
||||
}
|
||||
|
||||
log_error() {
|
||||
echo -e "${RED}[ERROR]${NC} $1"
|
||||
}
|
||||
|
||||
# 检查环境
|
||||
check_environment() {
|
||||
log_info "检查构建环境..."
|
||||
|
||||
# 检查 Flutter
|
||||
if ! command -v flutter &> /dev/null; then
|
||||
log_error "Flutter 未安装或不在 PATH 中"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# 检查 Xcode
|
||||
if ! command -v xcodebuild &> /dev/null; then
|
||||
log_error "Xcode 未安装或不在 PATH 中"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# 检查必要的环境变量
|
||||
if [ -z "$APPLE_ID" ] || [ -z "$TEAM_ID" ] || [ -z "$BUNDLE_ID" ]; then
|
||||
log_error "请先运行: source ios_signing_config.sh"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
log_success "环境检查通过"
|
||||
}
|
||||
|
||||
# 检查证书
|
||||
check_certificates() {
|
||||
log_info "检查开发者证书..."
|
||||
|
||||
# 检查开发证书
|
||||
if ! security find-identity -v -p codesigning | grep -q "iPhone Developer\|Apple Development"; then
|
||||
log_error "未找到有效的开发证书"
|
||||
log_info "请确保已安装开发者证书"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
log_success "找到有效的开发证书"
|
||||
}
|
||||
|
||||
# 清理构建
|
||||
clean_build() {
|
||||
log_info "清理之前的构建..."
|
||||
|
||||
flutter clean
|
||||
rm -rf build/ios
|
||||
rm -rf ios/build
|
||||
|
||||
log_success "清理完成"
|
||||
}
|
||||
|
||||
# 获取依赖
|
||||
get_dependencies() {
|
||||
log_info "获取 Flutter 依赖..."
|
||||
|
||||
flutter pub get
|
||||
|
||||
log_success "依赖获取完成"
|
||||
}
|
||||
|
||||
# 构建 iOS 应用
|
||||
build_ios_app() {
|
||||
local build_type=$1
|
||||
local configuration=$2
|
||||
|
||||
log_info "开始构建 iOS 应用 (${build_type})..."
|
||||
|
||||
# 设置构建参数
|
||||
local build_args="--release"
|
||||
if [ "$build_type" = "debug" ]; then
|
||||
build_args="--debug"
|
||||
fi
|
||||
|
||||
# 构建 Flutter 应用
|
||||
flutter build ios $build_args --no-codesign
|
||||
|
||||
# 检查构建结果
|
||||
local app_path="build/ios/iphoneos/Runner.app"
|
||||
if [ ! -d "$app_path" ]; then
|
||||
log_error "iOS 应用构建失败: $app_path 不存在"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
log_success "iOS 应用构建完成: $app_path"
|
||||
}
|
||||
|
||||
# 签名应用
|
||||
sign_app() {
|
||||
local app_path=$1
|
||||
local identity=$2
|
||||
local provisioning_profile=$3
|
||||
|
||||
log_info "开始签名应用..."
|
||||
|
||||
# 移除旧的签名
|
||||
codesign --remove-signature "$app_path"
|
||||
|
||||
# 签名应用
|
||||
codesign --force --sign "$identity" \
|
||||
--entitlements ios/Runner/Runner.entitlements \
|
||||
"$app_path"
|
||||
|
||||
# 验证签名
|
||||
codesign --verify --verbose "$app_path"
|
||||
|
||||
if [ $? -eq 0 ]; then
|
||||
log_success "应用签名成功"
|
||||
else
|
||||
log_error "应用签名失败"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
# 创建 IPA 文件
|
||||
create_ipa() {
|
||||
local app_path=$1
|
||||
local ipa_path=$2
|
||||
|
||||
log_info "创建 IPA 文件..."
|
||||
|
||||
# 创建 Payload 目录
|
||||
local payload_dir="build/ios/Payload"
|
||||
mkdir -p "$payload_dir"
|
||||
|
||||
# 复制应用
|
||||
cp -R "$app_path" "$payload_dir/"
|
||||
|
||||
# 创建 IPA
|
||||
cd build/ios
|
||||
zip -r "${ipa_path##*/}" Payload/
|
||||
cd ../..
|
||||
|
||||
# 清理 Payload 目录
|
||||
rm -rf "$payload_dir"
|
||||
|
||||
if [ -f "$ipa_path" ]; then
|
||||
log_success "IPA 文件创建成功: $ipa_path"
|
||||
else
|
||||
log_error "IPA 文件创建失败"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
# 创建 DMG 文件
|
||||
create_dmg() {
|
||||
local ipa_path=$1
|
||||
local dmg_path=$2
|
||||
|
||||
log_info "创建 DMG 文件..."
|
||||
|
||||
# 创建临时目录
|
||||
local temp_dir="build/ios/temp_dmg"
|
||||
mkdir -p "$temp_dir"
|
||||
|
||||
# 复制 IPA 到临时目录
|
||||
cp "$ipa_path" "$temp_dir/"
|
||||
|
||||
# 创建 DMG
|
||||
hdiutil create -srcfolder "$temp_dir" \
|
||||
-volname "BearVPN iOS" \
|
||||
-fs HFS+ \
|
||||
-format UDZO \
|
||||
-imagekey zlib-level=9 \
|
||||
"$dmg_path"
|
||||
|
||||
# 清理临时目录
|
||||
rm -rf "$temp_dir"
|
||||
|
||||
if [ -f "$dmg_path" ]; then
|
||||
log_success "DMG 文件创建成功: $dmg_path"
|
||||
else
|
||||
log_error "DMG 文件创建失败"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
# 验证构建结果
|
||||
verify_build() {
|
||||
local ipa_path=$1
|
||||
local dmg_path=$2
|
||||
|
||||
log_info "验证构建结果..."
|
||||
|
||||
# 检查文件大小
|
||||
local ipa_size=$(du -h "$ipa_path" | cut -f1)
|
||||
local dmg_size=$(du -h "$dmg_path" | cut -f1)
|
||||
|
||||
log_info "IPA 大小: $ipa_size"
|
||||
log_info "DMG 大小: $dmg_size"
|
||||
|
||||
# 验证 IPA 内容
|
||||
unzip -l "$ipa_path" | grep -q "Payload/Runner.app"
|
||||
if [ $? -eq 0 ]; then
|
||||
log_success "IPA 内容验证通过"
|
||||
else
|
||||
log_error "IPA 内容验证失败"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
# 显示构建结果
|
||||
show_result() {
|
||||
local ipa_path=$1
|
||||
local dmg_path=$2
|
||||
|
||||
log_success "=========================================="
|
||||
log_success "iOS 构建完成!"
|
||||
log_success "=========================================="
|
||||
log_info "应用名称: $APP_NAME"
|
||||
log_info "版本: $VERSION"
|
||||
log_info "Bundle ID: $BUNDLE_ID"
|
||||
log_info "IPA 文件: $ipa_path"
|
||||
log_info "DMG 文件: $dmg_path"
|
||||
log_info "开发者: $SIGNING_IDENTITY"
|
||||
log_success "=========================================="
|
||||
log_info "现在可以安装到设备或上传到 App Store"
|
||||
log_success "=========================================="
|
||||
}
|
||||
|
||||
# 主函数
|
||||
main() {
|
||||
local build_type=${1:-"release"}
|
||||
|
||||
log_info "开始 iOS 构建流程..."
|
||||
log_info "构建类型: $build_type"
|
||||
log_info "=========================================="
|
||||
|
||||
check_environment
|
||||
check_certificates
|
||||
clean_build
|
||||
get_dependencies
|
||||
build_ios_app "$build_type"
|
||||
|
||||
# 设置路径
|
||||
local app_path="build/ios/iphoneos/Runner.app"
|
||||
local ipa_path="$IPA_PATH"
|
||||
local dmg_path="$DMG_PATH"
|
||||
|
||||
# 创建输出目录
|
||||
mkdir -p "$(dirname "$ipa_path")"
|
||||
mkdir -p "$(dirname "$dmg_path")"
|
||||
|
||||
# 签名应用
|
||||
sign_app "$app_path" "$SIGNING_IDENTITY" ""
|
||||
|
||||
# 创建 IPA
|
||||
create_ipa "$app_path" "$ipa_path"
|
||||
|
||||
# 创建 DMG
|
||||
create_dmg "$ipa_path" "$dmg_path"
|
||||
|
||||
# 验证结果
|
||||
verify_build "$ipa_path" "$dmg_path"
|
||||
|
||||
# 显示结果
|
||||
show_result "$ipa_path" "$dmg_path"
|
||||
|
||||
log_success "所有操作完成!"
|
||||
}
|
||||
|
||||
# 显示帮助信息
|
||||
show_help() {
|
||||
echo "iOS 自动化构建脚本"
|
||||
echo ""
|
||||
echo "用法: $0 [选项]"
|
||||
echo ""
|
||||
echo "选项:"
|
||||
echo " debug 构建调试版本"
|
||||
echo " release 构建发布版本 (默认)"
|
||||
echo " help 显示此帮助信息"
|
||||
echo ""
|
||||
echo "示例:"
|
||||
echo " $0 # 构建发布版本"
|
||||
echo " $0 debug # 构建调试版本"
|
||||
echo " $0 release # 构建发布版本"
|
||||
echo ""
|
||||
echo "注意: 请先运行 'source ios_signing_config.sh' 配置签名信息"
|
||||
}
|
||||
|
||||
# 处理命令行参数
|
||||
case "${1:-}" in
|
||||
"help"|"-h"|"--help")
|
||||
show_help
|
||||
exit 0
|
||||
;;
|
||||
"debug"|"release")
|
||||
main "$1"
|
||||
;;
|
||||
"")
|
||||
main "release"
|
||||
;;
|
||||
*)
|
||||
log_error "未知选项: $1"
|
||||
show_help
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
@ -1,358 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
# iOS App Store 构建和上传脚本
|
||||
# 支持自动构建、签名、上传到 App Store Connect
|
||||
|
||||
set -e
|
||||
|
||||
# 颜色输出
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
BLUE='\033[0;34m'
|
||||
NC='\033[0m' # No Color
|
||||
|
||||
# 日志函数
|
||||
log_info() {
|
||||
echo -e "${BLUE}[INFO]${NC} $1"
|
||||
}
|
||||
|
||||
log_success() {
|
||||
echo -e "${GREEN}[SUCCESS]${NC} $1"
|
||||
}
|
||||
|
||||
log_warning() {
|
||||
echo -e "${YELLOW}[WARNING]${NC} $1"
|
||||
}
|
||||
|
||||
log_error() {
|
||||
echo -e "${RED}[ERROR]${NC} $1"
|
||||
}
|
||||
|
||||
# 检查环境
|
||||
check_environment() {
|
||||
log_info "检查 App Store 构建环境..."
|
||||
|
||||
# 检查必要的环境变量
|
||||
if [ -z "$APPLE_ID" ] || [ -z "$APPLE_PASSWORD" ] || [ -z "$TEAM_ID" ]; then
|
||||
log_error "请先运行: source ios_signing_config.sh"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# 检查 Xcode
|
||||
if ! command -v xcodebuild &> /dev/null; then
|
||||
log_error "Xcode 未安装或不在 PATH 中"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# 检查 xcrun altool
|
||||
if ! command -v xcrun &> /dev/null; then
|
||||
log_error "xcrun 不可用"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
log_success "环境检查通过"
|
||||
}
|
||||
|
||||
# 检查证书和配置文件
|
||||
check_certificates_and_profiles() {
|
||||
log_info "检查证书和配置文件..."
|
||||
|
||||
# 检查分发证书
|
||||
if ! security find-identity -v -p codesigning | grep -q "iPhone Distribution\|Apple Distribution"; then
|
||||
log_error "未找到有效的分发证书"
|
||||
log_info "请确保已安装 Apple Distribution 证书"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# 检查配置文件
|
||||
local profiles_dir="$HOME/Library/MobileDevice/Provisioning Profiles"
|
||||
if [ ! -d "$profiles_dir" ]; then
|
||||
log_error "配置文件目录不存在: $profiles_dir"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
log_success "证书和配置文件检查通过"
|
||||
}
|
||||
|
||||
# 清理构建
|
||||
clean_build() {
|
||||
log_info "清理之前的构建..."
|
||||
|
||||
flutter clean
|
||||
rm -rf build/ios
|
||||
rm -rf ios/build
|
||||
|
||||
log_success "清理完成"
|
||||
}
|
||||
|
||||
# 获取依赖
|
||||
get_dependencies() {
|
||||
log_info "获取 Flutter 依赖..."
|
||||
|
||||
flutter pub get
|
||||
|
||||
log_success "依赖获取完成"
|
||||
}
|
||||
|
||||
# 构建 iOS 应用
|
||||
build_ios_app() {
|
||||
log_info "开始构建 iOS 应用 (App Store)..."
|
||||
|
||||
# 构建 Flutter 应用
|
||||
flutter build ios --release --no-codesign
|
||||
|
||||
# 检查构建结果
|
||||
local app_path="build/ios/iphoneos/Runner.app"
|
||||
if [ ! -d "$app_path" ]; then
|
||||
log_error "iOS 应用构建失败: $app_path 不存在"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
log_success "iOS 应用构建完成: $app_path"
|
||||
}
|
||||
|
||||
# 使用 Xcode 构建和签名
|
||||
build_with_xcode() {
|
||||
log_info "使用 Xcode 构建和签名..."
|
||||
|
||||
# 进入 iOS 目录
|
||||
cd ios
|
||||
|
||||
# 使用 xcodebuild 构建
|
||||
xcodebuild -workspace Runner.xcworkspace \
|
||||
-scheme Runner \
|
||||
-configuration Release \
|
||||
-destination generic/platform=iOS \
|
||||
-archivePath ../build/ios/Runner.xcarchive \
|
||||
archive
|
||||
|
||||
if [ $? -ne 0 ]; then
|
||||
log_error "Xcode 构建失败"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# 返回项目根目录
|
||||
cd ..
|
||||
|
||||
log_success "Xcode 构建完成"
|
||||
}
|
||||
|
||||
# 导出 IPA
|
||||
export_ipa() {
|
||||
log_info "导出 IPA 文件..."
|
||||
|
||||
# 创建导出选项文件
|
||||
local export_options_plist="ios/ExportOptions.plist"
|
||||
cat > "$export_options_plist" << EOF
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>method</key>
|
||||
<string>app-store</string>
|
||||
<key>teamID</key>
|
||||
<string>$TEAM_ID</string>
|
||||
<key>uploadBitcode</key>
|
||||
<false/>
|
||||
<key>uploadSymbols</key>
|
||||
<true/>
|
||||
<key>compileBitcode</key>
|
||||
<false/>
|
||||
</dict>
|
||||
</plist>
|
||||
EOF
|
||||
|
||||
# 导出 IPA
|
||||
xcodebuild -exportArchive \
|
||||
-archivePath build/ios/Runner.xcarchive \
|
||||
-exportPath build/ios/export \
|
||||
-exportOptionsPlist "$export_options_plist"
|
||||
|
||||
if [ $? -ne 0 ]; then
|
||||
log_error "IPA 导出失败"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# 移动 IPA 文件
|
||||
local ipa_path="$IPA_PATH"
|
||||
mkdir -p "$(dirname "$ipa_path")"
|
||||
mv build/ios/export/Runner.ipa "$ipa_path"
|
||||
|
||||
log_success "IPA 文件导出成功: $ipa_path"
|
||||
}
|
||||
|
||||
# 验证 IPA
|
||||
validate_ipa() {
|
||||
local ipa_path=$1
|
||||
|
||||
log_info "验证 IPA 文件..."
|
||||
|
||||
# 使用 xcrun altool 验证
|
||||
xcrun altool --validate-app \
|
||||
-f "$ipa_path" \
|
||||
-t ios \
|
||||
-u "$APPLE_ID" \
|
||||
-p "$APPLE_PASSWORD"
|
||||
|
||||
if [ $? -eq 0 ]; then
|
||||
log_success "IPA 验证通过"
|
||||
else
|
||||
log_error "IPA 验证失败"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
# 上传到 App Store
|
||||
upload_to_appstore() {
|
||||
local ipa_path=$1
|
||||
|
||||
log_info "上传到 App Store Connect..."
|
||||
|
||||
# 使用 xcrun altool 上传
|
||||
xcrun altool --upload-app \
|
||||
-f "$ipa_path" \
|
||||
-t ios \
|
||||
-u "$APPLE_ID" \
|
||||
-p "$APPLE_PASSWORD"
|
||||
|
||||
if [ $? -eq 0 ]; then
|
||||
log_success "上传到 App Store Connect 成功"
|
||||
else
|
||||
log_error "上传到 App Store Connect 失败"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
# 创建 DMG
|
||||
create_dmg() {
|
||||
local ipa_path=$1
|
||||
local dmg_path=$2
|
||||
|
||||
log_info "创建 DMG 文件..."
|
||||
|
||||
# 创建临时目录
|
||||
local temp_dir="build/ios/temp_dmg"
|
||||
mkdir -p "$temp_dir"
|
||||
|
||||
# 复制 IPA 到临时目录
|
||||
cp "$ipa_path" "$temp_dir/"
|
||||
|
||||
# 创建 DMG
|
||||
hdiutil create -srcfolder "$temp_dir" \
|
||||
-volname "BearVPN iOS App Store" \
|
||||
-fs HFS+ \
|
||||
-format UDZO \
|
||||
-imagekey zlib-level=9 \
|
||||
"$dmg_path"
|
||||
|
||||
# 清理临时目录
|
||||
rm -rf "$temp_dir"
|
||||
|
||||
if [ -f "$dmg_path" ]; then
|
||||
log_success "DMG 文件创建成功: $dmg_path"
|
||||
else
|
||||
log_error "DMG 文件创建失败"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
# 显示构建结果
|
||||
show_result() {
|
||||
local ipa_path=$1
|
||||
local dmg_path=$2
|
||||
|
||||
log_success "=========================================="
|
||||
log_success "iOS App Store 构建完成!"
|
||||
log_success "=========================================="
|
||||
log_info "应用名称: $APP_NAME"
|
||||
log_info "版本: $VERSION"
|
||||
log_info "Bundle ID: $BUNDLE_ID"
|
||||
log_info "IPA 文件: $ipa_path"
|
||||
log_info "DMG 文件: $dmg_path"
|
||||
log_info "开发者: $DISTRIBUTION_IDENTITY"
|
||||
log_success "=========================================="
|
||||
log_info "应用已上传到 App Store Connect"
|
||||
log_info "请在 App Store Connect 中完成最终发布"
|
||||
log_success "=========================================="
|
||||
}
|
||||
|
||||
# 主函数
|
||||
main() {
|
||||
local upload=${1:-"true"}
|
||||
|
||||
log_info "开始 iOS App Store 构建流程..."
|
||||
log_info "上传到 App Store: $upload"
|
||||
log_info "=========================================="
|
||||
|
||||
check_environment
|
||||
check_certificates_and_profiles
|
||||
clean_build
|
||||
get_dependencies
|
||||
build_ios_app
|
||||
build_with_xcode
|
||||
export_ipa
|
||||
|
||||
# 设置路径
|
||||
local ipa_path="$IPA_PATH"
|
||||
local dmg_path="$DMG_PATH"
|
||||
|
||||
# 创建输出目录
|
||||
mkdir -p "$(dirname "$dmg_path")"
|
||||
|
||||
# 验证 IPA
|
||||
validate_ipa "$ipa_path"
|
||||
|
||||
# 上传到 App Store(如果启用)
|
||||
if [ "$upload" = "true" ]; then
|
||||
upload_to_appstore "$ipa_path"
|
||||
else
|
||||
log_info "跳过上传到 App Store"
|
||||
fi
|
||||
|
||||
# 创建 DMG
|
||||
create_dmg "$ipa_path" "$dmg_path"
|
||||
|
||||
# 显示结果
|
||||
show_result "$ipa_path" "$dmg_path"
|
||||
|
||||
log_success "所有操作完成!"
|
||||
}
|
||||
|
||||
# 显示帮助信息
|
||||
show_help() {
|
||||
echo "iOS App Store 构建和上传脚本"
|
||||
echo ""
|
||||
echo "用法: $0 [选项]"
|
||||
echo ""
|
||||
echo "选项:"
|
||||
echo " upload 构建并上传到 App Store Connect (默认)"
|
||||
echo " build 仅构建,不上传"
|
||||
echo " help 显示此帮助信息"
|
||||
echo ""
|
||||
echo "示例:"
|
||||
echo " $0 # 构建并上传到 App Store"
|
||||
echo " $0 upload # 构建并上传到 App Store"
|
||||
echo " $0 build # 仅构建,不上传"
|
||||
echo ""
|
||||
echo "注意: 请先运行 'source ios_signing_config.sh' 配置签名信息"
|
||||
}
|
||||
|
||||
# 处理命令行参数
|
||||
case "${1:-}" in
|
||||
"help"|"-h"|"--help")
|
||||
show_help
|
||||
exit 0
|
||||
;;
|
||||
"upload"|"build")
|
||||
main "$1"
|
||||
;;
|
||||
"")
|
||||
main "upload"
|
||||
;;
|
||||
*)
|
||||
log_error "未知选项: $1"
|
||||
show_help
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
382
build_ios_dmg.sh
382
build_ios_dmg.sh
@ -1,382 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
# iOS 签名打包 DMG 脚本
|
||||
# 专门用于创建签名的 iOS 应用 DMG 文件
|
||||
|
||||
set -e
|
||||
|
||||
# 颜色输出
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
BLUE='\033[0;34m'
|
||||
NC='\033[0m' # No Color
|
||||
|
||||
# 日志函数
|
||||
log_info() {
|
||||
echo -e "${BLUE}[INFO]${NC} $1"
|
||||
}
|
||||
|
||||
log_success() {
|
||||
echo -e "${GREEN}[SUCCESS]${NC} $1"
|
||||
}
|
||||
|
||||
log_warning() {
|
||||
echo -e "${YELLOW}[WARNING]${NC} $1"
|
||||
}
|
||||
|
||||
log_error() {
|
||||
echo -e "${RED}[ERROR]${NC} $1"
|
||||
}
|
||||
|
||||
# 检查环境
|
||||
check_environment() {
|
||||
log_info "检查构建环境..."
|
||||
|
||||
# 检查必要的环境变量
|
||||
if [ -z "$APPLE_ID" ] || [ -z "$TEAM_ID" ] || [ -z "$BUNDLE_ID" ]; then
|
||||
log_error "请先运行: source ios_signing_config.sh"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# 检查 Flutter
|
||||
if ! command -v flutter &> /dev/null; then
|
||||
log_error "Flutter 未安装或不在 PATH 中"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# 检查 Xcode
|
||||
if ! command -v xcodebuild &> /dev/null; then
|
||||
log_error "Xcode 未安装或不在 PATH 中"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
log_success "环境检查通过"
|
||||
}
|
||||
|
||||
# 检查证书
|
||||
check_certificates() {
|
||||
log_info "检查开发者证书..."
|
||||
|
||||
# 检查是否有可用的签名身份
|
||||
local identities=$(security find-identity -v -p codesigning 2>/dev/null)
|
||||
if [ $? -ne 0 ] || [ -z "$identities" ]; then
|
||||
log_error "未找到可用的开发者证书"
|
||||
log_info "请确保已安装开发者证书"
|
||||
log_info "您可以通过以下方式获取证书:"
|
||||
log_info "1. 登录 https://developer.apple.com"
|
||||
log_info "2. 进入 'Certificates, Identifiers & Profiles'"
|
||||
log_info "3. 创建 'iOS Development' 证书"
|
||||
log_info "4. 下载并双击安装证书"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# 显示可用的证书
|
||||
log_info "找到以下可用证书:"
|
||||
echo "$identities"
|
||||
|
||||
log_success "证书检查通过"
|
||||
}
|
||||
|
||||
# 清理构建
|
||||
clean_build() {
|
||||
log_info "清理之前的构建..."
|
||||
|
||||
flutter clean
|
||||
rm -rf build/ios
|
||||
rm -rf ios/build
|
||||
|
||||
log_success "清理完成"
|
||||
}
|
||||
|
||||
# 获取依赖
|
||||
get_dependencies() {
|
||||
log_info "获取 Flutter 依赖..."
|
||||
|
||||
flutter pub get
|
||||
|
||||
log_success "依赖获取完成"
|
||||
}
|
||||
|
||||
# 构建 iOS 应用
|
||||
build_ios_app() {
|
||||
local build_type=${1:-"release"}
|
||||
|
||||
log_info "开始构建 iOS 应用 (${build_type})..."
|
||||
|
||||
# 设置构建参数
|
||||
local build_args="--release"
|
||||
if [ "$build_type" = "debug" ]; then
|
||||
build_args="--debug"
|
||||
fi
|
||||
|
||||
# 构建 Flutter 应用
|
||||
flutter build ios $build_args --no-codesign
|
||||
|
||||
# 检查构建结果
|
||||
local app_path="build/ios/iphoneos/Runner.app"
|
||||
if [ ! -d "$app_path" ]; then
|
||||
log_error "iOS 应用构建失败: $app_path 不存在"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
log_success "iOS 应用构建完成: $app_path"
|
||||
}
|
||||
|
||||
# 使用 Xcode 构建和签名
|
||||
build_with_xcode() {
|
||||
log_info "使用 Xcode 构建和签名..."
|
||||
|
||||
# 进入 iOS 目录
|
||||
cd ios
|
||||
|
||||
# 使用 xcodebuild 构建
|
||||
xcodebuild -workspace Runner.xcworkspace \
|
||||
-scheme Runner \
|
||||
-configuration Release \
|
||||
-destination generic/platform=iOS \
|
||||
-archivePath ../build/ios/Runner.xcarchive \
|
||||
archive
|
||||
|
||||
if [ $? -ne 0 ]; then
|
||||
log_error "Xcode 构建失败"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# 返回项目根目录
|
||||
cd ..
|
||||
|
||||
log_success "Xcode 构建完成"
|
||||
}
|
||||
|
||||
# 导出 IPA
|
||||
export_ipa() {
|
||||
log_info "导出 IPA 文件..."
|
||||
|
||||
# 创建导出选项文件
|
||||
local export_options_plist="ios/ExportOptions.plist"
|
||||
cat > "$export_options_plist" << EOF
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>method</key>
|
||||
<string>development</string>
|
||||
<key>teamID</key>
|
||||
<string>$TEAM_ID</string>
|
||||
<key>uploadBitcode</key>
|
||||
<false/>
|
||||
<key>uploadSymbols</key>
|
||||
<true/>
|
||||
<key>compileBitcode</key>
|
||||
<false/>
|
||||
</dict>
|
||||
</plist>
|
||||
EOF
|
||||
|
||||
# 导出 IPA
|
||||
xcodebuild -exportArchive \
|
||||
-archivePath build/ios/Runner.xcarchive \
|
||||
-exportPath build/ios/export \
|
||||
-exportOptionsPlist "$export_options_plist"
|
||||
|
||||
if [ $? -ne 0 ]; then
|
||||
log_error "IPA 导出失败"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# 移动 IPA 文件
|
||||
local ipa_path="$IPA_PATH"
|
||||
mkdir -p "$(dirname "$ipa_path")"
|
||||
mv build/ios/export/Runner.ipa "$ipa_path"
|
||||
|
||||
log_success "IPA 文件导出成功: $ipa_path"
|
||||
}
|
||||
|
||||
# 创建 DMG 文件
|
||||
create_dmg() {
|
||||
local ipa_path=$1
|
||||
local dmg_path=$2
|
||||
|
||||
log_info "创建 DMG 文件..."
|
||||
|
||||
# 创建临时目录
|
||||
local temp_dir="build/ios/temp_dmg"
|
||||
mkdir -p "$temp_dir"
|
||||
|
||||
# 复制 IPA 到临时目录
|
||||
cp "$ipa_path" "$temp_dir/"
|
||||
|
||||
# 创建 DMG
|
||||
hdiutil create -srcfolder "$temp_dir" \
|
||||
-volname "BearVPN iOS" \
|
||||
-fs HFS+ \
|
||||
-format UDZO \
|
||||
-imagekey zlib-level=9 \
|
||||
"$dmg_path"
|
||||
|
||||
# 清理临时目录
|
||||
rm -rf "$temp_dir"
|
||||
|
||||
if [ -f "$dmg_path" ]; then
|
||||
log_success "DMG 文件创建成功: $dmg_path"
|
||||
else
|
||||
log_error "DMG 文件创建失败"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
# 签名 DMG
|
||||
sign_dmg() {
|
||||
local dmg_path=$1
|
||||
|
||||
log_info "签名 DMG 文件..."
|
||||
|
||||
# 获取可用的签名身份
|
||||
local signing_identity=$(security find-identity -v -p codesigning | grep "iPhone Developer\|Apple Development" | head -1 | cut -d'"' -f2)
|
||||
|
||||
if [ -z "$signing_identity" ]; then
|
||||
log_warning "未找到可用的签名身份,跳过 DMG 签名"
|
||||
return 0
|
||||
fi
|
||||
|
||||
# 签名 DMG
|
||||
codesign --force --sign "$signing_identity" "$dmg_path"
|
||||
|
||||
if [ $? -eq 0 ]; then
|
||||
log_success "DMG 签名成功"
|
||||
else
|
||||
log_warning "DMG 签名失败,但继续执行"
|
||||
fi
|
||||
}
|
||||
|
||||
# 验证构建结果
|
||||
verify_build() {
|
||||
local ipa_path=$1
|
||||
local dmg_path=$2
|
||||
|
||||
log_info "验证构建结果..."
|
||||
|
||||
# 检查文件大小
|
||||
local ipa_size=$(du -h "$ipa_path" | cut -f1)
|
||||
local dmg_size=$(du -h "$dmg_path" | cut -f1)
|
||||
|
||||
log_info "IPA 大小: $ipa_size"
|
||||
log_info "DMG 大小: $dmg_size"
|
||||
|
||||
# 验证 IPA 内容
|
||||
unzip -l "$ipa_path" | grep -q "Payload/Runner.app"
|
||||
if [ $? -eq 0 ]; then
|
||||
log_success "IPA 内容验证通过"
|
||||
else
|
||||
log_error "IPA 内容验证失败"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# 验证 DMG
|
||||
hdiutil verify "$dmg_path" > /dev/null 2>&1
|
||||
if [ $? -eq 0 ]; then
|
||||
log_success "DMG 验证通过"
|
||||
else
|
||||
log_warning "DMG 验证失败,但文件可能仍然可用"
|
||||
fi
|
||||
}
|
||||
|
||||
# 显示构建结果
|
||||
show_result() {
|
||||
local ipa_path=$1
|
||||
local dmg_path=$2
|
||||
local build_type=$3
|
||||
|
||||
log_success "=========================================="
|
||||
log_success "iOS DMG 构建完成!"
|
||||
log_success "=========================================="
|
||||
log_info "应用名称: $APP_NAME"
|
||||
log_info "版本: $VERSION"
|
||||
log_info "Bundle ID: $BUNDLE_ID"
|
||||
log_info "构建类型: $build_type"
|
||||
log_info "IPA 文件: $ipa_path"
|
||||
log_info "DMG 文件: $dmg_path"
|
||||
log_info "开发者: $SIGNING_IDENTITY"
|
||||
log_success "=========================================="
|
||||
log_info "现在可以分发 DMG 文件给用户"
|
||||
log_info "用户可以通过 Xcode 或 Apple Configurator 安装 IPA"
|
||||
log_success "=========================================="
|
||||
}
|
||||
|
||||
# 主函数
|
||||
main() {
|
||||
local build_type=${1:-"release"}
|
||||
|
||||
log_info "开始 iOS DMG 构建流程..."
|
||||
log_info "构建类型: $build_type"
|
||||
log_info "=========================================="
|
||||
|
||||
check_environment
|
||||
check_certificates
|
||||
clean_build
|
||||
get_dependencies
|
||||
build_ios_app "$build_type"
|
||||
build_with_xcode
|
||||
export_ipa
|
||||
|
||||
# 设置路径
|
||||
local ipa_path="$IPA_PATH"
|
||||
local dmg_path="$DMG_PATH"
|
||||
|
||||
# 创建输出目录
|
||||
mkdir -p "$(dirname "$ipa_path")"
|
||||
mkdir -p "$(dirname "$dmg_path")"
|
||||
|
||||
# 创建 DMG
|
||||
create_dmg "$ipa_path" "$dmg_path"
|
||||
|
||||
# 签名 DMG
|
||||
sign_dmg "$dmg_path"
|
||||
|
||||
# 验证结果
|
||||
verify_build "$ipa_path" "$dmg_path"
|
||||
|
||||
# 显示结果
|
||||
show_result "$ipa_path" "$dmg_path" "$build_type"
|
||||
|
||||
log_success "所有操作完成!"
|
||||
}
|
||||
|
||||
# 显示帮助信息
|
||||
show_help() {
|
||||
echo "iOS DMG 构建脚本"
|
||||
echo ""
|
||||
echo "用法: $0 [选项]"
|
||||
echo ""
|
||||
echo "选项:"
|
||||
echo " debug 构建调试版本"
|
||||
echo " release 构建发布版本 (默认)"
|
||||
echo " help 显示此帮助信息"
|
||||
echo ""
|
||||
echo "示例:"
|
||||
echo " $0 # 构建发布版本"
|
||||
echo " $0 debug # 构建调试版本"
|
||||
echo " $0 release # 构建发布版本"
|
||||
echo ""
|
||||
echo "注意: 请先运行 'source ios_signing_config.sh' 配置签名信息"
|
||||
}
|
||||
|
||||
# 处理命令行参数
|
||||
case "${1:-}" in
|
||||
"help"|"-h"|"--help")
|
||||
show_help
|
||||
exit 0
|
||||
;;
|
||||
"debug"|"release")
|
||||
main "$1"
|
||||
;;
|
||||
"")
|
||||
main "release"
|
||||
;;
|
||||
*)
|
||||
log_error "未知选项: $1"
|
||||
show_help
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
@ -1,252 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
# 简化的 iOS 构建脚本(无签名版本)
|
||||
# 用于快速测试和开发
|
||||
|
||||
set -e
|
||||
|
||||
# 颜色输出
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
BLUE='\033[0;34m'
|
||||
NC='\033[0m' # No Color
|
||||
|
||||
# 日志函数
|
||||
log_info() {
|
||||
echo -e "${BLUE}[INFO]${NC} $1"
|
||||
}
|
||||
|
||||
log_success() {
|
||||
echo -e "${GREEN}[SUCCESS]${NC} $1"
|
||||
}
|
||||
|
||||
log_warning() {
|
||||
echo -e "${YELLOW}[WARNING]${NC} $1"
|
||||
}
|
||||
|
||||
log_error() {
|
||||
echo -e "${RED}[ERROR]${NC} $1"
|
||||
}
|
||||
|
||||
# 检查环境
|
||||
check_environment() {
|
||||
log_info "检查构建环境..."
|
||||
|
||||
# 检查 Flutter
|
||||
if ! command -v flutter &> /dev/null; then
|
||||
log_error "Flutter 未安装或不在 PATH 中"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# 检查 Xcode
|
||||
if ! command -v xcodebuild &> /dev/null; then
|
||||
log_error "Xcode 未安装或不在 PATH 中"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
log_success "环境检查通过"
|
||||
}
|
||||
|
||||
# 清理构建
|
||||
clean_build() {
|
||||
log_info "清理之前的构建..."
|
||||
|
||||
flutter clean
|
||||
rm -rf build/ios
|
||||
rm -rf ios/build
|
||||
|
||||
log_success "清理完成"
|
||||
}
|
||||
|
||||
# 获取依赖
|
||||
get_dependencies() {
|
||||
log_info "获取 Flutter 依赖..."
|
||||
|
||||
flutter pub get
|
||||
|
||||
log_success "依赖获取完成"
|
||||
}
|
||||
|
||||
# 构建 iOS 应用
|
||||
build_ios_app() {
|
||||
local build_type=${1:-"debug"}
|
||||
|
||||
log_info "开始构建 iOS 应用 (${build_type})..."
|
||||
|
||||
# 设置构建参数
|
||||
local build_args="--debug"
|
||||
if [ "$build_type" = "release" ]; then
|
||||
build_args="--release"
|
||||
fi
|
||||
|
||||
# 构建 Flutter 应用
|
||||
flutter build ios $build_args --no-codesign
|
||||
|
||||
# 检查构建结果
|
||||
local app_path="build/ios/iphoneos/Runner.app"
|
||||
if [ ! -d "$app_path" ]; then
|
||||
log_error "iOS 应用构建失败: $app_path 不存在"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
log_success "iOS 应用构建完成: $app_path"
|
||||
}
|
||||
|
||||
# 创建 IPA 文件
|
||||
create_ipa() {
|
||||
local app_path=$1
|
||||
local ipa_path=$2
|
||||
|
||||
log_info "创建 IPA 文件..."
|
||||
|
||||
# 创建 Payload 目录
|
||||
local payload_dir="build/ios/Payload"
|
||||
mkdir -p "$payload_dir"
|
||||
|
||||
# 复制应用
|
||||
cp -R "$app_path" "$payload_dir/"
|
||||
|
||||
# 创建 IPA
|
||||
cd build/ios
|
||||
zip -r "${ipa_path##*/}" Payload/
|
||||
cd ../..
|
||||
|
||||
# 清理 Payload 目录
|
||||
rm -rf "$payload_dir"
|
||||
|
||||
if [ -f "$ipa_path" ]; then
|
||||
log_success "IPA 文件创建成功: $ipa_path"
|
||||
else
|
||||
log_error "IPA 文件创建失败"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
# 创建 DMG 文件
|
||||
create_dmg() {
|
||||
local ipa_path=$1
|
||||
local dmg_path=$2
|
||||
|
||||
log_info "创建 DMG 文件..."
|
||||
|
||||
# 创建临时目录
|
||||
local temp_dir="build/ios/temp_dmg"
|
||||
mkdir -p "$temp_dir"
|
||||
|
||||
# 复制 IPA 到临时目录
|
||||
cp "$ipa_path" "$temp_dir/"
|
||||
|
||||
# 创建 DMG
|
||||
hdiutil create -srcfolder "$temp_dir" \
|
||||
-volname "BearVPN iOS" \
|
||||
-fs HFS+ \
|
||||
-format UDZO \
|
||||
-imagekey zlib-level=9 \
|
||||
"$dmg_path"
|
||||
|
||||
# 清理临时目录
|
||||
rm -rf "$temp_dir"
|
||||
|
||||
if [ -f "$dmg_path" ]; then
|
||||
log_success "DMG 文件创建成功: $dmg_path"
|
||||
else
|
||||
log_error "DMG 文件创建失败"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
# 显示构建结果
|
||||
show_result() {
|
||||
local ipa_path=$1
|
||||
local dmg_path=$2
|
||||
local build_type=$3
|
||||
|
||||
log_success "=========================================="
|
||||
log_success "iOS 构建完成!"
|
||||
log_success "=========================================="
|
||||
log_info "构建类型: $build_type"
|
||||
log_info "IPA 文件: $ipa_path"
|
||||
log_info "DMG 文件: $dmg_path"
|
||||
log_success "=========================================="
|
||||
log_warning "注意: 此版本未签名,需要开发者证书才能安装到设备"
|
||||
log_info "要创建签名版本,请使用: ./build_ios.sh"
|
||||
log_success "=========================================="
|
||||
}
|
||||
|
||||
# 主函数
|
||||
main() {
|
||||
local build_type=${1:-"debug"}
|
||||
|
||||
log_info "开始 iOS 简化构建流程..."
|
||||
log_info "构建类型: $build_type"
|
||||
log_info "=========================================="
|
||||
|
||||
check_environment
|
||||
clean_build
|
||||
get_dependencies
|
||||
build_ios_app "$build_type"
|
||||
|
||||
# 设置路径
|
||||
local app_path="build/ios/iphoneos/Runner.app"
|
||||
local ipa_path="build/ios/BearVPN-${build_type}.ipa"
|
||||
local dmg_path="build/ios/BearVPN-${build_type}-iOS.dmg"
|
||||
|
||||
# 创建输出目录
|
||||
mkdir -p "$(dirname "$ipa_path")"
|
||||
mkdir -p "$(dirname "$dmg_path")"
|
||||
|
||||
# 创建 IPA
|
||||
create_ipa "$app_path" "$ipa_path"
|
||||
|
||||
# 创建 DMG
|
||||
create_dmg "$ipa_path" "$dmg_path"
|
||||
|
||||
# 显示结果
|
||||
show_result "$ipa_path" "$dmg_path" "$build_type"
|
||||
|
||||
log_success "所有操作完成!"
|
||||
}
|
||||
|
||||
# 显示帮助信息
|
||||
show_help() {
|
||||
echo "iOS 简化构建脚本"
|
||||
echo ""
|
||||
echo "用法: $0 [选项]"
|
||||
echo ""
|
||||
echo "选项:"
|
||||
echo " debug 构建调试版本 (默认)"
|
||||
echo " release 构建发布版本"
|
||||
echo " help 显示此帮助信息"
|
||||
echo ""
|
||||
echo "示例:"
|
||||
echo " $0 # 构建调试版本"
|
||||
echo " $0 debug # 构建调试版本"
|
||||
echo " $0 release # 构建发布版本"
|
||||
echo ""
|
||||
echo "注意: 此脚本创建未签名的版本,仅用于测试"
|
||||
}
|
||||
|
||||
# 处理命令行参数
|
||||
case "${1:-}" in
|
||||
"help"|"-h"|"--help")
|
||||
show_help
|
||||
exit 0
|
||||
;;
|
||||
"debug"|"release")
|
||||
main "$1"
|
||||
;;
|
||||
"")
|
||||
main "debug"
|
||||
;;
|
||||
*)
|
||||
log_error "未知选项: $1"
|
||||
show_help
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@ -1,175 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
# macOS DMG 构建和签名脚本
|
||||
# 需要配置以下环境变量:
|
||||
# - APPLE_ID: 您的 Apple ID
|
||||
# - APPLE_PASSWORD: App 专用密码
|
||||
# - TEAM_ID: 您的开发者团队 ID
|
||||
# - SIGNING_IDENTITY: 代码签名身份
|
||||
|
||||
set -e
|
||||
|
||||
# 颜色输出
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
NC='\033[0m' # No Color
|
||||
|
||||
echo -e "${GREEN}🚀 开始构建 macOS DMG...${NC}"
|
||||
|
||||
# 检查必要的环境变量
|
||||
if [ -z "$APPLE_ID" ]; then
|
||||
echo -e "${RED}❌ 请设置 APPLE_ID 环境变量${NC}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ -z "$APPLE_PASSWORD" ]; then
|
||||
echo -e "${RED}❌ 请设置 APPLE_PASSWORD 环境变量(App 专用密码)${NC}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ -z "$TEAM_ID" ]; then
|
||||
echo -e "${RED}❌ 请设置 TEAM_ID 环境变量${NC}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# 设置默认签名身份(如果没有设置)
|
||||
if [ -z "$SIGNING_IDENTITY" ]; then
|
||||
SIGNING_IDENTITY="Developer ID Application: Your Name (${TEAM_ID})"
|
||||
echo -e "${YELLOW}⚠️ 使用默认签名身份: ${SIGNING_IDENTITY}${NC}"
|
||||
fi
|
||||
|
||||
# 清理之前的构建
|
||||
echo -e "${YELLOW}🧹 清理之前的构建...${NC}"
|
||||
flutter clean
|
||||
rm -rf build/macos/Build/Products/Release/kaer_with_panels.app
|
||||
rm -rf build/macos/Build/Products/Release/kaer_with_panels.dmg
|
||||
|
||||
# 构建 Flutter macOS 应用
|
||||
echo -e "${YELLOW}🔨 构建 Flutter macOS 应用...${NC}"
|
||||
flutter build macos --release
|
||||
|
||||
# 检查应用是否构建成功
|
||||
APP_PATH="build/macos/Build/Products/Release/BearVPN.app"
|
||||
if [ ! -d "$APP_PATH" ]; then
|
||||
echo -e "${RED}❌ 应用构建失败: $APP_PATH 不存在${NC}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo -e "${GREEN}✅ 应用构建成功: $APP_PATH${NC}"
|
||||
|
||||
# 代码签名
|
||||
echo -e "${YELLOW}🔐 开始代码签名...${NC}"
|
||||
|
||||
# 签名应用
|
||||
echo -e "${YELLOW}📝 签名应用...${NC}"
|
||||
codesign --force --deep --sign "$SIGNING_IDENTITY" \
|
||||
--options runtime \
|
||||
--timestamp \
|
||||
--entitlements macos/Runner/Runner.entitlements \
|
||||
"$APP_PATH"
|
||||
|
||||
# 验证签名
|
||||
echo -e "${YELLOW}🔍 验证应用签名...${NC}"
|
||||
codesign --verify --verbose "$APP_PATH"
|
||||
spctl --assess --verbose "$APP_PATH"
|
||||
|
||||
echo -e "${GREEN}✅ 应用签名成功${NC}"
|
||||
|
||||
# 创建 DMG
|
||||
echo -e "${YELLOW}📦 创建 DMG 安装包...${NC}"
|
||||
|
||||
DMG_PATH="build/macos/Build/Products/Release/BearVPN.dmg"
|
||||
TEMP_DMG="build/macos/Build/Products/Release/temp.dmg"
|
||||
|
||||
# 创建临时 DMG
|
||||
hdiutil create -srcfolder "$APP_PATH" -volname "Kaer VPN" -fs HFS+ -fsargs "-c c=64,a=16,e=16" -format UDRW -size 200m "$TEMP_DMG"
|
||||
|
||||
# 挂载临时 DMG
|
||||
MOUNT_POINT=$(hdiutil attach -readwrite -noverify -noautoopen "$TEMP_DMG" | egrep '^/dev/' | sed 1q | awk '{print $3}')
|
||||
|
||||
# 等待挂载完成
|
||||
sleep 2
|
||||
|
||||
# 设置 DMG 属性
|
||||
echo -e "${YELLOW}🎨 设置 DMG 属性...${NC}"
|
||||
|
||||
# 创建应用程序链接
|
||||
ln -s /Applications "$MOUNT_POINT/Applications"
|
||||
|
||||
# 设置 DMG 背景和图标(可选)
|
||||
# cp dmg_background.png "$MOUNT_POINT/.background/"
|
||||
# cp app_icon.icns "$MOUNT_POINT/.VolumeIcon.icns"
|
||||
|
||||
# 设置窗口属性
|
||||
osascript <<EOF
|
||||
tell application "Finder"
|
||||
tell disk "Kaer VPN"
|
||||
open
|
||||
set current view of container window to icon view
|
||||
set toolbar visible of container window to false
|
||||
set statusbar visible of container window to false
|
||||
set the bounds of container window to {400, 100, 900, 450}
|
||||
set theViewOptions to the icon view options of container window
|
||||
set arrangement of theViewOptions to not arranged
|
||||
set icon size of theViewOptions to 128
|
||||
set background picture of theViewOptions to file ".background:dmg_background.png"
|
||||
make new alias file at container window to POSIX file "/Applications" with properties {name:"Applications"}
|
||||
set position of item "BearVPN.app" of container window to {150, 200}
|
||||
set position of item "Applications" of container window to {350, 200}
|
||||
close
|
||||
open
|
||||
update without registering applications
|
||||
delay 2
|
||||
end tell
|
||||
end tell
|
||||
EOF
|
||||
|
||||
# 卸载 DMG
|
||||
hdiutil detach "$MOUNT_POINT"
|
||||
|
||||
# 转换为只读 DMG
|
||||
hdiutil convert "$TEMP_DMG" -format UDZO -imagekey zlib-level=9 -o "$DMG_PATH"
|
||||
|
||||
# 清理临时文件
|
||||
rm "$TEMP_DMG"
|
||||
|
||||
echo -e "${GREEN}✅ DMG 创建成功: $DMG_PATH${NC}"
|
||||
|
||||
# 签名 DMG
|
||||
echo -e "${YELLOW}🔐 签名 DMG...${NC}"
|
||||
INSTALLER_IDENTITY="Developer ID Installer: Your Name (${TEAM_ID})"
|
||||
codesign --sign "$INSTALLER_IDENTITY" "$DMG_PATH"
|
||||
|
||||
# 验证 DMG 签名
|
||||
codesign --verify --verbose "$DMG_PATH"
|
||||
|
||||
echo -e "${GREEN}✅ DMG 签名成功${NC}"
|
||||
|
||||
# 公证 DMG
|
||||
echo -e "${YELLOW}📋 开始公证 DMG...${NC}"
|
||||
|
||||
# 上传到 Apple 进行公证
|
||||
xcrun notarytool submit "$DMG_PATH" \
|
||||
--apple-id "$APPLE_ID" \
|
||||
--password "$APPLE_PASSWORD" \
|
||||
--team-id "$TEAM_ID" \
|
||||
--wait
|
||||
|
||||
echo -e "${GREEN}✅ DMG 公证成功${NC}"
|
||||
|
||||
# 装订公证票据
|
||||
echo -e "${YELLOW}📎 装订公证票据...${NC}"
|
||||
xcrun stapler staple "$DMG_PATH"
|
||||
|
||||
# 验证最终 DMG
|
||||
echo -e "${YELLOW}🔍 验证最终 DMG...${NC}"
|
||||
spctl --assess --verbose "$DMG_PATH"
|
||||
|
||||
echo -e "${GREEN}🎉 DMG 构建完成!${NC}"
|
||||
echo -e "${GREEN}📁 文件位置: $DMG_PATH${NC}"
|
||||
echo -e "${GREEN}📏 文件大小: $(du -h "$DMG_PATH" | cut -f1)${NC}"
|
||||
|
||||
# 显示 DMG 信息
|
||||
echo -e "${YELLOW}📊 DMG 信息:${NC}"
|
||||
hdiutil imageinfo "$DMG_PATH"
|
||||
@ -1,108 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
# 简化的 macOS DMG 构建脚本(无签名版本)
|
||||
# 注意:此版本需要用户在安装时手动允许
|
||||
|
||||
set -e
|
||||
|
||||
# 颜色输出
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
NC='\033[0m' # No Color
|
||||
|
||||
echo -e "${GREEN}🚀 开始构建 macOS DMG(简化版本)...${NC}"
|
||||
|
||||
# 清理之前的构建
|
||||
echo -e "${YELLOW}🧹 清理之前的构建...${NC}"
|
||||
flutter clean
|
||||
|
||||
# 构建 Flutter macOS 应用
|
||||
echo -e "${YELLOW}🔨 构建 Flutter macOS 应用...${NC}"
|
||||
flutter build macos --release
|
||||
|
||||
# 检查应用是否构建成功
|
||||
APP_PATH="build/macos/Build/Products/Release/BearVPN.app"
|
||||
if [ ! -d "$APP_PATH" ]; then
|
||||
echo -e "${RED}❌ 应用构建失败: $APP_PATH 不存在${NC}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo -e "${GREEN}✅ 应用构建成功: $APP_PATH${NC}"
|
||||
|
||||
# 创建 DMG
|
||||
echo -e "${YELLOW}📦 创建 DMG 安装包...${NC}"
|
||||
|
||||
DMG_PATH="build/macos/Build/Products/Release/BearVPN.dmg"
|
||||
TEMP_DMG="build/macos/Build/Products/Release/temp.dmg"
|
||||
|
||||
# 创建临时 DMG
|
||||
hdiutil create -srcfolder "$APP_PATH" -volname "Kaer VPN" -fs HFS+ -fsargs "-c c=64,a=16,e=16" -format UDRW -size 200m "$TEMP_DMG"
|
||||
|
||||
# 挂载临时 DMG
|
||||
echo -e "${YELLOW}📂 挂载临时 DMG...${NC}"
|
||||
MOUNT_OUTPUT=$(hdiutil attach -readwrite -noverify -noautoopen "$TEMP_DMG")
|
||||
MOUNT_POINT=$(echo "$MOUNT_OUTPUT" | grep "/Volumes" | awk '{print $3}')
|
||||
|
||||
if [ -z "$MOUNT_POINT" ]; then
|
||||
echo -e "${RED}❌ 无法挂载 DMG${NC}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo -e "${GREEN}✅ DMG 已挂载到: $MOUNT_POINT${NC}"
|
||||
|
||||
# 等待挂载完成
|
||||
sleep 3
|
||||
|
||||
# 设置 DMG 属性
|
||||
echo -e "${YELLOW}🎨 设置 DMG 属性...${NC}"
|
||||
|
||||
# 创建应用程序链接
|
||||
ln -s /Applications "$MOUNT_POINT/Applications"
|
||||
|
||||
# 设置窗口属性
|
||||
echo -e "${YELLOW}🖼️ 设置窗口属性...${NC}"
|
||||
osascript <<EOF
|
||||
tell application "Finder"
|
||||
tell disk "Kaer VPN"
|
||||
open
|
||||
set current view of container window to icon view
|
||||
set toolbar visible of container window to false
|
||||
set statusbar visible of container window to false
|
||||
set the bounds of container window to {400, 100, 900, 450}
|
||||
set theViewOptions to the icon view options of container window
|
||||
set arrangement of theViewOptions to not arranged
|
||||
set icon size of theViewOptions to 128
|
||||
make new alias file at container window to POSIX file "/Applications" with properties {name:"Applications"}
|
||||
set position of item "BearVPN.app" of container window to {150, 200}
|
||||
set position of item "Applications" of container window to {350, 200}
|
||||
close
|
||||
open
|
||||
update without registering applications
|
||||
delay 2
|
||||
end tell
|
||||
end tell
|
||||
EOF
|
||||
|
||||
# 卸载 DMG
|
||||
echo -e "${YELLOW}📤 卸载 DMG...${NC}"
|
||||
hdiutil detach "$MOUNT_POINT" -force
|
||||
|
||||
# 转换为只读 DMG
|
||||
hdiutil convert "$TEMP_DMG" -format UDZO -imagekey zlib-level=9 -o "$DMG_PATH"
|
||||
|
||||
# 清理临时文件
|
||||
rm "$TEMP_DMG"
|
||||
|
||||
echo -e "${GREEN}✅ DMG 创建成功: $DMG_PATH${NC}"
|
||||
|
||||
# 显示 DMG 信息
|
||||
echo -e "${YELLOW}📊 DMG 信息:${NC}"
|
||||
hdiutil imageinfo "$DMG_PATH"
|
||||
|
||||
echo -e "${GREEN}🎉 DMG 构建完成!${NC}"
|
||||
echo -e "${GREEN}📁 文件位置: $DMG_PATH${NC}"
|
||||
echo -e "${GREEN}📏 文件大小: $(du -h "$DMG_PATH" | cut -f1)${NC}"
|
||||
|
||||
echo -e "${YELLOW}⚠️ 注意:此 DMG 未签名,用户安装时需要在安全隐私设置中手动允许${NC}"
|
||||
echo -e "${YELLOW}💡 要避免手动允许,请使用 build_macos_dmg.sh 脚本进行签名和公证${NC}"
|
||||
@ -1,138 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
# 检查公证状态的脚本
|
||||
# 作者: AI Assistant
|
||||
|
||||
set -e
|
||||
|
||||
# 配置变量
|
||||
APPLE_ID="kieran@newlifeephrata.us"
|
||||
TEAM_ID="3UR892FAP3"
|
||||
PASSWORD="gtvp-izmw-cubf-yxfe"
|
||||
SUBMISSION_ID=""
|
||||
|
||||
# 颜色输出
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
BLUE='\033[0;34m'
|
||||
NC='\033[0m'
|
||||
|
||||
log_info() {
|
||||
echo -e "${BLUE}[INFO]${NC} $1"
|
||||
}
|
||||
|
||||
log_success() {
|
||||
echo -e "${GREEN}[SUCCESS]${NC} $1"
|
||||
}
|
||||
|
||||
log_warning() {
|
||||
echo -e "${YELLOW}[WARNING]${NC} $1"
|
||||
}
|
||||
|
||||
log_error() {
|
||||
echo -e "${RED}[ERROR]${NC} $1"
|
||||
}
|
||||
|
||||
# 检查历史提交记录
|
||||
check_history() {
|
||||
log_info "检查历史提交记录..."
|
||||
|
||||
xcrun notarytool history \
|
||||
--apple-id "$APPLE_ID" \
|
||||
--password "$PASSWORD" \
|
||||
--team-id "$TEAM_ID"
|
||||
}
|
||||
|
||||
# 检查特定提交状态
|
||||
check_submission() {
|
||||
if [ -z "$SUBMISSION_ID" ]; then
|
||||
log_error "请提供提交 ID"
|
||||
log_info "使用方法: $0 <submission_id>"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
log_info "检查提交状态: $SUBMISSION_ID"
|
||||
|
||||
xcrun notarytool info "$SUBMISSION_ID" \
|
||||
--apple-id "$APPLE_ID" \
|
||||
--password "$PASSWORD" \
|
||||
--team-id "$TEAM_ID"
|
||||
}
|
||||
|
||||
# 检查日志
|
||||
check_log() {
|
||||
if [ -z "$SUBMISSION_ID" ]; then
|
||||
log_error "请提供提交 ID"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
log_info "获取提交日志: $SUBMISSION_ID"
|
||||
|
||||
xcrun notarytool log "$SUBMISSION_ID" \
|
||||
--apple-id "$APPLE_ID" \
|
||||
--password "$PASSWORD" \
|
||||
--team-id "$TEAM_ID"
|
||||
}
|
||||
|
||||
# 实时监控
|
||||
monitor_status() {
|
||||
log_info "开始实时监控公证状态..."
|
||||
|
||||
while true; do
|
||||
echo "=========================================="
|
||||
echo "时间: $(date)"
|
||||
echo "=========================================="
|
||||
|
||||
# 检查历史记录
|
||||
check_history
|
||||
|
||||
echo "=========================================="
|
||||
echo "等待 30 秒后刷新..."
|
||||
sleep 30
|
||||
done
|
||||
}
|
||||
|
||||
# 显示帮助
|
||||
show_help() {
|
||||
echo "用法: $0 [选项]"
|
||||
echo ""
|
||||
echo "选项:"
|
||||
echo " history - 查看历史提交记录"
|
||||
echo " info <submission_id> - 查看特定提交状态"
|
||||
echo " log <submission_id> - 查看提交日志"
|
||||
echo " monitor - 实时监控状态"
|
||||
echo " help - 显示此帮助"
|
||||
echo ""
|
||||
echo "示例:"
|
||||
echo " $0 history"
|
||||
echo " $0 info 12345678-1234-1234-1234-123456789012"
|
||||
echo " $0 log 12345678-1234-1234-1234-123456789012"
|
||||
echo " $0 monitor"
|
||||
}
|
||||
|
||||
# 主函数
|
||||
main() {
|
||||
case "${1:-help}" in
|
||||
"history")
|
||||
check_history
|
||||
;;
|
||||
"info")
|
||||
SUBMISSION_ID="$2"
|
||||
check_submission
|
||||
;;
|
||||
"log")
|
||||
SUBMISSION_ID="$2"
|
||||
check_log
|
||||
;;
|
||||
"monitor")
|
||||
monitor_status
|
||||
;;
|
||||
"help"|*)
|
||||
show_help
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
# 运行主函数
|
||||
main "$@"
|
||||
@ -1,237 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
# 完成公证流程脚本
|
||||
# 作者: AI Assistant
|
||||
|
||||
set -e
|
||||
|
||||
# 配置变量
|
||||
APPLE_ID="kieran@newlifeephrata.us"
|
||||
PASSWORD="gtvp-izmw-cubf-yxfe"
|
||||
TEAM_ID="3UR892FAP3"
|
||||
DMG_FILE="build/macos/Build/Products/Release/BearVPN-1.0.0-macOS-Signed.dmg"
|
||||
|
||||
# 颜色输出
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
BLUE='\033[0;34m'
|
||||
NC='\033[0m'
|
||||
|
||||
log_info() {
|
||||
echo -e "${BLUE}[INFO]${NC} $1"
|
||||
}
|
||||
|
||||
log_success() {
|
||||
echo -e "${GREEN}[SUCCESS]${NC} $1"
|
||||
}
|
||||
|
||||
log_warning() {
|
||||
echo -e "${YELLOW}[WARNING]${NC} $1"
|
||||
}
|
||||
|
||||
log_error() {
|
||||
echo -e "${RED}[ERROR]${NC} $1"
|
||||
}
|
||||
|
||||
# 检查提交状态
|
||||
check_status() {
|
||||
local submission_id="$1"
|
||||
|
||||
log_info "检查提交状态: $submission_id"
|
||||
|
||||
local status=$(xcrun notarytool info "$submission_id" \
|
||||
--apple-id "$APPLE_ID" \
|
||||
--password "$PASSWORD" \
|
||||
--team-id "$TEAM_ID" \
|
||||
--output-format json | jq -r '.status')
|
||||
|
||||
echo "$status"
|
||||
}
|
||||
|
||||
# 等待完成
|
||||
wait_for_completion() {
|
||||
local submission_id="$1"
|
||||
|
||||
log_info "等待公证完成..."
|
||||
|
||||
while true; do
|
||||
local status=$(check_status "$submission_id")
|
||||
|
||||
case "$status" in
|
||||
"Accepted")
|
||||
log_success "公证成功!"
|
||||
return 0
|
||||
;;
|
||||
"Invalid")
|
||||
log_error "公证失败!"
|
||||
show_log "$submission_id"
|
||||
return 1
|
||||
;;
|
||||
"In Progress")
|
||||
log_info "状态: 进行中... ($(date))"
|
||||
sleep 30
|
||||
;;
|
||||
*)
|
||||
log_warning "未知状态: $status"
|
||||
sleep 30
|
||||
;;
|
||||
esac
|
||||
done
|
||||
}
|
||||
|
||||
# 显示日志
|
||||
show_log() {
|
||||
local submission_id="$1"
|
||||
|
||||
log_info "获取公证日志..."
|
||||
|
||||
xcrun notarytool log "$submission_id" \
|
||||
--apple-id "$APPLE_ID" \
|
||||
--password "$PASSWORD" \
|
||||
--team-id "$TEAM_ID"
|
||||
}
|
||||
|
||||
# 装订公证
|
||||
staple_notarization() {
|
||||
log_info "装订公证到 DMG..."
|
||||
|
||||
if [ ! -f "$DMG_FILE" ]; then
|
||||
log_error "DMG 文件不存在: $DMG_FILE"
|
||||
return 1
|
||||
fi
|
||||
|
||||
xcrun stapler staple "$DMG_FILE"
|
||||
|
||||
if [ $? -eq 0 ]; then
|
||||
log_success "装订成功!"
|
||||
return 0
|
||||
else
|
||||
log_error "装订失败!"
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
# 验证最终结果
|
||||
verify_result() {
|
||||
log_info "验证最终结果..."
|
||||
|
||||
# 检查装订状态
|
||||
xcrun stapler validate "$DMG_FILE"
|
||||
|
||||
if [ $? -eq 0 ]; then
|
||||
log_success "DMG 已成功装订公证!"
|
||||
log_info "现在可以在其他 Mac 上正常打开了"
|
||||
else
|
||||
log_error "DMG 装订验证失败!"
|
||||
fi
|
||||
}
|
||||
|
||||
# 自动完成流程
|
||||
auto_complete() {
|
||||
local submission_id="$1"
|
||||
|
||||
log_info "开始自动完成流程..."
|
||||
|
||||
# 等待完成
|
||||
if wait_for_completion "$submission_id"; then
|
||||
# 装订公证
|
||||
if staple_notarization; then
|
||||
# 验证结果
|
||||
verify_result
|
||||
log_success "整个流程完成!"
|
||||
else
|
||||
log_error "装订失败"
|
||||
return 1
|
||||
fi
|
||||
else
|
||||
log_error "公证失败"
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
# 手动完成流程
|
||||
manual_complete() {
|
||||
local submission_id="$1"
|
||||
|
||||
log_info "手动完成流程..."
|
||||
|
||||
# 检查当前状态
|
||||
local status=$(check_status "$submission_id")
|
||||
log_info "当前状态: $status"
|
||||
|
||||
case "$status" in
|
||||
"Accepted")
|
||||
log_success "公证已完成,开始装订..."
|
||||
staple_notarization
|
||||
verify_result
|
||||
;;
|
||||
"In Progress")
|
||||
log_warning "公证仍在进行中,请稍后再试"
|
||||
;;
|
||||
"Invalid")
|
||||
log_error "公证失败,请查看日志"
|
||||
show_log "$submission_id"
|
||||
;;
|
||||
*)
|
||||
log_warning "未知状态: $status"
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
# 显示帮助
|
||||
show_help() {
|
||||
echo "用法: $0 [选项] <submission_id>"
|
||||
echo ""
|
||||
echo "选项:"
|
||||
echo " auto - 自动等待并完成"
|
||||
echo " manual - 手动检查并完成"
|
||||
echo " status - 仅检查状态"
|
||||
echo " log - 查看日志"
|
||||
echo " staple - 仅装订公证"
|
||||
echo " verify - 验证结果"
|
||||
echo ""
|
||||
echo "示例:"
|
||||
echo " $0 auto b7414dba-adb5-4e0a-9535-ae51815736c4"
|
||||
echo " $0 manual b7414dba-adb5-4e0a-9535-ae51815736c4"
|
||||
echo " $0 status b7414dba-adb5-4e0a-9535-ae51815736c4"
|
||||
}
|
||||
|
||||
# 主函数
|
||||
main() {
|
||||
local action="${1:-help}"
|
||||
local submission_id="$2"
|
||||
|
||||
if [ -z "$submission_id" ] && [ "$action" != "help" ]; then
|
||||
log_error "请提供提交 ID"
|
||||
show_help
|
||||
exit 1
|
||||
fi
|
||||
|
||||
case "$action" in
|
||||
"auto")
|
||||
auto_complete "$submission_id"
|
||||
;;
|
||||
"manual")
|
||||
manual_complete "$submission_id"
|
||||
;;
|
||||
"status")
|
||||
check_status "$submission_id"
|
||||
;;
|
||||
"log")
|
||||
show_log "$submission_id"
|
||||
;;
|
||||
"staple")
|
||||
staple_notarization
|
||||
;;
|
||||
"verify")
|
||||
verify_result
|
||||
;;
|
||||
"help"|*)
|
||||
show_help
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
# 运行主函数
|
||||
main "$@"
|
||||
149
create_dmg.sh
149
create_dmg.sh
@ -1,149 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
# BearVPN macOS DMG 打包脚本
|
||||
# 作者: AI Assistant
|
||||
# 日期: $(date)
|
||||
|
||||
set -e
|
||||
|
||||
# 配置变量
|
||||
APP_NAME="BearVPN"
|
||||
APP_VERSION="1.0.0"
|
||||
DMG_NAME="${APP_NAME}-${APP_VERSION}-macOS"
|
||||
APP_PATH="build/macos/Build/Products/Release/${APP_NAME}.app"
|
||||
DMG_PATH="build/macos/Build/Products/Release/${DMG_NAME}.dmg"
|
||||
TEMP_DMG_PATH="build/macos/Build/Products/Release/temp_${DMG_NAME}.dmg"
|
||||
|
||||
# 颜色输出
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
BLUE='\033[0;34m'
|
||||
NC='\033[0m' # No Color
|
||||
|
||||
# 日志函数
|
||||
log_info() {
|
||||
echo -e "${BLUE}[INFO]${NC} $1"
|
||||
}
|
||||
|
||||
log_success() {
|
||||
echo -e "${GREEN}[SUCCESS]${NC} $1"
|
||||
}
|
||||
|
||||
log_warning() {
|
||||
echo -e "${YELLOW}[WARNING]${NC} $1"
|
||||
}
|
||||
|
||||
log_error() {
|
||||
echo -e "${RED}[ERROR]${NC} $1"
|
||||
}
|
||||
|
||||
# 检查应用是否存在
|
||||
check_app() {
|
||||
log_info "检查应用文件..."
|
||||
if [ ! -d "$APP_PATH" ]; then
|
||||
log_error "应用文件不存在: $APP_PATH"
|
||||
log_info "请先运行: flutter build macos --release"
|
||||
exit 1
|
||||
fi
|
||||
log_success "应用文件检查通过"
|
||||
}
|
||||
|
||||
# 清理旧的 DMG 文件
|
||||
cleanup() {
|
||||
log_info "清理旧的 DMG 文件..."
|
||||
rm -f "$DMG_PATH" "$TEMP_DMG_PATH"
|
||||
log_success "清理完成"
|
||||
}
|
||||
|
||||
# 创建 DMG
|
||||
create_dmg() {
|
||||
log_info "开始创建 DMG 文件..."
|
||||
|
||||
# 使用 create-dmg 创建 DMG
|
||||
create-dmg \
|
||||
--volname "$APP_NAME" \
|
||||
--volicon "macos/Runner/Assets.xcassets/AppIcon.appiconset/icon-256@2x.png" \
|
||||
--window-pos 200 120 \
|
||||
--window-size 600 400 \
|
||||
--icon-size 100 \
|
||||
--icon "$APP_NAME.app" 175 190 \
|
||||
--hide-extension "$APP_NAME.app" \
|
||||
--app-drop-link 425 190 \
|
||||
--no-internet-enable \
|
||||
"$DMG_PATH" \
|
||||
"$APP_PATH"
|
||||
|
||||
if [ $? -eq 0 ]; then
|
||||
log_success "DMG 文件创建成功: $DMG_PATH"
|
||||
else
|
||||
log_error "DMG 文件创建失败"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
# 验证 DMG
|
||||
verify_dmg() {
|
||||
log_info "验证 DMG 文件..."
|
||||
|
||||
# 检查文件大小
|
||||
DMG_SIZE=$(du -h "$DMG_PATH" | cut -f1)
|
||||
log_info "DMG 文件大小: $DMG_SIZE"
|
||||
|
||||
# 检查文件类型
|
||||
FILE_TYPE=$(file "$DMG_PATH")
|
||||
log_info "文件类型: $FILE_TYPE"
|
||||
|
||||
# 尝试挂载 DMG 验证
|
||||
log_info "验证 DMG 内容..."
|
||||
MOUNT_POINT=$(hdiutil attach "$DMG_PATH" -nobrowse | grep -E '^/dev/' | sed 1q | awk '{print $3}')
|
||||
|
||||
if [ -n "$MOUNT_POINT" ]; then
|
||||
log_success "DMG 挂载成功: $MOUNT_POINT"
|
||||
|
||||
# 检查应用是否在 DMG 中
|
||||
if [ -d "$MOUNT_POINT/$APP_NAME.app" ]; then
|
||||
log_success "应用文件在 DMG 中验证成功"
|
||||
else
|
||||
log_error "应用文件在 DMG 中未找到"
|
||||
fi
|
||||
|
||||
# 卸载 DMG
|
||||
hdiutil detach "$MOUNT_POINT" -quiet
|
||||
log_info "DMG 已卸载"
|
||||
else
|
||||
log_error "DMG 挂载失败"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
# 显示结果
|
||||
show_result() {
|
||||
log_success "=========================================="
|
||||
log_success "DMG 打包完成!"
|
||||
log_success "=========================================="
|
||||
log_info "应用名称: $APP_NAME"
|
||||
log_info "版本: $APP_VERSION"
|
||||
log_info "DMG 文件: $DMG_PATH"
|
||||
log_info "文件大小: $(du -h "$DMG_PATH" | cut -f1)"
|
||||
log_success "=========================================="
|
||||
log_info "你可以将 DMG 文件分发给用户安装"
|
||||
log_info "用户双击 DMG 文件,然后将应用拖拽到 Applications 文件夹即可"
|
||||
}
|
||||
|
||||
# 主函数
|
||||
main() {
|
||||
log_info "开始 BearVPN macOS DMG 打包流程..."
|
||||
log_info "=========================================="
|
||||
|
||||
check_app
|
||||
cleanup
|
||||
create_dmg
|
||||
verify_dmg
|
||||
show_result
|
||||
|
||||
log_success "所有操作完成!"
|
||||
}
|
||||
|
||||
# 运行主函数
|
||||
main "$@"
|
||||
@ -1,179 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
# 创建包含安装脚本的 DMG
|
||||
# 此脚本会创建一个包含 BearVPN.app 和安装脚本的 DMG
|
||||
|
||||
set -e
|
||||
|
||||
# 配置变量
|
||||
APP_NAME="BearVPN"
|
||||
APP_VERSION="1.0.0"
|
||||
DMG_NAME="${APP_NAME}-${APP_VERSION}-macOS-Signed"
|
||||
APP_PATH="build/macos/Build/Products/Release/${APP_NAME}.app"
|
||||
DMG_PATH="build/macos/Build/Products/Release/${DMG_NAME}.dmg"
|
||||
TEMP_DIR="/tmp/BearVPN_DMG"
|
||||
|
||||
# 颜色输出
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
BLUE='\033[0;34m'
|
||||
NC='\033[0m' # No Color
|
||||
|
||||
# 日志函数
|
||||
log_info() {
|
||||
echo -e "${BLUE}[INFO]${NC} $1"
|
||||
}
|
||||
|
||||
log_success() {
|
||||
echo -e "${GREEN}[SUCCESS]${NC} $1"
|
||||
}
|
||||
|
||||
log_warning() {
|
||||
echo -e "${YELLOW}[WARNING]${NC} $1"
|
||||
}
|
||||
|
||||
log_error() {
|
||||
echo -e "${RED}[ERROR]${NC} $1"
|
||||
}
|
||||
|
||||
# 清理临时目录
|
||||
cleanup_temp() {
|
||||
log_info "清理临时目录..."
|
||||
rm -rf "$TEMP_DIR"
|
||||
mkdir -p "$TEMP_DIR"
|
||||
}
|
||||
|
||||
# 准备 DMG 内容
|
||||
prepare_dmg_content() {
|
||||
log_info "准备 DMG 内容..."
|
||||
|
||||
# 复制应用
|
||||
cp -R "$APP_PATH" "$TEMP_DIR/"
|
||||
|
||||
# 复制安装脚本
|
||||
cp "install_bearvpn.sh" "$TEMP_DIR/"
|
||||
chmod +x "$TEMP_DIR/install_bearvpn.sh"
|
||||
|
||||
# 创建 README 文件
|
||||
cat > "$TEMP_DIR/README.txt" << 'EOF'
|
||||
🐻 BearVPN 安装说明
|
||||
==================
|
||||
|
||||
欢迎使用 BearVPN!
|
||||
|
||||
📱 安装方法:
|
||||
1. 双击 "BearVPN.app" 直接安装
|
||||
2. 或运行 "install_bearvpn.sh" 脚本进行自动安装
|
||||
|
||||
⚠️ 如果应用无法打开:
|
||||
1. 右键点击 BearVPN.app → "打开"
|
||||
2. 在系统偏好设置 → 安全性与隐私 → 允许从以下位置下载的应用 → 选择 "任何来源"
|
||||
3. 或运行:sudo spctl --master-disable
|
||||
|
||||
🔧 技术支持:
|
||||
如有问题,请联系技术支持团队
|
||||
|
||||
感谢使用 BearVPN!
|
||||
EOF
|
||||
|
||||
# 创建 Applications 链接
|
||||
ln -s /Applications "$TEMP_DIR/Applications"
|
||||
|
||||
log_success "DMG 内容准备完成"
|
||||
}
|
||||
|
||||
# 创建 DMG
|
||||
create_dmg() {
|
||||
log_info "开始创建 DMG..."
|
||||
|
||||
# 删除旧的 DMG
|
||||
rm -f "$DMG_PATH"
|
||||
|
||||
# 使用 create-dmg 创建 DMG
|
||||
create-dmg \
|
||||
--volname "$APP_NAME" \
|
||||
--volicon "macos/Runner/Assets.xcassets/AppIcon.appiconset/icon-256@2x.png" \
|
||||
--window-pos 200 120 \
|
||||
--window-size 700 500 \
|
||||
--icon-size 100 \
|
||||
--icon "$APP_NAME.app" 100 200 \
|
||||
--icon "install_bearvpn.sh" 300 200 \
|
||||
--icon "README.txt" 500 200 \
|
||||
--icon "Applications" 100 350 \
|
||||
--hide-extension "$APP_NAME.app" \
|
||||
--no-internet-enable \
|
||||
"$DMG_PATH" \
|
||||
"$TEMP_DIR"
|
||||
|
||||
if [ $? -eq 0 ]; then
|
||||
log_success "DMG 文件创建成功: $DMG_PATH"
|
||||
else
|
||||
log_error "DMG 文件创建失败"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
# 签名 DMG
|
||||
sign_dmg() {
|
||||
log_info "开始签名 DMG 文件..."
|
||||
|
||||
DEVELOPER_ID="Developer ID Application: Civil Rights Corps (3UR892FAP3)"
|
||||
|
||||
# 签名 DMG
|
||||
codesign --force --sign "$DEVELOPER_ID" "$DMG_PATH"
|
||||
|
||||
if [ $? -eq 0 ]; then
|
||||
log_success "DMG 签名成功"
|
||||
else
|
||||
log_error "DMG 签名失败"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# 验证 DMG 签名
|
||||
log_info "验证 DMG 签名..."
|
||||
codesign --verify --verbose "$DMG_PATH"
|
||||
|
||||
if [ $? -eq 0 ]; then
|
||||
log_success "DMG 签名验证通过"
|
||||
else
|
||||
log_error "DMG 签名验证失败"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
# 显示结果
|
||||
show_result() {
|
||||
log_success "=========================================="
|
||||
log_success "包含安装脚本的 DMG 创建完成!"
|
||||
log_success "=========================================="
|
||||
log_info "DMG 路径: $DMG_PATH"
|
||||
log_info "DMG 大小: $(du -h "$DMG_PATH" | cut -f1)"
|
||||
log_info "包含内容:"
|
||||
log_info " - BearVPN.app (应用)"
|
||||
log_info " - install_bearvpn.sh (安装脚本)"
|
||||
log_info " - README.txt (说明文档)"
|
||||
log_info " - Applications (快捷方式)"
|
||||
log_success "=========================================="
|
||||
log_info "用户可以通过以下方式安装:"
|
||||
log_info "1. 直接拖拽 BearVPN.app 到 Applications"
|
||||
log_info "2. 运行 install_bearvpn.sh 脚本"
|
||||
log_success "=========================================="
|
||||
}
|
||||
|
||||
# 主函数
|
||||
main() {
|
||||
log_info "开始创建包含安装脚本的 DMG..."
|
||||
log_info "=========================================="
|
||||
|
||||
cleanup_temp
|
||||
prepare_dmg_content
|
||||
create_dmg
|
||||
sign_dmg
|
||||
show_result
|
||||
|
||||
log_success "所有操作完成!"
|
||||
}
|
||||
|
||||
# 运行主函数
|
||||
main "$@"
|
||||
@ -1,174 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
# BearVPN 连接调试脚本
|
||||
# 用于调试 macOS 平台下的节点连接超时问题
|
||||
|
||||
set -e
|
||||
|
||||
# 颜色输出
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
BLUE='\033[0;34m'
|
||||
NC='\033[0m' # No Color
|
||||
|
||||
# 日志函数
|
||||
log_info() {
|
||||
echo -e "${BLUE}[INFO]${NC} $1"
|
||||
}
|
||||
|
||||
log_success() {
|
||||
echo -e "${GREEN}[SUCCESS]${NC} $1"
|
||||
}
|
||||
|
||||
log_warning() {
|
||||
echo -e "${YELLOW}[WARNING]${NC} $1"
|
||||
}
|
||||
|
||||
log_error() {
|
||||
echo -e "${RED}[ERROR]${NC} $1"
|
||||
}
|
||||
|
||||
# 检查网络连接
|
||||
check_network() {
|
||||
log_info "检查网络连接..."
|
||||
|
||||
# 检查基本网络连接
|
||||
if ping -c 3 8.8.8.8 > /dev/null 2>&1; then
|
||||
log_success "基本网络连接正常"
|
||||
else
|
||||
log_error "基本网络连接失败"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# 检查 DNS 解析
|
||||
if nslookup google.com > /dev/null 2>&1; then
|
||||
log_success "DNS 解析正常"
|
||||
else
|
||||
log_error "DNS 解析失败"
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
# 检查代理设置
|
||||
check_proxy() {
|
||||
log_info "检查系统代理设置..."
|
||||
|
||||
# 检查 HTTP 代理
|
||||
if [ -n "$http_proxy" ] || [ -n "$HTTP_PROXY" ]; then
|
||||
log_warning "检测到 HTTP 代理设置: $http_proxy$HTTP_PROXY"
|
||||
else
|
||||
log_info "未检测到 HTTP 代理设置"
|
||||
fi
|
||||
|
||||
# 检查 HTTPS 代理
|
||||
if [ -n "$https_proxy" ] || [ -n "$HTTPS_PROXY" ]; then
|
||||
log_warning "检测到 HTTPS 代理设置: $https_proxy$HTTPS_PROXY"
|
||||
else
|
||||
log_info "未检测到 HTTPS 代理设置"
|
||||
fi
|
||||
}
|
||||
|
||||
# 检查防火墙
|
||||
check_firewall() {
|
||||
log_info "检查防火墙状态..."
|
||||
|
||||
# 检查 macOS 防火墙
|
||||
local firewall_status=$(sudo /usr/libexec/ApplicationFirewall/socketfilterfw --getglobalstate 2>/dev/null || echo "unknown")
|
||||
log_info "防火墙状态: $firewall_status"
|
||||
|
||||
if [ "$firewall_status" = "enabled" ]; then
|
||||
log_warning "防火墙已启用,可能影响连接"
|
||||
fi
|
||||
}
|
||||
|
||||
# 测试常见端口连接
|
||||
test_ports() {
|
||||
log_info "测试常见端口连接..."
|
||||
|
||||
local ports=(80 443 8080 8443)
|
||||
local hosts=("google.com" "cloudflare.com" "github.com")
|
||||
|
||||
for host in "${hosts[@]}"; do
|
||||
for port in "${ports[@]}"; do
|
||||
if timeout 5 bash -c "echo >/dev/tcp/$host/$port" 2>/dev/null; then
|
||||
log_success "$host:$port 连接正常"
|
||||
else
|
||||
log_warning "$host:$port 连接失败或超时"
|
||||
fi
|
||||
done
|
||||
done
|
||||
}
|
||||
|
||||
# 检查 libcore 库
|
||||
check_libcore() {
|
||||
log_info "检查 libcore 库..."
|
||||
|
||||
if [ -f "libcore/bin/libcore.dylib" ]; then
|
||||
log_success "找到 libcore.dylib"
|
||||
|
||||
# 检查库的架构
|
||||
local arch=$(file libcore/bin/libcore.dylib)
|
||||
log_info "库架构: $arch"
|
||||
|
||||
# 检查库的依赖
|
||||
log_info "库依赖:"
|
||||
otool -L libcore/bin/libcore.dylib | head -10
|
||||
else
|
||||
log_error "未找到 libcore.dylib"
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
# 检查应用配置
|
||||
check_app_config() {
|
||||
log_info "检查应用配置..."
|
||||
|
||||
# 检查当前域名配置
|
||||
if [ -f "lib/app/common/app_config.dart" ]; then
|
||||
log_info "检查域名配置..."
|
||||
grep -n "kr_baseDomains\|kr_currentDomain" lib/app/common/app_config.dart | head -5
|
||||
fi
|
||||
|
||||
# 检查超时配置
|
||||
log_info "检查超时配置..."
|
||||
grep -n "kr_domainTimeout\|kr_totalTimeout" lib/app/common/app_config.dart | head -5
|
||||
}
|
||||
|
||||
# 监控应用日志
|
||||
monitor_logs() {
|
||||
log_info "开始监控应用日志..."
|
||||
log_info "请运行应用并尝试连接节点,然后按 Ctrl+C 停止监控"
|
||||
|
||||
# 监控 Flutter 日志
|
||||
flutter logs --device-id=macos 2>/dev/null | grep -E "(ERROR|WARNING|INFO|超时|连接|节点|SingBox)" || true
|
||||
}
|
||||
|
||||
# 主函数
|
||||
main() {
|
||||
log_info "开始 BearVPN 连接调试..."
|
||||
log_info "=========================================="
|
||||
|
||||
check_network
|
||||
check_proxy
|
||||
check_firewall
|
||||
test_ports
|
||||
check_libcore
|
||||
check_app_config
|
||||
|
||||
log_info "=========================================="
|
||||
log_info "基础检查完成"
|
||||
log_info "=========================================="
|
||||
|
||||
# 询问是否监控日志
|
||||
read -p "是否开始监控应用日志?(y/n): " -n 1 -r
|
||||
echo
|
||||
if [[ $REPLY =~ ^[Yy]$ ]]; then
|
||||
monitor_logs
|
||||
fi
|
||||
|
||||
log_success "调试完成"
|
||||
}
|
||||
|
||||
# 运行主函数
|
||||
main "$@"
|
||||
@ -1,35 +0,0 @@
|
||||
@echo off
|
||||
REM Fix NuGet installation using Chocolatey
|
||||
|
||||
echo Installing NuGet via Chocolatey...
|
||||
|
||||
REM Check if Chocolatey is available
|
||||
where choco >nul 2>nul
|
||||
if %errorlevel% neq 0 (
|
||||
echo Chocolatey not found, installing Chocolatey first...
|
||||
powershell -Command "Set-ExecutionPolicy Bypass -Scope Process -Force; [System.Net.ServicePointManager]::SecurityProtocol = [System.Net.ServicePointManager]::SecurityProtocol -bor 3072; iex ((New-Object System.Net.WebClient).DownloadString('https://community.chocolatey.org/install.ps1'))"
|
||||
)
|
||||
|
||||
echo Installing NuGet via Chocolatey...
|
||||
choco install nuget.commandline -y
|
||||
|
||||
if %errorlevel% equ 0 (
|
||||
echo SUCCESS: NuGet installed via Chocolatey
|
||||
echo Updating PATH...
|
||||
set PATH=C:\ProgramData\chocolatey\bin;%PATH%
|
||||
setx PATH "C:\ProgramData\chocolatey\bin;%PATH%"
|
||||
|
||||
echo Verifying installation...
|
||||
nuget help | findstr "NuGet"
|
||||
|
||||
if %errorlevel% equ 0 (
|
||||
echo NuGet is working correctly!
|
||||
) else (
|
||||
echo WARNING: NuGet installed but not accessible
|
||||
)
|
||||
) else (
|
||||
echo ERROR: NuGet installation failed
|
||||
exit /b 1
|
||||
)
|
||||
|
||||
pause
|
||||
@ -1,67 +0,0 @@
|
||||
# NuGet 安装脚本 - 解决 SSL 问题
|
||||
Write-Host "=== 安装 NuGet ===" -ForegroundColor Green
|
||||
|
||||
# 设置 TLS 1.2
|
||||
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
|
||||
|
||||
# 检查是否已安装
|
||||
$nugetExists = Get-Command nuget -ErrorAction SilentlyContinue
|
||||
if ($nugetExists) {
|
||||
Write-Host "NuGet 已安装: $($nugetExists.Source)" -ForegroundColor Green
|
||||
exit 0
|
||||
}
|
||||
|
||||
# 下载 NuGet
|
||||
Write-Host "下载 NuGet CLI..." -ForegroundColor Yellow
|
||||
$downloadUrls = @(
|
||||
"https://dist.nuget.org/win-x86-commandline/latest/nuget.exe",
|
||||
"https://dist.nuget.org/win-x86-commandline/v6.7.0/nuget.exe",
|
||||
"http://dist.nuget.org/win-x86-commandline/v6.7.0/nuget.exe"
|
||||
)
|
||||
|
||||
$downloaded = $false
|
||||
foreach ($url in $downloadUrls) {
|
||||
try {
|
||||
Write-Host "尝试下载: $url"
|
||||
$webClient = New-Object System.Net.WebClient
|
||||
$webClient.DownloadFile($url, "C:\nuget.exe")
|
||||
$downloaded = $true
|
||||
Write-Host "下载成功!" -ForegroundColor Green
|
||||
break
|
||||
} catch {
|
||||
Write-Host "下载失败: $($_.Exception.Message)" -ForegroundColor Red
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
if (-not $downloaded) {
|
||||
Write-Host "所有下载都失败了,尝试使用 Chocolatey..." -ForegroundColor Yellow
|
||||
try {
|
||||
choco install nuget.commandline -y
|
||||
if (Get-Command nuget -ErrorAction SilentlyContinue) {
|
||||
Write-Host "通过 Chocolatey 安装成功!" -ForegroundColor Green
|
||||
exit 0
|
||||
}
|
||||
} catch {
|
||||
Write-Host "Chocolatey 安装也失败了: $($_.Exception.Message)" -ForegroundColor Red
|
||||
}
|
||||
|
||||
Write-Host "NuGet 安装失败,但继续构建..." -ForegroundColor Yellow
|
||||
exit 0
|
||||
}
|
||||
|
||||
# 验证安装
|
||||
if (Test-Path "C:\nuget.exe") {
|
||||
$env:PATH = "C:\;$env:PATH"
|
||||
Write-Host "NuGet 安装完成" -ForegroundColor Green
|
||||
|
||||
# 测试
|
||||
try {
|
||||
$version = & "C:\nuget.exe" help | Select-String -Pattern "NuGet Version" | Select-Object -First 1
|
||||
Write-Host "版本信息: $version" -ForegroundColor Green
|
||||
} catch {
|
||||
Write-Host "NuGet 可用,但版本检查失败" -ForegroundColor Yellow
|
||||
}
|
||||
} else {
|
||||
Write-Host "NuGet 文件不存在" -ForegroundColor Red
|
||||
}
|
||||
@ -1,64 +0,0 @@
|
||||
# Windows 构建修复脚本
|
||||
# 以管理员身份运行
|
||||
|
||||
Write-Host "=== Windows Flutter 构建修复 ===" -ForegroundColor Green
|
||||
|
||||
# 1. 安装 NuGet
|
||||
Write-Host "`n1. 安装 NuGet..." -ForegroundColor Yellow
|
||||
$nugetPath = "C:\nuget.exe"
|
||||
if (-not (Test-Path $nugetPath)) {
|
||||
try {
|
||||
Write-Host "下载 NuGet..."
|
||||
Invoke-WebRequest -Uri "https://dist.nuget.org/win-x86-commandline/latest/nuget.exe" -OutFile $nugetPath
|
||||
$env:PATH = "C:\;$env:PATH"
|
||||
Write-Host "NuGet 安装成功" -ForegroundColor Green
|
||||
} catch {
|
||||
Write-Host "NuGet 下载失败: $_" -ForegroundColor Red
|
||||
exit 1
|
||||
}
|
||||
} else {
|
||||
Write-Host "NuGet 已存在" -ForegroundColor Green
|
||||
}
|
||||
|
||||
# 2. 启用长路径支持
|
||||
Write-Host "`n2. 启用长路径支持..." -ForegroundColor Yellow
|
||||
try {
|
||||
Set-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Control\FileSystem" -Name "LongPathsEnabled" -Value 1 -Type DWORD -Force
|
||||
Write-Host "长路径支持已启用" -ForegroundColor Green
|
||||
} catch {
|
||||
Write-Host "长路径设置失败: $_" -ForegroundColor Red
|
||||
}
|
||||
|
||||
# 3. 清理构建缓存
|
||||
Write-Host "`n3. 清理构建缓存..." -ForegroundColor Yellow
|
||||
if (Test-Path "build") {
|
||||
Remove-Item -Path "build" -Recurse -Force
|
||||
Write-Host "Build 目录已清理" -ForegroundColor Green
|
||||
}
|
||||
|
||||
if (Test-Path "windows") {
|
||||
Remove-Item -Path "windows" -Recurse -Force
|
||||
Write-Host "Windows 目录已清理" -ForegroundColor Green
|
||||
}
|
||||
|
||||
# 4. 重新创建 Windows 项目
|
||||
Write-Host "`n4. 重新创建 Windows 项目..." -ForegroundColor Yellow
|
||||
flutter create --platforms=windows .
|
||||
|
||||
# 5. 获取依赖
|
||||
Write-Host "`n5. 获取依赖..." -ForegroundColor Yellow
|
||||
flutter pub get
|
||||
|
||||
# 6. 构建 Debug 版本
|
||||
Write-Host "`n6. 构建 Debug 版本..." -ForegroundColor Yellow
|
||||
flutter build windows
|
||||
|
||||
if ($LASTEXITCODE -eq 0) {
|
||||
Write-Host "`n=== 构建成功! ===" -ForegroundColor Green
|
||||
Write-Host "输出目录: build\windows\runner\Debug\" -ForegroundColor Cyan
|
||||
} else {
|
||||
Write-Host "`n=== 构建失败 ===" -ForegroundColor Red
|
||||
Write-Host "请检查错误信息并尝试手动修复" -ForegroundColor Red
|
||||
}
|
||||
|
||||
Write-Host "`n修复完成!" -ForegroundColor Green
|
||||
172
get_team_id.sh
172
get_team_id.sh
@ -1,172 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
# 获取 Apple Developer Team ID 的脚本
|
||||
|
||||
set -e
|
||||
|
||||
# 颜色输出
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
BLUE='\033[0;34m'
|
||||
NC='\033[0m' # No Color
|
||||
|
||||
# 日志函数
|
||||
log_info() {
|
||||
echo -e "${BLUE}[INFO]${NC} $1"
|
||||
}
|
||||
|
||||
log_success() {
|
||||
echo -e "${GREEN}[SUCCESS]${NC} $1"
|
||||
}
|
||||
|
||||
log_warning() {
|
||||
echo -e "${YELLOW}[WARNING]${NC} $1"
|
||||
}
|
||||
|
||||
log_error() {
|
||||
echo -e "${RED}[ERROR]${NC} $1"
|
||||
}
|
||||
|
||||
# 检查 Apple ID 和密码
|
||||
check_credentials() {
|
||||
if [ -z "$APPLE_ID" ] || [ -z "$APPLE_PASSWORD" ]; then
|
||||
log_error "请先设置 APPLE_ID 和 APPLE_PASSWORD 环境变量"
|
||||
log_info "运行: export APPLE_ID='your-apple-id@example.com'"
|
||||
log_info "运行: export APPLE_PASSWORD='your-app-password'"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
log_info "使用 Apple ID: $APPLE_ID"
|
||||
}
|
||||
|
||||
# 方法1: 通过 xcrun altool 获取
|
||||
get_team_id_altool() {
|
||||
log_info "尝试通过 xcrun altool 获取 Team ID..."
|
||||
|
||||
local output
|
||||
if output=$(xcrun altool --list-providers -u "$APPLE_ID" -p "$APPLE_PASSWORD" 2>&1); then
|
||||
local team_id=$(echo "$output" | grep -o 'Team ID: [A-Z0-9]*' | head -1 | cut -d' ' -f3)
|
||||
if [ -n "$team_id" ]; then
|
||||
echo "$team_id"
|
||||
return 0
|
||||
fi
|
||||
fi
|
||||
|
||||
return 1
|
||||
}
|
||||
|
||||
# 方法2: 通过 xcodebuild 获取
|
||||
get_team_id_xcodebuild() {
|
||||
log_info "尝试通过 xcodebuild 获取 Team ID..."
|
||||
|
||||
# 检查是否有 Xcode 项目
|
||||
if [ -f "ios/Runner.xcodeproj/project.pbxproj" ]; then
|
||||
local team_id=$(grep -o 'DEVELOPMENT_TEAM = [A-Z0-9]*' ios/Runner.xcodeproj/project.pbxproj | head -1 | cut -d' ' -f3)
|
||||
if [ -n "$team_id" ]; then
|
||||
echo "$team_id"
|
||||
return 0
|
||||
fi
|
||||
fi
|
||||
|
||||
return 1
|
||||
}
|
||||
|
||||
# 方法3: 通过开发者证书获取
|
||||
get_team_id_certificates() {
|
||||
log_info "尝试通过开发者证书获取 Team ID..."
|
||||
|
||||
local identities=$(security find-identity -v -p codesigning 2>/dev/null)
|
||||
if [ $? -eq 0 ] && [ -n "$identities" ]; then
|
||||
local team_id=$(echo "$identities" | grep -o '([A-Z0-9]*)' | head -1 | tr -d '()')
|
||||
if [ -n "$team_id" ]; then
|
||||
echo "$team_id"
|
||||
return 0
|
||||
fi
|
||||
fi
|
||||
|
||||
return 1
|
||||
}
|
||||
|
||||
# 方法4: 手动输入
|
||||
get_team_id_manual() {
|
||||
log_warning "无法自动获取 Team ID"
|
||||
log_info "请手动输入您的 Team ID:"
|
||||
log_info "1. 登录 https://developer.apple.com"
|
||||
log_info "2. 进入 'Account' -> 'Membership'"
|
||||
log_info "3. 查看 'Team ID' 字段"
|
||||
echo ""
|
||||
read -p "请输入您的 Team ID: " team_id
|
||||
|
||||
if [ -n "$team_id" ]; then
|
||||
echo "$team_id"
|
||||
return 0
|
||||
else
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
# 更新配置文件
|
||||
update_config() {
|
||||
local team_id=$1
|
||||
|
||||
if [ -z "$team_id" ]; then
|
||||
log_error "Team ID 为空"
|
||||
return 1
|
||||
fi
|
||||
|
||||
log_info "更新 ios_signing_config.sh 文件..."
|
||||
|
||||
# 备份原文件
|
||||
cp ios_signing_config.sh ios_signing_config.sh.backup
|
||||
|
||||
# 更新 Team ID
|
||||
sed -i '' "s/export TEAM_ID=\"YOUR_TEAM_ID\"/export TEAM_ID=\"$team_id\"/" ios_signing_config.sh
|
||||
|
||||
# 更新签名身份
|
||||
sed -i '' "s/export SIGNING_IDENTITY=\"iPhone Developer: Your Name (YOUR_TEAM_ID)\"/export SIGNING_IDENTITY=\"iPhone Developer: Your Name ($team_id)\"/" ios_signing_config.sh
|
||||
sed -i '' "s/export DISTRIBUTION_IDENTITY=\"iPhone Distribution: Your Name (YOUR_TEAM_ID)\"/export DISTRIBUTION_IDENTITY=\"iPhone Distribution: Your Name ($team_id)\"/" ios_signing_config.sh
|
||||
|
||||
log_success "配置文件已更新"
|
||||
log_info "Team ID: $team_id"
|
||||
}
|
||||
|
||||
# 主函数
|
||||
main() {
|
||||
log_info "开始获取 Apple Developer Team ID..."
|
||||
log_info "=========================================="
|
||||
|
||||
check_credentials
|
||||
|
||||
local team_id=""
|
||||
|
||||
# 尝试各种方法获取 Team ID
|
||||
if team_id=$(get_team_id_altool); then
|
||||
log_success "通过 xcrun altool 获取到 Team ID: $team_id"
|
||||
elif team_id=$(get_team_id_xcodebuild); then
|
||||
log_success "通过 xcodebuild 获取到 Team ID: $team_id"
|
||||
elif team_id=$(get_team_id_certificates); then
|
||||
log_success "通过开发者证书获取到 Team ID: $team_id"
|
||||
else
|
||||
team_id=$(get_team_id_manual)
|
||||
if [ $? -eq 0 ]; then
|
||||
log_success "手动输入 Team ID: $team_id"
|
||||
else
|
||||
log_error "无法获取 Team ID"
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
# 更新配置文件
|
||||
update_config "$team_id"
|
||||
|
||||
log_success "=========================================="
|
||||
log_success "Team ID 获取完成!"
|
||||
log_success "=========================================="
|
||||
log_info "现在可以运行: source ios_signing_config.sh"
|
||||
log_info "然后运行: ./build_ios_dmg.sh"
|
||||
log_success "=========================================="
|
||||
}
|
||||
|
||||
# 运行主函数
|
||||
main "$@"
|
||||
@ -1,68 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
# BearVPN 安装脚本
|
||||
# 此脚本帮助用户在 macOS 上安全安装 BearVPN
|
||||
|
||||
echo "🐻 BearVPN 安装助手"
|
||||
echo "===================="
|
||||
echo ""
|
||||
|
||||
# 检查是否在正确的目录
|
||||
if [ ! -f "BearVPN.app/Contents/Info.plist" ]; then
|
||||
echo "❌ 错误:请在包含 BearVPN.app 的目录中运行此脚本"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "📱 正在检查应用..."
|
||||
APP_PATH="./BearVPN.app"
|
||||
|
||||
# 检查应用是否存在
|
||||
if [ ! -d "$APP_PATH" ]; then
|
||||
echo "❌ 错误:找不到 BearVPN.app"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "✅ 找到 BearVPN.app"
|
||||
|
||||
# 移除隔离属性
|
||||
echo "🔓 正在移除隔离属性..."
|
||||
sudo xattr -rd com.apple.quarantine "$APP_PATH"
|
||||
if [ $? -eq 0 ]; then
|
||||
echo "✅ 隔离属性已移除"
|
||||
else
|
||||
echo "⚠️ 警告:无法移除隔离属性,请手动操作"
|
||||
fi
|
||||
|
||||
# 检查签名状态
|
||||
echo "🔍 检查应用签名状态..."
|
||||
codesign -dv "$APP_PATH" 2>&1 | grep -q "Developer ID"
|
||||
if [ $? -eq 0 ]; then
|
||||
echo "✅ 应用已使用开发者证书签名"
|
||||
else
|
||||
echo "⚠️ 应用未使用开发者证书签名"
|
||||
fi
|
||||
|
||||
# 移动到应用程序文件夹
|
||||
echo "📁 正在安装到应用程序文件夹..."
|
||||
if [ -d "/Applications/BearVPN.app" ]; then
|
||||
echo "⚠️ 发现已存在的 BearVPN,正在备份..."
|
||||
mv "/Applications/BearVPN.app" "/Applications/BearVPN.app.backup.$(date +%Y%m%d_%H%M%S)"
|
||||
fi
|
||||
|
||||
cp -R "$APP_PATH" "/Applications/"
|
||||
if [ $? -eq 0 ]; then
|
||||
echo "✅ BearVPN 已安装到 /Applications/"
|
||||
else
|
||||
echo "❌ 安装失败"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "🎉 安装完成!"
|
||||
echo ""
|
||||
echo "📋 如果应用无法打开,请尝试以下步骤:"
|
||||
echo "1. 右键点击 BearVPN.app → '打开'"
|
||||
echo "2. 在系统偏好设置 → 安全性与隐私 → 允许从以下位置下载的应用 → 选择 '任何来源'"
|
||||
echo "3. 或者运行:sudo spctl --master-disable"
|
||||
echo ""
|
||||
echo "🔧 如需帮助,请联系技术支持"
|
||||
@ -1,64 +0,0 @@
|
||||
@echo off
|
||||
:: This script checks for and installs all necessary tools for building and packaging the Flutter application on Windows.
|
||||
|
||||
:: 1. Check for Administrator Privileges
|
||||
net session >nul 2>&1
|
||||
if %errorLevel% == 0 (
|
||||
echo Administrator privileges detected. Continuing...
|
||||
) else (
|
||||
echo Requesting Administrator privileges to install tools...
|
||||
powershell -Command "Start-Process cmd.exe -ArgumentList '/c %~s0' -Verb RunAs" >nul 2>&1
|
||||
exit /b
|
||||
)
|
||||
|
||||
:: 2. Check for and Install Chocolatey
|
||||
echo.
|
||||
echo === Checking for Chocolatey ===
|
||||
where choco >nul 2>&1
|
||||
if %errorlevel% equ 0 (
|
||||
echo Chocolatey is already installed.
|
||||
) else (
|
||||
echo Chocolatey not found. Installing now...
|
||||
powershell -NoProfile -ExecutionPolicy Bypass -Command "[System.Net.ServicePointManager]::SecurityProtocol = [System.Net.SecurityProtocolType]::Tls12; iex ((New-Object System.Net.WebClient).DownloadString('https://community.chocolatey.org/install.ps1'))"
|
||||
if %errorlevel% neq 0 (
|
||||
echo ERROR: Failed to install Chocolatey. Please install it manually from https://chocolatey.org
|
||||
pause
|
||||
exit /b 1
|
||||
)
|
||||
echo Chocolatey installed successfully.
|
||||
:: Add Chocolatey to the PATH for the current session
|
||||
set "PATH=%PATH%;%ALLUSERSPROFILE%\chocolatey\bin"
|
||||
)
|
||||
|
||||
:: 3. Install Required Tools via Chocolatey
|
||||
echo.
|
||||
echo === Installing Build Tools (7-Zip and Enigma Virtual Box) ===
|
||||
|
||||
:: Install 7-Zip
|
||||
echo Installing 7-Zip...
|
||||
choco install 7zip -y
|
||||
if %errorlevel% neq 0 (
|
||||
echo ERROR: Failed to install 7-Zip.
|
||||
pause
|
||||
exit /b 1
|
||||
)
|
||||
echo 7-Zip installed successfully.
|
||||
|
||||
:: Install Enigma Virtual Box
|
||||
echo Installing Enigma Virtual Box...
|
||||
choco install enigma-virtual-box -y
|
||||
if %errorlevel% neq 0 (
|
||||
echo ERROR: Failed to install Enigma Virtual Box.
|
||||
pause
|
||||
exit /b 1
|
||||
)
|
||||
echo Enigma Virtual Box installed successfully.
|
||||
|
||||
|
||||
echo.
|
||||
echo =======================================================
|
||||
echo All required build tools have been installed.
|
||||
echo You can now use 'package_windows_single_exe.ps1' to build your single-file executable.
|
||||
echo =======================================================
|
||||
echo.
|
||||
pause
|
||||
@ -1,44 +0,0 @@
|
||||
@echo off
|
||||
REM Simple Flutter installer for Windows Gitea Runner
|
||||
|
||||
echo Installing Flutter for Windows...
|
||||
|
||||
REM Create flutter directory
|
||||
mkdir C:\flutter 2>nul
|
||||
|
||||
REM Download Flutter stable version
|
||||
echo Downloading Flutter...
|
||||
powershell -Command "(New-Object Net.WebClient).DownloadFile('https://storage.googleapis.com/flutter_infra_release/releases/stable/windows/flutter_windows_3.24.5-stable.zip', 'C:\flutter.zip')"
|
||||
|
||||
if %errorlevel% neq 0 (
|
||||
echo Download failed, trying alternative...
|
||||
powershell -Command "Invoke-WebRequest -Uri 'https://storage.googleapis.com/flutter_infra_release/releases/stable/windows/flutter_windows_3.24.5-stable.zip' -OutFile 'C:\flutter.zip'"
|
||||
)
|
||||
|
||||
REM Extract Flutter
|
||||
echo Extracting Flutter...
|
||||
powershell -Command "Expand-Archive -Path 'C:\flutter.zip' -DestinationPath 'C:\' -Force"
|
||||
|
||||
REM Set PATH
|
||||
echo Setting PATH...
|
||||
set PATH=C:\flutter\bin;%PATH%
|
||||
setx PATH "C:\flutter\bin;%PATH%"
|
||||
|
||||
REM Clean up
|
||||
del C:\flutter.zip
|
||||
|
||||
REM Verify installation
|
||||
echo Verifying Flutter installation...
|
||||
C:\flutter\bin\flutter --version
|
||||
|
||||
if %errorlevel% equ 0 (
|
||||
echo SUCCESS: Flutter installed successfully
|
||||
echo Running flutter doctor...
|
||||
C:\flutter\bin\flutter doctor
|
||||
) else (
|
||||
echo ERROR: Flutter installation failed
|
||||
exit /b 1
|
||||
)
|
||||
|
||||
echo Flutter installation complete!
|
||||
pause
|
||||
@ -1,39 +0,0 @@
|
||||
@echo off
|
||||
REM Simple NuGet installer for Windows
|
||||
|
||||
echo Installing NuGet...
|
||||
|
||||
REM Check if NuGet exists
|
||||
where nuget >nul 2>nul
|
||||
if %errorlevel% == 0 (
|
||||
echo NuGet already installed
|
||||
nuget help | findstr "NuGet"
|
||||
pause
|
||||
exit /b 0
|
||||
)
|
||||
|
||||
REM Download NuGet using PowerShell
|
||||
echo Downloading NuGet CLI...
|
||||
powershell -Command "(New-Object Net.WebClient).DownloadFile('http://dist.nuget.org/win-x86-commandline/v6.7.0/nuget.exe', 'C:\nuget.exe')"
|
||||
|
||||
if %errorlevel% neq 0 (
|
||||
echo Download failed, trying alternative...
|
||||
powershell -Command "(New-Object Net.WebClient).DownloadFile('https://dist.nuget.org/win-x86-commandline/v6.7.0/nuget.exe', 'C:\nuget.exe')"
|
||||
)
|
||||
|
||||
REM Add to PATH
|
||||
set PATH=C:\;%PATH%
|
||||
setx PATH "C:\;%PATH%"
|
||||
|
||||
REM Verify installation
|
||||
echo Verifying NuGet installation...
|
||||
C:\nuget.exe help | findstr "NuGet"
|
||||
|
||||
if %errorlevel% equ 0 (
|
||||
echo SUCCESS: NuGet installed successfully
|
||||
) else (
|
||||
echo ERROR: NuGet installation failed
|
||||
exit /b 1
|
||||
)
|
||||
|
||||
pause
|
||||
@ -1,46 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
# iOS 签名配置脚本
|
||||
# 请根据您的开发者账户信息修改以下配置
|
||||
|
||||
# Apple Developer 账户信息
|
||||
export APPLE_ID="kieran@newlifeephrata.us"
|
||||
export APPLE_PASSWORD="Asd112211@"
|
||||
export TEAM_ID="3UR892FAP3"
|
||||
|
||||
# 应用信息
|
||||
export APP_NAME="BearVPN"
|
||||
export BUNDLE_ID="app.baer.com"
|
||||
export VERSION="1.0.0"
|
||||
export BUILD_NUMBER="1"
|
||||
|
||||
# 代码签名身份(运行 security find-identity -v -p codesigning 查看可用身份)
|
||||
export SIGNING_IDENTITY="Mac Developer: Kieran Parker (R36D2VJYBT)"
|
||||
|
||||
# 分发签名身份(用于 App Store 或 Ad Hoc 分发)
|
||||
export DISTRIBUTION_IDENTITY="Developer ID Application: Kieran Parker (3UR892FAP3)"
|
||||
|
||||
# 配置文件名称(需要在 Apple Developer Portal 中创建)
|
||||
export PROVISIONING_PROFILE_NAME="BearVPN Development Profile"
|
||||
export DISTRIBUTION_PROFILE_NAME="BearVPN Distribution Profile"
|
||||
|
||||
# 输出路径
|
||||
export OUTPUT_DIR="build/ios"
|
||||
export IPA_PATH="${OUTPUT_DIR}/BearVPN-${VERSION}.ipa"
|
||||
export DMG_PATH="${OUTPUT_DIR}/BearVPN-${VERSION}-iOS.dmg"
|
||||
|
||||
echo "🔧 iOS 签名配置已加载"
|
||||
echo "📧 Apple ID: $APPLE_ID"
|
||||
echo "🏢 Team ID: $TEAM_ID"
|
||||
echo "📱 Bundle ID: $BUNDLE_ID"
|
||||
echo "🔐 签名身份: $SIGNING_IDENTITY"
|
||||
echo ""
|
||||
echo "💡 使用方法:"
|
||||
echo "1. 修改此文件中的配置信息"
|
||||
echo "2. 运行: source ios_signing_config.sh"
|
||||
echo "3. 运行: ./build_ios.sh"
|
||||
echo ""
|
||||
echo "⚠️ 请确保:"
|
||||
echo "- 已在 Apple Developer Portal 中创建了 App ID"
|
||||
echo "- 已下载并安装了 Provisioning Profile"
|
||||
echo "- 已安装了开发者证书"
|
||||
@ -16,7 +16,6 @@ import '../../../localization/app_translations.dart';
|
||||
import '../../../localization/kr_language_utils.dart';
|
||||
import '../../../model/business/kr_group_outbound_list.dart';
|
||||
import '../../../model/business/kr_outbound_item.dart';
|
||||
import '../../../services/kr_announcement_service.dart';
|
||||
import '../../../utils/kr_event_bus.dart';
|
||||
import '../../../utils/kr_update_util.dart';
|
||||
import '../../../widgets/dialogs/kr_dialog.dart';
|
||||
@ -595,7 +594,6 @@ class KRHomeController extends GetxController with WidgetsBindingObserver {
|
||||
kr_currentViewStatus.value = KRHomeViewsStatus.kr_loggedIn;
|
||||
KRLogUtil.kr_i('登录状态变化:设置为已登录', tag: 'HomeController');
|
||||
|
||||
KRAnnouncementService().kr_checkAnnouncement();
|
||||
|
||||
// 订阅服务已在 splash 页面初始化,此处无需重复初始化
|
||||
KRLogUtil.kr_i('订阅服务已在启动页初始化,跳过重复初始化', tag: 'HomeController');
|
||||
|
||||
@ -1,153 +0,0 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:flutter_html/flutter_html.dart';
|
||||
import 'package:flutter_markdown/flutter_markdown.dart';
|
||||
import 'package:flutter_screenutil/flutter_screenutil.dart';
|
||||
|
||||
import '../model/response/kr_message_list.dart';
|
||||
import 'api_service/kr_api.user.dart';
|
||||
import '../utils/kr_common_util.dart';
|
||||
import '../widgets/dialogs/kr_dialog.dart';
|
||||
import '../localization/app_translations.dart';
|
||||
|
||||
class KRAnnouncementService {
|
||||
static final KRAnnouncementService _instance = KRAnnouncementService._internal();
|
||||
final KRUserApi _kr_userApi = KRUserApi();
|
||||
bool _kr_hasShownAnnouncement = false;
|
||||
|
||||
factory KRAnnouncementService() {
|
||||
return _instance;
|
||||
}
|
||||
|
||||
KRAnnouncementService._internal();
|
||||
|
||||
// 重置公告显示状态(用于退出登录时)
|
||||
void kr_reset() {
|
||||
_kr_hasShownAnnouncement = false;
|
||||
}
|
||||
|
||||
// 检查是否需要显示公告弹窗
|
||||
Future<void> kr_checkAnnouncement() async {
|
||||
if (_kr_hasShownAnnouncement) {
|
||||
return;
|
||||
}
|
||||
|
||||
final either = await _kr_userApi.kr_getMessageList(1, 1, popup: true);
|
||||
either.fold(
|
||||
(error) {
|
||||
KRCommonUtil.kr_showToast(error.msg);
|
||||
},
|
||||
(list) {
|
||||
if (list.announcements.isNotEmpty) {
|
||||
// 按创建时间降序排序,获取最新的公告
|
||||
final sortedAnnouncements = list.announcements;
|
||||
|
||||
final latestAnnouncement = sortedAnnouncements.first;
|
||||
|
||||
// 如果需要弹窗显示
|
||||
if (latestAnnouncement.popup) {
|
||||
_kr_hasShownAnnouncement = true;
|
||||
KRDialog.show(
|
||||
title: latestAnnouncement.title,
|
||||
message: null,
|
||||
confirmText: AppTranslations.kr_dialog.kr_iKnow,
|
||||
onConfirm: () {
|
||||
// Navigator.of(Get.context!).pop();
|
||||
},
|
||||
customMessageWidget: _kr_buildMessageContent(latestAnnouncement.content, Get.context!),
|
||||
);
|
||||
}
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
// 构建消息内容
|
||||
Widget _kr_buildMessageContent(String content, BuildContext context) {
|
||||
// 判断内容类型
|
||||
final bool kr_isHtml = content.contains('<') && content.contains('>');
|
||||
final bool kr_isMarkdown = content.contains('**') ||
|
||||
content.contains('*') ||
|
||||
content.contains('#') ||
|
||||
content.contains('- ') ||
|
||||
content.contains('[');
|
||||
|
||||
final textStyle = TextStyle(
|
||||
fontSize: 14.sp,
|
||||
color: Theme.of(context).textTheme.bodySmall?.color,
|
||||
fontFamily: 'AlibabaPuHuiTi-Regular',
|
||||
height: 1.4,
|
||||
);
|
||||
|
||||
if (kr_isHtml) {
|
||||
// 使用 flutter_html 处理 HTML 内容
|
||||
return Html(
|
||||
data: content,
|
||||
style: {
|
||||
'body': Style(
|
||||
margin: Margins.all(0),
|
||||
padding: HtmlPaddings.all(0),
|
||||
fontSize: FontSize(14.sp),
|
||||
color: Theme.of(context).textTheme.bodySmall?.color,
|
||||
fontFamily: 'AlibabaPuHuiTi-Regular',
|
||||
lineHeight: LineHeight(1.4),
|
||||
),
|
||||
'p': Style(
|
||||
margin: Margins.only(bottom: 8.h),
|
||||
),
|
||||
'b': Style(
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
'i': Style(
|
||||
fontStyle: FontStyle.italic,
|
||||
),
|
||||
'a': Style(
|
||||
color: Colors.blue,
|
||||
textDecoration: TextDecoration.underline,
|
||||
),
|
||||
},
|
||||
shrinkWrap: true,
|
||||
);
|
||||
} else if (kr_isMarkdown) {
|
||||
// 使用 flutter_markdown 处理 Markdown 内容
|
||||
return MarkdownBody(
|
||||
data: content,
|
||||
styleSheet: MarkdownStyleSheet(
|
||||
p: TextStyle(
|
||||
fontSize: 14.sp,
|
||||
color: Theme.of(context).textTheme.bodySmall?.color,
|
||||
fontFamily: 'AlibabaPuHuiTi-Regular',
|
||||
height: 1.4,
|
||||
),
|
||||
strong: TextStyle(
|
||||
fontSize: 14.sp,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Theme.of(context).textTheme.bodySmall?.color,
|
||||
fontFamily: 'AlibabaPuHuiTi-Regular',
|
||||
height: 1.4,
|
||||
),
|
||||
em: TextStyle(
|
||||
fontSize: 14.sp,
|
||||
fontStyle: FontStyle.italic,
|
||||
color: Theme.of(context).textTheme.bodySmall?.color,
|
||||
fontFamily: 'AlibabaPuHuiTi-Regular',
|
||||
height: 1.4,
|
||||
),
|
||||
a: TextStyle(
|
||||
fontSize: 14.sp,
|
||||
color: Colors.blue,
|
||||
decoration: TextDecoration.underline,
|
||||
fontFamily: 'AlibabaPuHuiTi-Regular',
|
||||
height: 1.4,
|
||||
),
|
||||
),
|
||||
);
|
||||
} else {
|
||||
// 普通文本
|
||||
return Text(
|
||||
content,
|
||||
style: textStyle,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,5 +0,0 @@
|
||||
create-dmg "HiFastVPN.app" \
|
||||
--overwrite \
|
||||
--dmg-title="HiFastVPN" \
|
||||
--app-drop-link \
|
||||
--dmg "HiFastVPN.dmg"
|
||||
@ -1,25 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
# macOS 签名配置脚本
|
||||
# 请根据您的开发者账户信息修改以下配置
|
||||
|
||||
# Apple Developer 账户信息
|
||||
export APPLE_ID="kieran@newlifeephrata.us"
|
||||
export APPLE_PASSWORD="gtvp-izmw-cubf-yxfe"
|
||||
export TEAM_ID="3UR892FAP3"
|
||||
|
||||
# 代码签名身份(运行 security find-identity -v -p codesigning 查看可用身份)
|
||||
export SIGNING_IDENTITY="Developer ID Application: Civil Rights Corps (3UR892FAP3)"
|
||||
|
||||
# 安装包签名身份
|
||||
export INSTALLER_IDENTITY="Developer ID Installer: Civil Rights Corps (3UR892FAP3)"
|
||||
|
||||
echo "🔧 macOS 签名配置已加载"
|
||||
echo "📧 Apple ID: $APPLE_ID"
|
||||
echo "🏢 Team ID: $TEAM_ID"
|
||||
echo "🔐 签名身份: $SIGNING_IDENTITY"
|
||||
echo ""
|
||||
echo "💡 使用方法:"
|
||||
echo "1. 修改此文件中的配置信息"
|
||||
echo "2. 运行: source macos_signing_config.sh"
|
||||
echo "3. 运行: ./build_macos_dmg.sh"
|
||||
@ -1,169 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
# 手动公证脚本
|
||||
# 作者: AI Assistant
|
||||
|
||||
set -e
|
||||
|
||||
# 配置变量
|
||||
APP_NAME="BearVPN"
|
||||
APP_VERSION="1.0.0"
|
||||
APP_PATH="build/macos/Build/Products/Release/${APP_NAME}.app"
|
||||
DMG_PATH="build/macos/Build/Products/Release/${APP_NAME}-${APP_VERSION}-macOS-Signed.dmg"
|
||||
|
||||
# 颜色输出
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
BLUE='\033[0;34m'
|
||||
NC='\033[0m' # No Color
|
||||
|
||||
# 日志函数
|
||||
log_info() {
|
||||
echo -e "${BLUE}[INFO]${NC} $1"
|
||||
}
|
||||
|
||||
log_success() {
|
||||
echo -e "${GREEN}[SUCCESS]${NC} $1"
|
||||
}
|
||||
|
||||
log_warning() {
|
||||
echo -e "${YELLOW}[WARNING]${NC} $1"
|
||||
}
|
||||
|
||||
log_error() {
|
||||
echo -e "${RED}[ERROR]${NC} $1"
|
||||
}
|
||||
|
||||
# 检查文件
|
||||
check_files() {
|
||||
if [ ! -d "$APP_PATH" ]; then
|
||||
log_error "应用文件不存在: $APP_PATH"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ ! -f "$DMG_PATH" ]; then
|
||||
log_error "DMG 文件不存在: $DMG_PATH"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
log_success "找到应用和 DMG 文件"
|
||||
}
|
||||
|
||||
# 创建 ZIP 文件
|
||||
create_zip() {
|
||||
log_info "创建 ZIP 文件用于公证..."
|
||||
ZIP_PATH="build/macos/Build/Products/Release/${APP_NAME}-${APP_VERSION}.zip"
|
||||
ditto -c -k --keepParent "$APP_PATH" "$ZIP_PATH"
|
||||
log_success "ZIP 文件创建完成: $ZIP_PATH"
|
||||
}
|
||||
|
||||
# 手动公证应用
|
||||
notarize_app_manual() {
|
||||
log_info "开始手动公证应用..."
|
||||
|
||||
# 创建 ZIP 文件
|
||||
create_zip
|
||||
ZIP_PATH="build/macos/Build/Products/Release/${APP_NAME}-${APP_VERSION}.zip"
|
||||
|
||||
log_warning "请按照以下步骤进行手动公证:"
|
||||
echo ""
|
||||
log_info "1. 打开浏览器,访问:https://developer.apple.com/account/resources/certificates/list"
|
||||
log_info "2. 登录您的 Apple Developer 账户"
|
||||
log_info "3. 点击左侧菜单的 'Services' > 'Notarization'"
|
||||
log_info "4. 点击 'Upload' 按钮"
|
||||
log_info "5. 选择文件:$ZIP_PATH"
|
||||
log_info "6. 等待公证完成(通常需要几分钟)"
|
||||
echo ""
|
||||
|
||||
read -p "按回车键继续,当公证完成后..."
|
||||
|
||||
# 检查公证状态
|
||||
log_info "检查公证状态..."
|
||||
xcrun stapler validate "$APP_PATH" 2>/dev/null || log_warning "应用尚未公证或公证失败"
|
||||
}
|
||||
|
||||
# 手动公证 DMG
|
||||
notarize_dmg_manual() {
|
||||
log_info "开始手动公证 DMG..."
|
||||
|
||||
log_warning "请按照以下步骤进行手动公证:"
|
||||
echo ""
|
||||
log_info "1. 打开浏览器,访问:https://developer.apple.com/account/resources/certificates/list"
|
||||
log_info "2. 登录您的 Apple Developer 账户"
|
||||
log_info "3. 点击左侧菜单的 'Services' > 'Notarization'"
|
||||
log_info "4. 点击 'Upload' 按钮"
|
||||
log_info "5. 选择文件:$DMG_PATH"
|
||||
log_info "6. 等待公证完成(通常需要几分钟)"
|
||||
echo ""
|
||||
|
||||
read -p "按回车键继续,当公证完成后..."
|
||||
|
||||
# 检查公证状态
|
||||
log_info "检查公证状态..."
|
||||
xcrun stapler validate "$DMG_PATH" 2>/dev/null || log_warning "DMG 尚未公证或公证失败"
|
||||
}
|
||||
|
||||
# 装订公证票据
|
||||
staple_tickets() {
|
||||
log_info "装订公证票据..."
|
||||
|
||||
# 装订应用
|
||||
log_info "装订应用公证票据..."
|
||||
xcrun stapler staple "$APP_PATH" 2>/dev/null || log_warning "应用公证票据装订失败"
|
||||
|
||||
# 装订 DMG
|
||||
log_info "装订 DMG 公证票据..."
|
||||
xcrun stapler staple "$DMG_PATH" 2>/dev/null || log_warning "DMG 公证票据装订失败"
|
||||
}
|
||||
|
||||
# 验证最终结果
|
||||
verify_result() {
|
||||
log_info "验证最终结果..."
|
||||
|
||||
# 检查应用签名
|
||||
log_info "应用签名状态:"
|
||||
codesign -dv "$APP_PATH" 2>&1 | grep -E "(Authority|TeamIdentifier|BundleId)" || true
|
||||
|
||||
# 检查 DMG 签名
|
||||
log_info "DMG 签名状态:"
|
||||
codesign -dv "$DMG_PATH" 2>&1 | grep -E "(Authority|TeamIdentifier)" || true
|
||||
|
||||
# 检查公证状态
|
||||
log_info "应用公证状态:"
|
||||
xcrun stapler validate "$APP_PATH" 2>/dev/null && log_success "应用已公证" || log_warning "应用未公证"
|
||||
|
||||
log_info "DMG 公证状态:"
|
||||
xcrun stapler validate "$DMG_PATH" 2>/dev/null && log_success "DMG 已公证" || log_warning "DMG 未公证"
|
||||
}
|
||||
|
||||
# 显示结果
|
||||
show_result() {
|
||||
log_success "=========================================="
|
||||
log_success "手动公证完成!"
|
||||
log_success "=========================================="
|
||||
log_info "应用: $APP_PATH"
|
||||
log_info "DMG: $DMG_PATH"
|
||||
log_success "=========================================="
|
||||
log_info "现在应用已通过 Apple 公证"
|
||||
log_info "可以在任何 Mac 上安全运行"
|
||||
log_success "=========================================="
|
||||
}
|
||||
|
||||
# 主函数
|
||||
main() {
|
||||
log_info "开始 BearVPN macOS 手动公证流程..."
|
||||
log_info "=========================================="
|
||||
|
||||
check_files
|
||||
notarize_app_manual
|
||||
notarize_dmg_manual
|
||||
staple_tickets
|
||||
verify_result
|
||||
show_result
|
||||
|
||||
log_success "手动公证完成!"
|
||||
}
|
||||
|
||||
# 运行主函数
|
||||
main "$@"
|
||||
@ -1,255 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Nextcloud 文件管理脚本
|
||||
# 用于管理员查看和管理用户文件
|
||||
|
||||
set -e
|
||||
|
||||
# 配置变量
|
||||
NEXTCLOUD_PATH="/var/www/nextcloud"
|
||||
DATA_PATH="/var/www/nextcloud/data"
|
||||
WEB_USER="www-data"
|
||||
|
||||
# 颜色输出
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
BLUE='\033[0;34m'
|
||||
NC='\033[0m'
|
||||
|
||||
# 日志函数
|
||||
log_info() {
|
||||
echo -e "${BLUE}[INFO]${NC} $1"
|
||||
}
|
||||
|
||||
log_success() {
|
||||
echo -e "${GREEN}[SUCCESS]${NC} $1"
|
||||
}
|
||||
|
||||
log_warning() {
|
||||
echo -e "${YELLOW}[WARNING]${NC} $1"
|
||||
}
|
||||
|
||||
log_error() {
|
||||
echo -e "${RED}[ERROR]${NC} $1"
|
||||
}
|
||||
|
||||
# 检查 Nextcloud 安装
|
||||
check_nextcloud() {
|
||||
if [ ! -d "$NEXTCLOUD_PATH" ]; then
|
||||
log_error "Nextcloud 未安装在 $NEXTCLOUD_PATH"
|
||||
log_info "请修改脚本中的 NEXTCLOUD_PATH 变量"
|
||||
exit 1
|
||||
fi
|
||||
log_success "找到 Nextcloud 安装"
|
||||
}
|
||||
|
||||
# 显示所有用户
|
||||
list_users() {
|
||||
log_info "获取所有用户列表..."
|
||||
echo ""
|
||||
echo "=== 用户列表 ==="
|
||||
sudo -u $WEB_USER php $NEXTCLOUD_PATH/occ user:list
|
||||
echo ""
|
||||
}
|
||||
|
||||
# 显示用户详细信息
|
||||
show_user_info() {
|
||||
local username=$1
|
||||
if [ -z "$username" ]; then
|
||||
log_error "请提供用户名"
|
||||
return 1
|
||||
fi
|
||||
|
||||
log_info "获取用户 $username 的详细信息..."
|
||||
echo ""
|
||||
echo "=== 用户 $username 信息 ==="
|
||||
sudo -u $WEB_USER php $NEXTCLOUD_PATH/occ user:info "$username"
|
||||
echo ""
|
||||
}
|
||||
|
||||
# 显示用户文件统计
|
||||
show_user_files() {
|
||||
local username=$1
|
||||
if [ -z "$username" ]; then
|
||||
log_error "请提供用户名"
|
||||
return 1
|
||||
fi
|
||||
|
||||
local user_path="$DATA_PATH/$username/files"
|
||||
|
||||
if [ ! -d "$user_path" ]; then
|
||||
log_error "用户 $username 的文件目录不存在"
|
||||
return 1
|
||||
fi
|
||||
|
||||
log_info "分析用户 $username 的文件..."
|
||||
echo ""
|
||||
echo "=== 用户 $username 文件统计 ==="
|
||||
echo "文件总数: $(find "$user_path" -type f | wc -l)"
|
||||
echo "目录总数: $(find "$user_path" -type d | wc -l)"
|
||||
echo "总大小: $(du -sh "$user_path" | cut -f1)"
|
||||
echo ""
|
||||
|
||||
echo "=== 按文件类型统计 ==="
|
||||
find "$user_path" -type f -name ".*" -prune -o -type f -print | \
|
||||
sed 's/.*\.//' | sort | uniq -c | sort -nr | head -10
|
||||
echo ""
|
||||
|
||||
echo "=== 最大的 10 个文件 ==="
|
||||
find "$user_path" -type f -exec ls -lh {} + | \
|
||||
sort -k5 -hr | head -10 | awk '{print $5, $9}'
|
||||
echo ""
|
||||
}
|
||||
|
||||
# 搜索文件
|
||||
search_files() {
|
||||
local username=$1
|
||||
local pattern=$2
|
||||
|
||||
if [ -z "$username" ] || [ -z "$pattern" ]; then
|
||||
log_error "请提供用户名和搜索模式"
|
||||
return 1
|
||||
fi
|
||||
|
||||
local user_path="$DATA_PATH/$username/files"
|
||||
|
||||
if [ ! -d "$user_path" ]; then
|
||||
log_error "用户 $username 的文件目录不存在"
|
||||
return 1
|
||||
fi
|
||||
|
||||
log_info "在用户 $username 的文件中搜索: $pattern"
|
||||
echo ""
|
||||
echo "=== 搜索结果 ==="
|
||||
find "$user_path" -iname "*$pattern*" -type f | head -20
|
||||
echo ""
|
||||
}
|
||||
|
||||
# 显示存储使用情况
|
||||
show_storage_usage() {
|
||||
log_info "分析存储使用情况..."
|
||||
echo ""
|
||||
echo "=== 存储使用统计 ==="
|
||||
|
||||
# 总存储使用
|
||||
total_size=$(du -sh "$DATA_PATH" | cut -f1)
|
||||
echo "总存储使用: $total_size"
|
||||
echo ""
|
||||
|
||||
# 按用户统计
|
||||
echo "=== 各用户存储使用 ==="
|
||||
for user_dir in "$DATA_PATH"/*; do
|
||||
if [ -d "$user_dir" ] && [ "$(basename "$user_dir")" != "appdata_oc" ]; then
|
||||
username=$(basename "$user_dir")
|
||||
user_size=$(du -sh "$user_dir" | cut -f1)
|
||||
file_count=$(find "$user_dir/files" -type f 2>/dev/null | wc -l)
|
||||
echo "$username: $user_size ($file_count 个文件)"
|
||||
fi
|
||||
done
|
||||
echo ""
|
||||
}
|
||||
|
||||
# 清理用户文件
|
||||
cleanup_user_files() {
|
||||
local username=$1
|
||||
local pattern=$2
|
||||
|
||||
if [ -z "$username" ]; then
|
||||
log_error "请提供用户名"
|
||||
return 1
|
||||
fi
|
||||
|
||||
local user_path="$DATA_PATH/$username/files"
|
||||
|
||||
if [ ! -d "$user_path" ]; then
|
||||
log_error "用户 $username 的文件目录不存在"
|
||||
return 1
|
||||
fi
|
||||
|
||||
log_warning "即将清理用户 $username 的文件"
|
||||
if [ -n "$pattern" ]; then
|
||||
log_warning "清理模式: $pattern"
|
||||
fi
|
||||
|
||||
read -p "确认继续?(y/N): " confirm
|
||||
if [[ $confirm != [yY] ]]; then
|
||||
log_info "操作已取消"
|
||||
return 0
|
||||
fi
|
||||
|
||||
if [ -n "$pattern" ]; then
|
||||
find "$user_path" -iname "*$pattern*" -type f -delete
|
||||
log_success "已清理匹配 $pattern 的文件"
|
||||
else
|
||||
log_error "请提供清理模式"
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
# 显示帮助
|
||||
show_help() {
|
||||
echo "Nextcloud 文件管理脚本"
|
||||
echo ""
|
||||
echo "用法: $0 [选项] [参数]"
|
||||
echo ""
|
||||
echo "选项:"
|
||||
echo " list-users 显示所有用户"
|
||||
echo " user-info <用户名> 显示用户详细信息"
|
||||
echo " user-files <用户名> 显示用户文件统计"
|
||||
echo " search <用户名> <模式> 搜索用户文件"
|
||||
echo " storage 显示存储使用情况"
|
||||
echo " cleanup <用户名> <模式> 清理用户文件(危险操作)"
|
||||
echo " help 显示此帮助信息"
|
||||
echo ""
|
||||
echo "示例:"
|
||||
echo " $0 list-users"
|
||||
echo " $0 user-info john"
|
||||
echo " $0 user-files john"
|
||||
echo " $0 search john *.pdf"
|
||||
echo " $0 storage"
|
||||
echo " $0 cleanup john *.tmp"
|
||||
}
|
||||
|
||||
# 主函数
|
||||
main() {
|
||||
case "$1" in
|
||||
"list-users")
|
||||
check_nextcloud
|
||||
list_users
|
||||
;;
|
||||
"user-info")
|
||||
check_nextcloud
|
||||
show_user_info "$2"
|
||||
;;
|
||||
"user-files")
|
||||
check_nextcloud
|
||||
show_user_files "$2"
|
||||
;;
|
||||
"search")
|
||||
check_nextcloud
|
||||
search_files "$2" "$3"
|
||||
;;
|
||||
"storage")
|
||||
check_nextcloud
|
||||
show_storage_usage
|
||||
;;
|
||||
"cleanup")
|
||||
check_nextcloud
|
||||
cleanup_user_files "$2" "$3"
|
||||
;;
|
||||
"help"|"--help"|"-h")
|
||||
show_help
|
||||
;;
|
||||
*)
|
||||
log_error "未知选项: $1"
|
||||
show_help
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
# 运行主函数
|
||||
main "$@"
|
||||
|
||||
|
||||
@ -1,102 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
# 异步公证脚本 - 不等待结果
|
||||
# 作者: AI Assistant
|
||||
|
||||
set -e
|
||||
|
||||
# 配置变量
|
||||
APPLE_ID="kieran@newlifeephrata.us"
|
||||
PASSWORD="gtvp-izmw-cubf-yxfe"
|
||||
TEAM_ID="3UR892FAP3"
|
||||
ZIP_FILE="build/macos/Build/Products/Release/BearVPN-1.0.0.zip"
|
||||
|
||||
# 颜色输出
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
BLUE='\033[0;34m'
|
||||
NC='\033[0m'
|
||||
|
||||
log_info() {
|
||||
echo -e "${BLUE}[INFO]${NC} $1"
|
||||
}
|
||||
|
||||
log_success() {
|
||||
echo -e "${GREEN}[SUCCESS]${NC} $1"
|
||||
}
|
||||
|
||||
log_warning() {
|
||||
echo -e "${YELLOW}[WARNING]${NC} $1"
|
||||
}
|
||||
|
||||
log_error() {
|
||||
echo -e "${RED}[ERROR]${NC} $1"
|
||||
}
|
||||
|
||||
# 检查文件
|
||||
check_file() {
|
||||
if [ ! -f "$ZIP_FILE" ]; then
|
||||
log_error "ZIP 文件不存在: $ZIP_FILE"
|
||||
exit 1
|
||||
fi
|
||||
log_success "找到文件: $ZIP_FILE"
|
||||
}
|
||||
|
||||
# 异步提交公证
|
||||
submit_async() {
|
||||
log_info "开始异步提交公证..."
|
||||
|
||||
# 提交但不等待结果
|
||||
SUBMISSION_ID=$(xcrun notarytool submit "$ZIP_FILE" \
|
||||
--apple-id "$APPLE_ID" \
|
||||
--password "$PASSWORD" \
|
||||
--team-id "$TEAM_ID" \
|
||||
--output-format json | jq -r '.id')
|
||||
|
||||
if [ -z "$SUBMISSION_ID" ] || [ "$SUBMISSION_ID" = "null" ]; then
|
||||
log_error "提交失败,无法获取提交 ID"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
log_success "提交成功!"
|
||||
log_info "提交 ID: $SUBMISSION_ID"
|
||||
|
||||
# 保存提交 ID 到文件
|
||||
echo "$SUBMISSION_ID" > .notarization_id
|
||||
echo "$(date)" > .notarization_time
|
||||
|
||||
log_info "提交 ID 已保存到 .notarization_id"
|
||||
log_warning "请稍后使用 ./check_notarization_status.sh 检查状态"
|
||||
}
|
||||
|
||||
# 显示后续操作
|
||||
show_next_steps() {
|
||||
log_success "=========================================="
|
||||
log_success "异步提交完成!"
|
||||
log_success "=========================================="
|
||||
log_info "提交 ID: $SUBMISSION_ID"
|
||||
log_info "提交时间: $(date)"
|
||||
log_success "=========================================="
|
||||
log_info "后续操作:"
|
||||
log_info "1. 检查状态: ./check_notarization_status.sh info $SUBMISSION_ID"
|
||||
log_info "2. 查看历史: ./check_notarization_status.sh history"
|
||||
log_info "3. 实时监控: ./check_notarization_status.sh monitor"
|
||||
log_info "4. 完成后装订: xcrun stapler staple BearVPN-1.0.0-macOS-Signed.dmg"
|
||||
log_success "=========================================="
|
||||
}
|
||||
|
||||
# 主函数
|
||||
main() {
|
||||
log_info "开始异步公证提交..."
|
||||
log_info "=========================================="
|
||||
|
||||
check_file
|
||||
submit_async
|
||||
show_next_steps
|
||||
|
||||
log_success "提交完成,您可以继续其他工作!"
|
||||
}
|
||||
|
||||
# 运行主函数
|
||||
main "$@"
|
||||
175
notarize_only.sh
175
notarize_only.sh
@ -1,175 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
# BearVPN macOS 公证脚本
|
||||
# 作者: AI Assistant
|
||||
|
||||
set -e
|
||||
|
||||
# 配置变量
|
||||
APP_NAME="BearVPN"
|
||||
APP_VERSION="1.0.0"
|
||||
APP_PATH="build/macos/Build/Products/Release/${APP_NAME}.app"
|
||||
DMG_PATH="build/macos/Build/Products/Release/${APP_NAME}-${APP_VERSION}-macOS-Signed.dmg"
|
||||
|
||||
# 颜色输出
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
BLUE='\033[0;34m'
|
||||
NC='\033[0m' # No Color
|
||||
|
||||
# 日志函数
|
||||
log_info() {
|
||||
echo -e "${BLUE}[INFO]${NC} $1"
|
||||
}
|
||||
|
||||
log_success() {
|
||||
echo -e "${GREEN}[SUCCESS]${NC} $1"
|
||||
}
|
||||
|
||||
log_warning() {
|
||||
echo -e "${YELLOW}[WARNING]${NC} $1"
|
||||
}
|
||||
|
||||
log_error() {
|
||||
echo -e "${RED}[ERROR]${NC} $1"
|
||||
}
|
||||
|
||||
# 检查文件是否存在
|
||||
check_files() {
|
||||
if [ ! -d "$APP_PATH" ]; then
|
||||
log_error "应用文件不存在: $APP_PATH"
|
||||
log_info "请先运行 ./sign_and_package.sh 构建应用"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ ! -f "$DMG_PATH" ]; then
|
||||
log_error "DMG 文件不存在: $DMG_PATH"
|
||||
log_info "请先运行 ./sign_and_package.sh 构建 DMG"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
log_success "找到应用和 DMG 文件"
|
||||
}
|
||||
|
||||
# 公证应用
|
||||
notarize_app() {
|
||||
log_info "开始公证应用..."
|
||||
|
||||
# 创建 ZIP 文件用于公证
|
||||
ZIP_PATH="build/macos/Build/Products/Release/${APP_NAME}-${APP_VERSION}.zip"
|
||||
log_info "创建 ZIP 文件: $ZIP_PATH"
|
||||
ditto -c -k --keepParent "$APP_PATH" "$ZIP_PATH"
|
||||
|
||||
log_warning "请确保您已生成应用专用密码:"
|
||||
log_warning "1. 访问 https://appleid.apple.com"
|
||||
log_warning "2. 登录您的 Apple ID"
|
||||
log_warning "3. 在'安全'部分生成应用专用密码"
|
||||
log_warning "4. 使用该密码进行公证"
|
||||
echo ""
|
||||
|
||||
# 上传进行公证
|
||||
log_info "上传应用进行公证..."
|
||||
|
||||
xcrun notarytool submit "$ZIP_PATH" \
|
||||
--apple-id "kieran@newlifeephrata.us" \
|
||||
--password "gtvp-izmw-cubf-yxfe" \
|
||||
--team-id "3UR892FAP3" \
|
||||
--wait
|
||||
|
||||
if [ $? -eq 0 ]; then
|
||||
log_success "应用公证成功"
|
||||
|
||||
# 装订公证票据
|
||||
log_info "装订公证票据到应用..."
|
||||
xcrun stapler staple "$APP_PATH"
|
||||
|
||||
if [ $? -eq 0 ]; then
|
||||
log_success "公证票据装订成功"
|
||||
else
|
||||
log_warning "公证票据装订失败,但应用已公证"
|
||||
fi
|
||||
else
|
||||
log_error "应用公证失败"
|
||||
log_info "请检查 Apple ID 和应用专用密码是否正确"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# 清理 ZIP 文件
|
||||
rm -f "$ZIP_PATH"
|
||||
}
|
||||
|
||||
# 公证 DMG
|
||||
notarize_dmg() {
|
||||
log_info "开始公证 DMG..."
|
||||
|
||||
# 上传 DMG 进行公证
|
||||
log_info "上传 DMG 进行公证..."
|
||||
|
||||
xcrun notarytool submit "$DMG_PATH" \
|
||||
--apple-id "kieran@newlifeephrata.us" \
|
||||
--password "gtvp-izmw-cubf-yxfe" \
|
||||
--team-id "3UR892FAP3" \
|
||||
--wait
|
||||
|
||||
if [ $? -eq 0 ]; then
|
||||
log_success "DMG 公证成功"
|
||||
|
||||
# 装订公证票据
|
||||
log_info "装订公证票据到 DMG..."
|
||||
xcrun stapler staple "$DMG_PATH"
|
||||
|
||||
if [ $? -eq 0 ]; then
|
||||
log_success "DMG 公证票据装订成功"
|
||||
else
|
||||
log_warning "DMG 公证票据装订失败,但 DMG 已公证"
|
||||
fi
|
||||
else
|
||||
log_error "DMG 公证失败"
|
||||
log_info "请检查 Apple ID 和应用专用密码是否正确"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
# 验证公证状态
|
||||
verify_notarization() {
|
||||
log_info "验证公证状态..."
|
||||
|
||||
# 验证应用公证
|
||||
log_info "应用公证状态:"
|
||||
xcrun stapler validate "$APP_PATH"
|
||||
|
||||
# 验证 DMG 公证
|
||||
log_info "DMG 公证状态:"
|
||||
xcrun stapler validate "$DMG_PATH"
|
||||
}
|
||||
|
||||
# 显示结果
|
||||
show_result() {
|
||||
log_success "=========================================="
|
||||
log_success "公证完成!"
|
||||
log_success "=========================================="
|
||||
log_info "应用: $APP_PATH"
|
||||
log_info "DMG: $DMG_PATH"
|
||||
log_success "=========================================="
|
||||
log_info "现在应用已通过 Apple 公证"
|
||||
log_info "可以在任何 Mac 上安全运行"
|
||||
log_success "=========================================="
|
||||
}
|
||||
|
||||
# 主函数
|
||||
main() {
|
||||
log_info "开始 BearVPN macOS 公证流程..."
|
||||
log_info "=========================================="
|
||||
|
||||
check_files
|
||||
notarize_app
|
||||
notarize_dmg
|
||||
verify_notarization
|
||||
show_result
|
||||
|
||||
log_success "公证完成!"
|
||||
}
|
||||
|
||||
# 运行主函数
|
||||
main "$@"
|
||||
@ -1,137 +0,0 @@
|
||||
# Flutter Windows 单文件 EXE 打包指南
|
||||
|
||||
## 🎯 目标
|
||||
将 Flutter Windows 应用打包成单个可执行文件,便于分发和部署。
|
||||
|
||||
## 📋 当前状态
|
||||
|
||||
### 现有构建输出
|
||||
```
|
||||
build/windows/x64/runner/Release/
|
||||
├── hostexecutor.exe # 主程序
|
||||
├── flutter_windows.dll # Flutter 引擎
|
||||
├── msvcp140.dll # Visual C++ 运行时
|
||||
├── vcruntime140.dll # Visual C++ 运行时
|
||||
├── vcruntime140_1.dll # Visual C++ 运行时
|
||||
└── data/ # 应用数据文件夹
|
||||
├── app.so # Dart 代码编译结果
|
||||
└── flutter_assets/ # 资源文件
|
||||
```
|
||||
|
||||
### 问题分析
|
||||
Flutter 默认构建会生成多个文件,因为:
|
||||
1. **Flutter 引擎** (`flutter_windows.dll`) - 必须包含
|
||||
2. **Visual C++ 运行时** - 系统依赖
|
||||
3. **应用数据** (`data` 文件夹) - 包含资源和 Dart 代码
|
||||
|
||||
## 🔧 解决方案
|
||||
|
||||
### 方案一:使用 Enigma Virtual Box(推荐)
|
||||
|
||||
#### 步骤 1:下载安装
|
||||
1. 下载 [Enigma Virtual Box](https://enigmaprotector.com/en/downloads.html)
|
||||
2. 安装并运行
|
||||
|
||||
#### 步骤 2:打包配置
|
||||
1. **主程序文件**: 选择 `build/windows/x64/runner/Release/hostexecutor.exe`
|
||||
2. **添加文件夹**: 选择整个 `Release` 文件夹
|
||||
3. **输出文件**: 设置输出路径和文件名
|
||||
4. **文件选项**: 勾选"压缩文件"
|
||||
|
||||
#### 步骤 3:生成单文件
|
||||
点击"Process"生成单个可执行文件。
|
||||
|
||||
### 方案二:使用 Inno Setup(安装程序)
|
||||
|
||||
#### 步骤 1:下载安装
|
||||
1. 下载 [Inno Setup](https://jrsoftware.org/isinfo.php)
|
||||
2. 安装并运行
|
||||
|
||||
#### 步骤 2:创建脚本
|
||||
```ini
|
||||
[Setup]
|
||||
AppName=HostExecutor
|
||||
AppVersion=1.0.0
|
||||
DefaultDirName={autopf}\HostExecutor
|
||||
OutputBaseFilename=HostExecutor_Setup
|
||||
|
||||
[Files]
|
||||
Source: "build\windows\x64\runner\Release\*"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs
|
||||
|
||||
[Icons]
|
||||
Name: "{group}\HostExecutor"; Filename: "{app}\hostexecutor.exe"
|
||||
```
|
||||
|
||||
#### 步骤 3:编译生成
|
||||
编译脚本生成安装程序。
|
||||
|
||||
### 方案三:使用 Windows 自带工具(高级)
|
||||
|
||||
#### 使用 `dotnet publish`(需要 .NET 包装)
|
||||
```bash
|
||||
# 需要创建 .NET 包装器项目
|
||||
dotnet publish -c Release -r win-x64 --self-contained true -p:PublishSingleFile=true
|
||||
```
|
||||
|
||||
## 🚀 自动化脚本
|
||||
|
||||
### Enigma Virtual Box 自动化脚本
|
||||
```powershell
|
||||
# package_single_exe.ps1
|
||||
$enigmaPath = "C:\Program Files\Enigma Virtual Box\enigmavb.exe"
|
||||
$inputFile = "build\windows\x64\runner\Release\hostexecutor.exe"
|
||||
$outputFile = "dist\HostExecutor_Single.exe"
|
||||
$folderPath = "build\windows\x64\runner\Release"
|
||||
|
||||
& $enigmaPath /sf $inputFile /lf $outputFile /folder $folderPath /compress
|
||||
```
|
||||
|
||||
## 📦 打包后文件结构
|
||||
|
||||
### 单文件输出
|
||||
```
|
||||
dist/
|
||||
└── HostExecutor_Single.exe # 单个可执行文件(50-100MB)
|
||||
```
|
||||
|
||||
### 文件大小分析
|
||||
- **原始文件**: 约 30-50MB
|
||||
- **压缩后**: 约 25-40MB(取决于压缩率)
|
||||
- **最终大小**: 50-100MB(包含所有依赖)
|
||||
|
||||
## ⚠️ 注意事项
|
||||
|
||||
### 运行时依赖
|
||||
1. **Windows 版本**: Windows 10 版本 1903+ 或 Windows 11
|
||||
2. **系统组件**: 确保目标系统有最新的 Windows 更新
|
||||
3. **防病毒软件**: 单文件可能被误报,需要添加信任
|
||||
|
||||
### 性能影响
|
||||
- **启动时间**: 单文件启动会稍慢(需要解压)
|
||||
- **内存使用**: 运行时内存占用相同
|
||||
- **文件大小**: 比多文件版本大约 10-20%
|
||||
|
||||
## 🎯 推荐方案
|
||||
|
||||
### 开发阶段
|
||||
使用多文件版本,便于调试和更新。
|
||||
|
||||
### 分发阶段
|
||||
使用 Enigma Virtual Box 创建单文件版本:
|
||||
1. **简单易用** - 图形化界面
|
||||
2. **压缩率高** - 有效减小文件大小
|
||||
3. **兼容性好** - 支持所有 Windows 版本
|
||||
4. **免费** - 个人和商业使用都免费
|
||||
|
||||
## 📋 下一步行动
|
||||
|
||||
1. **安装 Enigma Virtual Box**
|
||||
2. **测试单文件打包**
|
||||
3. **验证运行效果**
|
||||
4. **创建自动化打包流程**
|
||||
|
||||
## 🔗 相关资源
|
||||
|
||||
- [Enigma Virtual Box 官网](https://enigmaprotector.com/en/downloads.html)
|
||||
- [Inno Setup 官网](https://jrsoftware.org/isinfo.php)
|
||||
- [Flutter Windows 文档](https://docs.flutter.dev/desktop)
|
||||
@ -1,168 +0,0 @@
|
||||
#Requires -RunAsAdministrator
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Builds and packages a Flutter Windows application into a single executable.
|
||||
|
||||
.DESCRIPTION
|
||||
This script automates the entire process of creating a single-file executable for a Flutter Windows project.
|
||||
It performs the following steps:
|
||||
1. Checks for and installs Chocolatey if not present.
|
||||
2. Installs Enigma Virtual Box and 7-Zip using Chocolatey.
|
||||
3. Builds the Flutter application in release mode.
|
||||
4. Packages the build output into a single EXE using Enigma Virtual Box.
|
||||
5. If Enigma fails, it falls back to creating a 7-Zip self-extracting archive.
|
||||
6. Places the final packaged executable in the 'dist' directory.
|
||||
|
||||
.NOTES
|
||||
- This script must be run with Administrator privileges to install software.
|
||||
- An internet connection is required for the first run to download and install dependencies.
|
||||
#>
|
||||
|
||||
# --- Configuration ---
|
||||
$ErrorActionPreference = "Stop"
|
||||
$buildPath = ".\build\windows\x64\runner\Release"
|
||||
$outputPath = ".\dist"
|
||||
|
||||
# --- Helper Functions ---
|
||||
function Test-CommandExists {
|
||||
param($command)
|
||||
return (Get-Command $command -ErrorAction SilentlyContinue)
|
||||
}
|
||||
|
||||
function Install-Chocolatey {
|
||||
if (Test-CommandExists "choco") {
|
||||
Write-Host "✅ Chocolatey is already installed."
|
||||
return
|
||||
}
|
||||
|
||||
Write-Host "Chocolatey not found. Installing..."
|
||||
try {
|
||||
Set-ExecutionPolicy Bypass -Scope Process -Force
|
||||
[System.Net.ServicePointManager]::SecurityProtocol = [System.Net.ServicePointManager]::SecurityProtocol -bor 3072
|
||||
iex ((New-Object System.Net.WebClient).DownloadString('https://community.chocolatey.org/install.ps1'))
|
||||
Write-Host "✅ Chocolatey installed successfully."
|
||||
} catch {
|
||||
Write-Host "❌ Failed to install Chocolatey. Please install it manually and re-run the script."
|
||||
exit 1
|
||||
}
|
||||
}
|
||||
|
||||
function Install-Tools {
|
||||
Write-Host "Checking for required tools (Enigma Virtual Box, 7-Zip)..."
|
||||
$tools = @("enigma-virtual-box", "7zip")
|
||||
foreach ($tool in $tools) {
|
||||
if (choco list --local-only --exact $tool) {
|
||||
Write-Host "✅ $tool is already installed."
|
||||
} else {
|
||||
Write-Host "Installing $tool..."
|
||||
try {
|
||||
choco install $tool -y --force
|
||||
Write-Host "✅ $tool installed successfully."
|
||||
} catch {
|
||||
Write-Host "❌ Failed to install $tool."
|
||||
exit 1
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# --- Main Script ---
|
||||
|
||||
# 1. Setup Environment
|
||||
Write-Host "=== 1/4: Setting up build environment ==="
|
||||
Install-Chocolatey
|
||||
Install-Tools
|
||||
|
||||
# 2. Build Flutter App
|
||||
Write-Host "=== 2/4: Building Flutter Windows application (Release) ==="
|
||||
try {
|
||||
flutter build windows --release
|
||||
} catch {
|
||||
Write-Host "❌ Flutter build failed."
|
||||
exit 1
|
||||
}
|
||||
|
||||
# 3. Package Application
|
||||
Write-Host "=== 3/4: Packaging into a single executable ==="
|
||||
if (-not (Test-Path $buildPath)) {
|
||||
Write-Host "❌ Build directory not found: $buildPath"
|
||||
exit 1
|
||||
}
|
||||
|
||||
# Create output directory
|
||||
New-Item -ItemType Directory -Path $outputPath -Force | Out-Null
|
||||
|
||||
# Get main executable
|
||||
$exeFile = Get-ChildItem -Path $buildPath -Filter "*.exe" | Select-Object -First 1
|
||||
if (-not $exeFile) {
|
||||
Write-Host "❌ No executable found in build directory."
|
||||
exit 1
|
||||
}
|
||||
|
||||
$inputExe = $exeFile.FullName
|
||||
$outputExe = "$outputPath\$($exeFile.BaseName)_Single.exe"
|
||||
$enigmaCliPath = "C:\Program Files\Enigma Virtual Box\enigmavb.exe"
|
||||
$packageSuccess = $false
|
||||
|
||||
# Attempt to package with Enigma Virtual Box
|
||||
if (Test-Path $enigmaCliPath) {
|
||||
Write-Host "Attempting to package with Enigma Virtual Box..."
|
||||
try {
|
||||
& $enigmaCliPath /quiet /project "$outputPath\project.evb" /input "$inputExe" /output "$outputExe" /folder "$buildPath" /compress 3 /deleteext
|
||||
|
||||
if ($?) {
|
||||
Write-Host "✅ Enigma Virtual Box packaging successful."
|
||||
$packageSuccess = $true
|
||||
} else {
|
||||
Write-Host "⚠️ Enigma Virtual Box packaging failed. Exit code: $lastexitcode"
|
||||
}
|
||||
} catch {
|
||||
Write-Host "⚠️ An error occurred during Enigma Virtual Box packaging."
|
||||
}
|
||||
} else {
|
||||
Write-Host "⚠️ Enigma Virtual Box not found at $enigmaCliPath."
|
||||
}
|
||||
|
||||
# 4. Fallback to 7-Zip if Enigma failed
|
||||
if (-not $packageSuccess) {
|
||||
Write-Host "=== 4/4: Fallback: Packaging with 7-Zip SFX ==="
|
||||
$7zipCliPath = "C:\Program Files\7-Zip\7z.exe"
|
||||
$outputSfx = "$outputPath\$($exeFile.BaseName)_Package.exe"
|
||||
|
||||
if (-not (Test-Path $7zipCliPath)) {
|
||||
Write-Host "❌ 7-Zip not found. Cannot create self-extracting archive."
|
||||
exit 1
|
||||
}
|
||||
|
||||
$sfxConfig = @'
|
||||
;!@Install@!UTF-8!
|
||||
Title="Flutter Application"
|
||||
BeginPrompt="Do you want to extract and run the application?"
|
||||
RunProgram="%%T\%%S\$($exeFile.Name)"
|
||||
;!@InstallEnd@!
|
||||
'@
|
||||
$sfxConfigFile = "$outputPath\sfx_config.txt"
|
||||
Set-Content -Path $sfxConfigFile -Value $sfxConfig
|
||||
|
||||
try {
|
||||
& $7zipCliPath a -sfx7z.sfx "$outputSfx" "$buildPath\*" -r "-i!$sfxConfigFile"
|
||||
Write-Host "✅ 7-Zip self-extracting archive created successfully."
|
||||
$outputExe = $outputSfx
|
||||
$packageSuccess = $true
|
||||
} catch {
|
||||
Write-Host "❌ 7-Zip packaging failed."
|
||||
exit 1
|
||||
}
|
||||
}
|
||||
|
||||
# --- Final Summary ---
|
||||
if ($packageSuccess) {
|
||||
$finalSize = (Get-Item $outputExe).Length / 1MB
|
||||
Write-Host "================================================"
|
||||
Write-Host "✅ Packaging Complete!"
|
||||
Write-Host " Output file: $outputExe"
|
||||
Write-Host " Size: $([math]::Round($finalSize, 2)) MB"
|
||||
Write-Host "================================================"
|
||||
} else {
|
||||
Write-Host "❌ All packaging attempts failed."
|
||||
}
|
||||
@ -1,97 +0,0 @@
|
||||
# Flutter Windows 单文件打包脚本 - Enigma Virtual Box
|
||||
# 以管理员身份运行
|
||||
|
||||
Write-Host "=== Flutter Windows 单文件打包工具 ===" -ForegroundColor Green
|
||||
|
||||
# 配置参数
|
||||
$projectRoot = Split-Path -Parent $MyInvocation.MyCommand.Path
|
||||
$buildPath = "$projectRoot\build\windows\x64\runner\Release"
|
||||
$outputPath = "$projectRoot\dist"
|
||||
$enigmaPath = "C:\Program Files\Enigma Virtual Box\enigmavb.exe"
|
||||
|
||||
# 检查构建文件
|
||||
Write-Host "`n检查构建文件..." -ForegroundColor Yellow
|
||||
if (-not (Test-Path $buildPath)) {
|
||||
Write-Host "错误: 构建文件不存在,请先运行 flutter build windows" -ForegroundColor Red
|
||||
exit 1
|
||||
}
|
||||
|
||||
# 创建输出目录
|
||||
if (-not (Test-Path $outputPath)) {
|
||||
New-Item -ItemType Directory -Path $outputPath -Force | Out-Null
|
||||
}
|
||||
|
||||
# 检查 Enigma Virtual Box
|
||||
if (-not (Test-Path $enigmaPath)) {
|
||||
Write-Host "错误: Enigma Virtual Box 未安装" -ForegroundColor Red
|
||||
Write-Host "请下载安装: https://enigmaprotector.com/en/downloads.html" -ForegroundColor Yellow
|
||||
exit 1
|
||||
}
|
||||
|
||||
# 获取主程序名称
|
||||
$exeFile = Get-ChildItem -Path $buildPath -Filter "*.exe" | Select-Object -First 1
|
||||
if (-not $exeFile) {
|
||||
Write-Host "错误: 未找到可执行文件" -ForegroundColor Red
|
||||
exit 1
|
||||
}
|
||||
|
||||
$inputExe = $exeFile.FullName
|
||||
$outputExe = "$outputPath\$($exeFile.BaseName)_Single.exe"
|
||||
|
||||
Write-Host "主程序: $inputExe" -ForegroundColor Cyan
|
||||
Write-Host "输出文件: $outputExe" -ForegroundColor Cyan
|
||||
|
||||
# 创建打包配置文件
|
||||
$configContent = @"
|
||||
; Enigma Virtual Box 配置文件
|
||||
[Config]
|
||||
InputFile=$inputExe
|
||||
OutputFile=$outputExe
|
||||
Files=%DEFAULT FOLDER%
|
||||
VirtualizationMode=Never Write To Disk
|
||||
Compression=Yes
|
||||
ShareVirtualSystem=Yes
|
||||
|
||||
[Files]
|
||||
; 添加整个 Release 文件夹
|
||||
Folder=$buildPath\*
|
||||
"@
|
||||
|
||||
$configFile = "$outputPath\package_config.evb"
|
||||
Set-Content -Path $configFile -Value $configContent
|
||||
|
||||
# 执行打包
|
||||
Write-Host "`n开始打包..." -ForegroundColor Yellow
|
||||
Write-Host "这可能需要几分钟时间,请耐心等待..." -ForegroundColor Yellow
|
||||
|
||||
$process = Start-Process -FilePath $enigmaPath -ArgumentList "/sf", $inputExe, "/lf", $outputExe, "/folder", $buildPath, "/compress" -Wait -PassThru
|
||||
|
||||
if ($process.ExitCode -eq 0) {
|
||||
Write-Host "`n✅ 打包成功!" -ForegroundColor Green
|
||||
|
||||
# 显示结果信息
|
||||
$originalSize = (Get-Item $inputExe).Length / 1MB
|
||||
$packedSize = (Get-Item $outputExe).Length / 1MB
|
||||
$compressionRatio = [math]::Round((1 - $packedSize / $originalSize) * 100, 2)
|
||||
|
||||
Write-Host "`n打包结果:" -ForegroundColor Cyan
|
||||
Write-Host "原始大小: $([math]::Round($originalSize, 2)) MB" -ForegroundColor White
|
||||
Write-Host "打包大小: $([math]::Round($packedSize, 2)) MB" -ForegroundColor White
|
||||
Write-Host "压缩率: $compressionRatio%" -ForegroundColor White
|
||||
Write-Host "输出文件: $outputExe" -ForegroundColor White
|
||||
|
||||
Write-Host "`n📋 使用说明:" -ForegroundColor Yellow
|
||||
Write-Host "1. 将生成的单文件复制到目标计算机" -ForegroundColor White
|
||||
Write-Host "2. 直接运行即可,无需安装其他依赖" -ForegroundColor White
|
||||
Write-Host "3. 首次运行可能需要管理员权限" -ForegroundColor White
|
||||
|
||||
} else {
|
||||
Write-Host "`n❌ 打包失败!" -ForegroundColor Red
|
||||
Write-Host "错误代码: $($process.ExitCode)" -ForegroundColor Red
|
||||
}
|
||||
|
||||
# 清理临时文件
|
||||
Remove-Item $configFile -ErrorAction SilentlyContinue
|
||||
|
||||
Write-Host "`n按任意键退出..." -ForegroundColor Gray
|
||||
$null = $Host.UI.RawUI.ReadKey("NoEcho,IncludeKeyDown")
|
||||
@ -249,14 +249,6 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.4.3"
|
||||
crisp_sdk:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: crisp_sdk
|
||||
sha256: "46f3112b9212bf78f166fc4e3bf7121fad2c7cada204c0dfdedef4f3d99f3cf5"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.1.0"
|
||||
cross_file:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
||||
@ -1,166 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
# iOS 签名设置助手脚本
|
||||
|
||||
set -e
|
||||
|
||||
# 颜色输出
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
BLUE='\033[0;34m'
|
||||
NC='\033[0m' # No Color
|
||||
|
||||
# 日志函数
|
||||
log_info() {
|
||||
echo -e "${BLUE}[INFO]${NC} $1"
|
||||
}
|
||||
|
||||
log_success() {
|
||||
echo -e "${GREEN}[SUCCESS]${NC} $1"
|
||||
}
|
||||
|
||||
log_warning() {
|
||||
echo -e "${YELLOW}[WARNING]${NC} $1"
|
||||
}
|
||||
|
||||
log_error() {
|
||||
echo -e "${RED}[ERROR]${NC} $1"
|
||||
}
|
||||
|
||||
# 检查证书
|
||||
check_certificates() {
|
||||
log_info "检查已安装的证书..."
|
||||
|
||||
local identities=$(security find-identity -v -p codesigning 2>/dev/null)
|
||||
if [ -n "$identities" ] && echo "$identities" | grep -q "iPhone Developer\|Apple Development"; then
|
||||
log_success "找到可用的开发证书:"
|
||||
echo "$identities" | grep "iPhone Developer\|Apple Development"
|
||||
return 0
|
||||
else
|
||||
log_warning "未找到可用的开发证书"
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
# 安装证书
|
||||
install_certificate() {
|
||||
local cert_file=$1
|
||||
|
||||
if [ -z "$cert_file" ]; then
|
||||
log_error "请提供证书文件路径"
|
||||
return 1
|
||||
fi
|
||||
|
||||
if [ ! -f "$cert_file" ]; then
|
||||
log_error "证书文件不存在: $cert_file"
|
||||
return 1
|
||||
fi
|
||||
|
||||
log_info "安装证书: $cert_file"
|
||||
|
||||
# 安装证书到钥匙串
|
||||
security import "$cert_file" -k ~/Library/Keychains/login.keychain
|
||||
|
||||
if [ $? -eq 0 ]; then
|
||||
log_success "证书安装成功"
|
||||
else
|
||||
log_error "证书安装失败"
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
# 打开 Apple Developer Portal
|
||||
open_developer_portal() {
|
||||
log_info "打开 Apple Developer Portal..."
|
||||
|
||||
# 打开 App IDs 页面
|
||||
open "https://developer.apple.com/account/resources/identifiers/list"
|
||||
|
||||
# 打开 Profiles 页面
|
||||
open "https://developer.apple.com/account/resources/profiles/list"
|
||||
|
||||
log_info "请在浏览器中完成以下步骤:"
|
||||
echo "1. 创建 App ID (Bundle ID: com.bearvpn.app)"
|
||||
echo "2. 创建 Provisioning Profile"
|
||||
echo "3. 下载并安装 Provisioning Profile"
|
||||
}
|
||||
|
||||
# 验证完整设置
|
||||
verify_setup() {
|
||||
log_info "验证签名设置..."
|
||||
|
||||
# 检查证书
|
||||
if ! check_certificates; then
|
||||
log_error "证书检查失败"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# 检查 Provisioning Profile
|
||||
local profiles_dir="$HOME/Library/MobileDevice/Provisioning Profiles"
|
||||
if [ -d "$profiles_dir" ] && [ "$(ls -A "$profiles_dir" 2>/dev/null)" ]; then
|
||||
log_success "找到 Provisioning Profile"
|
||||
ls -la "$profiles_dir"
|
||||
else
|
||||
log_warning "未找到 Provisioning Profile"
|
||||
log_info "请确保已下载并安装了 Provisioning Profile"
|
||||
fi
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
# 显示使用说明
|
||||
show_instructions() {
|
||||
log_info "iOS 签名设置说明"
|
||||
echo "=========================================="
|
||||
echo "1. 证书安装:"
|
||||
echo " - 双击桌面上的 .cer 文件安装到钥匙串"
|
||||
echo " - 或运行: ./setup_ios_signing.sh install ~/Desktop/your_cert.cer"
|
||||
echo ""
|
||||
echo "2. 创建 App ID:"
|
||||
echo " - Bundle ID: com.bearvpn.app"
|
||||
echo " - 选择需要的功能(如 Network Extensions)"
|
||||
echo ""
|
||||
echo "3. 创建 Provisioning Profile:"
|
||||
echo " - 选择 iOS App Development"
|
||||
echo " - 选择刚创建的 App ID"
|
||||
echo " - 选择您的证书"
|
||||
echo " - 下载并双击安装 .mobileprovision 文件"
|
||||
echo ""
|
||||
echo "4. 验证设置:"
|
||||
echo " - 运行: ./setup_ios_signing.sh verify"
|
||||
echo ""
|
||||
echo "5. 构建签名应用:"
|
||||
echo " - 运行: source ios_signing_config.sh"
|
||||
echo " - 运行: ./build_ios_dmg.sh"
|
||||
echo "=========================================="
|
||||
}
|
||||
|
||||
# 主函数
|
||||
main() {
|
||||
case "${1:-}" in
|
||||
"install")
|
||||
if [ -z "$2" ]; then
|
||||
log_error "请提供证书文件路径"
|
||||
echo "用法: $0 install <certificate_file>"
|
||||
exit 1
|
||||
fi
|
||||
install_certificate "$2"
|
||||
;;
|
||||
"verify")
|
||||
verify_setup
|
||||
;;
|
||||
"open")
|
||||
open_developer_portal
|
||||
;;
|
||||
"check")
|
||||
check_certificates
|
||||
;;
|
||||
*)
|
||||
show_instructions
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
# 运行主函数
|
||||
main "$@"
|
||||
@ -1,214 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
# BearVPN macOS 签名和打包脚本
|
||||
# 作者: AI Assistant
|
||||
# 日期: $(date)
|
||||
|
||||
set -e
|
||||
|
||||
# 配置变量
|
||||
APP_NAME="BearVPN"
|
||||
APP_VERSION="1.0.0"
|
||||
DMG_NAME="${APP_NAME}-${APP_VERSION}-macOS-Signed"
|
||||
APP_PATH="build/macos/Build/Products/Release/${APP_NAME}.app"
|
||||
DMG_PATH="build/macos/Build/Products/Release/${DMG_NAME}.dmg"
|
||||
|
||||
# 签名配置
|
||||
DEVELOPER_ID="Developer ID Application: Civil Rights Corps (3UR892FAP3)"
|
||||
TEAM_ID="3UR892FAP3"
|
||||
|
||||
# 颜色输出
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
BLUE='\033[0;34m'
|
||||
NC='\033[0m' # No Color
|
||||
|
||||
# 日志函数
|
||||
log_info() {
|
||||
echo -e "${BLUE}[INFO]${NC} $1"
|
||||
}
|
||||
|
||||
log_success() {
|
||||
echo -e "${GREEN}[SUCCESS]${NC} $1"
|
||||
}
|
||||
|
||||
log_warning() {
|
||||
echo -e "${YELLOW}[WARNING]${NC} $1"
|
||||
}
|
||||
|
||||
log_error() {
|
||||
echo -e "${RED}[ERROR]${NC} $1"
|
||||
}
|
||||
|
||||
# 检查证书
|
||||
check_certificates() {
|
||||
log_info "检查开发者证书..."
|
||||
|
||||
# 检查证书是否存在
|
||||
if ! security find-certificate -a -c "Civil Rights Corps" > /dev/null 2>&1; then
|
||||
log_error "未找到 Developer ID Application 证书"
|
||||
log_info "请确保已安装有效的开发者证书"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
log_success "找到 Developer ID Application 证书"
|
||||
}
|
||||
|
||||
# 清理旧文件
|
||||
cleanup() {
|
||||
log_info "清理旧的构建文件..."
|
||||
rm -rf build/macos/Build/Products/Release/*
|
||||
log_success "清理完成"
|
||||
}
|
||||
|
||||
# 构建应用
|
||||
build_app() {
|
||||
log_info "开始构建 macOS 应用..."
|
||||
|
||||
flutter build macos --release
|
||||
|
||||
if [ ! -d "$APP_PATH" ]; then
|
||||
log_error "应用构建失败"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
log_success "应用构建完成"
|
||||
}
|
||||
|
||||
# 签名应用
|
||||
sign_app() {
|
||||
log_info "开始签名应用..."
|
||||
|
||||
# 签名应用
|
||||
codesign --force --deep --sign "$DEVELOPER_ID" "$APP_PATH"
|
||||
|
||||
if [ $? -eq 0 ]; then
|
||||
log_success "应用签名成功"
|
||||
else
|
||||
log_error "应用签名失败"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# 验证签名
|
||||
log_info "验证应用签名..."
|
||||
codesign --verify --verbose "$APP_PATH"
|
||||
|
||||
if [ $? -eq 0 ]; then
|
||||
log_success "应用签名验证通过"
|
||||
else
|
||||
log_error "应用签名验证失败"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# 显示签名信息
|
||||
log_info "签名信息:"
|
||||
codesign -dv --verbose=4 "$APP_PATH"
|
||||
}
|
||||
|
||||
# 创建 DMG
|
||||
create_dmg() {
|
||||
log_info "开始创建签名的 DMG 文件..."
|
||||
|
||||
# 使用 create-dmg 创建 DMG
|
||||
create-dmg \
|
||||
--volname "$APP_NAME" \
|
||||
--volicon "macos/Runner/Assets.xcassets/AppIcon.appiconset/icon-256@2x.png" \
|
||||
--window-pos 200 120 \
|
||||
--window-size 600 400 \
|
||||
--icon-size 100 \
|
||||
--icon "$APP_NAME.app" 175 190 \
|
||||
--hide-extension "$APP_NAME.app" \
|
||||
--app-drop-link 425 190 \
|
||||
--no-internet-enable \
|
||||
"$DMG_PATH" \
|
||||
"$APP_PATH"
|
||||
|
||||
if [ $? -eq 0 ]; then
|
||||
log_success "DMG 文件创建成功: $DMG_PATH"
|
||||
else
|
||||
log_error "DMG 文件创建失败"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
# 签名 DMG
|
||||
sign_dmg() {
|
||||
log_info "开始签名 DMG 文件..."
|
||||
|
||||
# 签名 DMG
|
||||
codesign --force --sign "$DEVELOPER_ID" "$DMG_PATH"
|
||||
|
||||
if [ $? -eq 0 ]; then
|
||||
log_success "DMG 签名成功"
|
||||
else
|
||||
log_error "DMG 签名失败"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# 验证 DMG 签名
|
||||
log_info "验证 DMG 签名..."
|
||||
codesign --verify --verbose "$DMG_PATH"
|
||||
|
||||
if [ $? -eq 0 ]; then
|
||||
log_success "DMG 签名验证通过"
|
||||
else
|
||||
log_error "DMG 签名验证失败"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
# 验证最终结果
|
||||
verify_final() {
|
||||
log_info "验证最终结果..."
|
||||
|
||||
# 检查文件大小
|
||||
APP_SIZE=$(du -h "$APP_PATH" | cut -f1)
|
||||
DMG_SIZE=$(du -h "$DMG_PATH" | cut -f1)
|
||||
|
||||
log_info "应用大小: $APP_SIZE"
|
||||
log_info "DMG 大小: $DMG_SIZE"
|
||||
|
||||
# 检查签名状态
|
||||
log_info "应用签名状态:"
|
||||
codesign -dv "$APP_PATH" 2>&1 | grep -E "(Authority|TeamIdentifier|BundleId)"
|
||||
|
||||
log_info "DMG 签名状态:"
|
||||
codesign -dv "$DMG_PATH" 2>&1 | grep -E "(Authority|TeamIdentifier)"
|
||||
}
|
||||
|
||||
# 显示结果
|
||||
show_result() {
|
||||
log_success "=========================================="
|
||||
log_success "签名和打包完成!"
|
||||
log_success "=========================================="
|
||||
log_info "应用名称: $APP_NAME"
|
||||
log_info "版本: $APP_VERSION"
|
||||
log_info "签名应用: $APP_PATH"
|
||||
log_info "签名 DMG: $DMG_PATH"
|
||||
log_info "开发者: $DEVELOPER_ID"
|
||||
log_success "=========================================="
|
||||
log_info "现在可以安全地分发给用户"
|
||||
log_info "用户安装时不会看到安全警告"
|
||||
log_success "=========================================="
|
||||
}
|
||||
|
||||
# 主函数
|
||||
main() {
|
||||
log_info "开始 BearVPN macOS 签名和打包流程..."
|
||||
log_info "=========================================="
|
||||
|
||||
check_certificates
|
||||
cleanup
|
||||
build_app
|
||||
sign_app
|
||||
create_dmg
|
||||
sign_dmg
|
||||
verify_final
|
||||
show_result
|
||||
|
||||
log_success "所有操作完成!"
|
||||
}
|
||||
|
||||
# 运行主函数
|
||||
main "$@"
|
||||
@ -1,42 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
# 简化的连接测试脚本
|
||||
echo "🔍 开始连接调试..."
|
||||
|
||||
# 测试基本网络连接
|
||||
echo "📡 测试基本网络连接..."
|
||||
if ping -c 3 8.8.8.8 > /dev/null 2>&1; then
|
||||
echo "✅ 基本网络连接正常"
|
||||
else
|
||||
echo "❌ 基本网络连接失败"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# 测试 DNS 解析
|
||||
echo "🌐 测试 DNS 解析..."
|
||||
if nslookup google.com > /dev/null 2>&1; then
|
||||
echo "✅ DNS 解析正常"
|
||||
else
|
||||
echo "❌ DNS 解析失败"
|
||||
fi
|
||||
|
||||
# 测试常见端口
|
||||
echo "🔌 测试常见端口连接..."
|
||||
for port in 80 443 8080; do
|
||||
if timeout 3 bash -c "echo >/dev/tcp/google.com/$port" 2>/dev/null; then
|
||||
echo "✅ google.com:$port 连接正常"
|
||||
else
|
||||
echo "❌ google.com:$port 连接失败"
|
||||
fi
|
||||
done
|
||||
|
||||
# 检查 libcore 库
|
||||
echo "📚 检查 libcore 库..."
|
||||
if [ -f "libcore/bin/libcore.dylib" ]; then
|
||||
echo "✅ 找到 libcore.dylib"
|
||||
file libcore/bin/libcore.dylib
|
||||
else
|
||||
echo "❌ 未找到 libcore.dylib"
|
||||
fi
|
||||
|
||||
echo "🎯 调试完成,现在可以运行应用查看详细日志"
|
||||
@ -1,23 +0,0 @@
|
||||
@echo off
|
||||
REM 测试 Flutter 安装和版本
|
||||
|
||||
REM 设置 Flutter 路径(假设已安装,如果未安装需手动设置)
|
||||
set FLUTTER_ROOT=C:\flutter REM 替换为您的 Flutter 安装路径
|
||||
|
||||
REM 添加 Flutter 到 PATH
|
||||
set PATH=%FLUTTER_ROOT%\bin;%PATH%
|
||||
|
||||
REM 检查 Flutter 版本
|
||||
flutter --version
|
||||
|
||||
REM 如果需要,升级到指定版本
|
||||
REM flutter upgrade --force
|
||||
|
||||
REM 启用 Windows desktop
|
||||
flutter config --enable-windows-desktop
|
||||
|
||||
REM 获取依赖(假设在项目目录中运行)
|
||||
flutter pub get
|
||||
|
||||
REM 暂停以查看输出
|
||||
pause
|
||||
@ -1,38 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
# 测试 URL 连通性脚本
|
||||
echo "🔍 测试 URL 连通性..."
|
||||
|
||||
# 测试 Google 的连通性测试 URL
|
||||
echo "📡 测试 http://www.gstatic.com/generate_204"
|
||||
if curl -s -o /dev/null -w "%{http_code}" --connect-timeout 5 "http://www.gstatic.com/generate_204" | grep -q "204"; then
|
||||
echo "✅ http://www.gstatic.com/generate_204 连接正常"
|
||||
else
|
||||
echo "❌ http://www.gstatic.com/generate_204 连接失败"
|
||||
fi
|
||||
|
||||
# 测试 Google 的专用连通性测试 URL
|
||||
echo "📡 测试 http://connectivitycheck.gstatic.com/generate_204"
|
||||
if curl -s -o /dev/null -w "%{http_code}" --connect-timeout 5 "http://connectivitycheck.gstatic.com/generate_204" | grep -q "204"; then
|
||||
echo "✅ http://connectivitycheck.gstatic.com/generate_204 连接正常"
|
||||
else
|
||||
echo "❌ http://connectivitycheck.gstatic.com/generate_204 连接失败"
|
||||
fi
|
||||
|
||||
# 测试 Cloudflare 的连通性测试 URL
|
||||
echo "📡 测试 http://cp.cloudflare.com"
|
||||
if curl -s -o /dev/null -w "%{http_code}" --connect-timeout 5 "http://cp.cloudflare.com" | grep -q "200\|204"; then
|
||||
echo "✅ http://cp.cloudflare.com 连接正常"
|
||||
else
|
||||
echo "❌ http://cp.cloudflare.com 连接失败"
|
||||
fi
|
||||
|
||||
# 测试其他常用的连通性测试 URL
|
||||
echo "📡 测试 http://www.cloudflare.com"
|
||||
if curl -s -o /dev/null -w "%{http_code}" --connect-timeout 5 "http://www.cloudflare.com" | grep -q "200"; then
|
||||
echo "✅ http://www.cloudflare.com 连接正常"
|
||||
else
|
||||
echo "❌ http://www.cloudflare.com 连接失败"
|
||||
fi
|
||||
|
||||
echo "🎯 URL 连通性测试完成"
|
||||
@ -1,101 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
# 更新 TEAM_ID 的简单脚本
|
||||
|
||||
set -e
|
||||
|
||||
# 颜色输出
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
BLUE='\033[0;34m'
|
||||
NC='\033[0m' # No Color
|
||||
|
||||
log_info() {
|
||||
echo -e "${BLUE}[INFO]${NC} $1"
|
||||
}
|
||||
|
||||
log_success() {
|
||||
echo -e "${GREEN}[SUCCESS]${NC} $1"
|
||||
}
|
||||
|
||||
log_warning() {
|
||||
echo -e "${YELLOW}[WARNING]${NC} $1"
|
||||
}
|
||||
|
||||
log_error() {
|
||||
echo -e "${RED}[ERROR]${NC} $1"
|
||||
}
|
||||
|
||||
# 获取 Team ID
|
||||
get_team_id() {
|
||||
log_info "请提供您的 Apple Developer Team ID"
|
||||
log_info "您可以通过以下方式获取:"
|
||||
log_info "1. 登录 https://developer.apple.com"
|
||||
log_info "2. 进入 'Account' -> 'Membership'"
|
||||
log_info "3. 查看 'Team ID' 字段(格式类似:ABC123DEF4)"
|
||||
echo ""
|
||||
|
||||
read -p "请输入您的 Team ID: " team_id
|
||||
|
||||
if [ -z "$team_id" ]; then
|
||||
log_error "Team ID 不能为空"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# 验证 Team ID 格式(应该是10位字母数字组合)
|
||||
if [[ ! "$team_id" =~ ^[A-Z0-9]{10}$ ]]; then
|
||||
log_warning "Team ID 格式可能不正确,但继续执行"
|
||||
fi
|
||||
|
||||
echo "$team_id"
|
||||
}
|
||||
|
||||
# 更新配置文件
|
||||
update_config() {
|
||||
local team_id=$1
|
||||
|
||||
log_info "更新 ios_signing_config.sh 文件..."
|
||||
|
||||
# 备份原文件
|
||||
cp ios_signing_config.sh ios_signing_config.sh.backup
|
||||
|
||||
# 更新 Team ID
|
||||
sed -i '' "s/export TEAM_ID=\"YOUR_TEAM_ID\"/export TEAM_ID=\"$team_id\"/" ios_signing_config.sh
|
||||
|
||||
# 更新签名身份
|
||||
sed -i '' "s/export SIGNING_IDENTITY=\"iPhone Developer: Your Name (YOUR_TEAM_ID)\"/export SIGNING_IDENTITY=\"iPhone Developer: Your Name ($team_id)\"/" ios_signing_config.sh
|
||||
sed -i '' "s/export DISTRIBUTION_IDENTITY=\"iPhone Distribution: Your Name (YOUR_TEAM_ID)\"/export DISTRIBUTION_IDENTITY=\"iPhone Distribution: Your Name ($team_id)\"/" ios_signing_config.sh
|
||||
|
||||
log_success "配置文件已更新"
|
||||
log_info "Team ID: $team_id"
|
||||
}
|
||||
|
||||
# 显示更新后的配置
|
||||
show_config() {
|
||||
log_info "更新后的配置:"
|
||||
echo "----------------------------------------"
|
||||
grep -E "export (APPLE_ID|TEAM_ID|BUNDLE_ID|SIGNING_IDENTITY)" ios_signing_config.sh
|
||||
echo "----------------------------------------"
|
||||
}
|
||||
|
||||
# 主函数
|
||||
main() {
|
||||
log_info "开始更新 TEAM_ID..."
|
||||
log_info "=========================================="
|
||||
|
||||
local team_id=$(get_team_id)
|
||||
update_config "$team_id"
|
||||
show_config
|
||||
|
||||
log_success "=========================================="
|
||||
log_success "TEAM_ID 更新完成!"
|
||||
log_success "=========================================="
|
||||
log_info "现在可以运行:"
|
||||
log_info "1. source ios_signing_config.sh"
|
||||
log_info "2. ./build_ios_dmg.sh"
|
||||
log_success "=========================================="
|
||||
}
|
||||
|
||||
# 运行主函数
|
||||
main "$@"
|
||||
119
构建日志分析.md
119
构建日志分析.md
@ -1,119 +0,0 @@
|
||||
# Windows 构建日志分析
|
||||
|
||||
## 🎉 构建结果:成功!
|
||||
|
||||
### ✅ 成功状态
|
||||
- **Debug 构建**: ✓ 成功 (282.5s)
|
||||
- **Release 构建**: ✓ 成功 (27.0s)
|
||||
- **最终状态**: ✅ Job succeeded
|
||||
|
||||
### 📁 构建输出位置
|
||||
```
|
||||
Debug: build\windows\x64\runner\Debug\hostexecutor.exe
|
||||
Release: build\windows\x64\runner\Release\hostexecutor.exe
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## ⚠️ 警告信息分析
|
||||
|
||||
### 1. CMake 警告(可忽略)
|
||||
```
|
||||
CMake Warning (dev) at flutter_inappwebview_windows/windows/CMakeLists.txt:31
|
||||
Policy CMP0175 is not set: add_custom_command() rejects invalid arguments.
|
||||
```
|
||||
- **影响**: 无影响,这是开发者警告
|
||||
- **解决方案**: 添加 `-Wno-dev` 参数可抑制
|
||||
|
||||
### 2. WebView2 编译警告(可忽略)
|
||||
```
|
||||
warning C4244: conversion from '__int64' to 'int', possible loss of data
|
||||
warning C4458: declaration of 'value' hides class member
|
||||
```
|
||||
- **影响**: 无功能影响,只是类型转换警告
|
||||
- **状态**: 正常现象,不影响使用
|
||||
|
||||
---
|
||||
|
||||
## 📤 产物上传问题
|
||||
|
||||
### 问题描述
|
||||
```
|
||||
::warning::No files were found with the provided path: build/windows/runner/Debug/
|
||||
::warning::No files were found with the provided path: build/windows/runner/Release/
|
||||
```
|
||||
|
||||
### 根本原因
|
||||
**路径配置错误**:workflow 配置的路径 `build/windows/runner/Debug/` 与实际构建输出路径 `build/windows/x64/runner/Debug/` 不匹配。
|
||||
|
||||
### 🔧 解决方案
|
||||
|
||||
需要更新 `.gitea/workflows/docker.yml` 中的上传路径:
|
||||
|
||||
```yaml
|
||||
# 原配置(错误)
|
||||
- name: Upload Debug build artifacts
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: windows-debug-build
|
||||
path: build/windows/runner/Debug/ # ❌ 错误路径
|
||||
|
||||
# 正确配置
|
||||
- name: Upload Debug build artifacts
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: windows-debug-build
|
||||
path: build/windows/x64/runner/Debug/ # ✅ 正确路径
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🏗️ 构建过程总结
|
||||
|
||||
### 成功的步骤
|
||||
1. ✅ NuGet 安装成功(通过 Chocolatey)
|
||||
2. ✅ Flutter 环境配置正确
|
||||
3. ✅ Windows 桌面支持启用
|
||||
4. ✅ 依赖获取成功
|
||||
5. ✅ 代码生成成功
|
||||
6. ✅ Windows Debug 构建成功
|
||||
7. ✅ Windows Release 构建成功
|
||||
|
||||
### 耗时分析
|
||||
- **Debug 构建**: 282.5秒(约4.7分钟)
|
||||
- **Release 构建**: 27秒(优化后更快)
|
||||
- **总耗时**: 约5分钟
|
||||
|
||||
---
|
||||
|
||||
## 🎯 下一步行动
|
||||
|
||||
### 立即修复
|
||||
1. **修复上传路径** - 更新 workflow 中的产物路径
|
||||
2. **验证可执行文件** - 确认 `hostexecutor.exe` 可正常运行
|
||||
|
||||
### 可选优化
|
||||
1. **缓存优化** - 添加 Flutter 和依赖缓存
|
||||
2. **并行构建** - Debug 和 Release 可并行执行
|
||||
3. **压缩产物** - 减少上传文件大小
|
||||
|
||||
---
|
||||
|
||||
## 📋 构建环境信息
|
||||
|
||||
### 软件版本
|
||||
- **Flutter**: 3.24.5
|
||||
- **Visual Studio**: 2022 Enterprise
|
||||
- **NuGet**: 6.14.0 (通过 Chocolatey)
|
||||
- **CMake**: 最新版本
|
||||
|
||||
### 系统环境
|
||||
- **操作系统**: Windows Server
|
||||
- **架构**: x64
|
||||
- **构建工具**: MSBuild
|
||||
|
||||
---
|
||||
|
||||
## 🚀 结论
|
||||
|
||||
**构建完全成功!** 主要的可执行文件已经生成,只是上传配置需要微调。Windows 应用构建流程已经稳定运行。
|
||||
147
自动打包说明.md
147
自动打包说明.md
@ -1,147 +0,0 @@
|
||||
# 工作流自动打包单文件 EXE 说明
|
||||
|
||||
## 🎯 功能概述
|
||||
|
||||
现在工作流会自动完成以下步骤:
|
||||
1. ✅ 构建 Windows 应用(Debug 和 Release)
|
||||
2. ✅ 自动下载打包工具
|
||||
3. ✅ 创建单文件 EXE
|
||||
4. ✅ 上传打包结果
|
||||
|
||||
## 🚀 工作流程
|
||||
|
||||
### 步骤 1: Enigma Virtual Box 打包(首选方案)
|
||||
- **工具**: 自动下载 Enigma Virtual Box
|
||||
- **输出**: `hostexecutor_single.exe`
|
||||
- **特点**: 真正的单文件,高压缩率
|
||||
|
||||
### 步骤 2: 7-Zip 自解压方案(备选方案)
|
||||
- **工具**: 使用系统自带的 7-Zip
|
||||
- **输出**: `hostexecutor_package.exe`
|
||||
- **特点**: 自解压安装包,兼容性更好
|
||||
|
||||
### 步骤 3: 产物上传
|
||||
- **单文件 EXE**: `windows-single-exe` 工件
|
||||
- **原始文件**: `windows-release-build` 工件
|
||||
|
||||
## 📦 打包产物
|
||||
|
||||
### 成功时你会得到:
|
||||
```
|
||||
工件列表:
|
||||
├── windows-debug-build # Debug 版本(多文件)
|
||||
├── windows-release-build # Release 版本(多文件)
|
||||
└── windows-single-exe # 单文件 EXE(自动打包)
|
||||
```
|
||||
|
||||
### 文件结构:
|
||||
```
|
||||
单文件 EXE:
|
||||
└── hostexecutor_single.exe # 50-80MB,直接运行
|
||||
|
||||
原始文件:
|
||||
└── hostexecutor.exe # 主程序
|
||||
├── flutter_windows.dll # Flutter 引擎
|
||||
├── *.dll # 运行时库
|
||||
└── data/ # 应用数据
|
||||
```
|
||||
|
||||
## ⚙️ 技术实现
|
||||
|
||||
### 自动下载 Enigma Virtual Box
|
||||
```powershell
|
||||
$enigmaUrl = "https://enigmaprotector.com/assets/files/enigmavb.exe"
|
||||
Invoke-WebRequest -Uri $enigmaUrl -OutFile "C:\enigmavb.exe"
|
||||
```
|
||||
|
||||
### 智能打包逻辑
|
||||
1. **尝试 Enigma** - 创建真正的单文件
|
||||
2. **失败时回退** - 使用 7-Zip 自解压
|
||||
3. **总是成功** - 确保有输出文件
|
||||
|
||||
### 压缩优化
|
||||
- **压缩级别**: 最高压缩率 (`/compress`)
|
||||
- **虚拟化模式**: 内存运行,不写磁盘
|
||||
- **多线程**: 并行处理,加快速度
|
||||
|
||||
## 📋 使用说明
|
||||
|
||||
### 获取单文件 EXE
|
||||
1. 进入 Gitea Actions 页面
|
||||
2. 找到最新的成功构建
|
||||
3. 下载 `windows-single-exe` 工件
|
||||
4. 直接运行 `hostexecutor_single.exe`
|
||||
|
||||
### 验证打包结果
|
||||
```powershell
|
||||
# 检查文件大小(应该在 50-80MB)
|
||||
dir hostexecutor_single.exe
|
||||
|
||||
# 验证单文件(应该只有 1 个文件)
|
||||
dir *.exe
|
||||
```
|
||||
|
||||
## 🎯 优势特点
|
||||
|
||||
### 完全自动化
|
||||
- ✅ 无需手动操作
|
||||
- ✅ 自动下载工具
|
||||
- ✅ 智能错误处理
|
||||
- ✅ 保证输出结果
|
||||
|
||||
### 高质量打包
|
||||
- ✅ 真正的单文件
|
||||
- ✅ 高压缩率
|
||||
- ✅ 内存运行,不写临时文件
|
||||
- ✅ 兼容所有 Windows 版本
|
||||
|
||||
### 可靠回退
|
||||
- ✅ Enigma 失败时自动切换 7-Zip
|
||||
- ✅ 总是生成可用的输出
|
||||
- ✅ 详细的构建日志
|
||||
|
||||
## 🔧 自定义配置
|
||||
|
||||
### 修改压缩级别
|
||||
在工作流脚本中找到:
|
||||
```powershell
|
||||
# 最高压缩(当前设置)
|
||||
/compress
|
||||
|
||||
# 快速压缩(速度优先)
|
||||
/compress:fast
|
||||
|
||||
# 平衡压缩(推荐)
|
||||
/compress:normal
|
||||
```
|
||||
|
||||
### 修改输出文件名
|
||||
```powershell
|
||||
# 当前设置
|
||||
$outputExe = "$outputPath\$($exeFile.BaseName)_Single.exe"
|
||||
|
||||
# 自定义名称
|
||||
$outputExe = "$outputPath\MyApp_Portable.exe"
|
||||
```
|
||||
|
||||
## 📊 性能指标
|
||||
|
||||
### 构建时间
|
||||
- **Debug 构建**: ~4.7 分钟
|
||||
- **Release 构建**: ~27 秒
|
||||
- **单文件打包**: ~2-5 分钟
|
||||
- **总时间**: ~8-12 分钟
|
||||
|
||||
### 文件大小
|
||||
- **原始文件**: ~70MB(多文件)
|
||||
- **单文件**: ~50-80MB(压缩后)
|
||||
- **压缩率**: 10-30% 减小
|
||||
|
||||
## 🎉 总结
|
||||
|
||||
**现在工作流完全自动化了!**
|
||||
- 🔄 每次推送代码 → 自动构建 → 自动打包单文件
|
||||
- 📦 构建完成后 → 直接下载单文件 EXE 使用
|
||||
- 🔧 无需手动配置 → 开箱即用
|
||||
|
||||
**你只需要**:等待构建完成,下载单文件,直接运行!✨
|
||||
Loading…
x
Reference in New Issue
Block a user