05 | 自动化准备:如何使用 Fastlane 管理自动化操作?

[toc]

本文来自拉勾网课程整理。

前言

要成为一个优秀的iOS开发者,我们要做的事情远多于“开发”,例如我们要构建和打包 App,管理证书,为 App 进行签名,把 App分发给测试组,上传 AppApp Store,等等。这些操作不但繁琐费时,而且容易出错。那么,有没有更便利的方法呢?有,那就是使用 fastlane 来完成这些重复性的工作。接下来这一讲,我们主要聊的也就是这个主题。

fastlane 安装

fastlane 是用 Ruby 语言编写的一个命令行工具,可以自动化几乎所有iOS开发所需要的操作,例如自动打包和签名App,自动上传到 App Store 等等。有了 fastlane,我们就可以开发一套统一的、可靠的和可共享的配置,团队所有成员都可以通过这套配置实现自动化操作,减少重复性劳动。

如何安装 fastlane 呢?我记得在第一讲就曾提到过,可以使用 Bundler 来安装,只需要在 Gemfile 文件里面添加以下一行即可:

gem "fastlane", "2.166.0"

注意,由于是通过Bundler来安装fastlane,每次执行fastlane命令前,都需要加上bundle execbundle exec fastlane)。不过为了简洁,在这里后面凡涉及 fastlane 命令时,我会省略bundle exec部分。

Action 与 Lane

fastlane 为我们提供了一百多个 Action,它们是 iOS 项目开发中所有自动化操作的基础。所谓的Action,你可以理解成是 fastlane 自动化流程中的最小执行单元。一般常用的Action有:

  • scan,用于自动测试App
  • cert,用于自动生成和管理 iOS App 签名的证书;
  • sigh,用于自动生成、更新、下载和修复 Provisioning Profile
  • match,为整个团队自动管理和同步证书和 Provisioning Profile
  • gym,用于自动构建和打包 App
  • snapshot,用于自动在不同设备上截屏;
  • pilot,用于自动把 App 部署到 TestFlight 并管理测试用户;
  • deliver,用于自动把 App 上传到 App Store
  • pem,用于自动生成和更新推送消息的 Profile

这些Action怎么执行呢?我们可以通过fastlane <action>(例如fastlane scan)来执行。下面是执行效果,它提示我选择其中一个 Scheme 来继续执行。

164adc06b1a5743685167ac2988e7c6f

从运行情况可知,尽管这些 Action 为我们提供了不少便利,但还是需要手工输入来继续。所以,我不推荐你直接使用这些 Action,而是根据项目需要,在开发自己的自动化操作时通过传入合适的参数来调用 fastlane所提供的Action

具体来说,我们可以把所需的 Action 组合在一起,开发出对应的自动化操作。在 fastlane 中,我们把这个自动化操作或者任务叫作Lane。实际上, iOS 开发中的所有自动化操作,主要通过Lane来封装和配置的。

11638f2e59f5242254d3e241c12d465e

LaneAction 的关系如上图所示, 一条 Lane 可以通过参数调用一个或几个 Action 。以 Moments app 为例,我们要自动打包和签名 App,那么我就建了一条名叫archive_appstoreLane。因为这条Lane用到的“更新签名”和“打包”在 fastalne 里已经提供了相关的 Action——update_code_signing_settingsgym,我就可以到它官网去寻找,从而减轻了开发工作量。

一般,iOS 项目所需的自动化操作都配置为 Lane 并保存在 Fastfile文件,由 Git 统一管理起来,共享给所有成员。然后,大家就可以使用统一的自动化配置了。

这里的Fastfile文件是怎么出来的呢?

它是由fastlane init命令自动生成。这条命令会建立一个 fastlane 文件夹,文件夹里除了 Fastfile,还有 Appfile,以及执行过程中所生成的一些中间文件(如截图、日志与报告等)。因为我们之前已经在 .gitignore 文件里把这些中间文件忽略了,因此这些中间文件不再保存到 Git 里面。

