2026 年公证链路五类失败:该改脚本、改磁盘、改队列还是改选区
Apple 的公证接口对自动化友好,但失败信息常被 CI 日志截断成一句英文,团队会习惯性重跑。实际上公证失败几乎总能归入五类:凭证与 Team ID 绑定错误、制品签章与 entitlements 不一致、上传阶段超时或 TLS 握手抖动、wait 阶段被动排队、以及 staple 阶段读错路径或 ticket 未落盘。云租独占 Mac Mini M4 的价值在于把「同一套命令、同一套网络出口、同一套磁盘档」固定下来,让你能对比两次失败是否同源;这与在笔记本上偶发成功、在 CI 上偶发失败完全不同。
当团队把公证与夜间 archive 绑在同一台机器时,还要叠加 Xcode 编译缓存、模拟器残留与并行 lane 的 IO 峰值:notarytool 本身不占满 CPU,但会写放大临时文件;若磁盘只剩个位数 GB,会出现「submit 成功、wait 间歇失败」的假随机。跨区场景下,若制品构建在新加坡而公证机在美西,上传阶段测到的是跨洋 RTT 与窗口大小,而不是苹果服务本身。把五类失败写成工单枚举,财务与平台才能讨论「要不要从 512GB 上到 1TB」「要不要把公证队列迁到与 Git 同洲的节点」,而不是无限加人盯屏。
把 wait 超时一律当成苹果故障:应先对照本节点 df -h 与临时目录挂载点,再对照同制品在本地的 wait 时长分布。
staple 前不校验 ticket 路径:应在流水线里显式打印 submission id 与输出 json 落盘路径,避免并行 job 互相覆盖。
并行 submit 不设并发上限:会把上行与钥匙串解锁窗口打满,表现为偶发 TLS reset,复盘时极难对齐。
忽略 entitlements 漂移:同一 target 在 Debug 与 Release profile 下签名内容不同,公证通过但 Gatekeeper 用户侧仍可能拦截,应把 profile 写进变更单。
跨区换节点却不更新制品拉取锚点:公证快了但 archive 慢了,整体发版墙钟不变,容易被误判为「公证无效」。
当你把以上五条写进 nightly 复盘模板,和 性能回归与 Core ML 里强调的「并行语义合同」是同一套治理语言:先固定观测对象,再谈换区或换档。KVMNODE 一类独占节点供应商的意义,是把区域与配置梯度变成可下单字段,而不是口头约定。
对照表:上传耗时与选区亲和,以及大体积制品与磁盘档、M4 Pro 并行队列
没有一张表能替代你们自己的样本,但可以用「制品体积 × 出口洲 × 并行 submit 数」三因子做初判。2026 年常见做法是:公证队列与 heavy archive 队列在 orchestrator 语义上互斥,即使暂时落在同一云账户,也要在标签上拆开,否则你会看到「昨晚 Green、今晚同一 commit Red」的幽灵故障。下表给出适合贴进内部 Wiki 的口径,可与 存储与内存选配 中的 NVMe 档描述对齐。
| 制品与队列组合 | 同洲构建+同洲公证 | 跨洲上传 | 备注 |
|---|---|---|---|
| 单个 .pkg 小于 300MB | 通常稳定,可并行两条 wait | 可接受,建议串行 submit | 重点看 TLS 与 DNS 出口一致性 |
| 单个 .dmg 大于 2GB | 推荐独占 NVMe 与充足临时目录 | 高风险,优先迁构建或迁公证机 | 与 1TB/2TB 档强相关 |
| 夜间并行多应用公证 | 需要队列令牌与独立 keychain | 不推荐与 archive 同机抢 IO | M4 Pro 更适合多 lane |
| 磁盘与机型档 | 典型适用 | 风险信号 |
|---|---|---|
| M4 16GB/256GB | 单应用、偶发公证 | 临时目录满、wait 长尾 |
| M4 24GB/512GB | 多数团队默认公证池 | 并行 staple 与大 archive 同开 |
| M4 Pro 64GB/2TB | 多 lane、大体积分发并行 | 仍需限制 submit 并发防出口打满 |
公证稳定性的第一性原理:先固定制品锚点与出口洲,再谈压缩命令或重试次数。
若你已经在 Xcode Cloud 与独占池混搭 里拆过队列,可把「公证」视为第四条独立管道:Cloud 管提交频率,独占池管可复现签名环境,公证池管对外分发门槛。KVMNODE 节点负责把「选哪区、哪档盘、是否并联资源」写成采购表字段,而不是散在 README 里。
补充一个常被忽略的财务视角:公证失败导致的不是「多几次重试」而是发版墙钟与值班人力。把同一制品在两周内的失败按五类打标签,你会看到大量重复劳动其实来自临时目录与并行策略,而不是苹果侧。把这张标签表附在月度复盘,采购会更容易理解为何要把公证池从 256GB 抬到 1TB,或为何要把机区从美西迁到与制品同洲的 APAC。
从 submit 到 staple:可粘贴的幂等骨架与六区自检清单
工程化目标是把三次关键状态落盘:submission id、notarization info 路径、以及 staple 成功后的校验哈希。云节点上建议固定 NOTARY_TMP 到大磁盘分区,避免默认临时目录落在系统卷。新加坡、日本、韩国、香港与美东美西之间没有唯一正确答案,但变更单里至少写明:源码与制品默认区、公证机所在区、以及对象存储上传区;否则复核时无法回答「变慢是不是因为换了 Runner 标签」。独占云 Mac 让你可以把三条锚点钉在同一供应商语义内,减少不可审计变量。
xcrun notarytool submit "$PKG" --keychain-profile "$PROFILE" --wait --output-format json > notary.json
SUB=$(/usr/bin/python3 -c 'import json;print(json.load(open("notary.json"))["id"])')
xcrun stapler staple "$PKG"
xcrun stapler validate "$PKG"
spctl -a -vv -t install "$PKG"
提示:把 notary.json 与 spctl 输出作为构建工件上传;复盘时先对照 submission id 是否被并行 job 覆盖,再打开代码 diff。
若团队同时在跑 TestFlight 流水线,请避免让 IPA 上传高峰与大体积分发公证抢同一出口;此类耦合故障常被误报为「苹果 notary 抖动」。更稳妥是把「ASC 机区」和「分发公证机区」写在预算表两行,即使暂时映射到同一云账户,也要在标签层拆开。
实务上还可以在流水线里加一道轻量预检:对即将提交的包先做本地 codesign --verify --deep --strict 与 spctl 的 dry-run 组合,把「签名结构错误」拦截在 submit 之前;这类错误的重试不会改善结果,只会消耗团队注意力与配额感知。云节点上预检脚本应与正式公证同一套环境变量,避免「预检过、正式挂」的二次扯皮。
六步:把公证流水线写成可验收的采购与运维字段
冻结 keychain profile:在 CI 专用钥匙串创建 notarytool profile,写明 Team ID 与 App Store Connect API 角色,禁止与人机调试共用默认钥匙串。
固定临时目录与清理策略:每次 job 结束删除中间 dmg 切片与重复 log,避免磁盘水位触发假随机失败。
为 wait 设置分层超时:短超时用于探测性提交,长超时用于大体积分发;两层阈值写进 Grafana 面板。
双区对照一周:在候选 KVMNODE 区域各跑同制品公证,记录 submit、wait、staple 三段墙钟。
写入档位合同:在订购字段对齐 订购入口 中的区域与磁盘描述,并注明是否允许并行 lane。
评估并联资源:若公证池仍需与 heavy 构建隔离,引用 并联资源决策 写第二节点预算。
可引用口径:宽限期、重试策略与日志留存字段
宽限期:Gatekeeper 用户侧体验与 staple 完成时间强相关;流水线应把 staple 失败视为阻断级,而不是「下次发版再修」。
重试:对 TLS reset 类错误采用指数退避且上限三次;对签名内容错误禁止自动重试以免刷爆配额。
留存:至少保留 submission id、notarytool 输出 json、以及 stapler 校验输出各一份,满足事后审计与跨团队对齐。
注意:嵌套虚拟化或非 Apple 原生调度下的 macOS 实例对 codesign 与公证行为边界与裸金属不同,不适合作为唯一真相源。
短期借用个人笔记本或与他人分时共用未隔离钥匙串,看似省钱,却把「凭证生命周期、临时目录与出口路由」埋在个人习惯里;一旦要与财务对齐发版 SLA,就很难证明瓶颈来自制品还是环境。相反,把独占 Apple Silicon 节点与公证脚本合同化,可以让对外分发从玄学变成工程问题。对于要在多地区组合节点、并在 256GB 到 2TB 与 M4 Pro 之间做清晰分叉、还需要把租期与队列命名写进预算表字段的团队,KVMNODE 的 Mac Mini 云端租赁通常是更优解:硬件独占、区域可选、档位完整。更多网络与下单说明见 帮助中心 与 定价页。