fastlane 介绍
Fastlane是一套使用Ruby写的自动化工具集,旨在简化Android和iOS的部署过程,自动化你的工作流。它可以简化一些乏味、单调、重复的工作,像截图、代码签名以及发布App
文档地址:
安装
$ sudo gem install fastlane
如果用的是mac自带的ruby,需要 sudo权限
使用: sudo gem install fastlane
如果报错:ERROR: While executing gem ... (Errno::EPERM) Operation not permitted - /usr/bin/commander
使用: sudo gem install -n /usr/local/bin fastlane
初始化:
在项目根目录下,初始化Fastlane:
fastlane init
新版本安装的时候出现了下面的分支选择,按要求选择就行
1. 📸 Automate screenshots
2. 👩✈️ Automate beta distribution to TestFlight (自动testfilght型配置)
3. 🚀 Automate App Store distribution (自动发布型配置)
4. 🛠 Manual setup - manually setup your project to automate your (需要手动配置内容)
fastlane操作改变后我们再了解一下有哪些配置文件:
Appfile: 存储有关开发者账号相关信息
Fastfile: 核心文件,主要用于 命令行调用和处理具体的流程,lane相对于一个方法或者函数
Deliverfile: deliver工具的配置文件
metadata: 元数据文件夹
Matchfile: Match操作对应的配置文件
screenshots: 截图文件夹
除开手动配置项,fastlane 会要求填写Apple ID,选择你的Team(如果有多个) 然后fastlane会自动检测当前目录下项目的App Name和App Identifier、Project。然后自行确认并按流程执行。
常用命令:
- fastlane actions: 展示所有有效action列表
- fastlane action [action_name]: 展示一个action的详细说明,使用方法等
- fastlane lanes: 展示fastfile中的所有lane
- fastlane list: 展示fastfile中的所有的有效的lane
- fastlane new_action: 创建一个新的action
- fastlane env: 打印fastlane、ruby环境,一般提bug到issue的时候会要求提供
生命周期:
执行顺序 | 方法名 | 说明 |
---|---|---|
1 | before_all | 在执行 lane 之前只执行一次 |
2 | before_each | 每次执行 lane 之前都会执行一次 |
3 | lane | 自定义的任务 |
4 | after_each | 每次执行 lane 之后都会执行一次 |
5 | after_all | 在执行 lane 成功结束之后执行一次 |
6 | error | 在执行上述情况任意环境报错都会中止并执行一次 |
其他:
如果Deliverfile、screenshots和metadata没有自动生成,通过deliver init 可以重新初始化
fastlane的配置会要求输入开发者账号密码,通过spaceship与Apple交互,并会产生一份有效期一个月的cookies文件:文件地址: ~/.fastlane/spaceship/[email]/cookie (两步验证问题)
Matchfile: match 这个action的配置文件,fastlane match init 自动生成,存放git地址等
fastlane 使用
lane的使用
lane是fastfile中的方法定义标签,可以理解为swift中定义一个函数,前面的 func。fastlane 都是基于ruby,所以fastfile中也是使用ruby语法的。
定义一个简单的无参lane
lane :package
puts "这是一个lane"
end
定义一个带参的lane,在fastfile中option类似于一个字典集。我们可以通过 option[:configuration] 取其中value
lane :package do |option|
configuration = option[:configuration]
puts configuration
end
//lane的调用
package(configuration: 'Release', export_method: 'ad-hoc')
常用Action
Action列表文档: Actions
我们常用的主要包括下面几部分,其他action的使用可以参考官方文档:
- scan => 自动运行测试工具,并且可以生成漂亮的HTML报告
- match => 一个新的证书和配置文件管理工具。把所有需要用到的证书传到git私有库上,任何需要配置的机器直接用match同步回来就不用管证书问题了
- gym => Fastlane家族的自动化编译工具,和其他工具配合的非常默契
- deliver => 自动上传截图,APP的元数据,二进制(ipa)文件到iTunes Connect
- pilot => 管理TestFlight的测试用户,上传二进制文件
- spaceship => 为pilot,boarding和deliver等工具提供和 iTC 和 ADC 的交互API。spaceship本来是个独立的项目,后来被Fastlane收编进来 非官方的iTunes Connect JSON API的文档
常用Action使用
scan
release情况下无法正常运行scan,需要手动去Build Setting中更改enable Testability 在release 下的状态,改为 yes才可以运行。但是官方不建议做release下开启,Test一般在development configuration 下执行。
match
一个新的证书和配置文件管理工具。它会把所有需要用到的证书传到git私有库上,任何需要配置的机器直接用match同步回来就不用管证书问题了。保证大家用的都是同一份。不过我们一般都是一台机器需要用到distribution证书,所以意义不大。
1.match只认识通过match方式创建的pp文件 证书,其他方式创建的不予理会。
2.使用match 需要先撤销现在的证书。
3.如果苹果端的证书,pp文件已删除,那么远端git上的文件也会失效,并且在重新match的时候会失败,好像就只能删光 git端内容,重新match一遍。
常用参数:
git_url : 指定对应git地址
git_branch : 指定对应branch
type :请求文件类型, appstore, adhoc, development, enterprise
app_identifier : app_bundle_identify
clone_branch_directly : 只更新对应branch,只有在存在这个branch时才生效
force_for_new_devices : 如果设备devices列表更新了,就强制更新配置概要文件
verbose :打印出额外的信息和所有的命令
gym
常用参数:
scheme :指定打的哪个scheme
project :指定project (未使用cocopods)
workspace :指定workspace (使用cocopods)
clean :打包前clean
xcargs : 附加一些参数传递给xcodebuild 如: xcargs: 'DEBUG_INFORMATION_FORMAT="dwarf-with-dsym"',
export_method :出包方法 app-store, ad-hoc, package, enterprise, development
configuration : 指定构建App的配置 Release、Debug、自定义
output_directory : 输出目录
output_name :输出名称
include_symbols :是否包含调试符号
include_bitcode :是否开启bitcode
纯swift工程打包,在非appstore证书下签出来的包都缺少一个swiftsupport文件夹,里面放的是swift的支持库。
deliver
用于直接发包到appstore,可以选择跳过图片和元数据上传,只提包,后面再配图和数据:如下 skip_screenshots 和 skip_metadata 参数
deliver(
ipa: "#{OUTPUT_DIRECTORY}" + "/" + "#{IPA_NAME}",
skip_screenshots: true,
skip_metadata: true
)
pilot
用于发布testflight内部测试,属于testflight action的别名
常用参数:
ipa :要提交的包地址
team_name、team_id :如果有多个team 用于区分team
skip_waiting_for_build_processing : 在提交完成后的等待是否跳过,一般跳过
changelog
testflight(
ipa : '../xx.ipa'
)
spaceship [常见问题官方解释]
spaceship其实一般fastfile中不会使用到,但是由于涉及到与ADC的通信,会出现一些奇奇怪怪的问题,所以对它也要有一点了解。
当第一次使用fastlane安装的时候,会要求输入账号密码核实你的身份来连接ADC,这个时候你提供的登录验证会处理为会话存到 spaceship 的 cookie,会话大概一个月有效期,一个月后失效,通常我们只有在打包失败后才会发现这个问题。
上面提到的这些action都是常用的,正常打包流程必不可少的部分,还有一些常用于辅助作用的Action
- resign :重新签名
fastlane sigh resign dev.ipa --signing_identity "证书ID" -p “dev.mobileprovision"
- get_info_plist_value: 获取info.plist中得某个key的值
- set_info_plist_value: 设置info.plist中得某个key的值
- increment_build_number :自动递增项目build号
- increment_version_number :自动递增项目版本号
- get_version_number: 获取版本号(新fastlane版本不能再添加scheme参数,只需要target即可)
以上两个都需要先配置好xcode, 配置文档
自定义Action
由于开发需求各自不同,已有的action不满足的情况下,Fastlane支持定义自己的Action。Fastlane为我们提供了现成的模板,即使你对Ruby的语法不熟悉,也没有关系,Fastlane是开源的嘛,可以直接下载源码看看别人的Action是怎么写的就知道了,我们可以在这个目录下找到所有的Action文件:
假设,我们针对pod的执行创建一个action来针对下面三种情况的执行
pod install --no-repo-update (避免master repo的每次更新耗时)
pod update --no-repo-update (避免master repo的每次更新耗时)
pod repo update XXX (私有repo的更新)
自定义Action的流程大约如下,首先,我们在终端中执行命令:
fastlane new_action
然后根据提示,在命令行中敲入action的名字pod,然后Fastlane会在当前目录的actions文件夹中帮我们创建了一个pod.rb的Ruby文件 (此处只有部分代码)
module Fastlane
module Actions
module SharedValues
POD_CUSTOM_VALUE = :POD_CUSTOM_VALUE
end
class PodAction < Action
def self.run(params)
UI.message "Parameter API Token: #{params[:api_token]}"
end
......
def self.available_options
# Define all options your action supports.
end
......
可以看到,自定义的Action都是隶属于Fastlane/Actions这个module,并且继承自Action这个父类。虽然模板中的内容还挺多,不过不用担心,大部分内容都是一些简单的文本描述,对于我们来说只需要重点关注这两个方法就行:
- self.run方法:这里放置的是实际的业务处理代码。
- self.available_options方法:这里声明需要对外暴露出的参数,没有声明的参数在执行过程中无法使用。
最终写完结果如下:
module Fastlane
module Actions
module SharedValues
POD_INSTALL_CUSTOM_VALUE = :POD_INSTALL_CUSTOM_VALUE
end
class PodInstallAction < Action
def self.run(params)
repo = "-no-repo-update"
command = []
command << "pod install"
if params[:repo_update]
repo = "--repo-update"
end
command << repo
if params[:verbose]
command << "--verbose"
end
result = Actions.sh(command.join(' '))
UI.success(command.join(' ') + " Successfully ")
return result
end
def self.description
"pod install action"
end
def self.details
"verbose / repo-update"
end
def self.available_options
[
FastlaneCore::ConfigItem.new(key: :verbose,
description: "Allow output detail in console",
optional: true,
is_string: false,
default_value: false),
FastlaneCore::ConfigItem.new(key: :repo_update,
description: "Allow output detail in console",
optional: true,
is_string: false,
default_value: false)
]
end
def self.output
end
def self.return_value
end
def self.authors
["yang"]
end
def self.is_supported?(platform)
platform == :ios
end
end
end
end
Action引用机制
远程引用
# 远程Git引用:
import_from_git(url: 'https://github.com/xilankong/ruby', branch: 'master')
# 复写发布项目的lane
lane :do_deliver_app do |options|
# ...
end
本地引用
import "../GeneralFastfile"
actions_path '../custom_actions_folder/'
lane :appstore do |options|
# ...
end
Plugin
我们在使用Fastlane的时候常常会遇到这样的场景:
- 我的自定义Action需要在多个内部项目中使用
- 我觉得这个自定义Action很不错,想共享给其他的团队使用
此时,拷贝粘贴虽然可以解决问题,但并不是一个聪明的方案。将Action发布到Fastlane的官方仓库倒是一个不错的选择,但是官方仓库本身对Action的要求比较高,并不会接收非通用性的Action,即使接收了,整个发布周期也会比较长,而且以后无论是升级还是Bug修复,都依赖Fastlane本身的发版,大大降低了灵活性。
所以从1.93开始,Fastlane提供了一种Plugin的机制来解决这种问题。大家可以理解为:Plugin就是在Action的基础上做了一层包装,这个包装巧妙的利用了RubyGems这个相当成熟的Ruby库管理系统,所以其可以独立于Fastlane主仓库进行查找,安装,发布和删除。
我们甚至可以简单的认为:Plugin就是RubyGem封装的Action,我们可以像管理RubyGems一样来管理Fastlane的Plugin。
但是,如果为了多项目共享任务,或者共享fastfile,可以通过Action的远程引用机制。所以Plugin不过多介绍。
持续化打包还需要的Action
- monkey
- ftp 提交远程服务器 (已解决)
- 测试日志解析
- jira提交bug
- 自动打framework (已解决)
常见问题
1. gem源问题
gem ruby源已更新为 https://gems.ruby-china.com,本机版本2.7.7
$ gem sources --add https://gems.ruby-china.com/ --remove https://rubygems.org/
$ gem sources -l
https://gems.ruby-china.com
# 确保只有 gems.ruby-china.com
2、ruby版本必须高于2.0,测试本机2.0也无效,所以最好2.0+
以下是之前2.3.1正常安装的时候的操作:
rvm安装
curl -L get.rvm.io | bash -s stable
安装成功后、启用rvm
source ~/.bashrc
source ~/.bash_profile
测试安装结果
rvm -v
升级ruby
rvm install 2.3.1
查看安装的所有ruby
rvm list
切换ruby
rvm use 2.3.1
设置rvm默认版本
rvm --default 2.3.1
fastlane 安装:
sudo gem install -n /usr/local/bin fastlane
3、关于开启两步验证如何全自动化问题
开启两步验证后,提交testfilght或者appstore会出现如下提示,要求手动确认并 输入6位code
Two Factor Authentication for account 'xxxxx@xx.com' is enabled
If you're running this in a non-interactive session (e.g. server or CI)
check out https://github.com/fastlane/fastlane/tree/master/spaceship#2-step-verification
Please enter the 6 digit code:
这样明显影响全自动化提交操作。
解决方案:
fastlane提供的两步验证解决方案:
1.访问 https://appleid.apple.com/account/manage
2.生成一个 APP-SPECIFIC PASSWORDS,保留生成的特殊密码
3.使用环境变量提供这个密码给fastlane: FASTLANE_APPLE_APPLICATION_SPECIFIC_PASSWORD
4.执行fastlane spaceauth -u user@email.com,生成session cookie。
5.通过环境变量FASTLANE_SESSION 提供session cookies。
配置地方:
打包机:~/.bash_profile 中,配置 FASTLANE_APPLE_APPLICATION_SPECIFIC_PASSWORD 和 FASTLANE_SESSION
例如:
export FASTLANE_APPLE_APPLICATION_SPECIFIC_PASSWORD=特殊密码
export FASTLANE_SESSION=session cookie
本机使用的是Item2 /bin/zsh 所以配置在 ~/.bash_profile
Jenkins:配置对应环境变量即可
还有一个小伙伴告知另一个配置方式
ENV["FASTLANE_APPLE_APPLICATION_SPECIFIC_PASSWORD"] = "xxxxxx"
这个和环境变量配置是一个意思,但是涉及密码写在配置文件里面
但是,本文测试一个月后发现依然失效,最后选择的方案是开设一个未开启两步验证的账号作为开发账号的子账号专门用来打包。
如有不过期方式,欢迎补充
4. 附录一份简单的fastfile文件,涉及到私密数据部分地方会符号代替, 与fastlane无关的部分会删除,本文用的是Jenkins + fastlane + apphost ,以下提供部分fastfile文件、Jenkins shell、本地脚本fastlane.sh
fastfile 部分
#声明
APP_NAME = “XXX”
WORKSPACE = “XXX.xcworkspace"
SCHEME = “XXX”
IPA_TIME = Time.now.strftime("%Y%m%d_%H%M")
OUTPUT_DIRECTORY = "packages"
APP_INFO_PLIST_PATH = ‘./XXX/Info.plist'
ENV_PREFIX=""
IPA_NAME = ""
platform :ios do
#
before_all do
xcode_select "/Applications/Xcode.app"
FASTLANE_XCODEBUILD_SETTINGS_TIMEOUT = "40"
end
#debug包
lane :iosDebug do
ENV_PREFIX="debug_"
EXPORT_METHOD = "development"
package(configuration: "Debug")
end
#release包
lane :iosRelease do
ENV_PREFIX="adhoc_"
EXPORT_METHOD = "ad-hoc"
package(configuration: "Release")
end
#发布包
lane :iosAppStore do
ENV_PREFIX="appstore_"
EXPORT_METHOD = "app-store"
package(configuration: "Release")
end
#打包函数
lane :package do |option|
cocoapods
PLIST_INFO_VERSION = get_version_number(target: "#{SCHEME}")
PLIST_BUILD_VERSION = get_info_plist_value(path: "#{APP_INFO_PLIST_PATH}", key: 'CFBundleVersion')
IPA_NAME = "#{ENV_PREFIX}" + "#{APP_NAME}_" + "#{IPA_TIME}_" + "#{PLIST_INFO_VERSION}" +"_#{PLIST_BUILD_VERSION}"+ ".ipa"
#打包
gym(
scheme: "#{SCHEME}",
export_method: "#{EXPORT_METHOD}",
configuration: option[:configuration],
output_directory: "#{OUTPUT_DIRECTORY}",
include_symbols: true,
include_bitcode: false,
xcargs: 'DEBUG_INFORMATION_FORMAT="dwarf-with-dsym"',
output_name: "#{IPA_NAME}",
export_xcargs: "-allowProvisioningUpdates"
)
xcclean(
workspace: "#{WORKSPACE}",
scheme: "#{SCHEME}"
)
end
Jenkins部分脚本
#!/bin/bash -l
sh ./script/fastlane.sh "Debug"
fastlane.sh部分脚本
#解决ArgumentError - invalid byte sequence in US-ASCII错误
#修改终端语言、地区等国际化环境变量
export LANG=en_US.UTF-8
export LANGUAGE=en_US.UTF-8
export LC_ALL=en_US.UTF-8
export PATH=$PATH:/usr/local/bin
#更换ruby环境,对应的修改有 打包机下面的 .bashrc .zshrc .zlogin
source $HOME/.rvm/scripts/rvm #source ~/.bashrc
#解锁keychain,是其它工具可以访问证书,解锁后设置keychain关闭时间为1小时, xxx为用户名
security -v unlock-keychain -p "xxx" "/Users/xxx/Library/Keychains/login.keychain"
security set-keychain-settings -t 3600 -l "/Users/xxx/Library/Keychains/login.keychain"
/usr/local/bin/pod update --verbose --no-repo-update
# fastlane profile
if [[ $1 == "Debug" ]]; then
fastlane iosDebug
elif [[ $1 == "Release" ]]; then
fastlane iosRelease
elif [[ $1 == "TestFlight" ]]; then
fastlane iosTestFlight
elif [[ $1 == "AppStore" ]]; then
fastlane iosAppStore
fi