CI / CD
把 Xcode 构建搬到远程 Mac:可重复、可交接的构建流程
用托管 Mac mini 做 iOS 构建机:固定 Xcode 版本、隔离 DerivedData、管理签名证书、用脚本统一入口,让同一个 commit 稳定产出同一个包。
“在我电脑上能编译”是 iOS 团队最常见的一句话。把构建搬到一台专门的远程 Mac,目标只有一个:同一个 commit,在任何时候都产出同样的结果。
1. 固定 Xcode 版本
构建机上可以并存多个 Xcode,用 xcode-select 明确指定,并把版本写进仓库:
# 仓库根目录放一个 .xcode-version,例如 16.4
sudo xcode-select -s /Applications/Xcode_16.4.app
xcodebuild -version
构建脚本第一步就校验当前版本和 .xcode-version 一致,不一致直接失败,比事后排查玄学问题省事得多。
2. 统一的构建入口
不要让每个人记一长串 xcodebuild 参数。在仓库里放一个 scripts/build.sh:
#!/bin/zsh
set -euo pipefail
SCHEME=${1:-App}
DD="$PWD/.build/DerivedData"
xcodebuild \
-scheme "$SCHEME" \
-configuration Release \
-destination 'generic/platform=iOS' \
-derivedDataPath "$DD" \
-archivePath ".build/$SCHEME.xcarchive" \
archive
本地和构建机跑的是同一个脚本,差异就只剩环境本身。
3. 隔离 DerivedData 与缓存
- 用
-derivedDataPath把中间产物放在项目目录内,避免多个项目、多个分支互相污染; - 依赖(Swift Package、CocoaPods)锁定版本:提交
Package.resolved/Podfile.lock; - 出现诡异编译错误时,第一步永远是清掉这个目录重来。
4. 签名:最容易出问题的一环
- 给构建机单独创建一个钥匙串,只放构建所需证书,脚本里先解锁再构建;
- 证书和描述文件集中管理(例如加密后存放在私有仓库,用 fastlane match 一类工具同步),不要靠某个人手动导入;
- 通过 SSH 构建时,钥匙串可能处于锁定状态,导致
errSecInternalComponent,这是解锁步骤缺失的典型症状:
security unlock-keychain -p "$KEYCHAIN_PASSWORD" build.keychain
5. 测试与产物
xcodebuild test -scheme App \
-destination 'platform=iOS Simulator,name=iPhone 16' \
-resultBundlePath .build/test.xcresult
- 每次构建的
.xcarchive、.ipa、.xcresult按 commit 号归档; - 保留 dSYM,否则线上崩溃日志无法符号化。
6. 接入 CI
托管 Mac mini 可以注册为 GitHub Actions / GitLab 的自托管 Runner,构建脚本不变,只是触发方式从手动变成推送。自托管 Runner 请只用于私有仓库,避免执行陌生人提交的代码。
核心思路:环境写进仓库,入口写成脚本,产物按 commit 归档。做到这三点,构建就不再依赖“某个人的电脑”。
想要一台常开的远程 Mac?
马来西亚住宅宽带 · 东八区 · 中文支持。早鸟仅限前 20 名。