1. 前言
match這個(gè)Tool的作用可以分為兩步
- 獲取合適的cert和profile
- 將獲取的cert和profile安裝到本機(jī)
其簡(jiǎn)單流程如下圖所示

- 首先,從
:git_url指定的git倉(cāng)庫(kù)中獲取cert和profile,如果沒有,則從AppleID賬號(hào)中獲取。
當(dāng)從AppleID賬號(hào)中獲取cert時(shí),即使你的AppleID賬號(hào)中有創(chuàng)建好的簽名證書,match也只會(huì)嘗試去創(chuàng)建一個(gè)新的的簽名證書,如果你的AppleID賬號(hào)中簽名證書的數(shù)目已達(dá)到了2個(gè),則會(huì)創(chuàng)建失敗,然后match異常退出;
當(dāng)從AppleID賬號(hào)中獲取profile時(shí),會(huì)強(qiáng)制創(chuàng)建一個(gè)新的來使用。具體過程可查看上一節(jié)cert和sigh。 -
match的最終目的就是把cert和profile安裝到本機(jī)
cert被導(dǎo)入到鑰匙鏈中
profile被安裝到xcode中
2. 內(nèi)部流程
macth特別適合在一個(gè)團(tuán)隊(duì)里面使用,在使用match時(shí),一般會(huì)先使用match創(chuàng)建一套可使用的cert和profile上傳到git倉(cāng)庫(kù)中,然后共享這個(gè)git倉(cāng)庫(kù)給團(tuán)隊(duì)的其他成員,這樣就可以一次創(chuàng)建,多次使用了。另外,macth提供了一個(gè)只讀模式,可以防止你修改AppleID賬號(hào)中或git倉(cāng)庫(kù)中的cert和profile。
2.1. 只讀模式
當(dāng):readonly的值是true時(shí),match不會(huì)訪問AppleID賬號(hào),只會(huì)從git倉(cāng)庫(kù)獲取cert和profile,并且不會(huì)修改git倉(cāng)庫(kù)。在只讀模式中,:force必須是false,否則,程序異常退出。在只讀模式中,:force_for_new_devices不會(huì)生效。

