先按 4 层判断树排查,今天不要运行 match nuke fastlane 官方文档明确说明,match 负责同步证书、私钥和 Provisioning Profile,而 readonly 只读取现有签名资产,不会替 CI 创建或更新缺失内容。(match 官方文档)

本周建议动作是:先保留一份脱敏日志,用同一提交在远程 Mac 上重跑一次最小 Archive;依次检查签名仓库与解密、Keychain 中的完整签名身份、Provisioning Profile 映射,以及项目 entitlements 和 CI 策略。只有确认资产已经失效或无法恢复,才进入轮换流程。

这篇文章适合 3 类人:

  • 在远程 Mac 上运行 fastlane match,但 Archive 突然提示缺少签名身份的独立开发者;
  • 本地 Xcode 构建成功,而 CI 或无人值守任务失败的小团队;
  • 准备把手工证书管理迁移到集中式签名资产管理的 iOS / macOS 开发者。

先判断故障发生在哪一层

No signing certificate”并不等于证书已经失效。它可能表示签名仓库没有拉取成功、资产没有解密、私钥没有导入当前 Keychain,也可能只是项目的 CODE_SIGN_IDENTITY 指向了当前机器不存在的身份。

你可以先按下面的判断树缩小范围:

match 是否成功完成?
├─ 否:查仓库访问、分支、Team、存储凭据、MATCH_PASSWORD
└─ 是:Keychain 是否有带私钥的完整签名身份?
   ├─ 否:查导入目标、Keychain 解锁状态、构建用户权限
   └─ 是:Profile 是否匹配 Target?
      ├─ 否:查 Bundle ID、Team、类型、证书和 entitlements
      └─ 是:查 Scheme、Configuration、export options 与最终验证

先保存以下证据,再修改配置:

  1. fastlanexcodebuild 输出中的首个明确错误;
  2. 任务退出状态,例如 exit status 65,但不要把它当成根因;
  3. 当前活动 Xcode 路径与版本;
  4. Scheme、Configuration 和 Export Method;
  5. 目标 Target 的 Bundle ID、Team 与签名设置;
  6. 脱敏后的 match 配置、Profile 名称和 Keychain 路径。

fastlane 签名故障排查文档建议阅读完整的原始 xcodebuild 输出,而不是只看 fastlane 最后汇总的几行。Apple 的构建设置中,CODE_SIGN_IDENTITYCODE_SIGN_ENTITLEMENTSCODE_SIGN_STYLE 分别对应签名身份、entitlements 文件和签名获取方式。

⚠️ 日志中的仓库地址、分支、Bundle ID、Team ID、证书名称、Token、Keychain 路径和密码都要替换成占位符。不要直接上传包含 MATCH_PASSWORD、部署密钥或对象存储凭据的 CI 日志。

仓库访问失败,不代表 Apple 签名资产已经损坏

fastlane match 使用独立的签名资产存储。Git、对象存储和其它存储模式的认证链路不同,源码仓库可以正常拉取,并不代表签名仓库同样具备读取权限。

Git 模式下,证书与私钥会使用 OpenSSL 加密;CI 还需要单独提供 MATCH_PASSWORD。(match 官方文档)所以应把“仓库访问失败”和“资产解密失败”分开验证,不要把两类错误混成证书问题。

先验证签名存储凭据

命令中的值必须使用占位符:

git ls-remote "<SIGNING_REPO_URL>" --heads

如果使用对象存储,不要用源码仓库的部署密钥代替签名存储凭据。分别确认:

  • 当前 CI 任务是否注入了签名仓库读取权限;
  • Git 模式下是否读取了正确分支;
  • 对象存储模式下是否使用了正确 Team 目录或存储桶;
  • 任务运行用户是否能读取对应环境变量;
  • 凭据是否被 CI 的分支、环境或权限策略屏蔽。

再验证解密密码

MATCH_PASSWORD="<REDACTED>" bundle exec fastlane match appstore --readonly --verbose

如果出现解密失败,先确认密码是否来自当前项目的密钥管理系统,是否存在前后空格、换行、错误环境或旧分支覆盖新变量的问题。

