经过前 4 篇文章的基础知识和不同的场景介绍后,相信大家已经对 Fastlane 有了一个较为完整的认识,同样,今天我还是结合几个实际的场景,来讲讲 Fastlane 的一些高级用法。
前言
软件开发就像是完成一件艺术品一样,是一个循序渐进,不断打磨的过程。刚开始的时候由于我们对某个语言或框架了解的不够充分,所以往往只停留在实现功能的阶段,而不会过多的考虑到是否能够有更好的特性和更高效的方法来解决问题。
随着编程经验的积累和对语言框架的日益了解,发现之前写过的很多代码其实都存在不少问题,都有不少优化和提升的空间,于是出于责任心和对编程的热爱,我们一定会花时间不断的重构和优化代码,直到自己满意为止。
使用 Fastlane 的过程也同样如此,在第一次发现了这样一个新工具时,心中莫名的兴奋,于是不管三七二十一,立刻就动手使用了,然后随着使用的场景增多,问题也逐渐凸显了出来,于是我们开始探寻 Fastlane 的一些高级用法,以便更高效,更优雅的解决问题。
前置和后置 Action
一般情况,我们在处理 iOS 的自动化流程时,会面临着单元测试,库编译发布,APP 打包等等多种不同的场景,而 APP 打包又需要区分不同的环境,比如:Test,Adhoc 和 AppStore,而这些场景和环境的处理方式是有所不同的,所以针对每种情况,我们都需要编写对应的 Lane,比如我们新建一个名叫ios_fastfile的文件,内容如下:
# 单元测试的 Lane lane :test do |options| git_pull cocoapods xctest end # AdHoc 环境打包的 Lane lane :adhoc do |options| git_pull cocoapods increment_build_number gym upload_to_fir end # AppStore 环境打包的 Lane lane :appstore do |options| git_pull cocoapods increment_build_number gym deliver end
当然以上的这个些 Lane 都是简写,只是个示意,实际情况会复杂的多。从这些 Lane 中我们可以看到,每个流程其实都有前置的条件:
- git pull 最新的代码
- 更新最新的 cocoapods 依赖
所以,大家肯定会问,是否有类似 Ruby on Rails 中 controller 的前置过滤器之类的机制,能够方便的处理这个情况。答案是肯定的,类似这样,在一个 Fastfile 中,每个 Lane 共有的前置流程的情况,我们可以借助 Fastlane 提供的 before_all 方法来处理,在ios_fastfile中我们增加如下的代码:
before_all do |lane, options| git_pull cocoapods end
before_all 顾名思义,就是在在执行每个方法之前首先执行的代码,使用 before_all 我们可以将ios_fastfile的代码就可以简化为:
before_all do |lane, options| git_pull cocoapods end # 单元测试的 Lane lane :test do |options| xctest end # AdHoc 环境打包的 Lane lane :adhoc do |options| increment_build_number gym upload_to_fir end # AppStore 环境打包的 Lane lane :appstore do |options| increment_build_number gym deliver end
嗯,看上去代码简化不少,而且以后要添加公用的前置代码,都可以放在 before_all 中进行处理,维护起来非常方便。
另外,Lane 本身和使用到的 Options 参数们也可以很方便的传递给 before_all 方法,这样可以更加方便的处理各种特殊情况,比如:有的 Lane 没有处理 git_pull 的流程,那么我们只需要在方法中加一个判断即可。
当然,除了 before_all 之外,Fastlane 还提供 after_all 来处理共有的后置逻辑,比如:我们在所有的 lane 之后,要通过 Slack 或 Hipchat 通知到相关的工程师们,那么我们就可以把这些 action 写在 after_all 方法中。
after_all do |lane,options| slack(message: "fastlane was successful", success: true) end
对于每个 Lane 在执行过程中,如果遇到错误,我们也需要通过 Slack 或 Hipchat 等工具通知到大家,此时,我们可以在 Fastfile 中添加一个全局的 error 方法:
error do |lane, exception| slack(message: exception.message, success: false) end
引用机制
当一项新的技术在团队内部引入的时候,往往会从一个非主业务的项目中进行灰度尝试。我们也不例外,所以我们首先将 Fastlane 引入到我们的医生版的 iOS 客户端中,使用一段时间感觉不错之后,就推广到了 Android 平台和用户版客户端中,最后引入到各种私有库项目的管理中。目前大约涉及到了 30 多个项目,在这个过程中,我们发现了两个问题:
- 由于我们自定义了很多 action,那么这些 action 都需要拷贝到各个项目的 fastlane 目录中,这样就导致了一个维护的问题,即:添加或修改任意的自定义 action,都需要去各个项目中处理一遍。
- 我们目前的项目按类型分为 4 种:iOS App,iOS Pod 库,Android App,Android AAR 库,对于同样的项目类型,lane 的处理流程基本上是一样的,所以每个相同类型的项目的 Fastfile 基本上都是一致的,这就带来了和 1 一样的问题:如果要修改任意一个项目的 Fastfile,那么就意味着同样也得修改其它同类型项目中 Fastfile,因为这些 Fastfile 都是放在各自项目目录下的 fastlane 文件夹中的。
刚开始项目比较少的时候,手工还可以处理,当项目逐渐增多的时候,问题就愈发严重了,不但要改的地方多,而且还容易出错,于是我们思考是否有能够由一种引用的机制存在,能够在顶层维护一份公用的 action 和 fastfile,这样在项目中只需要引入这些顶层的文件们就能解决上面的问题。
经过仔细研究 Fastlane 的特性之后,我们发现这个问题早就在其作者的考虑之中了。Fastlane 不但提供了引用机制 import,还提供了远程引用模式,即:import_from_git。
有了这个机制之后,我们不但可以在将自定义 action 们和 Fastfile 们统一管理,而且还可以放在 git 上进行远程分布式管理,其带来的好处就是:各个项目只需要在自己的 Fastfile 的顶部进行引用声明即可:
# 远程 Git 引用: import_from_git(url: 'https://github.com/GengmeiRD/Fastfiles', branch: 'master') lane :appstore do |options| # ... end {1}
这样,每次执行 fastlane 的命令时,首先会从 git 上将需要的文件 clone 这个项目到本地的临时文件夹中,然后再执行相关命令。当然,如果某个项目中,你不想使用远端上的某个 lane,而是需要自定义一份,那么只需要在项目中的 Fastfile 中复写这个 lane 即可:
# 远程 Git 引用: import_from_git(url: 'https://github.com/GengmeiRD/Fastfiles', branch: 'master') # 复写发布项目的 lane lane :do_deliver_app do |options| # ... end
当然,如果你觉得远程管理这些 action 和 fastfile 们比较麻烦的话,同样可以使用本地引用进行管理,fastlane 提供了两个命令,分别用来引入本地的 Fastfile 和 action 目录:
import "../GeneralFastfile" actions_path '../custom_actions_folder/' lane :appstore do |options| # ... end
我们将团队内部使用到的远程 action 和 Fastfile 管理已经发布到了 github 上,有兴趣的同学可以参考:
https://github.com/GengmeiRD/Fastfiles
上下文常量
在使用 Fastlane 的过程中,我们往往需要一些和上下文相关的常量,比如:AppStore 的账号和密码,ipa 和 dsym 文件的输出的地址,模拟器或设备的 UDID 等等。这些常量如果直接写死在 Fastfile 中显然是不利于维护的。所以我们可以考虑使用以下的方式来处理:
使用 dotenv
首先使用 gem 安装 dotenv ,然后新增一个.env 文件,然后将所有用到的常量定义在里面,比如:
WORKSPACE=YourApp.xcworkspace ITUNESCONNECT_ACCOUNT=your-itunesconnect-account
然后在 fastfile 中使用 ENV 进行调用:
lane :appstore do |options| increment_build_number gym(workspace: ENV['WORKSPACE']) deliver(username: ENV['ITUNESCONNECT_ACCOUNT'],) end
最后将这个.env 文件拷贝到和 Fastfile 的同级目录即可。当然如果有常量需要在多个项目中公用的话(比如:iTunesConnect 的账号),可以建立一个软连接指向同一个.env 文件,然后在目录中创建一个.env.default 文件,放置本项目下专属的常量。
使用 export 命令
如果你使用的是 Mac 或 linux 操作系统,可以在系统的环境的变量中使用 export 命令直接定义系统级别的常量,当然为了不和其它常量冲突,建议增加一个 FASTLANE 前缀,比如:
#set env vars export FASTLANE_WORKSPACE="<YourApp.xcworkspace>" export FASTLANE_ITUNESCONNECT_ACCOUNT="<your-itunesconnect-account>"
调用方法和使用.env 一样。
lane :appstore do |options| increment_build_number gym(workspace: ENV['FASTLANE_WORKSPACE']) deliver(username: ENV['FASTLANE_ITUNESCONNECT_ACCOUNT'],) end
结语
以上的几个高级用法只是笔者在使用过程中遇到的一隅,更多的惊喜还等待大家自己去探索去发现,官方文档中有全面的介绍: https://docs.fastlane.tools/advanced/
Fastlane 是一个持续维护,快速发展的工具,从笔者的第一篇文章到现在,短短两个月的时间,Fastlane 已经发布了 20 多个版本,在 Github 上新增了 2000 多个 star,同时又多了 100 多位工程师加入到了 Contributors 的大家庭中。事实再次证明优秀的开源项目总能得到大家的认可,关注和参与。
本文是 Fastlane 实战系列的第五篇文章也是最后一篇,希望这个系列的文章能够真正帮助大家了解 Fastlane,也希望看完这个系列的文章后,有更多的同学借助 Fastlane 和各种自动化工具来提升自己的效率,毕竟重复性的劳动和流程化的工作就交给机器们去做吧。
最后祝大家使用 Fastlane 愉快。
感谢徐川对本文的审校。
给InfoQ 中文站投稿或者参与内容翻译工作,请邮件至 editors@cn.infoq.com 。也欢迎大家通过新浪微博( @InfoQ , @丁晓昀),微信(微信号: InfoQChina )关注我们。
评论 1 条评论