- 從倉(cāng)庫(kù)克隆cert和profile
通過:git_url指定git倉(cāng)庫(kù)的地址,默認(rèn)情況是克隆master分支,可通過:git_branch指定分支。如果在同一個(gè)倉(cāng)庫(kù)中管理多個(gè)team,可使用不同的分支來存儲(chǔ)不同的team。
克隆完畢之后,整個(gè)庫(kù)會(huì)被存儲(chǔ)在一個(gè)臨時(shí)地址中,庫(kù)的文件結(jié)構(gòu)如下圖所示
-certs
-enterprise
-#{cert_id}.cer
-#{cert_id}.p12
-development
-#{cert_id}.cer
-#{cert_id}.p12
-distribution
-#{cert_id}.cer
-#{cert_id}.p12
-profiles
-enterprise
-InHouse_#{bundleID}.mobileprovision
-appstore
-AppStore_#{bundleID}.mobileprovision
-adhoc
-AdHoc_#{bundleID}.mobileprovision
-development
-Development_#{bundleID}.mobileprovision
上述文件結(jié)構(gòu)列出了certs/和profiles/目錄中所有可能的子目錄,具體使用哪個(gè)子目錄,則是由:type指定,比如在使用企業(yè)賬號(hào)時(shí),且:type等于enterprise,則certs目錄下會(huì)有一個(gè)enterprise子文件夾。
cert和profile文件的命名必須按照上述格式,否則match會(huì)識(shí)別不出來。
:type與子目錄名對(duì)照表
| type | certs/下子目錄名 | profiles/下子目錄名 |
|---|---|---|
| enterprise | enterprise | enterprise |
| appstore | distribution | appstore |
| adhoc | distribution | adhoc |
| development | development | development |
倉(cāng)庫(kù)中存儲(chǔ)的cert和profile文件都被OpenSSL加密過,所以克隆下來之后的第一件事就是解密,這時(shí)match需要一個(gè)密碼用來解密,可以通過環(huán)境變量MATCH_PASSWORD來指定,如果沒有指定,則match會(huì)嘗試去鑰匙鏈的密碼列表中尋找名稱為match_#{:git_url}的鑰匙串項(xiàng),如果仍然沒有,則需要用戶手動(dòng)輸入。在CI環(huán)境中,最好事先使用環(huán)境變量MATCH_PASSWORD設(shè)置好。
match使用類似下列終端命令的代碼來加解密
#加密
openssl aes-256-cbc -k "<password>" -in "<fileYouWantToEncryptPath>" -out "<encryptedFilePath>" -a -e
#解密
openssl aes-256-cbc -k "<password>" -in "<fileYouWantToDecryptPath>" -out "<decryptedFilePath>" -a -d
如果cert和profile已創(chuàng)建好,可以使用上述命令加密下載的cert和profile,然后上傳到git倉(cāng)庫(kù)。
獲取bundleIDs
通過:app_identifier來指定bundleID,如果有多個(gè)bundleID,可以通過逗號(hào)分隔。
git倉(cāng)庫(kù)的同一個(gè)分支中,可以存放多個(gè)app的profile,因?yàn)閜rofile文件名中帶有bundleID,所以可以通過文件名來區(qū)分不同的app的profile。從倉(cāng)庫(kù)獲取cert
首先,根據(jù):type指定的類型,假設(shè)其值為enterprise,則match遍歷倉(cāng)庫(kù)的/certs/enterprise/目錄下的所有文件,將文件名符合*.cer格式的最后一個(gè)當(dāng)做簽名證書,將文件名符合*.p12格式的最后一個(gè)當(dāng)做其對(duì)應(yīng)私鑰。
3.1 如果簽名證書和其私鑰都存在,則將簽名證書的文件名去掉擴(kuò)展名,剩余部分作為此證書的id。
3.2 如果簽名證書或其私鑰不存在,則異常退出。
(其詳細(xì)流程可查看本文的第3小節(jié))安裝證書到鑰匙鏈
match使用如下命令將[步驟3]中獲取的證書和其私鑰分別安裝到鑰匙鏈中
# certificate_path 表示要導(dǎo)入證書的路徑
# keychain_path 表示鑰匙鏈的路徑,一般是`~/Library/Keychains/login.keychain-db`
# certificate_password 表示證書的密碼,默認(rèn)是空字符串,通過`cert`創(chuàng)建的證書的密碼為空
# -T usr/bin/codesign 表示使用`usr/bin/codesign`訪問這個(gè)證書的時(shí)候不需要授權(quán),也就是不需要輸入鑰匙鏈的密碼,使用xcodebuild打包app的使用會(huì)使用這個(gè)命令
security import certificate_path -k keychain_path -P certificate_password -T /usr/bin/codesign -T /usr/bin/security
從倉(cāng)庫(kù)獲取profile
和[步驟3]類似,首先,根據(jù):type指定的類型,假設(shè)其值為enterprise,則match遍歷倉(cāng)庫(kù)的/profiles/enterprise/目錄下的所有文件,將文件名符合InHouse_#{bundleID}.mobileprovision格式的最后一個(gè)作為要使用的profile。
5.1 profile存在,則執(zhí)行[步驟6]
5.2 profile不存在,則異常退出。
(其詳細(xì)流程可查看本文的第4小節(jié))安裝profile到xcode
將[步驟5]中獲取的profile文件復(fù)制到~/Library/MobileDevice/Provisioning Profiles/目錄下,文件名為#{uuid}.mobileprovision,其中uuid是profile的uuid然后正常退出
注意,在獲取cert和其私鑰時(shí),match只是粗略的通過文件擴(kuò)展名來做篩選,并沒有驗(yàn)證證書和其私鑰是否是一一對(duì)應(yīng)的,所以不要把兩套cert放在同一個(gè)子目錄下。同理,在獲取profile時(shí),也沒有驗(yàn)證cert和profile中關(guān)聯(lián)的cert是否是同一個(gè)簽名證書。所以可能出現(xiàn)一種情況,macth執(zhí)行成功,但是app打包不成功。當(dāng)然,如果你沒有手動(dòng)修改git倉(cāng)庫(kù),只通過match來創(chuàng)建和更新,則不會(huì)出現(xiàn)這種情況。
2.2 非只讀模式
當(dāng):readonly的值是false時(shí),如果git倉(cāng)庫(kù)中沒有找到可用的cert和profile,則match會(huì)從AppleID賬號(hào)中創(chuàng)建新的。