多个团队共用签名存储时,Git 模式可以按团队使用不同分支;对象存储模式则通常按 Team ID 组织目录。(match 官方文档)

因此,同时维护主 App、扩展和服务组件时,应在配置中显式列出目标:

match(
  type: "appstore",
  app_identifier: [
    "<MAIN_BUNDLE_ID>",
    "<EXTENSION_BUNDLE_ID>",
    "<SERVICE_BUNDLE_ID>"
  ],
  git_branch: "<TEAM_BRANCH>",
  readonly: is_ci
)

这段配置只展示结构,不能直接替换你的项目。你需要先确认每个 Target 是否使用同一 Team、同一发布渠道以及兼容的 Profile 类型。

证书、私钥和签名身份必须分开检查

Apple 的签名身份不是单独一张证书。证书文件、公钥和对应私钥共同组成可用于代码签名的身份;只导入 .cer 文件,或者只在钥匙串中看到证书名称,都不能证明当前构建用户拥有完整的 code-signing identity。(Apple 签名分发说明)

先运行:

security find-identity -v -p codesigning
security default-keychain

你要确认的不是“有没有一条证书记录”,而是:

  • 输出中是否出现目标发布身份;
  • 身份是否处于有效状态;
  • 证书条目是否存在对应私钥;
  • 当前任务使用的用户是否与手工检查时的用户一致;
  • 默认 Keychain 是否指向预期的登录或专用构建 Keychain。

如果使用专用 Keychain,可在受控环境中显式指定:

security list-keychains -d user
security unlock-keychain -p "<KEYCHAIN_PASSWORD>" "<BUILD_KEYCHAIN_PATH>"

解锁密码只能从 CI 密钥系统注入,不能写进脚本仓库。也不要把“允许所有应用访问”作为默认修复方案。放宽权限可能让任务暂时通过,却扩大私钥暴露范围,后续还难以判断真正原因。

远程 Mac 重启后,最容易出现的隐性问题有 3 个:

  1. Keychain 文件仍在,但自动任务使用了不同的构建用户;
  2. Keychain 存在,但重启后处于锁定状态;
  3. 私钥已经导入,却没有被当前签名工具访问。

如果每次重启都必须人工打开桌面会话才能恢复构建,这就不是稳定的无人值守环境。你应把“重启后恢复 Keychain、执行 match、完成最小 Archive”作为一条完整验收链路。

Provisioning Profile 与 Target 映射要逐项对齐

Provisioning Profile 不是可以任意复用的授权文件。Apple 说明,Profile 会把签名者、允许的 App、运行环境、有效时间和 entitlements 绑定起来;App 的签名声明必须落在 Profile 允许的范围内。(Provisioning Profile 结构说明)

你可以先查看 Profile 内容:

security cms -D -i "<PROFILE_PATH>" -o "<PROFILE_PLIST_PATH>"
plutil -p "<PROFILE_PLIST_PATH>"
检查维度 当前目标 常见失败表现 处理结论
Bundle ID <EXPECTED_BUNDLE_ID> Profile 的 App ID 与 Target 不同 修正 Target 或生成匹配 Profile
Team <EXPECTED_TEAM_ID> 本地和远程使用不同团队 统一账号、分支与 Team 配置
Profile 类型 app-storeadhocdevelopment Archive 使用错误类型 对齐 Archive 与导出渠道
证书关联 <CERTIFICATE_NAME> Profile 绑定的证书不在 Keychain 导入正确私钥或受控轮换
entitlements <REQUIRED_ENTITLEMENTS> 能力声明超出 Profile 范围 检查 App ID 能力与 Profile
Target 映射 主 App、扩展、服务 只有主 App 有 Profile 为每个签名 Target 配置独立映射

Apple 的文档指出,iOS Profile 中的 application-identifier 通常由 Team 前缀和 Bundle ID 组成。这是排查多个 Bundle ID 映射错误时最有价值的字段之一。(Provisioning Profile 结构说明)

多 Target 项目尤其要检查:

  • 通知服务扩展是否有自己的 Bundle ID;
  • Widget 是否误用了主 App 的 Profile;
  • macOS 辅助目标是否采用了不同的签名方式;
  • Debug、Release 和 Archive 是否读取不同配置文件;
  • exportOptions.plist 中的 provisioningProfiles 映射是否完整。

