使用fastlane的match工具可以非常方便地管理iOS项目的证书和provisioning profile。
match可以全自动地创建证书。但有些情况下,需要使用已有的证书来生成provisioning profile。

  • 比如项目组规定不得新发行distribution证书;
  • 比如不想增加development证书的数量,同时又不想让已分发的adhoc应用失效这种很微妙的特殊情况。
  • 等等

~(没错,说的都是我现在所在的这个麻烦的项目组…)~

网上搜到了一些手动将证书及p12文件导入match证书库的文章,但实际操作下来会报错。
一顿google后,终于在fastlane文档的角落里发现了官方支持的方案,将过程分享出来。

官方文档

官方文档在此,英文好的大佬们直接看文档就好了。
https://docs.fastlane.tools/advanced/other/#manually-manage-the-fastlane-match-repo

实践分享

0. 用半途而废的个人项目做实验

  • 使用rbenv限定local的ruby版本
  • 使用bundler来管理gems (fastlane, cocoapods)

项目文件夹

1. 获取cert id

首先我们需要对象证书的cert id,可以利用spaceship来获取。
将下述代码保存为{任意文件名}.rb文件,在Spaceship.login()处改为自己的开发者账号。

require 'spaceship'

Spaceship.login('{开发者账号your_developer_account@address.com}')
Spaceship.select_team

Spaceship.certificate.all.each do |cert| 
  cert_type = Spaceship::Portal::Certificate::CERTIFICATE_TYPE_IDS[cert.type_display_id].to_s.split("::")[-1]
  puts "Cert id: #{cert.id}, name: #{cert.name}, expires: #{cert.expires}, type: #{cert_type}"
end

打开终端执行ruby {任意文件名}.rb,然后比较证书的失效时期,找到我们需要使用的证书的cert id。

获取cert id

2. 导出.cer和.p12文件

从keychain中找到对象证书,导出.cer及.p12文件。
注意.p12文件导出时不要设置密码。match不支持导入设置过密码的.p12文件。
导出.cer及.p12文件
文件名设为步骤1中获取的cert id。

3. 下载并解密证书库

终端cd到项目目录下输入bundle console进入bundle控制台

$ bundle console
irb(main):001:0>

按下述所示,设置证书库url,分支branch,以及match密码。
match密码在match下载证书时需要用到,match会将其保存在keychain中。
在keychain中搜索match会发现match_{证书库url}的密码项,需要时发给项目组其他成员。
一个证书库对应一个match密码

irb(main):001:0> require 'match'
irb(main):002:0> git_url = 'https://github.com/fastlane/example-certificate-repo'
=> "https://github.com/fastlane/example-certificate-repo"
irb(main):003:0> shallow_clone = false
=> false
irb(main):004:0> ENV["MATCH_PASSWORD"] = 'example-password'
=> "example-password"
irb(main):005:0> branch = 'master'
=> "master"

然后依次执行以下操作,下载并解密证书库。

irb(main):006:0> storage = Match::Storage.for_mode("git", { git_url: git_url, shallow_clone: shallow_clone, git_branch: branch, clone_branch_directly: false})
irb(main):007:0> storage.download
irb(main):008:0> encryption = Match::Encryption.for_storage_mode("git", { git_url: git_url, working_directory: storage.working_directory})
irb(main):009:0> encryption.decrypt_files
[14:24:42]: 🔓  Successfully decrypted certificates repo
irb(main):010:0> storage.working_directory
=> "/var/folders/ql/4rgq9x7j51n_971xb332w9lc0000gn/T/d20181105-65220-1oalh6v"

末行的目录即是下载后解密得到的证书库本地目录

#关于证书库和分支

fastlane推荐的实践,是将独自建立一个私有库作为证书库,每个team设一个分支存放该team的证书及provisioning profile。
实际操作可以根据项目的需求来设定。

本人的实际操作如下

 ~/dev/RegiQ   master ●  bundle console
[DEPRECATED] bundle console will be replaced by `bin/console` generated by `bundle gem <name>`
irb(main):001:0> require 'match'
=> true
irb(main):002:0> git_url = 'git@github.com:itsuhi-shu/RegiQ.git'
=> "git@github.com:itsuhi-shu/RegiQ.git"
irb(main):003:0> shallow_clone = false
=> false
irb(main):004:0> ENV["MATCH_PASSWORD"] = '**********************'
=> "**********************"
irb(main):005:0> branch = 'certs'
=> "certs"
irb(main):006:0> storage = Match::Storage.for_mode("git", { git_url: git_url, shallow_clone: shallow_clone, git_branch: branch, clone_branch_directly: false})
=> #<Match::Storage::GitStorage:0x00007f8d4f689758 @git_url="git@github.com:itsuhi-shu/RegiQ.git", @shallow_clone=false, @skip_docs=nil, @branch="certs", @git_full_name=nil, @git_user_email=nil, @clone_branch_directly=false, @git_basic_authorization=nil, @git_bearer_authorization=nil, @type="", @platform="">
irb(main):007:0> storage.download
[16:47:27]: Cloning remote git repo...
[16:47:27]: If cloning the repo takes too long, you can use the `clone_branch_directly` option in match.
[16:47:30]: Checking out branch certs...
=> ["git checkout --orphan certs", "git reset --hard"]
irb(main):008:0> encryption = Match::Encryption.for_storage_mode("git", { git_url: git_url, working_directory: storage.working_directory})
=> #<Match::Encryption::OpenSSL:0x00007f8d4f6c54d8 @keychain_name="git@github.com:itsuhi-shu/RegiQ.git", @working_directory="/var/folders/dj/qqssnmsn6hdgdjjcl98tj98sp6yz76/T/d20200612-9861-zkzbwg">
irb(main):009:0> encryption.decrypt_files
[16:48:19]: 🔓  Successfully decrypted certificates repo
=> []
irb(main):010:0> storage.working_directory
=> "/var/folders/dj/qqssnmsn6hdgdjjcl98tj98sp6yz76/T/d20200612-9861-zkzbwg"

个人习惯,直接在代码库下建立certs分支作为证书库,
在log中看到,match自动为我创建了certs的孤儿分支,将其作为我的证书库。

4. 添加现有证书并上传

open /var/folders/*********{步骤3中证书库目录}打开证书库目录。
如果是空的可以按match的规格创建certs文件夹,在其下创建development和distribution文件夹分别储存开发和发布用证书。
建立certs目录并添加现有证书
然后进行下述操作加密并上传证书库

irb(main):010:0> encryption.encrypt_files
irb(main):011:0> files_to_commit = Dir[File.join(storage.working_directory, "**", "*.{cer,p12,mobileprovision}")]
irb(main):012:0> storage.save_changes!(files_to_commit: files_to_commit)

5. 完工

在项目的fastlane目录下配置Matchfile,并执行match操作之后,fastlane会使用上传的证书创建并下载provisioning profile。
证书库
接下来只需要在项目文件中设置provisioning profile为match打头的即可。大功告成!

参考

http://macoscope.com/blog/simplify-your-life-with-fastlane-match/#migration https://docs.fastlane.tools/advanced/other/#manually-manage-the-fastlane-match-repo