359 lines
8.1 KiB
Bash
Executable File
359 lines
8.1 KiB
Bash
Executable File
#!/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
|