macOS 项目还要注意,部分能力不需要 Profile 授权,但受限 entitlements 仍然必须匹配签名环境。不能因为主程序本地运行成功,就推断归档和分发链路也一定可用。

本地成功与远程失败,先对齐 5 个环境变量

本地 Xcode 能成功,不代表远程命令行使用了同一套签名链路。常见差异包括:

  • 本地使用 Xcode 自动签名,远程使用手动签名;
  • 本地登录用户已经解锁 Keychain,CI 用户没有;
  • 本地 Scheme 默认是 Debug,远程 Archive 使用 Release;
  • 本地自动选择了新 Profile,远程仍读取旧 Profile;
  • 本地调用了正确的 Xcode,远程 xcode-select 指向另一个版本。

fastlane 官方建议先执行 match,再执行构建动作;CI 通常使用 readonly,避免构建任务擅自创建或修改签名资产。(fastlane CI 配置说明)

一个更容易复现的最小流程是:

xcode-select -p
xcodebuild -version

bundle exec fastlane match appstore --readonly --verbose

bundle exec fastlane gym \
  --scheme "<SCHEME_NAME>" \
  --configuration "Release" \
  --export_method "app-store" \
  --export_options "<EXPORT_OPTIONS_PLIST>"

用同一提交完成 5 步复现

  1. 固定同一个 Git 提交,不要边排查边合并代码;
  2. 固定同一个 Scheme 与 Configuration;
  3. 在本地和远程使用相同的 Archive 命令;
  4. 保存 security find-identity 和 Profile 解码结果;
  5. 先完成 Archive 与导出,再测试上传。

如果 CI 使用:

match(type: "appstore", readonly: is_ci)

那么 readonly 找不到 Profile 时,代表当前环境没有可读取的现有资产,不代表 fastlane 应自动创建一个。创建或更新操作应由有 Apple Developer 权限的维护者在受控窗口执行,然后把新的加密资产同步到签名存储。

修复、轮换和重建是 3 种不同动作

原地修复:认证或映射错误

适用于:

  • Git 分支错误;
  • MATCH_PASSWORD 不匹配;
  • CI 没有签名仓库读取权限;
  • Profile 已同步但 Target 映射错误;
  • Keychain 路径或构建用户不正确;
  • 远程使用了错误的 Xcode 或 Scheme。

这类问题不应撤销任何证书。修复后重新执行最小 Archive,确认故障确实消失。

受控轮换:证书或 Profile 已失效

适用于:

  • 证书已经过期;
  • 证书被撤销;
  • Profile 因证书或能力变化而失效;
  • App ID 的能力配置发生变化;
  • 团队权限变化导致原有资产无法继续使用。

Apple 的 Profile 管理说明建议,对失效或过期 Profile 重新生成,并使用新 Profile 重新签名。若证书被撤销,关联 Profile 也可能受到影响。

轮换前至少记录:

  • 每个 Bundle ID 对应的 Profile;
  • 每个 Profile 绑定的证书;
  • 主 App 与扩展组件的关系;
  • 当前 App Store、TestFlight、Ad Hoc 发布链路;
  • 旧资产是否仍被其它分支或团队使用。

重建签名资产:不可恢复且影响范围已确认

match nuke 会撤销指定环境的证书和 Profile。fastlane 文档提醒,已经在 App Store 或 TestFlight 中的版本通常仍可运行,但 Ad Hoc 或 Enterprise 分发可能被禁用,并需要重新上传构建。(match nuke 官方文档)

因此,执行前不要只备份代码,还要备份签名关系和发布记录:

# 仅示意。执行前确认资产清单、备份和影响范围。
bundle exec fastlane match nuke development

上面的命令不适合处理一次错误分支、错误 Keychain 或错误 Profile 映射。只有在资产不可恢复、账号状态混乱且已确认影响范围时,才把它列入恢复计划。

FAQ:远程 Mac 上的 5 个签名故障判断

fastlane match 找不到签名证书时,应该先检查什么?