fastlane 文件夹里的Appfile,用于保存 App 的唯一标识符和Apple ID等信息。当 fastlane 在执行一个Action的时候,首先会使用传递进来的参数,当参数没有传递进来时,fastlane会从 Appfile 文件中查找并使用对应的信息。

比如,我们在 Appfile 配置了app_identifier "com.ibanimatable.moments"以后,在调用matchAction 时可以不传入app_identifier参数,fastlane 会自动把"com.ibanimatable.moments"作为app_identifier的值进行调用。

但是为了方便管理所有的Lane,保证每次执行的效果都一样,我建议在每次调用Action的时候,都明确传递每一个所需的参数,而不是从 Appfile 文件读取。下面我就演示下如何明确传递每一个参数来执行matchAction。

  match(
      type: "appstore",
      force: true,
      storage_mode: "git",
      git_url: "https://github.com/JakeLin/moments-codesign",
      app_identifier: "com.ibanimatable.moments", # pass  app_identifier explicitly
      team_id: "6HLFCRTYQU",
      api_key: api_key
  )

常用 Lane 定义

通过上面的介绍你已经知道,我们会使用Lane来封装项目所需的各个自动化操作。那么,这些Lane是如何开发定义的呢?接下来,我就为你介绍几种非常实用的Lane,一起来看看怎么做。

扫描和检查代码

每条Lane的定义都是放在一个lane <lane_name> do <lane_body> end的代码块里面。它以关键字lane开头,接着是这条 Lane 的名字。 下面是用于检查代码的 Lane 源码。

lane :lint_code do
  puts("Lint code using SwfitLint")
  swiftlint(
    mode: :lint,
    executable: "./Pods/SwiftLint/swiftlint",  # Important if you've installed it via CocoaPods
    config_file: './Moments/.swiftlint.yml',
    raise_if_swiftlint_error: true)
end

在上面的例子中,我们定义了一个叫作lint_codeLane。因为 fastlane 使用 Ruby 开发,所以在 Fastfile 里面,Lane 的名字也遵循它的编码规范,使用小写字母和下划线组合的蛇式命名法

Lane 的实现逻辑放在doend中间,我们可以调用fastlane提供的任意 Action。在这个例子中我们就调用了swiftlintAction,并把lint传递给mode参数,以此来执行代码扫描和检查操作。

特别需要注意的是,由于我们之前使用了CocoaPods 来安装 SwiftLint,因此要为executable参数指定 SwiftLint 的安装路径./Pods/SwiftLint/swiftlint。同时要把 .swiftlint.yml 文件的所在路径也传递给config_file参数。这样就能保证 fastlane 使用了统一的 SwiftLint 版本和规则文件,方便团队所有人执行该Lane时得到统一的效果。

当一条 Lane 开发配置完毕以后,我们就可以在项目的根目录执行 fastlane <lane_name>。比如扫描和检查代码的 Lane ,我们可以在终端输入fastlane lint_code看到它的执行效果。

Driving the lane 'ios lint_code'
Lint code using SwfitLint
--- Step: swiftlint ---
$ ./Pods/SwiftLint/swiftlint lint --config ./Moments/.swiftlint.yml
Linting Swift files in current working directory
Linting 'Strings.swift' (1/87)
Linting 'MomentListItemViewModel.swift' (2/87)
Linting ......s

