#!/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 method development teamID $TEAM_ID uploadBitcode uploadSymbols compileBitcode 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