先确认 match 仓库能够访问并成功解密,再用 security find-identity 检查目标 Keychain 中是否存在带私钥的完整签名身份。不要只看到 No signing certificate 就撤销证书,因为问题也可能来自错误分支、MATCH_PASSWORD、Keychain 未解锁或构建用户没有访问权限。

match readonly 找不到 Provisioning Profile,怎样判断是配置问题?

readonly 只读取现有资产,不会替你创建或更新缺失的 Profile。先核对 app_identifier、Team、Profile 类型、证书关联和目标 Xcode 配置,再检查 Profile 是否确实同步到当前机器;确认资产缺失后,应由有权限的维护者更新签名仓库。

远程 Mac 重启后 Keychain 无法解锁,怎样处理更稳妥?

先确认构建任务使用的是哪个用户、默认 Keychain 和登录会话,再检查 Keychain 是否仍存在、是否已解锁,以及签名工具是否拥有访问权限。优先使用专用构建 Keychain、明确指定路径,并在受控任务中解锁,不建议直接放宽全部 Keychain 权限。

多个 Bundle ID 应该怎样配置 fastlane match?

把主 App、扩展、通知服务或 macOS 辅助目标视为独立签名对象,明确列出每个 Bundle ID,并让 match 与 export options 使用同一套映射。多个团队还要使用独立分支或对应 Team 目录隔离资产,不能只凭主 App 的 Profile 推断其它 Target 可用。

证书过期后,怎样判断是否需要重建签名资产?

先判断是过期、撤销、权限变化,还是机器没有正确导入私钥。单纯失效通常进入受控轮换;只有资产不可恢复、账号状态混乱且已记录发布链路影响时,才考虑重建。执行前必须备份证书、Profile、Bundle ID 和分发关系。

用一次非紧急发布完成恢复验收

排障结束后,不要只看 match 命令返回成功。你需要安排一次不会影响正式用户的验收任务,按下面的里程碑执行。

第 1 个里程碑:环境确认

  • 固定 Git 提交;
  • 确认活动 Xcode;
  • 确认构建用户;
  • 确认 Keychain 路径与解锁状态;
  • 确认 CI 变量均来自密钥存储。

第 2 个里程碑:签名资产同步

bundle exec fastlane match appstore --readonly --verbose
security find-identity -v -p codesigning

保存脱敏后的身份列表、Profile 名称和退出状态。

第 3 个里程碑:Archive 与导出

使用与正式发布相同的 Scheme、Configuration 和导出方式完成 Archive。然后检查导出包中的签名和 Profile,不要仅凭 Xcode 界面显示“成功”判断。

第 4 个里程碑:重启恢复

重启远程 Mac,重新触发同一构建任务。若重启后仍能完成 match --readonly、Archive 和导出,才说明 Keychain 持久化与无人值守链路基本可靠。

你可以把这次结果整理成团队内部的 iOS 开发证书迁移验收清单;如果正在规划远程构建节点,也应把重启恢复测试加入交付验收,而不是等正式发布当天才验证。

什么时候常驻远程 Mac 比临时机器更合适

如果问题来自临时机器重置、Keychain 无法持久保存,或者构建任务经常被中断,当前方案的缺点通常不在 fastlane 本身,而在运行环境:

  • 临时机器的用户、Keychain 和 Xcode 状态经常变化;
  • 每次重建环境都要重新导入私钥与 Profile;
  • CI 失败后难以保留完整的排障现场;
  • 本地 Mac 还要承担日常开发,无法稳定充当常驻打包机。

这时,使用 VMSPIN 的远程 Mac 作为固定构建节点,更适合做签名链路复现和持续 Archive。你仍然需要自己管理 Apple 账号、签名仓库和密钥权限,但可以把“机器被重置、环境丢失、重启后无法恢复”从反复排障事项,变成一次明确的交付验收。

如果只是偶尔发布一次、需要物理 iPhone 调试,或者长期运行高强度本地任务,购买并维护自己的 Mac 可能更合适。若你需要临时测试环境、持续集成节点,或希望先验证远程 Mac 能否在重启后完成签名与 Archive,可以查看 VMSPIN 的 远程 Mac 租赁方案,再用一个非紧急发布分支完成完整验收。