Featured image of post 簡易使用 Fastlane Match

簡易使用 Fastlane Match

目的

在新開發的編譯環境,使用 match 自動下載最新憑證與 profile,或是在每次編譯時候更新最新的 profile。

版本

本文 fastlane version : 2.171.0 Match 包含 sigh and cert

  • sigh 操作 profile
  • cert 操作 certificate

Matchfile

初始化

1
bundle exec fastlane match init

跟目錄會出現一個 Matchfile,修改一下內容。

1
2
3
4
5
6
7
git_url("git@bitbucket.org:git_repo_user_name/your_ios_cert.git")
storage_mode("git")
git_full_name("git.user.name")
git_user_email("git.user.email")
verbose(true)
git_branch("master")
app_identifier(["tools.fastlane.app", "tools.fastlane.app2"])

執行:

之後可直接在 CLI 執行

1
bundle exec fastlane match

就會自動產生憑證與 profile 並且上傳到 git 之中。 第一次執行過程時,會多次要求輸入一些東西:

  1. 開發者帳號密碼
  2. 開發者帳號兩次驗證的驗證碼
  3. 將憑證與 profile 加密的密碼

上述輸入過的密碼都會儲存在 keychain 的密碼中。

Fastfile 範例

也可以在 lane 之中執行 match,並且加入個別條件的參數。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
platform :ios do
  lane :beta do
    match(
      git_branch: "master",
      app_identifier: "tools.fastlane.app",
      type: "adhoc",
      profile_name: "ProfileName",
    )

    build_ios_app(
        ...
    )
  end
end

type: development/adhoc/appstore

或是手建立一個 lane 只 readonly 下載所有 profile

1
2
3
4
5
lane :match_all do
  sh "fastlane match development --readonly"
  sh "fastlane match adhoc --readonly"
  sh "fastlane match appstore --readonly"
end

將所有 profile 更新到最新,並安裝至內部 ~/Library/MobileDevice/Provisioning Profiles/.....,後上傳至 git

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
platform :ios do
  desc "Description of what the lane does"
  lane :match_all do
    match(
      type: "appstore",
      api_key: your_api_key()
    )
    match(
      type: "adhoc",
      api_key: your_api_key()
    )
    match(
      type: "development",
      api_key: your_api_key()
    )

  end
  def your_api_key()
    api_key = app_store_connect_api_key(
      key_id: "6XXXXXXXXX",
      issuer_id: "4xxxxxxx-1xxx-43e4-axxx-xxxxxxxx474b",
      key_filepath: "~/fastlanefiles/[FastlaneUploadAppManagerKey01][ISSUE_ID][4xxxxxxx-1xxx-43e4-axxx-xxxxxxxx474b][KEY_ID][6XXXXXXXXX].p8",
      duration: 1200, # optional (maximum 1200)
      in_house: false # optional but may be required if using match/sigh
    )
    return api_key
  end
end

其他詳細參數使用方式可參考文件 https://docs.fastlane.tools/actions/match/#parameters

Git 目錄結構

match 會在 git 上依照此目錄結構放置檔案。

  • certs
    • development
      • Development 的 Cert id.cer
      • Development 的 Cert id.p12
    • distribution
      • Distribution 的 Cert id.cer
      • Distribution 的 Cert id.p12
  • profiles
    • development
      • Development_your.app.bundle.id.mobileprovision
    • adhoc
      • AppStore_your.app.bundle.id.mobileprovision
    • appstore
      • AdHoc_your.app.bundle.id.mobileprovision

Git 注意事項

  • match 會抓取憑證資料夾的最後一個 .cer 與 .p12 如果塞了多個憑證可能會拿到錯的憑證。
  • 如果你的 Certs 跟 profiles 打算手動產生,目錄結構與檔案名稱一定要按照此格式,在 非readonly模式下,match 會自動產生新的 Cert 與 profiles,就算你已經原本有 Cert 了,如果 Cert 已經有兩個會創建失敗。
  • AppID 要在開發者後台的 Identifiers 先建好,match 才能建立 cert 成功。
  • Profiles 在建立時,預設是全選 devices。
  • profile 名稱可以用 match 的 profile_name 修改預設名稱,預設是 match AdHoc/Development/AppStore your.bundle.id

手動管理憑證與 Profile

如果你的專案已經上線一段時間,重做憑證可能不太適合,這時可以用手動管理方式將憑證放置 git repo 中,但是須按照 上述的目錄規則,並且將檔案加密。

網路上有些教學加密方式與現在的版本不同,我是用參考文件(Sync Code Signing:Manual Decrypt)的手動解密方式去反過來加密,目前版本結果是可以正常加密解密成功。

match 會將 public key 的 cer 檔與 private key 的 p12 檔放置 Git Repo 中,以下為加密解密並且產生檔案的方式。

加密

