Fastlane
fastlane是用Ruby语言编写的一套自动化工具集和框架,每一个工具实际都对应一个Ruby脚本,用来执行某一个特定的任务,而fastlane核心框架则允许使用者通过类似配置文件的形式,将不同的工具有机而灵活的结合在一起,从而形成一个个完整的自动化流程。
主要突出的几个功能:
- sigh:生成并下载开发者证书
- produce:在 iTunes Connector 上创建你的应用
- gym:打包你的应用
- snapshot:实现应用截屏并取得图片
- pilot:将你的应用发布到 TestFlight 进行测试
- deliver:将你的应用的二进制代码,截屏和原数据等信息上传到 AppStore
...其实还有很多很突出也很实用的功能,几乎你能想到的正常的需求都做了,到目前为止已有共计超过210个actions。如果还不够,你还可以寻找额外的plugin,至少有超过250个额外的plugin你可以使用。
fastlane - 安装篇
gem install fastlane
安心等待一会,fastlane 就安装完成了。
可能会出现 bundle 报错的问题:
# 本地没有安装 bundle
bundle install
# 本地已经安装 bundle
bundle update
fastlane - 初始化篇
进入到工程目录,然后执行 fastlane 初始化操作:
fastlane init
首先会让你选择一下使用 fastlane 的目的,选择 4
就好了。
What would you like to use fastlane for?
1. 📸 Automate screenshots
2. 👩✈️ Automate beta distribution to TestFlight
3. 🚀 Automate App Store distribution
4. 🛠 Manual setup - manually setup your project to automate your tasks
之后静静等待并且无脑回车,最后看到一下输出就代表初始化成功了,文件目录中会多出 Gemfile & Gemfile.lock 这两个文件 和 fastlane 这个文件夹。
To try your new fastlane setup, just enter and run
[17:23:27]: $ fastlane custom_lane
match - 初始化篇
还是在工程目录中执行 match 初始化操作:
fastlane match init
首先会让你选择 match 描述文件和证书存放的地方,选择 git 仓库也就是 1
就好了。
fastlane match supports multiple storage modes, please select the one you want to use:
1. git
2. google_cloud
然后输入 git 仓库的地址,例如 git@xxxxxx/FastlaneProvingProfile.git
Please create a new, private git repository to store the certificates and profiles there
URL of the Git Repo:
接着看到下面的提示语代表 match 初始化完成了,fastlane 文件夹中会多出一个 Matchfile 文件:
Successfully created './fastlane/Matchfile'. You can open the file using a code editor.
match - 使用篇
有三个不同的命令可以拉取不同环境下的证书和描述文件:
- 拉取 development 模式下的证书和描述文件
fastlane match development
- 拉取 adhoc 模式下的证书和描述文件
fastlane match adhoc
- 拉取 appstore 模式下的证书和描述文件
fastlane match appstore
Fastlane + Jenkins 配置篇
依赖环境:
- Xcode10+
- macOS or Linux with Ruby 2.0.0 + (本文:ruby 2.3.4 )
首先看一下已经完成 fastlane 配置的目录结构:
项目根目录
.
├── Gemfile
├── Gemfile.lock
├── exportOptions_pre.plist
├── exportOptions_release.plist
├── fastlane
│ ├── Appfile # 存储有关开发者账号相关信息
│ ├── Fastfile # 核心文件,主要用于命令行调用和处理具体的流程,lane 相对于一个方法或者函数
│ ├── Gymfile # 打包工具相关配置项
│ ├── Deliverfile # 包上传和提审工具配置项
│ └── Matchfile # 证书和描述文件管理工具配置项
└── ...
1、如果你的项目中有集成 cocoapods, 需要在 Gemfile (fastlane初始化的时候创建)文件中添加上 pod 配置, 最好顺便更新一下 gem 源,避免使用过程中引起不适:
source "https://gems.ruby-china.com"
gem "fastlane"
gem "cocoapods"
2、配置 Appfile 文件(开发者账号的相关配置):
# 项目 bundle id
app_identifier("项目 bundle id")
# 苹果开发者账号
apple_id("苹果开发者账号")
# App Store Connect Team ID
itc_team_id("开发者账号的 Team ID")
# Developer Portal Team ID
team_id("开发者账号的 Team ID")
3、配置 Matchfile (证书和描述文件工具):
通常可以选择将 match 的证书和描述文件存放在一个 git 仓库下,需要预先创建一个 git 仓库来存放证书和描述文件。
# 存放证书和描述文件的 git 仓库地址,对就是刚刚初始化 Match 时候填写的仓库地址
git_url("git@xxxxxx/FastlaneProvingProfile.git")
# 分支名称(我是使用项目的 bundleID 来命名以区分多个项目)
git_branch("xxxxxxxxxx")
# 使用 git 服务
storage_mode("git")
# 默认的 match 类型(可以不设置)
type("development") # The default type, can be: appstore, adhoc, enterprise or development
# 项目的 bundle id(数组的形式)
app_identifier(["xxxxxxxxxx"])
# 有权限修改证书和描述文件的账号名称
username("xxxxxxxxxxxxx") # Your Apple Developer Portal username
4、 配置 Gymfile (打包工具):
# 需要打包的 scheme 名称
scheme("xxxxxx")
# 如果存在多个 workspace 的话需要指定打包的 workspace
workspace("./xxxxxx.xcworkspace")
# 打包的 SDK 版本,可以不设置
# sdk("iphoneos9.0")
# 打包之前执行 clean 操作
clean(true)
# 是否支持 bitcode,因为我们项目不支持,我选择了 false
include_bitcode(false)
5、配置 Fastfile(流程控制工具,支持 ruby 语法):
定义一个简单的无参 lane
如下:
desc "Push a new beta build to TestFlight"
lane :beta do
# 啥事也不干
end
你可以在一个 lane
中添加不同的 action 去执行各种操作。
通过 jenkins
打包完整的 lane
配置如下:
# 配置 jenkins 环境, 如果使用 Jenkins 打包的话需要设置
def cl_setup_jenkins
setup_jenkins(
unlock_keychain: true,
keychain_path: "钥匙串的全路径",
keychain_password: "钥匙串的密码"
)
end
# 更新仓库提交记录
def cl_git_update
git_submodule_update(
recursive: true,
init: true
)
end
# 更新 pod 仓库
def cl_pod_update
cocoapods(
podfile: "./Podfile",
repo_update: true,
verbose: true
)
end
# 更新 adhoc 证书
def cl_match_pre
match(
# 证书类型
type: "adhoc",
# 只拉取证书,不更新 apple connect 上的证书和描述文件
readonly: true
)
end
# 更新 release 证书
def cl_match_release
match(
# 证书类型
type: "appstore",
# 只拉取证书,不更新 apple connect 上的证书和描述文件
readonly: true
)
end
# release 打包配置
def cl_gym_release
gym(
# 打包的方式
export_method:"app-store",
# 最终输出的文件目录
output_directory: "./release_builds",
# 打包相关配置
export_options: "./exportOptions_release.plist",
# 打包使用的 configuration
configuration: "Release",
# 最终输出的包名
output_name: "xxxxxx"
)
end
# pre 打包配置
def cl_gym_pre
gym(
# 打包的方式
export_method:"ad-hoc",
# 最终输出的文件目录
output_directory: "./pre_builds",
# 打包相关配置
export_options: "./exportOptions_pre.plist",
# 打包使用的 configuration
configuration: "Pre",
# 最终输出的包名
output_name: "xxxxxx"
)
end
# 上传 appstore 配置
def cl_deliver_release
deliver(
# 即将要上传包的名称 (刚刚输出的包名填的啥就是啥)
ipa: "./release_builds/xxxxxx.ipa",
# 是否跳过上传截图
skip_screenshots: true,
# 是否跳过上传 metadata 文件
skip_metadata: true
)
end
def normal_release
# 更新仓库提交记录
cl_git_update
# 更新 pod 仓库
cl_pod_update
# 更新证书
cl_match_release
# 打包
cl_gym_release
# 上传 appstore
cl_deliver_release
end
def normal_pre
# 更新仓库提交记录
cl_git_update
# 更新 pod 仓库
cl_pod_update
# 更新证书
cl_match_pre
# 打包
cl_gym_pre
end
default_platform(:ios)
platform :ios do
desc "jenkins 打包发布到 appstore"
lane :jenkins_release do
cl_setup_jenkins
normal_release
end
desc "本地打包发布到 appstore"
lane :local_release do
normal_release
end
desc "上传到打包平台"
lane :jenkins_pre do
cl_setup_jenkins
normal_pre
end
end
上面我们配置了三个 lane
用于打不同的包,在项目的根目录执行 fastlane [lane_name]
即可执行 Fastfile 文件中定义的 lane
;
例子:如果需要在 jenkins 上打线上包,执行 fastlane jenkins_release
;
问题整理:
1、jenkins 打包遇到证书和描述文件找不到的问题
jenkins
上配置的节点账户和打包的 mac 用户权限不一致,默认的 keychain
并不是 login.keychain
,需要配置钥匙串的路径,并且解密钥匙串,然后把证书访问控制设置为允许所有应用程序访问此项目
setup_jenkins(
unlock_keychain: true,
keychain_path: "/Users/server/Library/Keychains/login.keychain-db",
keychain_password: "2345.Com"
)
2、账户开启了双重验证导致上传 app 包失败的问题
- 在 appleid.apple.com/account/manage 上生成一个 application specific password 。
- 通过环境变量
FASTLANE_APPLE_APPLICATION_SPECIFIC_PASSWORD
提供这个application specific password。 - 执行
fastlane spaceauth -u [useremail]
,生成session cookie。 - 通过环境变量
FASTLANE_SESSION
提供 session cookies。
配置如下所示:
FASTLANE_APPLE_APPLICATION_SPECIFIC_PASSWORD=xxxx-xxxx-xxxx-xxxx
FASTLANE_SESSION='---\n- !ruby/object:HTTP::Cookie\n name: myacinfo\n value: DAWTKNV2bef67707f668179532419ac5dbe088822b502efd13e90e844400cb68e02db5d3b81959d76389d93b7df4a46a60432aa88297a77f7a4183f323f989e262dc51140b95932d4be4fd82c3a7b4f50efc218d3ce600bc1829816015207f1faf6f0ac9ab718419b50c77217cee77353128d85fa4459054518195fcf4d11fc0006a85bf2b51072221b341f2d3d0cc926d6d208865bd35a236a634fff2db06bb7b113480e3af4d03f84347a9c2ddf5a1a1f47dafe419722c2723b5edbbb140bea317af75db4b9ebf1745fe83adc2a9774392454fa6d47b29be685fe20d2fe19967f793010f7ae55477c12bc1654d08bc6bdba20e5017a45c18905bd89f24d086c1822d5436613532633238376133353865346235643332363435613662646266343030636238303338336136MVRYV2\n domain: apple.com\n for_domain: true\n path: "/"\n secure: true\n httponly: true\n expires: \n max_age: \n created_at: 2019-03-04 12:11:26.341152000 +08:00\n accessed_at: 2019-03-04 13:40:40.936401000 +08:00\n- !ruby/object:HTTP::Cookie\n name: dqsid\n value: eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJqdGkiOiJkakxWdzl4MksxOE5OQ0xJU3ViMlF3In0.HYQQz28M8JMzQo8b3fq_PdB-LSKzMHB_PgDoWNCzErk\n domain: olympus.itunes.apple.com\n for_domain: false\n path: "/"\n secure: true\n httponly: true\n expires: \n max_age: 1800\n created_at: &1 2019-03-04 13:40:41.932636000 +08:00\n accessed_at: *1\n'