在我们进行团队开发的时候,避免不了证书和描述文件的管理,常规的有自动管理和手动管理两种方式。
1. 常规的管理方式
1.1 自动管理签名
需要在Xcode的 Targets->Signing & Capabilities
勾选 Automatically manage signing。用这种方式,所有的工作包括AppId、证书、描述文件(Provisioning Profile)的创建都由Xcode包办了,非常的方便。
这种方式对个人开发者非常友好,但是对团队开发来说有比较大的弊端,具体如下。
1.1.1 每个开发成员都有单独的证书和描述文件,会导致存在大量重复的文件,管理起来非常混乱
如图所示:
1.1.2 证书的创建是有上限的,可能会导致其他人无法创建证书。
1.1.3 每次添加新设备或证书过期时,都必须手动更新和下载最新的配置文件集
比如说,添加新设备后,如果描述文件配置里面没有勾选这个设备,这个设备是无法安装我们的应用的,所有每次都需要确保描述文件的Select All是否已勾选。
1.2 手动管理签名
首先,需要某个团队成员先在 Apple Developer 后台 分别创建开发环境和生产环境的证书和配置文件,然后将这些文件下载安装到本地。当其他人参与开发时,需要这个人将相关的文件导出给其他人。
然后需要在 Xcode上 取消勾选 Automatically manage signing
,同时设置对应的证书和描述文件(Provisioning Profile)。
这种方式的优点是所有开发人员都共用一份证书和描述文件,缺点也非常明显:每次证书过期或者添加新的设备后,都需要手动去更新,然后重新分发其他人,操作起来非常麻烦。
那么,有没有这么一个方案:在一个公共的地方存取这些证书和描述文件,自动化去处理更新和安装流程呢?这就是我们今天要讲的 match 工具。
2. 使用 fastlane match 自动化管理证书和配置文件
match 是 fastlane 工具套装其中的一个工具,它是 codesigning.guide 概念的实现。 它提供了一种全新的管理证书的方式,使团队所有成员共享一份代码签名,以减免不必要的证书创建、配置文件失效等问题。
2.1 使用 match 有什么优势?
- 所有的团队成员共享同一份证书和配置文件,减少了管理和维护成本
- 简化请求证书,生成描述文件,注册设备等一系列繁杂工作
- 能自动识别已过期的证书和失效的描述文件,对这些文件进行重置
- 对新开发者极其友好,match使用git管理所有的证书和描述文件,所以只要新人拥有git的访问权限,安装了fastlane,就能快速同步现有的证书配置,远离证书配置的大坑
2.2 如何使用 match ?
下面我分六个步骤详细介绍 match 的使用和配置流程。
第一步:准备一个私有的 git仓库
创建一个私有git仓库来存储证书和描述文件。建议在git账号中配置好SSH Key,这样就可以省去身份校验这一步。至于如何配置SSH Key,请参考:使用 SSH 连接到 GitHub。
另外,当有多个App时,建议一个git分支对应一个App(这里有问题,请参考本文 注意事项 那一节,这里我们先以 master 分支作为示例),这样,我们所有App的证书都在一个仓库里面,便于管理。
第二步:初始化 match 配置
在终端定位到项目目录,注意先看项目目录下是否存在fastlane目录,如果没有的话,先执行 fastlane init
命令来初始化fastlane服务。然后再执行 fastlane match init
命令,首先会提示让你选择存储方式,我们选git,然后再输入git仓库地址,最后会生成一个 Matchfile 的配置文件。接下来,我们修改一下 Matchfile。
git_url("git@github.com:YourUserName/certificates.git") # git仓库地址
storage_mode("git") # 存储方式
git_branch("master") # git分支名称
# 默认的Profile类型, 可以为: appstore, adhoc, enterprise or development
type("development")
# bundleId,可以填多个bundleId,如App内包含 extension
app_identifier(["tools.fastlane.app", "tools.fastlane.app2"])
ENV["MATCH_PASSWORD"] = "abc123" # 导出和打开 .p12文件的密码
# username("user@fastlane.tools") # Your Apple Developer Portal username
# For all available options run `fastlane match --help`
# Remove the # in the beginning of the line to enable the other options
# The docs are available on https://docs.fastlane.tools/actions/match
修改一下 git_url
、git_branch
、app_identifier
这几项内容,确保配置正确。其中 app_identifier
为你的App的BundleId,某些App可能有Extension,那这里就需要填多个,确保在开发者后台已创建你App的BundleId。
配置完 Matchfile
后,大部分教程都是让你通过以下三条命令来同步证书和配置文件。
fastlane match development
fastlane match adhoc
fastlane match appstore
但是这种方式使用起来非常不方便,特别是用于自动化构建脚本,因为每次都要输入AppleId和密码;有的团队的做法是在 Matchfile
文件配置一个公用的AppleId,这样就不用每次输入账户和密码。
git_url("git@github.com:YourUserName/certificates.git") # git仓库地址
# 省略其他内容...
username("APPLE_ID") # 公用的AppleId
ENV["FASTLANE_PASSWORD"] = "AppleId密码"
但是,还会遇到另外一个问题,需要进行双重验证,要求输入6位验证码,每次都要输入验证码,这样体验是非常不好的,而且没法用于CI/CD,所以我个人更推荐用 App Store Connect API 的方式,下面我将对这种方式做详细介绍。
第三步:创建 App Store Connect API 秘钥
App Store Connect API 是官方提供的一套 REST API,可让您在 App Store Connect 中执行的操作自动化。它主要提供了以下功能(包含了证书和描述文件的管理):
用有管理员权限的AppleID登录 AppStoreConnect 后台,选择 用户和访问->秘钥,点击添加按钮来生成API秘钥。
然后下载API秘钥(一个.p8文件),保存到项目的fastlane目录。注意:私钥只能下载一次,永远不会过期,保管好,如果丢失了,去App Store Connect后台撤销密钥,否则别人拿到也可以用。
第四步:创建自动化脚本
为了更加方便使用,我们通过 fastlane 来配置几个常用的命令,将以下内容添加到你的 fastlane目录下的 Fastfile 文件中:
# 定义一个全局变量api_key,下面都会要用到这个 api_key
# key_id 和 issuer_id 都可以在 AppStoreConnect后台 -> 用户和访问 -> 秘钥 这里找到
api_key = app_store_connect_api_key(
key_id: "D383SF739",
issuer_id: "6053b7fe-68a8-4acb-89be-165aa6465141",
key_filepath: "./AuthKey_D383SF739.p8", # 上面下载的p8文件路径
duration: 1200, # optional (maximum 1200)
in_house: false # optional but may be required if using match/sigh
)
desc "下载所有需要的证书和描述文件到本地,不会重新创建证书和描述文件(只读方式)"
lane :match_all do
match(api_key: api_key, type: "development", readonly: true)
match(api_key: api_key, type: "adhoc", readonly: true)
match(api_key: api_key, type: "appstore", readonly: true)
end
desc "同步证书,如果证书过期或新增了设备,会重新创建证书和描述文件"
desc "该方法仅限管理员使用,其他开发成员只需要使用 match_all 方法即可"
lane :force_match do
match(api_key: api_key, type: "development", force_for_new_devices: true)
match(api_key: api_key, type: "adhoc", force_for_new_devices: true)
match(api_key: api_key, type: "appstore")
end
desc "注册设备,并更新描述文件"
lane :sync_devices do
# devices.txt模板:
# http://devimages.apple.com/downloads/devices/Multiple-Upload-Samples.zip
register_devices(api_key: api_key, devices_file: "./devices.txt")
match(api_key: api_key, type: "development", force_for_new_devices: true)
match(api_key: api_key, type: "adhoc", force_for_new_devices: true)
end
# 构建测试包
lane :beta do
# 先同步adhoc证书和描述文件
match(api_key: api_key, type: "adhoc", readonly: true)
# 省略其他步骤...
build_app(scheme: "MyApp",
workspace: "Example.xcworkspace",
include_bitcode: true)
end
lane :release do
# 先同步appstore证书和描述文件
match(api_key: api_key, type: "appstore", readonly: true)
# 省略其他步骤...
build_app(scheme: "MyApp")
# 上传应用到AppStore
upload_to_app_store(
api_key: api_key,
force: true, # Skip HTMl report verification
skip_screenshots: true,
skip_metadata: true,
submit_for_review: false,
)
end
通过上面这个模板我定义了以下几个常用的命令:
fastlane match_all
:下载所有需要的证书和描述文件到本地,注意并不会重新创建证书和描述文件(只读方式)fastlane force_match
:强制同步证书和描述文件,如果证书过期或新增了设备,会重新创建证书和描述文件sync_devices
:注册设备,会同步更新描述文件,需要先在 devices.txt 文件录入新增的设备UDID。fastlane beta
:构建测试包,先通过 match 确保adhoc证书和描述文件都是最新且有效的fastlane release
:构建且上传到AppStore,先通过 match 确保 appstore证书和描述文件都是最新且有效的。
第五步:使用 fastlane force_match
命令一键更新
到这步,我们所有的准备工作都已经做完了,这时使用 fastlane force_match
命令来一键完成剩余的工作。它的主要工作流程如下, 首先为我们在开发者后台自动创建开发证书和描述文件,同时将这些证书和描述文件自动存储到我们设置的git仓库中,最后会将这些文件自动下载和安装到本地(可在钥匙串中查看证书是否已安装)。如下图,命令运行成功后,会自动git仓库创建certs和profiles两个目录,里面分别存储对应的证书和描述文件。
第六步:在Xcode上正确配置证书和描述文件
在Xcode的 Targets->Signing & Capabilities
取消勾选 Automatically manage signing,改成用手动签名的方式。在 Provisioning Profile 那里选择 match 为我们创建的描述文件,分为 Development、AdHoc、AppStore三种类型,需按类型正确配置,否则编译和打包会有问题。下图为我们App的配置情况,大家可以参考一下:
最后,在 Build Settings
搜索 Code Sign 确保签名证书配置是正确的,Debug模式选择用 Apple Development 证书,其他模式都选择 Apple Distrubution 证书。如下图所示:
看到这里,恭喜你,完成了整个流程,以后就可以一劳永逸了。
- 对于团队其他开发人员或者新入职人员,我们仅仅只需要一条
fastlane match_all
命令就自动配置好开发证书和描述文件了,节省了大量的时间。 - 对于开发证书过期和描述文件Invalid情况,我们可以使用
fastlane force_match
来一键重新生成更新配置。 - 对于需要添加新的测试设备,我们可以通过
fastlane sync_devices
来自动注册设备UDID,并同步到Profiles文件中
注意事项 -- 2023/07/04 新增
在实际使用过程中,我们发现一个问题,同一个开发者账号下,如果有多个App,每个App Matchfile 文件按如下方式配置:
# Matchfile 文件
# 省略其他内容...
git_branch("appName") # 分支名用App名称
我们以App名称作为分支名,每次执行 fastlane force_match
,每个App都会尝试去创建新的开发证书和分发证书。又因在同一个开发者账号下,所使用的API Key是一样,所以生成的证书名称都是一样的,这样会导致本地安装多份同名的证书,而描述文件和证书名称又是一一匹配的,Xcode在匹配的过程中无法识别具体要匹配哪一个(据观察,默认是去匹配最新创建的证书),这样就导致部分App匹配失败。
既然是多份开发证书的问题,那有没有办法多个App复用同一份开发证书呢,答案当然是有的,解决方案也很简单,就是同一个开发者账号下的App都用相同的分支名称:
# Matchfile文件
git_url("https://github.com/example/example-repo.git")
git_branch("teamName") # git分支名称,同一个开发者账号下用相同的分支名称,如Team Name
详情请参考 fastlane match 官方文档:
3. 其他Tips
3.1 如何快速注册新的设备
常规的方式,是要登录到Apple Developer后台其注册设备,然后手动更新描述文件。
这里我介绍一下我们在使用的fastlane命令的方式。
首先,要获取设备的UDID,获取的方法在 3.2 如何获取设备的UDID 有做介绍。
然后,编辑 devices.txt
文件,按格式来填写,内容之间用 Tab 来分隔开(注意不是空格):
- Device ID:为设备的UDID;
- Device Name:设备名称可以随便填,这里我用的是 时间-姓名 为格式,如
2023/01/11-Bob
; - Device Platform:平台名称,分ios和mac,因是ios设备,这里填ios
最后,在终端运行 fastlane sync_devices
来自动化注册设备和更新、同步描述文件。
3.2 如何获取设备的UDID
1. 通过Xcode来查看
先通过USB在电脑上连接iOS设备,然后在Xcode中打开菜单:Window -> Device and Simulators,上面显示的 Identifier 这一项就是我们所需要的设备UDID。
2. 通过第三方工具
如果当前无法使用Xcode来查看,如其他地区的同事,可以使用第三方工具。大部分提供应用分发的平台都支持获取UDID,如 Fir获取UDID、蒲公英-快速获取iOS设备的UDID。
3.3 如何判断某个测试包是否能在设备上运行
原理:描述文件里面包含了所有的支持安装的设备的UDID,所以我们只需要看描述文件里面是否包含该设备的UDID就行了。在我们构建IPA包时,里面会嵌入一个叫 embedded.mobileprovision 文件(其实就是描述文件),判断我们的设备UDID是否在包含这个文件中,就能判断是否能安装(当然这只是其中的一个条件,其他的没在本文范围内,不做过多介绍)。
但是,这个文件是无法直接打开查看的,因为它经过了特殊的编码,其实质是一个plist文件,我们可以通过以下方式来查看它:
1. 通过 security 命名解码查看
security cms -D -i embedded.mobileprovision > result.plist
open result.plist
2. 使用预览插件 ProvisionQL 查看
可以通过 brew 来安装 ProvisionQL,安装命令为: brew install --cask provisionql
。 在文件扩展名为 .ipa
、 .xcarchive
或 .mobileprovision
上可通过空格键来快速预览。
3.4 如何撤销所有的证书和描述文件
很少会有这种需求,如果确实需要清空所有证书和描述文件的话,可以通过 fastlane match_nuke
工具来处理:
desc "清空所有的证书和描述文件,慎用"
lane :nuke_all do
match_nuke(api_key: api_key, type: "development")
match_nuke(api_key: api_key, type: "adhoc")
match_nuke(api_key: api_key, type: "appstore")
end
注意:清空完所有的证书和描述文件后,已安装的测试包是无法使用的,谨慎使用。