首先從 kaychain export 包含 private key 的 cert.p12 檔案

Cer 檔案

1
2
3
4
5
6
7
8
# 從 cert.p12 分離出 Public Certificates 的 pem
openssl pkcs12 -clcerts -nokeys -in cert.p12 -out publicCert.pem

# 轉成 der
openssl x509 -outform der -in publicCert.pem -out publicCert.der

# 轉成 cer
openssl aes-256-cbc -k "<password>" -in publicCert.der -out cert_id.cer -a -e -md md5

p12 檔案

從 keychain export 憑證的 private key,不包含憑證本身的 private_key.p12 檔案

1
2
3
4
5
# 從 cert.p12 分離出 Private Key 的 pem
openssl pkcs12 -nocerts -nodes -in cert.p12 -out private_key.pem

# 轉成 p12 並加密碼
openssl aes-256-cbc -k "<password>" -in private_key.pem -out cert_id.p12 -a -e -md md5

cert_id 查詢方式可用 ruby 的腳本

解密

Cer 檔案

提取 Repo 的 certs/<type>/cert_id.cer

1
2
3
4
5
# 轉成 der
openssl aes-256-cbc -k "<password>" -in cert_id.cer -out cert.der -a -d -md md5

#轉成 pem
openssl x509 -inform der -in cert.der -out cert.pem

p12 檔案

提取 Repo 的 certs/<type>/cert_id.p12

1
2
# 轉成 pem
openssl aes-256-cbc -k "<password>" -in cert_id.p12 -out private_key.pem -a -d -md md5

合成 private_key.pem 與 cert.pem

1
2
openssl pkcs12 -export -out "cert.p12" -inkey "private_key.pem" -in "cert.pem" -password pass:<password>
# password 是 p12 匯入時候要輸入的密碼

會做成一個包含 private_key 與憑證 的 p12 檔案,就可以匯入 keychain

加解密 provisioning

Provisioning Profile 的加密方式也是用 openssl aes-256-cbc 方式

1
2
openssl aes-256-cbc -k "<password>" -in input.mobileprovision -out AdHoc_xxxxx.mobileprovision -a -d -md md5
# 加密 -e / 解密 -d

建立成 bash script

我這邊建立成 script 的方式,方便操作。 https://gist.github.com/pokeduck/bc4fc18f93d0e322f5bf229d6464f6be

Cert ID 的取得方式

  • 取得 cert id 的方式,這邊的 cert id 不是 team id ,在 Fastlane 內有 spaceship 的 Ruby Library 可以取得所有 Cert id。
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
#cert.rb
require 'spaceship'

Spaceship.login('your_developer_account@email.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.strftime("%Y-%m-%d")}, type: #{cert_type}"
end

然後執行 ruby cert.rb 就會列出該帳號底下所有已建立的

1
2
3
4
Cert id: XXX0000XXX, name: Development, expires: 20xx-xx-xx, type: AppleDevelopment
Cert id: XXX0000XXX, name: Distribution, expires: 20xx-xx-xx, type: AppleDistribution
Cert id: XXX0000XXX, name: APNs Development iOS, expires: 20xx-xx-xx, type: DevelopmentPush
...

在 Keychain 的密碼

開發者帳號

可以預先用 fastlane tool 在 keychain 新增開發者帳號密碼

1
2
3
4
5
# 新增Apple帳號密碼至keychain
fastlane fastlane-credentials add --username your.apple.dev.id@domain.com

# 刪除帳號密碼
fastlane fastlane-credentials remove --username your.apple.dev.id@domain.com

參考來源: https://github.com/fastlane/fastlane/tree/master/credentials_manager

執行成功後會在鑰匙圈存取 > 登入 > 密碼中,新增一組名為 deliver.your.apple.dev.id@domain.com 的資料。

憑證密碼

當 match 執行到無法解密的憑證時會在輸入框提示密碼

1
2
3
4
5
6
7
8
[16:51:44]: Cloning remote git repo...
[16:51:44]: If cloning the repo takes too long, you can use the `clone_branch_directly` option in match.
[16:51:46]: Checking out branch master...
[16:51:46]: Enter the passphrase that should be used to encrypt/decrypt your certificates
[16:51:46]: This passphrase is specific per repository and will be stored in your local keychain
[16:51:46]: Make sure to remember the password, as you'll need it when you run match on a different machine
/Users/your_name/.rbenv/versions/2.7.2/lib/ruby/gems/2.7.0/gems/highline-1.7.10/lib/highline.rb:624: warning: Using the last argument as keyword parameters is deprecated
[16:51:46]: Passphrase for Match storage:

解碼成功後會在鑰匙圈建立密碼,名稱:match_git@bitbucket.org:your_repo/your_cert_repo.git

參考

openssl

match

dinostack.cc
使用 Hugo 建立
主題 StackJimmy 設計