2026-05-20 Xcode 16 描述檔路徑與多車道併發 Archive:在無頭租用的 Apple Silicon 雲端 Mac 上落地(香港 / 東京 / 首爾 / 新加坡 / 美國)
在香港、東京、首爾、新加坡、美國租用Mac mini M4并以無頭 SSH运行 Xcode 16 的团队,会碰到一个并不张扬的破坏性变更:描述檔(Provisioning Profile)不再只住在传统的 MobileDevice 目录里。Xcode 16 以 UserData 为权威读写位置,而 2024 年以前的脚本、部分 Fastlane 动作與内部安装器仍只往旧路徑复制 .mobileprovision。当你在单台租机上叠加多車道併發 xcodebuild archive 时,路徑漂移会演变成簽名竞态、虚假的「找不到描述檔」以及仅在 CI 上复现的 errSecInternalComponent。本文在开头给出策略:在主机上所有工具都确认兼容 Xcode 16 之前,双写鏡像 UserData 與旧版路徑;为每条車道配置独立 CI 鑰匙串與DerivedData 根目录;在标配 Mac mini M4 上把併發 Archive 車道默认限制为两条。正文提供路徑对照表、多車道矩阵、無頭簽名排障、八步上線清單與 FAQ,并與《鑰匙串與描述檔 CI 基线》《自动與手动簽名》《併發 xcodebuild 纪律》互相衔接。
谁会遇到描述檔路徑漂移
失败信息很少直接写「目录错了」。更常见的是:車道 B 的 Archive 成功,車道 A 却报 No profile for team 'XXXXXXXXXX' matching,而两边明明导入同一 UUID。租机上问题会被放大:黄金鏡像里残留 2024 年的安装脚本、插件仍只刷新 ~/Library/MobileDevice/Provisioning Profiles,新人本地的 Fastlane 却把文件下载进 UserData,夜间 cron 却只同步旧目录。多分支 CI 若共用同一 macOS 用户且未刻意分区,home 目录下的隐式全局状态会把「独立任务」绑在一起。
- 发布工程师需要在单台 Mac mini M4 上为不同 Bundle ID 併發 Archive,并要求每条車道可预测地解析描述檔。
- 平台组维护黄金鏡像时必须写清:Xcode 16、
xcodebuild與辅助工具在archive/exportArchive时读哪条路徑。 - 安全评审要求证明車道 A 无法读取車道 B 的发行证书——热切换描述檔时,按車道隔离鑰匙串 不可省略。
设计评审可从 MacXCode 首页 出发:Apple Silicon 贴近 Apple API,再判断哪些簽名物料必须與租机检出同驻。若仍只复制到 MobileDevice,应视为阻碍多車道吞吐的技术债——请與《远程 Archive 指南》对照阅读(该文早于 UserData 变更)。
旧版 MobileDevice 與 Xcode 16 UserData 路徑
Xcode 16 在通过 Xcode「账户」下载描述檔,或使用 xcodebuild -allowProvisioningUpdates 刷新 entitlement 时,会把磁碟上的权威位置视为 ~/Library/Developer/Xcode/UserData/Provisioning Profiles。旧路徑 ~/Library/MobileDevice/Provisioning Profiles 仍被大量 CI 配方與脚本使用。在审计完所有入口之前,运维上的安全缺省是:每次轮换后把同一份 .mobileprovision 以 plist 内嵌的 UUID 为文件名,字节级一致地复制到两处(可用 security cms -D -i 读取 UUID)。
| 路徑 | 典型消费者 | Xcode 16 行为 | 租用机 CI 提示 |
|---|---|---|---|
~/Library/Developer/Xcode/UserData/Provisioning Profiles |
Xcode IDE、新版 xcodebuild |
GUI 下载的权威读写位置 | 复制前先 xcode-select 固定版本 |
~/Library/MobileDevice/Provisioning Profiles |
旧脚本、部分 Fastlane | 许多 CLI 流程仍会读取 | 脚本退役前继续鏡像 |
車道暂存(如 /var/ci/lane-a/profiles) |
自定义安装器 | 不被直接读取,降低 home 竞态 | 每 job 再复制到两条 canonical 路徑 |
ASC API + -allowProvisioningUpdates |
自动簽名流水线 | 可能只写入 UserData | 见《Fastlane 與原生 ASC》 |
security find-identity -v -p codesigning 一并写入日志,做法见《鑰匙串基线》。
单台 Mac mini M4 上的多車道併發 Archive 矩阵
併發 Archive 不是「免费并行」:统一内存、NVMe 队列深度與簽名会话状态在负载下仍会相互牵制。下表假设每条車道有独立 -derivedDataPath、独立 CI 鑰匙串與双路徑鏡像的描述檔——细节见《併發 xcodebuild》與《Scheme + xcconfig 多分支》。
| 車道数 | 典型 NVMe 余量 | 统一内存 | 运维结论 |
|---|---|---|---|
| 1 条串行 Archive | 系统卷至少 120GB 可用 | 16GB 起步,24GB 更舒适 | 共享 Release 列车默认方案 |
| 2 条併發 Archive | 每車道在 /var/ci 预留 120–180GB |
建议 24GB+ | 标配 M4 的务实上限 |
| 3 条及以上 | 2TB 级 NVMe + 积极清理 | 32GB+,否则易压缩抖动 | 需有指标支撑;配合《磁碟清理》 |
| Archive + 重测试分片 | 根目录分离,勿共享 ModuleCache | 为 XCTest 留 4GB 余量 | 优先串行 Archive 或分机 |
手动簽名、显式描述檔说明符的車道级 archive 示例:
KEYCHAIN_PATH=/var/ci/lane-a/ci.keychain-db DERIVED=/var/ci/lane-a/dd xcodebuild -workspace App.xcworkspace -scheme Release -configuration Release -archivePath /var/ci/lane-a/out/App.xcarchive -derivedDataPath "$DERIVED" CODE_SIGN_STYLE=Manual PROVISIONING_PROFILE_SPECIFIER='MyApp AppStore' archive
無頭簽名排障:描述檔「已安装」仍失败
SSH 像交互式会话;launchd 與 CI runner 不是。升级到 Xcode 16 后最常见三类问题:只复制到 MobileDevice;证书进了登录鑰匙串但 xcodebuild 指向空的 CI 鑰匙串;車道 B 解锁了車道 A 仍占用的鑰匙串。请把每次 Archive 的三段指纹当作例行输出:security list-keychains、security find-identity -v -p codesigning,以及对两个描述檔目录按 mtime 列出的最新三个 UUID。
若 codesign 报 errSecInternalComponent,在 archive 前非交互解锁 CI 鑰匙串、配置 codesign 分区列表,并核对描述檔 entitlement 與目标能力是否一致(推送、App Group、Associated Domains 在门户修改后尤需复查)。自动簽名 + ASC API 可能把路徑问题推迟到导出阶段才暴露;手动車道会立刻显现。混用車道前请先读《自动與手动簽名》。
/var/ci 下创建 ci-lane-a.keychain-db 與 ci-lane-b.keychain-db,用 security import -k "$KEYCHAIN_PATH" -P "$P12_PASS" -T /usr/bin/codesign 导入发行 .p12,并在每个 job 生命周期内用 security list-keychains -s 限定搜索列表。
| 现象 | 高概率原因 | 下一步 |
|---|---|---|
| Xcode 报告缺少描述檔 | UserData 为空,仅写了旧路徑 | 鏡像 UUID 到 UserData 后重跑 |
errSecInternalComponent |
鑰匙串未解锁或分区列表错误 | security unlock-keychain + set-key-partition-list |
| 同提交車道 A 过、B 败 | 共享 DerivedData 或复制竞态 | 分离 -derivedDataPath,串行化复制 |
| 导出成功、上传被拒 | 描述檔类型或 entitlement 过期 | 对照《导出與 ASC 上传》清单 |
Archive 完成后请先上传 dSYM 再清理車道目录——《dSYM 符号化》假设 /var/ci/…/out 下归档仍可寻址。
八步上线:Xcode 16 路徑 + 多車道 Archive
- 用
xcode-select固定到目标 Xcode 16.app,并把xcodebuild -version写入黄金鏡像清单。 - 审计所有复制
.mobileprovision的脚本,改为以 UUID 文件名双写 UserData 與 MobileDevice。 - 为每条車道创建 CI 鑰匙串并导入发行证书,禁止互访对方鑰匙串文件。
- 在 CI 环境块导出
KEYCHAIN_PATH、PROVISIONING_PROFILE_SPECIFIER與車道级DERIVED_DATA_ROOT。 - 每条車道干跑
xcodebuild -showBuildSettings,确认CODE_SIGN_STYLE與簽名策略一致。 - 仅在 ASC API 凭证已密封监控时启用
-allowProvisioningUpdates。 - 先稳定单車道 Archive,再开第二条車道并分离 NVMe 根與日志。
- 按周执行磁碟清理;轮换描述檔保留重叠,并在車道暂存目录保留热切换副本。
非交互安装描述檔(将 $PROFILE 换为車道暂存路徑):
UUID=$(security cms -D -i "$PROFILE" | plutil -extract UUID raw -) && cp "$PROFILE" "$HOME/Library/Developer/Xcode/UserData/Provisioning Profiles/$UUID.mobileprovision" && cp "$PROFILE" "$HOME/Library/MobileDevice/Provisioning Profiles/$UUID.mobileprovision"
常見問題
Xcode 16 把描述檔存在哪里? 主路徑为 ~/Library/Developer/Xcode/UserData/Provisioning Profiles。旧工具可能仍读 ~/Library/MobileDevice/Provisioning Profiles——在 CI 主机上应双写鏡像直至全部安装器升级。
一台 Mac mini M4 能跑几条併發 Archive? 默认建议两条車道,各预留 120–180GB NVMe;第三条需 2TB 存储、积极清理與审慎内存规划。
SSH 上 codesign 报 errSecInternalComponent 常见原因? 多为鑰匙串未解锁、CI 鑰匙串分区列表错误或仅写入旧版路徑而 Xcode 16 优先解析 UserData。
車道能否共享 DerivedData 提速? Release Archive 不建议——并行时 ModuleCache 與索引存储会竞态。请使用 per-lane -derivedDataPath;依赖缓存仅在单一写入者解析包后再以只读方式共享。
请把 部落格列表 與 說明中心 加入值班书签,避免在聊天窗口里翻找这篇运维手册。
为何 Mac mini M4 租用适合多車道 Xcode 16 Archive
Apple Silicon M4 在诚实隔离 DerivedData、控制車道数量时,足以并行编译两个中等规模的 iOS 工程图。原生 macOS 代码簽名可避免模拟 Mac 环境的鑰匙串怪异问题——当三条夜间分支都要刷新发行描述檔时尤为关键。租用把 CapEx 变成可按发布周启停的费用:贴近 App Store Connect 可选東京,服务东盟流量可选新加坡,需要美西 API 端点则选美國节点。请在 定價頁 对比区域,在 說明中心 预演 SSH;新仓库接入無頭租机时请與《远程 Archive》一并阅读。