一、前言:为什么需要自动化打包?
在iOS项目开发中,随着项目规模增大、版本迭代频繁,手动打包会面临以下问题:
- 效率低下:每次打包需要等待15-30分钟,占用开发时间
- 环境不一致:不同开发者打包环境配置差异导致包体不一致
- 流程复杂:证书管理、描述文件更新、多环境配置等操作繁琐
- 资源浪费:开发者机器持续高负载运行影响其他工作
二、环境准备与Jenkins安装
2.1 系统环境要求
| 组件 | 版本要求 | 说明 |
|---|---|---|
| macOS | 10.15+ | 建议使用较新版本 |
| Java | JDK 8+ | Jenkins运行依赖 |
| Xcode | 最新稳定版 | 需包含命令行工具 |
| Homebrew | 最新版 | 可选,用于安装管理 |
2.2 Jenkins安装(两种方式)
方式一:使用Homebrew安装(推荐)
bash
# 安装Homebrew(如未安装)
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
# 安装Jenkins LTS版本
brew install jenkins-lts
# 启动Jenkins服务
brew services start jenkins-lts
# 查看服务状态
brew services list | grep jenkins
方式二:使用安装包安装
bash
# 下载官方安装包
# 访问:https://jenkins.io/download/
# 或使用命令行下载
curl -L -o jenkins.pkg https://get.jenkins.io/osx/latest
# 安装
sudo installer -pkg jenkins.pkg -target /
2.3 初始配置
- 访问Jenkins :浏览器打开
http://localhost:8080 - 获取初始密码:
bash
# 密码存储位置
sudo cat /Users/Shared/Jenkins/Home/secrets/initialAdminPassword
# 或
sudo cat /var/lib/jenkins/secrets/initialAdminPassword
- 安装推荐插件:选择"Install suggested plugins"
- 创建管理员账户:设置用户名、密码、邮箱等信息
三、iOS专用插件配置
3.1 必要插件安装
在Jenkins的"系统管理" → "插件管理"中安装以下插件:
| 插件名称 | 作用 | 必需性 |
|---|---|---|
| Keychains and Provisioning Profiles Management | 管理iOS证书和描述文件 | 必需 |
| Xcode integration | Xcode项目集成 | 必需 |
| Git plugin | Git源代码管理 | 必需 |
| Pipeline | 流水线任务支持 | 推荐 |
3.2 证书与描述文件配置
步骤一:准备Keychain文件
bash
# 找到系统钥匙串文件
cp ~/Library/Keychains/login.keychain-db ~/Desktop/login.keychain
# 注意:需要复制并去除-db后缀才能上传到Jenkins
步骤二:准备描述文件
bash
# 描述文件默认路径
~/Library/MobileDevice/Provisioning\ Profiles/
# 可以查看所有描述文件
ls -la ~/Library/MobileDevice/Provisioning\ Profiles/
步骤三:Jenkins插件配置
- 进入 Manage Jenkins → Keychains and Provisioning Profiles Management
- 上传Keychain文件:
- 点击"Upload Keychain"
- 选择准备好的
login.keychain文件 - 在"Code Signing Identity"中添加证书名称(可在钥匙串访问中查看)
- 上传描述文件:
- 点击"Upload Provisioning Profile"
- 选择描述文件
- 设置目标路径:
/Users/共享用户名/Library/MobileDevice/Provisioning Profiles
四、完整自动化打包脚本
以下是一个功能完整的自动化打包脚本,支持多环境配置:
bash
#!/bin/bash
# iOS自动化打包脚本
# 文件名:ios_auto_build.sh
# 使用方法:./ios_auto_build.sh [环境] [版本号]
# 设置语言环境,避免编码问题
export LANG=en_US.UTF-8
export LC_ALL=en_US.UTF-8
# ============== 参数配置 ==============
# 默认配置
CONFIGURATION="Release"
EXPORT_METHOD="development" # development, ad-hoc, app-store, enterprise
BUILD_SCHEME="YourApp"
PROJECT_NAME="YourApp"
UPLOAD_TO_PGYER=false
VERSION_OVERRIDE=""
# 解析命令行参数
while [[ $# -gt 0 ]]; do
case $1 in
-e|--environment)
case $2 in
dev) EXPORT_METHOD="development" ;;
adhoc) EXPORT_METHOD="ad-hoc" ;;
appstore) EXPORT_METHOD="app-store" ;;
enterprise) EXPORT_METHOD="enterprise" ;;
*) echo "未知环境: $2"; exit 1 ;;
esac
shift 2
;;
-s|--scheme)
BUILD_SCHEME="$2"
shift 2
;;
-v|--version)
VERSION_OVERRIDE="$2"
shift 2
;;
-u|--upload)
UPLOAD_TO_PGYER=true
shift
;;
*)
echo "未知参数: $1"
echo "用法: $0 [-e dev|adhoc|appstore|enterprise] [-s scheme] [-v version] [-u]"
exit 1
;;
esac
done
# ============== 路径配置 ==============
# Jenkins工作空间路径(Jenkins环境变量)
if [ -z "$WORKSPACE" ]; then
WORKSPACE=$(pwd)
fi
# 项目相关路径
PROJECT_PATH="$WORKSPACE"
XCODEPROJ_PATH="$PROJECT_PATH/$PROJECT_NAME.xcodeproj"
WORKSPACE_PATH="$PROJECT_PATH/$PROJECT_NAME.xcworkspace"
INFOPLIST_PATH="$PROJECT_PATH/$PROJECT_NAME/Info.plist"
# 输出路径
BUILD_DATE=$(date +%Y%m%d_%H%M%S)
ARCHIVE_NAME="${BUILD_SCHEME}_${EXPORT_METHOD}_${BUILD_DATE}.xcarchive"
IPA_NAME="${BUILD_SCHEME}_${EXPORT_METHOD}_${BUILD_DATE}.ipa"
ARCHIVE_DIR="$WORKSPACE/build/archive"
IPA_DIR="$WORKSPACE/build/ipa"
LOG_DIR="$WORKSPACE/build/logs"
# 创建目录
mkdir -p "$ARCHIVE_DIR" "$IPA_DIR" "$LOG_DIR"
# ExportOptions.plist路径
EXPORT_OPTIONS_DIR="$PROJECT_PATH/ExportOptions"
EXPORT_OPTIONS_PLIST="$EXPORT_OPTIONS_DIR/${EXPORT_METHOD}.plist"
# ============== 日志函数 ==============
log_info() {
echo "[INFO] $(date '+%Y-%m-%d %H:%M:%S') - $1" | tee -a "$LOG_DIR/build.log"
}
log_error() {
echo "[ERROR] $(date '+%Y-%m-%d %H:%M:%S') - $1" | tee -a "$LOG_DIR/error.log"
}
log_success() {
echo "[SUCCESS] $(date '+%Y-%m-%d %H:%M:%S') - $1" | tee -a "$LOG_DIR/build.log"
}
# ============== 版本号管理 ==============
update_version_if_needed() {
if [ ! -z "$VERSION_OVERRIDE" ]; then
log_info "更新版本号为: $VERSION_OVERRIDE"
# 更新CFBundleShortVersionString (市场版本号)
/usr/libexec/PlistBuddy -c "Set :CFBundleShortVersionString $VERSION_OVERRIDE" "$INFOPLIST_PATH"
# 更新CFBundleVersion (构建版本号)
BUILD_NUMBER=$(date +%Y%m%d%H%M)
/usr/libexec/PlistBuddy -c "Set :CFBundleVersion $BUILD_NUMBER" "$INFOPLIST_PATH"
log_success "版本号更新完成: $VERSION_OVERRIDE (Build: $BUILD_NUMBER)"
fi
}
# ============== 依赖安装 ==============
install_dependencies() {
log_info "开始安装依赖..."
# 检查是否使用CocoaPods
if [ -f "$PROJECT_PATH/Podfile" ]; then
log_info "检测到Podfile,执行pod install..."
# 更新CocoaPods仓库(可选,首次或需要更新时)
# pod repo update
# 安装依赖
cd "$PROJECT_PATH"
pod install --repo-update
if [ $? -eq 0 ]; then
log_success "依赖安装成功"
else
log_error "依赖安装失败"
exit 1
fi
else
log_info "未检测到Podfile,跳过依赖安装"
fi
}
# ============== 清理项目 ==============
clean_project() {
log_info "开始清理项目..."
# 清理DerivedData
DERIVED_DATA_PATH="$HOME/Library/Developer/Xcode/DerivedData"
if [ -d "$DERIVED_DATA_PATH" ]; then
find "$DERIVED_DATA_PATH" -name "${PROJECT_NAME}*" -type d -exec rm -rf {} + 2>/dev/null || true
log_info "已清理DerivedData"
fi
# Xcode清理
xcodebuild clean \
-workspace "$WORKSPACE_PATH" \
-scheme "$BUILD_SCHEME" \
-configuration "$CONFIGURATION" \
-quiet \
2>&1 | tee -a "$LOG_DIR/clean.log"
if [ ${PIPESTATUS[0]} -eq 0 ]; then
log_success "项目清理完成"
else
log_error "项目清理失败"
exit 1
fi
}
# ============== 构建归档 ==============
build_archive() {
log_info "开始构建归档..."
# 构建归档
xcodebuild archive \
-workspace "$WORKSPACE_PATH" \
-scheme "$BUILD_SCHEME" \
-configuration "$CONFIGURATION" \
-archivePath "$ARCHIVE_DIR/$ARCHIVE_NAME" \
-destination "generic/platform=iOS" \
-allowProvisioningUpdates \
-quiet \
2>&1 | tee -a "$LOG_DIR/archive.log"
if [ ${PIPESTATUS[0]} -eq 0 ]; then
log_success "归档构建完成: $ARCHIVE_NAME"
else
log_error "归档构建失败"
exit 1
fi
}
# ============== 导出IPA ==============
export_ipa() {
log_info "开始导出IPA..."
# 检查ExportOptions.plist是否存在
if [ ! -f "$EXPORT_OPTIONS_PLIST" ]; then
log_error "ExportOptions.plist文件不存在: $EXPORT_OPTIONS_PLIST"
log_info "请先通过Xcode手动导出一次获取正确的plist文件"
exit 1
fi
# 导出IPA
xcodebuild -exportArchive \
-archivePath "$ARCHIVE_DIR/$ARCHIVE_NAME" \
-exportPath "$IPA_DIR" \
-exportOptionsPlist "$EXPORT_OPTIONS_PLIST" \
-allowProvisioningUpdates \
2>&1 | tee -a "$LOG_DIR/export.log"
if [ ${PIPESTATUS[0]} -eq 0 ]; then
# 重命名IPA文件
mv "$IPA_DIR/$BUILD_SCHEME.ipa" "$IPA_DIR/$IPA_NAME"
log_success "IPA导出完成: $IPA_NAME"
echo "IPA路径: $IPA_DIR/$IPA_NAME"
else
log_error "IPA导出失败"
exit 1
fi
}
# ============== 上传到分发平台 ==============
upload_to_pgyer() {
if [ "$UPLOAD_TO_PGYER" = false ]; then
return 0
fi
log_info "开始上传到蒲公英..."
# 蒲公英API配置(请替换为自己的账号信息)
PGYER_U_KEY="your_u_key_here"
PGYER_API_KEY="your_api_key_here"
IPA_PATH="$IPA_DIR/$IPA_NAME"
# 检查文件是否存在
if [ ! -f "$IPA_PATH" ]; then
log_error "IPA文件不存在: $IPA_PATH"
return 1
fi
# 上传到蒲公英
curl -F "file=@$IPA_PATH" \
-F "uKey=$PGYER_U_KEY" \
-F "_api_key=$PGYER_API_KEY" \
-F "installType=2" \
-F "password=123456" \
https://upload.pgyer.com/apiv1/app/upload \
2>&1 | tee -a "$LOG_DIR/upload.log"
if [ ${PIPESTATUS[0]} -eq 0 ]; then
log_success "上传到蒲公英成功"
else
log_error "上传到蒲公英失败"
fi
}
# ============== 生成构建报告 ==============
generate_build_report() {
log_info "生成构建报告..."
REPORT_FILE="$LOG_DIR/build_report_${BUILD_DATE}.md"
cat > "$REPORT_FILE" << EOF
# iOS构建报告
## 基本信息
- 项目名称: $PROJECT_NAME
- 构建方案: $BUILD_SCHEME
- 构建环境: $EXPORT_METHOD
- 构建配置: $CONFIGURATION
- 构建时间: $(date '+%Y-%m-%d %H:%M:%S')
## 版本信息
$(/usr/libexec/PlistBuddy -c "Print :CFBundleShortVersionString" "$INFOPLIST_PATH" 2>/dev/null | awk '{print "市场版本: "$0}')
$(/usr/libexec/PlistBuddy -c "Print :CFBundleVersion" "$INFOPLIST_PATH" 2>/dev/null | awk '{print "构建版本: "$0}')
## 产物信息
- 归档文件: $ARCHIVE_NAME
- IPA文件: $IPA_NAME
- IPA大小: $(du -h "$IPA_DIR/$IPA_NAME" | cut -f1)
## 构建状态
- 依赖安装: ✅ 成功
- 项目清理: ✅ 成功
- 归档构建: ✅ 成功
- IPA导出: ✅ 成功
$(if [ "$UPLOAD_TO_PGYER" = true ]; then echo "- 蒲公英上传: ✅ 成功"; fi)
## 文件路径
- 工作空间: $WORKSPACE
- IPA目录: $IPA_DIR
- 日志目录: $LOG_DIR
## 构建日志
\`\`\`
$(tail -20 "$LOG_DIR/build.log")
\`\`\`
---
*此报告由自动化构建系统生成*
EOF
log_success "构建报告已生成: $REPORT_FILE"
}
# ============== 主执行流程 ==============
main() {
log_info "========== 开始iOS自动化打包 =========="
log_info "项目: $PROJECT_NAME"
log_info "方案: $BUILD_SCHEME"
log_info "环境: $EXPORT_METHOD"
log_info "配置: $CONFIGURATION"
# 执行构建步骤
update_version_if_needed
install_dependencies
clean_project
build_archive
export_ipa
upload_to_pgyer
generate_build_report
log_success "========== iOS自动化打包完成 =========="
log_success "总耗时: $SECONDS 秒"
log_success "IPA文件: $IPA_DIR/$IPA_NAME"
# 打开输出目录(仅本地运行)
if [ -z "$JENKINS_HOME" ]; then
open "$IPA_DIR"
fi
return 0
}
# 异常处理
trap 'log_error "构建过程被中断"; exit 1' INT TERM
# 执行主函数
main "$@"
4.1 ExportOptions.plist文件模板
创建ExportOptions目录,根据不同的导出方式创建对应的plist文件:
DevelopmentExportOptions.plist
xml
<?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>compileBitcode</key>
<false/>
<key>destination</key>
<string>export</string>
<key>method</key>
<string>development</string>
<key>provisioningProfiles</key>
<dict>
<key>com.yourcompany.yourapp</key>
<string>Your App Development Profile</string>
</dict>
<key>signingCertificate</key>
<string>iPhone Developer</string>
<key>signingStyle</key>
<string>manual</string>
<key>stripSwiftSymbols</key>
<true/>
<key>teamID</key>
<string>YOUR_TEAM_ID</string>
<key>thinning</key>
<string><none></string>
</dict>
</plist>
AdHocExportOptions.plist
xml
<?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>compileBitcode</key>
<false/>
<key>destination</key>
<string>export</string>
<key>method</key>
<string>ad-hoc</string>
<key>provisioningProfiles</key>
<dict>
<key>com.yourcompany.yourapp</key>
<string>Your App AdHoc Profile</string>
</dict>
<key>signingCertificate</key>
<string>iPhone Distribution</string>
<key>signingStyle</key>
<string>manual</string>
<key>stripSwiftSymbols</key>
<true/>
<key>teamID</key>
<string>YOUR_TEAM_ID</string>
<key>thinning</key>
<string><none></string>
</dict>
</plist>
五、Jenkins任务配置
5.1 创建自由风格任务
-
新建任务:选择"新建Item" → 输入任务名称 → 选择"Freestyle project"
-
源码管理:配置Git仓库信息
- Repository URL:
git@github.com:yourname/yourapp.git - Credentials: 添加SSH密钥
- Branches to build:
*/main(根据实际情况)
- Repository URL:
-
构建触发器(可选):
- 定时构建:
H */2 * * *(每2小时构建一次) - GitHub hook触发
- 定时构建:
5.2 构建环境配置
在"构建环境"中勾选:
- ✅
Keychains and Code Signing Identities - ✅
Mobile Provisioning Profiles
选择对应的证书和描述文件。
5.3 构建步骤配置
添加构建步骤:
- Execute shell:
bash
# 设置执行权限
chmod +x ./scripts/ios_auto_build.sh
# 执行打包脚本(开发环境)
./scripts/ios_auto_build.sh -e dev -s YourApp -u
# 或者使用参数化构建
./scripts/ios_auto_build.sh -e ${BUILD_ENV} -s ${SCHEME} ${UPLOAD}
- 构建后操作 :
- Archive the artifacts:
build/ipa/*.ipa - 邮件通知(配置SMTP服务器)
- Archive the artifacts:
六、性能优化方案
6.1 构建速度优化策略
bash
#!/bin/bash
# 文件名:optimized_build.sh
# 构建优化脚本
# 1. 使用DerivedData缓存
DERIVED_DATA_PATH="$HOME/Library/Developer/Xcode/DerivedData/Cache"
mkdir -p "$DERIVED_DATA_PATH"
# 2. 仅清理必要部分,避免完全清理
clean_partial() {
# 只清理当前项目的DerivedData
find "$DERIVED_DATA_PATH" -name "${PROJECT_NAME}*" -mtime +7 -type d -exec rm -rf {} \;
}
# 3. 并行构建(如果项目支持)
xcodebuild archive \
-workspace "$WORKSPACE_PATH" \
-scheme "$BUILD_SCHEME" \
-configuration "$CONFIGURATION" \
-archivePath "$ARCHIVE_PATH" \
-parallelizeTargets \
-jobs 4 \ # 根据CPU核心数调整
-quiet
# 4. 使用ccache加速编译
export CCACHE_DIR="$HOME/.ccache"
export CCACHE_MAXSIZE=5G
export CCACHE_CPP2=true
# 检查ccache是否安装
if command -v ccache &> /dev/null; then
export CC="ccache clang"
export CXX="ccache clang++"
fi
6.2 Jenkins节点配置优化
- 增加构建节点:将构建任务分发到专用构建机
- 配置SSD存储:确保DerivedData在SSD上
- 内存优化:为Jenkins分配足够内存(4GB+)
七、常见问题解决方案
7.1 证书与签名问题
问题1:Code Signing Identity找不到
解决:确保Keychain文件正确上传,且在Jenkins构建环境中正确选择
问题2:描述文件过期
bash
# 自动检查描述文件过期脚本
#!/bin/bash
EXPIRY_DATE=$(security cms -D -i "profile.mobileprovision" | plutil -extract ExpirationDate xml1 -o - - | xmllint --xpath '/plist/date/text()' -)
TODAY=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
if [[ "$EXPIRY_DATE" < "$TODAY" ]]; then
echo "描述文件已过期,请更新!"
# 发送通知邮件
echo "描述文件过期" | mail -s "证书警报" admin@example.com
fi
7.2 构建失败问题
问题3:xcodebuild: error: Scheme is not currently configured for the build action.
markdown
解决:
1. 在Xcode中打开项目
2. Product → Scheme → Manage Schemes
3. 勾选对应Scheme的"Shared"复选框
4. 提交.xcscheme文件到Git仓库
问题4:Pod install失败
bash
# 在构建前添加环境变量
export COCOAPODS_DISABLE_STATS=true # 禁用数据统计,加速
export GIT_SSL_NO_VERIFY=true # 如果遇到SSL问题
7.3 性能问题
问题5:构建时间过长(>10分钟)
markdown
解决方案:
1. 启用增量编译:避免每次clean
2. 使用ccache:缓存编译结果
3. 分离资源:将图片等资源放在服务器动态加载
4. 模块化:将大项目拆分成多个模块
八、监控与维护
8.1 构建监控脚本
bash
#!/bin/bash
# 构建监控脚本
# monitor_builds.sh
JENKINS_URL="http://localhost:8080"
API_TOKEN="your_api_token"
JOB_NAME="iOS-Build"
# 获取最近构建状态
check_build_status() {
local response=$(curl -s -u "admin:$API_TOKEN" \
"$JENKINS_URL/job/$JOB_NAME/lastBuild/api/json")
local result=$(echo "$response" | grep -o '"result":"[^"]*"' | cut -d'"' -f4)
local number=$(echo "$response" | grep -o '"number":[0-9]*' | cut -d':' -f2)
local duration=$(echo "$response" | grep -o '"duration":[0-9]*' | cut -d':' -f2)
echo "构建 #$number: 状态=$result, 耗时=$((duration/1000))秒"
if [ "$result" = "FAILURE" ]; then
send_alert "构建失败" "构建 #$number 失败,请检查!"
fi
}
# 检查磁盘空间
check_disk_space() {
local usage=$(df -h / | awk 'NR==2 {print $5}' | sed 's/%//')
if [ "$usage" -gt 90 ]; then
send_alert "磁盘空间不足" "磁盘使用率: ${usage}%"
fi
}
# 发送警报
send_alert() {
local subject=$1
local message=$2
# 发送邮件
echo "$message" | mail -s "$subject" admin@example.com
# 发送Slack通知(可选)
curl -X POST -H 'Content-type: application/json' \
--data "{\"text\":\"$subject: $message\"}" \
https://hooks.slack.com/services/your/webhook/url
}
# 主监控循环
while true; do
check_build_status
check_disk_space
sleep 300 # 每5分钟检查一次
done
8.2 定期维护任务
- 每周清理:
bash
# 清理旧的构建产物
find ~/.jenkins/workspace -name "*.ipa" -mtime +30 -delete
find ~/.jenkins/workspace -name "*.xcarchive" -mtime +7 -delete
# 清理DerivedData
find ~/Library/Developer/Xcode/DerivedData -name "*" -type d -mtime +30 -exec rm -rf {} +
- 证书更新提醒:设置日历提醒,在证书到期前30天更新
九、高级功能扩展
9.1 Jenkins Pipeline配置
创建Jenkinsfile实现声明式流水线:
groovy
// Jenkinsfile
pipeline {
agent any
parameters {
choice(
name: 'BUILD_ENV',
choices: ['dev', 'adhoc', 'appstore'],
description: '选择构建环境'
)
string(
name: 'VERSION',
defaultValue: '',
description: '版本号(留空使用当前)'
)
}
environment {
PROJECT_NAME = 'YourApp'
SCHEME = 'YourApp'
}
stages {
stage('代码拉取') {
steps {
checkout scm
}
}
stage('依赖安装') {
steps {
sh 'pod install --repo-update'
}
}
stage('构建IPA') {
steps {
script {
sh """
chmod +x ./scripts/ios_auto_build.sh
./scripts/ios_auto_build.sh \
-e ${params.BUILD_ENV} \
-s ${SCHEME} \
${params.VERSION ? "-v ${params.VERSION}" : ""} \
-u
"""
}
}
}
stage('质量检查') {
steps {
// 运行单元测试
sh 'xcodebuild test -workspace ${PROJECT_NAME}.xcworkspace -scheme ${SCHEME} -destination "platform=iOS Simulator,name=iPhone 14"'
// 代码静态分析
sh 'xcodebuild analyze -workspace ${PROJECT_NAME}.xcworkspace -scheme ${SCHEME}'
}
}
stage('部署') {
when {
expression { params.BUILD_ENV == 'adhoc' || params.BUILD_ENV == 'appstore' }
}
steps {
// 上传到TestFlight或App Store
script {
if (params.BUILD_ENV == 'appstore') {
sh 'xcrun altool --upload-app -f build/ipa/*.ipa -u apple@example.com -p @keychain:App_Password'
}
}
}
}
}
post {
success {
emailext (
subject: "构建成功: ${env.JOB_NAME} #${env.BUILD_NUMBER}",
body: "构建成功!\n\n详情: ${env.BUILD_URL}",
to: 'team@example.com'
)
}
failure {
emailext (
subject: "构建失败: ${env.JOB_NAME} #${env.BUILD_NUMBER}",
body: "构建失败,请检查!\n\n日志: ${env.BUILD_URL}console",
to: 'devops@example.com'
)
}
}
}
9.2 多环境配置管理
创建配置文件管理不同环境:
bash
# configs/environments/dev.config
export APP_NAME="MyApp Dev"
export BUNDLE_ID="com.company.app.dev"
export API_BASE="https://dev.api.example.com"
export UPLOAD_TO_PGYER=true
# configs/environments/prod.config
export APP_NAME="MyApp"
export BUNDLE_ID="com.company.app"
export API_BASE="https://api.example.com"
export UPLOAD_TO_PGYER=false
在构建脚本中加载配置:
bash
# 加载环境配置
load_environment_config() {
local env=$1
local config_file="$PROJECT_PATH/configs/environments/${env}.config"
if [ -f "$config_file" ]; then
source "$config_file"
log_info "已加载环境配置: $env"
else
log_error "环境配置文件不存在: $config_file"
exit 1
fi
}
十、总结
通过本文的完整指南,你可以建立一套专业的iOS自动化打包系统,实现以下目标:
- 一键打包:支持多环境、多配置的自动化构建
- 持续集成:代码提交后自动构建、测试、部署
- 质量保障:集成代码检查、测试覆盖率分析
- 性能优化:通过缓存、并行构建等手段提升效率
- 监控告警:实时监控构建状态,及时发现问题
这套方案不仅适用于初创团队快速搭建CI/CD流程,也适合中大型团队优化现有构建系统。根据团队实际情况,可以灵活调整配置,逐步完善自动化流程。
后续优化建议:
- 容器化构建:使用Docker确保构建环境一致性
- 二进制缓存:将编译产物缓存到云端,加速CI/CD
- 自动化测试:集成UI自动化测试和性能测试
- 安全扫描:集成代码安全扫描工具
- 数据分析:收集构建数据,分析优化方向