從倉(cāng)庫(kù)克隆cert和profile
登錄AppleID和選擇teamID
檢測(cè)type
當(dāng):type的值是enterprise,但你使用的AppleID賬號(hào)不是一個(gè)企業(yè)賬號(hào)時(shí),異常退出。獲取bundleIDs
檢測(cè)bundleIDs的可用性
遍歷bundleIDs,如果在AppleID賬號(hào)中不存在對(duì)應(yīng)App ID,則異常退出,只要有一個(gè)不存在就會(huì)異常退出。獲取cert
首先嘗試從git倉(cāng)庫(kù)獲取cert,其具體流程和[2.1節(jié)的步驟3]完全一樣,如果git倉(cāng)庫(kù)中沒有,則嘗試從AppleID賬號(hào)中創(chuàng)建一個(gè)新的簽名證書。
為了實(shí)現(xiàn)創(chuàng)建新的證書這一功能,match調(diào)用了cert這個(gè)Tool,并設(shè)置cert的:force參數(shù)的值為true來強(qiáng)制創(chuàng)建,而不管本地鑰匙鏈中是否存在cert。如果創(chuàng)建成功,則下載創(chuàng)建的cert和其對(duì)應(yīng)私鑰到git倉(cāng)庫(kù)的子目錄certs/cert_type中。
(調(diào)用cert的詳細(xì)流程可查看cert和sigh,本步驟的詳細(xì)流程可查看本文的第3小節(jié))安裝cert到鑰匙鏈
檢測(cè)cert可用性
檢測(cè)在[步驟6]中獲取的cert是否存在于AppleID賬號(hào)中,具體而言就是比較cert和AppleID賬號(hào)中獲取的cert的唯一標(biāo)識(shí)符是否一致。
從步驟6可知,有兩個(gè)途徑獲取cert,git倉(cāng)庫(kù)和AppleID賬號(hào)。從AppID賬號(hào)中下載的cert肯定是沒問題的,這一步主要是驗(yàn)證從git倉(cāng)庫(kù)中得到的cert的唯一標(biāo)識(shí)符的可用性。上文中有提到,在git倉(cāng)庫(kù)中獲取的cert的唯一標(biāo)識(shí)符就是這個(gè)cert去掉了擴(kuò)展名之后的文件名。獲取profile
和[步驟6]類似,首先嘗試從git倉(cāng)庫(kù)獲取profile,如果git倉(cāng)庫(kù)中沒有,則嘗試從AppleID賬號(hào)中創(chuàng)建一個(gè)新的profile。
假設(shè):type的值為enterprise,從git倉(cāng)庫(kù)中獲取profile的具體流程是,遍歷倉(cāng)庫(kù)中/profiles/enterprise/目錄下的所有文件名符合InHouse_#{bundleID}.mobileprovision格式的profile文件,如果這個(gè)profile存在于AppleID賬號(hào)中,則返回這個(gè)profile。
為了實(shí)現(xiàn)創(chuàng)建新的profile這一功能,match調(diào)用了sigh這個(gè)Tool,并設(shè)置sigh的:force參數(shù)的值為true來強(qiáng)制創(chuàng)建,而不管AppleID賬號(hào)中是否已存在profile。同時(shí),也指定了sigh的:cert_id參數(shù)的值為[步驟6]中獲取的cert的唯一標(biāo)識(shí)符。
(調(diào)用sigh的詳細(xì)流程可查看cert和sigh,本步驟的詳細(xì)流程可查看本文的第4小節(jié))安裝profile到xcode
提交cert和profile到倉(cāng)庫(kù)
如果在[步驟6]創(chuàng)建了新的cert或在[步驟9]中創(chuàng)建新的profile,則需要將這些新創(chuàng)建的文件添加到倉(cāng)庫(kù),并推送的遠(yuǎn)程倉(cāng)庫(kù)中。
3. 獲取cert
下圖是獲取cert的詳細(xì)執(zhí)行流程

3. 獲取profile
下圖是獲取profile的詳細(xì)執(zhí)行流程
