Fastlane 自动化打包并发布 iOS 项目
打包一个iOS项目大致就是给一个编译好的app和对应的描述文件,用签名证书签名导出。那么除项目代码外的两样东西就是
- 签名证书,给开发者账号下的所有项目使用,有开发和分发两种
- 描述文件,每个项目对应的文件,且每个项目都可以有开发、分发两类描述文件,而分发有两种,一种是给有限设备使用的
ad-hoc
类型,一种是发布到TestFlight
或App store
的app-store
类型
这两样东西手动获取是非常繁琐的,项目越多越繁复。显然都是些固定的、重复的步骤,Fastlane
能很好地完成这部分工作,而且有很多不同的实践方式。
获取、管理所有项目的签名证书和描述文件
这个可以在每个项目里作,但每年证书过期的时候手动执行一行命令也不是很好的体验。所以我会建一个 fastlane 项目来专门管理证书,使用 Match 这个 Action。
先去 GitHub 建个私有仓库,再在本地建个空白文件夹,在这个文件夹录下执行 fastlane init ,建立一个空白的项目。
配置 MATCH_PASSWORD 环境变量,用于fastlane设置证书的密码
在生成的 fastlane文件夹里新建一个 Matchfile 文件,加入以下内容
1 | ## 刚在 GitHub 建的库 |
在 Fastfile 中编辑大致如下
1 | default_platform(:ios) |
接着执行 fastlane bundleid ,根据提示输入密码,双重认证码,fastlane 就会登录到苹果开发者中心,检查各种签名证书是否存在,不存在就自动请求生成再下载,再去下载(不存在就生成)app_identifier 的各种描述文件,然后把这些文件 push 到刚才创建的 GitHub 仓库里。
如果账号还有别的开发者账号权限,比如公司的,那么上述步骤还会要选择 team,可以在 Matchfile 中加入 team_id(“xxxxxxx”),以后就不用选择了。
以上,生成获取了签名证书和描述文件,并保存到 Github 仓库备用。
打包并发布项目
假定要打个包给内部测试,可选的方式有 ad-hoc 或 testflight。
adhoc,也就是需要提前收集测试设备id给开发者中心,再重新获取描述文件,match 里面加个 force: true 就是重新生成描述文件。(其实收录设备id这个步骤也可以让 fastlane 作,不过本文不会就这展开。)
首先在项目根目录下执行 fastlane init ,选择生成一个发布 testflight 的项目,生成的 fastlane 文件夹中的 Appfile 要有以下字段,没有的自行加上
1 | app_identifier("com.domian.bundleid") # The bundle identifier of your app |
Fastfile 中还是使用 Match 来获取证书和描述文件,readonly 加不加都无所谓,只是保证不产生新的签名证书和描述文件
1 | match( |
接着是编译打包
1 | build_app( |
接着是发布,这个一般是发布到内部平台或蒲公英之类的地方,蒲公英有对应 Action,配置一下密钥即可,我平时是发布到自建的平台上,一句 curl 命令即可。
Fastfile 总体大致如下
Ad-hoc
1 | default_platform(:ios) |
TestFlight
1 | default_platform(:ios) |
如果要保留 build 的产物,符号表和ipa,删除clean_build_artifacts即可,要保留每次build的内容,那就如下所示,不过记得编辑 .gitignore 文件,不然就把这些都提交
1 | default_platform(:ios) |
以上就是如何利用 fastlane 完成 iOS 项目的打包和发布。
至于其它文章都会提到的,申请个 app 专用密码,添加 FASTLANE_APPLE_APPLICATION_SPECIFIC_PASSWORD 到环境变量,在执行 fastlane spaceauth -u xxxx@gmail.com 获取session,添加 FASTLANE_SESSION 到环境变量这个操作,有一段时间可以不用输密码和双重认证码,有需要可以尝试一下。
但要说明的是,这个session的有效期非常飘忽,几小时到一个月不等,事实上我就没试过超过一天的。但设置 FASTLANE_APPLE_APPLICATION_SPECIFIC_PASSWORD 或 FASTLANE_PASSWORD(appleid的密码) 后就肯定不用输入密码了,最多只需要输入双重认证码。顺带一说,现在appleid 的双重认证是强制开启不能关闭的。