UIButtonExtensions.swift:14:46: warning: no_hardcoded_strings Violation: Please do not hardcode strings and add them to the appropriate Localizable.strings file; a build script compiles all strings into strongly typed resources available through Generated/Strings.swift, e.g. `L10n.accessCamera (no_hardcoded_strings)
Done linting! Found 6 violations, 0 serious in 87 files.
fastlane.tools finished successfully

在执行过程中,fastlane 先从 Fastfile 文件里名叫lint_codeLane的定义,然后执行了该 Lane里使用到的swiftlint Action。swiftlint Action会把项目下87Swift 源代码文件进行扫描检查,并把所有不符合规范的代码提示给我们

格式化代码

检查代码之后,接下来就是清理不符合规范的代码,比如删掉所有代码中不必要的空格或者空行,修正缩进的大小等等。我们可以定义一条叫作format_codeLane 来执行该功能。有了它以后,我们只需要执行fastlane format_code就能把整个项目所有的代码进行格式化。

lane :format_code do
  puts("Lint and format code using SwfitLint")
  swiftlint(
    mode: :autocorrect,
    executable: "./Pods/SwiftLint/swiftlint",  # Important if you've installed it via CocoaPods
    config_file: './Moments/.swiftlint.yml',
    raise_if_swiftlint_error: true)
end

format_codelint_code两条 Lane 都使用了swiftlintAction,唯一不同的地方是为mode参数传递了autocorrect

排序 Xcode 项目文件列表

在多人开发的项目下,我们经常会修改项目文件,这往往很容易引起合并冲突,而合并xcodeproj文件又是一件非常麻烦的事情。怎么办呢?

一个有效办法就是在每次新建源代码和资源文件时,把xcodeproj里面的文件列表进行重新排序。这样能极大地减低合并冲突的发生。我们把这一个经常使用到的操作也配置到 Fastfile里面,如下所示。

lane :sort_files do
  puts("Sort the files for the Xcode project")
  sh "../scripts/sort-Xcode-project-file.pl ../Moments/Moments.xcodeproj"
end

可以看到,fastlane 除了能调用其提供的Action以外,还可以通过sh来调用其他程序命令。在这里我们调用了由苹果公司提供的一个Perl 程序来为 xcodeproj 里面的文件列表进行排序。你也可以在代码仓库找到这个Perl 程序。

调用其他 Lane 操作

除了调用一些程序命令(如sh)以外,一条 Lane 还可以调用 Fastfile 里面其他的 Lane。例如我们定义了一条叫作prepare_prLane,它可以帮我们在提交 Pull Request 之前做一些必要的准备。下面这个代码表示的就是,这条 Lane 在内部调用了另外两条 Lane ——format_codesort_files,以此来同时完成格式化代码和排序 Xcode项目文件列表的操作。

lane :prepare_pr do
  format_code
  sort_files
end

定义私有 Lane 和返回值

类似于 Swift 语言能通过private来定义内部使用的方法,我们也能定义私有 LaneFastfile 内的其他Lane所调用,提高代码的复用。其做法就是把原先的关键字lane替换成private_lane。例如我们定义一条叫作get_pi的私有 Lane,代码如下。

private_lane :get_pi do
  pi = 3.1415
  pi
end

Lane 的实现体有两行代码,第一行是给一个临时变量pi赋值。第二行表示把pi作为返回值传递给调用者。例如下面就演示了如何调用get_pi并取得返回值。

lane :foo do
  pi = get_pi
  puts(pi)
end

这是执行fastlane foo的结果:

Driving the lane 'ios foo'
--- Step: Switch to ios get_pi lane ---
Cruising over to lane 'ios get_pi'
Cruising back to lane 'ios foo'
3.1415

fastlane 首先调用fooLane,然后进去get_piLane 并返回到foo,同时把返回结果打印出来。

总结

这一讲我介绍了如何从头开始搭建一个 fastlane 环境。在这里需要注意三点:

  1. 不要单独手工执行 fastlane 所提供的Action,而是使用 Fastfile 文件来统一开发、配置和管理日常中经常使用的所有自动化操作;
  2. 在开发我们的 Lane 时,要优先使用和调用 fastlane 提供的 Action,因为这些 Action 都是经过社区完善的,且会随着 Xcode 版本的升级而更新;
  3. 当我们调用 fastlane 所提供的 Action 时,要明确传递各个参数,在执行过程中就无须任何手工交互就能从头到尾执行整个操作。
4957c6568ef5caeb3f503edb88ac6510

有了项目需要的所有Lane以后,能有效减轻团队成员的重复劳动,并为项目的自动化和工程化打下坚实的基础。

源码地址:

Fastfile 文件地址:

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,053评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,527评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,779评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,685评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,699评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,609评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,989评论 3 396
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,654评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,890评论 1 298
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,634评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,716评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,394评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,976评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,950评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,191评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,849评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,458评论 2 342

推荐阅读更多精彩内容