cert、sigh和match
是Fastlane中的三个Tool,他们都是与证书相关的工具。cert
的做用是获取签名证书或删除过时的证书;sigh
的做用是管理配置文件(provisioning profile),好比建立新的、修复过时的、删除本地的等;match
的主要做用是使用cert
和sigh
建立新的证书和配置文件,并它们放置在git上,而后重复使用。ios
cert
这个Tool下定义了两个Command,分别是create
和revoke_expired
,其中create
是默认Command。 能够经过在终端中执行下列命令调用git
#调用create
fastlane cert
fastlane cert create
#调用revoke_expired
fastlane cert revoke_expired
复制代码
除了在终端使用,cert
还能够在lane中被当作action来调用,这也是使用最频繁的调用方式。 当cert
被当作action被调用时,其效果和在终端调用fastlane cert [create]
的效果是同样的。json
cert
中的create
的做用是获取签名证书和其私钥,而后将签名证书和其私钥(p12)导入到钥匙链中。 为了获取证书,首先它会去检测本地是否存在它想要的证书,若是没有则它会去你的AppleID帐号中尝试建立一个新的。数组
本文只讨论create
这个Command,下文中若是没有特殊说明,指的都是这种状况。 当在终端执行fastlane cert
时,其执行逻辑以下 xcode
建立:output_path
指向的目录bash
获取AppleID 可经过:username
、环境变量CERT_USERNAME、DELIVER_USER、DELIVER_USERNAME
或Appfile
三种途径获取;若是没有,则在终端请求用户输入AppleID。并发
获取AppleID对应密码 可经过环境变量FASTLANE_PASSWORD
和DELIVER_PASSWORD
设置;若是没有,则在终端使用security find-internet-password -g -s deliver.#{AppleID}
查看钥匙链中是否存储了对应密码,其中AppleID是[步骤2]中获取的;若是没有,则在终端请求用户输入,而且会将用户输入的密码存储在钥匙链中。app
登陆到苹果开发网站 若是有两步验证,则还须要输入对应手机的验证码ide
获取TeamID 若是这个AppleID帐号加入了多个Team,能够经过设置TeamID或TeamName来指定一个Team,具体来讲能够经过环境变量FASTLANE_TEAM_ID
、CERT_TEAM_ID
或:team_id
指定TeamID,经过环境变量FASTLANE_TEAM_NAME
,CERT_TEAM_NAME
或:team_name
指定TeamName,不然,须要用户手动来选择。若是你的AppleID帐号只加入了一个Team,则直接使用此Team的TeamID。工具
检测force 6.1. 当:force
为true
时,强制建立证书,执行[步骤8] 6.2. 当:force
为false
时,执行[步骤7]
检测本地证书 遍历AppleID帐号中的已建立证书,检测此证书是否存在于钥匙链中,或者:output_path
目录下是否存在此证书对应的密钥(p12),其具体的检测流程会在下文中讲到。 7.1. 本地有可用证书,执行[步骤9] 7.2. 本地无可用证书,执行[步骤8]
建立新证书 首先生成CSR文件和RSA密钥对
def create_certificate_signing_request
key = OpenSSL::PKey::RSA.new(2048)
csr = OpenSSL::X509::Request.new
csr.version = 0
csr.subject = OpenSSL::X509::Name.new([
['CN', 'PEM', OpenSSL::ASN1::UTF8STRING]
])
csr.public_key = key.public_key
csr.sign(key, OpenSSL::Digest::SHA1.new)
return [csr, key]
end
复制代码
而后生成请求
r = request(:post, "account/#{platform_slug(mac)}/certificate/submitCertificateRequest.action", {
teamId: team_id,
type: type,
csrContent: csr,
appIdId: app_id # optional
})
复制代码
若建立成功,则在output_path
目录下存储此新建立的CSR文件、签名证书和签名证书对应的私钥。 AppleID帐户下,相同类型的证书只能建立两个,若是已经建立了两个以后,再去尝试建立证书,则会报错。
security
命令来导入security import certificate_path -k keychain_path -P certificate_password -T /usr/bin/codesign -T /usr/bin/security
复制代码
其中certificate_path
表示要导入证书的路径; keychain_path
表示钥匙链的路径,通常是~/Library/Keychains/login.keychain-db
; certificate_password
表示证书的密码,默认是空字符串,经过cert
建立的证书的密码为空; -T usr/bin/codesign
表示使用usr/bin/codesign
访问这个证书的时候不须要受权,也就是不须要输入钥匙链的密码,这个在CI中会颇有用。 最后须要注意的是,若是证书原本就是在钥匙链中,则不会执行这个步骤,也不会执行这条命令,因此在CI中使用时,最好在构建脚本中加上security unlock-keychain -p certificate_password ~/Library/Keychains/login.keychain-db
,这条命令的做用和上面的-T
相似,可是范围更广,即访问整个钥匙链都不须要输入密码。
CER_CERTIFICATE_ID
和CER_FILE_PATH
这两个环境变量,分别表示证书的id和证书的路径,证书的路径就是:output_path
目录下的证书文件的路径。 若是是在lane中调用cert
,则还会设置环境变量SIGH_CERTIFICATE_ID
,这样设置以后,若是接下来sigh
须要建立一个配置文件,就会使用环境变量SIGH_CERTIFICATE_ID
指向的签名证书来建立。(环境变量SIGH_CERTIFICATE_ID
仅仅只是在建立新的配置文件的时候才会使用):development
指定证书的类型,true表示调试证书,false表示生产证书,默认是false,本步骤只获取指定类型的证书。证书列表中的对象的类型都是Spaceship::Portal::Certificate
或其子类。 类Spaceship::Portal::Certificate
中的实例变量module Spaceship
module Portal
class Certificate < PortalBase
# @return (String) The ID given from the developer portal. You'll probably not need it.
attr_accessor :id
# @return (String) The name of the certificate
attr_accessor :name
# @return (String) Status of the certificate
attr_accessor :status
# @return (Date) The date and time when the certificate was created
attr_accessor :created
# @return (Date) The date and time when the certificate will expire
attr_accessor :expires
# @return (String) The owner type that defines if it's a push profile or a code signing identity
# @example Code Signing Identity
# "team"
# @example Push Certificate
# "bundle"
attr_accessor :owner_type
# @return (String) The name of the owner
# @example Code Signing Identity (usually the company name)
# "SunApps Gmbh"
# @example Push Certificate (the bundle identifier)
# "tools.fastlane.app"
attr_accessor :owner_name
# @return (String) The ID of the owner, that can be used to fetch more information
attr_accessor :owner_id
# Indicates the type of this certificate
attr_accessor :type_display_id
# @return (Bool) Whether or not the certificate can be downloaded
attr_accessor :can_download
end
end
end
复制代码
<Spaceship::Portal::Certificate::InHouse
id="GF0ZY66W6D",
name="iOS Distribution",
status="Issued",
created=2017-12-19 02:52:11 UTC,
expires=2020-12-18 02:42:11 UTC,
owner_type="team",
owner_name="Communications Corporation Limited",
owner_id="12GF5VQGBX",
type_display_id="9RQEK7MSXA",
can_download=true>
复制代码
r = request(:get, "account/#{platform_slug(mac)}/certificate/downloadCertificateContent.action", {
teamId: team_id,
certificateId: certificate_id,
type: type
})
复制代码
将下载的证书文件存储在:output_path
指向的目录中,指定文件名为#{certificate.id}.cer
,certificate.id
表示上述证书对象的id。
SHA1
摘要来判断其是否存在。 使用security find-identity -v -p codesigning
获取钥匙链中可用的签名证书列表,下列每一条数据都包含了证书的SHA1
摘要和其名称wang:temp mac$ security find-identity -v -p codesigning
1) 9C3C5AE7820F33F6D919595E971C9B458519ACE5 "iPhone Developer"
2) 57F720F51EA851BA8E2D6EC4D4D752F9EF43D2F7 "iPhone Distribution"
2 valid identities found
复制代码
而后获取[步骤3]中证书文件的SHA1
摘要,若是这个摘要存在于上述输出中,则表示这个证书已经在钥匙链中了,执行[步骤8] 若是没有包含,则执行[步骤5]
output_path中检测私钥 检测:output_path
目录中是否存在#{certificate.id}.p12
,certificate.id
表示[步骤2]中获取的证书对象的id,这里是仅仅只是经过文件名来判断其是否存在。 若存在,说明本地存在可用证书,则执行[步骤8] 若不存在,说明本地不存在可用证书,则执行[步骤6]
从output_path中删除此证书 删除[步骤3]中下载的证书文件
本地没有可用证书
本地有可用证书
sigh
是用于管理配置文件profile,在 sigh
这个Tool中,其内部集成了多个Command,分别是renew、download_all、repair、resign、manage
,其中默认Command是renew
。 renew
的做用是从AppleID帐号中获取一个可用的配置文件profile,若是没有,则建立一个新的profile,而后将它按照到xcode中。
这里只讨论renew
,若是没有特殊说明,指的都是这种状况。 当在终端执行fastlane sigh [renew]
时,其执行逻辑以下
前几步与cert
相似,只是有一些用来传值的环境变量有些不一样。
获取AppleID 可经过:username
、环境变量SIGH_USERNAME、DELIVER_USER、DELIVER_USERNAME
或Appfile
三种途径获取;若是没有,则在终端请求用户输入AppleID。
获取AppleID对应密码
登陆到苹果开发网站
获取TeamID 经过环境变量FASTLANE_TEAM_ID
、环境变量SIGH_TEAM_ID
或:team_id
指定TeamID,经过环境变量FASTLANE_TEAM_NAME
,环境变量SIGH_TEAM_NAME
或:team_name
指定TeamName
获取profile列表 首先从AppleID帐号中,获取全部已建立的provisioning profiles的列表(也包含xcode管理的),而后通过一步步的过滤,最终获得全部可用的profile。 5.1 获取的profile列表有值,则执行[步骤6] 5.2 获取的profile列表有值,则执行[步骤16]
获取第一个profile
检测force :force
指定是否强制建立新的provisioning profile 7.1 :force
等于true,执行[步骤8] 7.2 :force
等于false,执行[步骤10]
在AppleID中删除此profile 在AppleID帐号中,删除[步骤6]中获取的profile
在AppleID中建立新的profile 若是是[步骤16]跳转过来的,还须要保证AppleID帐号中存在此:app_identifier
返回profile 若是:force
等于true,则返回[步骤9]中建立的profile; 若是:force
等于false,则返回[步骤6]中获取的profile.
下载profile文件 以前步骤中提到profile是provisioning profile的概要描述,这里下载的profile文件,则是在项目中使用的配置文件。下载完成后,将文件存储在临时目录中。
output_path目录下存储profile文件 将[步骤11]下载的文件移动到:output_path
目录下,若是指定了:filename
,则文件名为#{filename}.mobileprovision
;不然,文件名为#{type}_#{app_identifier}.mobileprovision
,其中type表示prifile的类型,多是AppStore、AdHoc、InHouse和Development。
检测skip_install :skip_install
指定是否安装profile到钥匙链中 若是:skip_install
等于true,则执行[步骤15] 若是:skip_install
等于false,则执行[步骤14]
安装profile到钥匙链中 将[步骤12]中的profile文件复制到~/Library/MobileDevice/Provisioning Profiles/
目录下,文件名为#{uuid}.mobileprovision
,其中uuid
指的是profile的uuid
返回output_path路径 返回:output_path
指定的目录路径,而后退出程序
检测readonly :readonly
指定是否在AppleID帐号中建立新的profile 若是:readonly
等于false,则执行[步骤9] 若是:readonly
等于true,异常退出
获取全部已建立的provisioning profiles的列表,而后通过一步步的过滤,最终获得全部可用的profile。
下载全部的profile 全部的pofile是指AppleID帐号中看获得的全部provisioning profile(即便是invalid)和经过xcode建立的,经过xcode建立的profile不会显示在AppleID中。
检测development和adhoc :development
和:adhoc
用来指定profile的类型,profile的类型总共有四种,分别是Development、AppStore、AdHoc、InHouse
检测force 若是:force
是true,则不会删除不可用的profile,由于后面会强制建立新的profile,不会使用当前这些profile,也就无所谓可用仍是不可用了。
过滤adhoc或appstore 下面是sigh
的源码,我的猜想,下载profile时,返回的json数据中有一个叫作distributionMethod
的key,这个key的取值范围是['inhouse', 'store', 'limited', 'direct']。adhoc
和appstore
类型的profile返回的distributionMethod
的值都是store。在本步骤以前都没有区分adhoc
和appstore
,在这一步骤中,会根据profile中是否带有device来区分这两种类型。
klass = case attrs['distributionMethod']
when 'limited'
Development
when 'store'
AppStore
when 'inhouse'
InHouse
when 'direct'
Direct # Mac-only
else
raise "Can't find class '#{attrs['distributionMethod']}'"
end
复制代码
下面是建立profile时,请求的参数
params = {
teamId: team_id,
provisioningProfileName: name,
appIdId: app_id,
distributionType: distribution_method,
certificateIds: certificate_ids,
deviceIds: device_ids
}
params[:subPlatform] = sub_platform if sub_platform
# if `template_name` is nil, Default entitlements will be used
params[:template] = template_name if template_name
复制代码
想要在AppleID帐号中建立新的profile,首先须要获取上述代码中的各个参数,主要是签名证书列表、包含的设备、发布类型和名称等
下图中,步骤1到步骤9都是在筛选可用的签名证书列表
下载当前平台和发布模式的证书列表 好比当前使用的AppleID帐号是一个企业开发者帐号,且:platform
等于ios
,:development
和:adhoc
都等于false
,则在本步骤中会下载ios
平台下全部的In-House
签名证书。
检测cert_id和cert_owner_name :cert_id
是签名证书的惟一标识符,:cert_owner_name
是签名证书所属的team的name。
删除不匹配的证书 当:cert_id
有值,且证书的cert_id和它不相等,则从证书列表中删除此证书; :cert_owner_name
有值,且证书的cert_owner_name和它不相等,则从证书列表中删除此证书;
检测skip_certificate_verification
删除不在钥匙链中的证书 检测证书是否在本地钥匙链,其具体步骤可查看2.1节的步骤4
检测剩余证书的数目 剩余的证书数据为0,异常退出
检测development
返回全部剩余证书 开发环境下的profile能够包含多个签名证书,全部返回全部的剩余证书
返回剩余证书中的第一个 生产环境下的profile只能包含一个签名证书,全部返回剩余证书中的第一个。若是想使用特定的签名证书,最好使用:cert_id
指定。
获取profile的name 首先,若是有设置:provisioning_name
,则使用设置的值做为profile的name;不然,使用#{bundle_id} #{profile_type}
这种格式,好比com.fastlane.demo InHouse
而后,若是skip_fetch_profiles
的值是fasle,则会去检测这个名字是否已经被使用了,若是被使用了,就在这个名字后面加上一个空格和一个当前的时间戳。
获取注册设备的ids 若是当前的发布模式是AppStore、InHouse、Direct
,即development=false and adhoc=false
,ids等于空数组; 不然,ids等于当前平台的全部注册设备的id集合;
获取其余参数 其余参数还包含:team_id、:app_identifier、:template_name
等,:app_identifier
指定的bundle_id必须在AppleID帐号中有建立对应的App ID,不然会异常退出。
生成并发出建立profile的请求 到了这一步,建立profile请求的参数都已经获取到了,接下来就是发出这个请求。
下面再来看看建立profile时的请求参数
params = {
teamId: team_id,
provisioningProfileName: name,
appIdId: app_id,
distributionType: distribution_method,
certificateIds: certificate_ids,
deviceIds: device_ids
}
params[:subPlatform] = sub_platform if sub_platform
# if `template_name` is nil, Default entitlements will be used
params[:template] = template_name if template_name
复制代码
建立profile的前提就是要构建好上述代码中的参数,而这些参数又依赖于执行fastlane sigh
时传入的外部参数。
下面列出了一些请求参数与外部参数的对照关系
请求参数 | 外部参数 |
---|---|
teamId | :team_id |
provisioningProfileName | :provisioning_name |
appIdId | :app_identifier |
distributionType | :adhoc、:development |
certificateIds | :cert_id、:cert_owner_name |
deviceIds | :platform、:development、:adhoc |
subPlatform | :platform |
template | template_name |
经过:platform
,能够指定建立profile时的平台。它有三种取值,分别是mac、ios、tvos
。
当:platform
等于mac
或ios
时,请求参数subPlatform等于nil;不然subPlatform等于tvos
。