Rake task vs. Fastlane lane

在做 CI 的过程中,会遇到很多任务处理,有的有互相依赖、有的有前置条件等,对此,我主要阅读了 Rake 的 task 和 Fastlane 的 lane 两种方式。各有千秋,从书写调用、任务依赖、挂载支持、复用等方面入手,最后会通过讨论不同的实现细节,来编写更适合当前 CI 流程的 DSL。

环境信息

fastlane 2.85.0
rake 12.3.1


很多 DSL 都用 Ruby 来实现,这得益于它的语法糖和强大的元编程能力。Rake 是一个很出名的 DSL in Ruby,类似于 Make,它将一个个 task 串起来,形成了完整的 Workflow。而 Fastlane 是移动端强大的自动化工具,当我们使用时,需要编写 Fastfile,将自动化任务串起来。Fastfile 中的关键字 lane 和 Rake 中的关键字 task 都表示定义一个任务,但是他们的实现、组织方式、依赖管理、复用方式都有很大不同。

书写与调用方式

任务定义

Rake 和 Fastlane 的任务定义基本相同,熟悉其中一种,就能很快熟悉另一种。

1
2
3
4
5
6
7
8
9
10
11
# rake
desc "Rake 任务描述"
task :rake_task, [:arg1] do |cmd, args|
puts "rake task"
end

# fastlane
desc "Fastlane 任务描述"
lane :lane_task do |options|
puts "lane task"
end

调用

1
2
3
4
5
6
7
8
9
# rake
rake rake_task
rake rake_task["A"]
rake rake_task ARG1=A
env ARG1=A rake rake_task

# fastlane
fastlane lane_task
fastlane lane_task ARG:A

在没有参数的时候,两种调用都差不多。但是在有参数的时候… Fastlane 的方案明显比 Rake 更加优雅。看到 Rake 能通过那么多种方式传参,好像很 6 的样子,但实际却恰恰相反,这正好说明了 Rake 传参的麻烦与不统一。为了能给 Rake 正确的传参,Stack Overflow 上还有很多相关的问答。其中一个高票回答大家感兴趣可以看看。

任务依赖

Rake

Rake 可为每个 task 添加依赖,比如:task_b 的先行条件 task_a:

1
2
3
4
5
6
7
8
9
desc "task_a desc"
task :task_a do |cmd, args|
puts "A"
end

desc "task_b desc"
task :task_b => [:task_a] do |cmd, args|
puts "B"
end

调用

1
rake task_b # => A B

Fastlane

Fastlane 的依赖和 Rake 不同,不能添加 lane_a 依赖 lane_b 中每个 lane 之间的调用关系。只能通过在 lane 内部调用来实现(或者是支持但我不知道…求告知):

1
2
3
4
5
6
7
8
9
10
desc "lane_a desc"
lane :lane_a do |options|
puts "A"
end

desc "lane_b desc"
lane :lane_b do |options|
lane_a(options) # 调用 lane_a
puts "B"
end

调用

1
fastlane lane_b # => A B

挂载点支持

Rake 并没有原生提供挂载点,网上很多例子都是自己实现,而 Fastlane 提供了 before_allbefore_eachafter_allafter_each 四种挂载点。可以在所有任务开始前后,每个任务开始前后处理一些事情,比如 pod install 等。

1
2
3
4
5
6
7
8
before_all do |options|
puts "-1"
end

desc "lane_a desc"
lane :lane_a do |options|
puts "A"
end

调用

1
fastlane lane_a # => -1 A

复用

书写方式两种方案类似,而调用另一个任务的方式不太一样。

Rake

Rake 的调用和传参一样恶心,硬编码,还需要 call 方法。

1
2
3
4
desc "task_b desc"
task :task_b do |cmb, args|
Rake::Task["task_a"].invoke
end

Fastlane

Fastlane 直接是函数调用的方式,更优雅。

1
2
3
4
desc "lane_b desc"
lane :lane_b do |options|
lane_a(options)
end

多参数

如果你觉得这还不足以展现出 Fastlane 的优雅,那么多参数的时候:

1
2
3
4
5
6
7
8
desc "lane_b desc"
lane :lane_b do |options|
lane_a(
name: "saitjr",
location: "shenzhen",
company: "Meitu Inc."
)
end

Rake 的传参…我想都不敢想。

优劣势

虽然我明显倾向于 Fastlane,但其实他们还是各有优劣势的。

命名空间

Rake 支持,Fastlane 不支持。

1
2
3
4
5
6
7
8
9
namespace :a_name do
desc "descccc"
task :task_a do |cmd, args|
puts "A"
end
end

# 调用
rake a_name:task_a

默认任务

Rake 支持,Fastlane 不支持。

1
2
3
4
task :default => [:task_a]

# 调用
rake # => A

私有任务

Rake 不支持,Fastlane 支持。

1
2
3
4
desc "private lane"
private_lane :lane_a do |options|
puts "A"
end

文件命名

Rake 灵活优雅,Fastlane 鸡肋。

Rake 会遍历当前或 tasks 等多个文件夹中,名称为 Rakefilerakefilexxx.rakexxx.rakefile 等文件。

Fastlane 的文件路径则必须是 fastlane/Fastfile

任务列表

都支持。

1
2
3
rake -T

fastlane lanes

其他

除以上的对比外,Fastlane 有几个明显的劣势:

  • 太重。因为 Fastlane 作为移动端的集成工具,DSL 这部分并没有单独封装,要用的话,还必须安装全部依赖。
  • 输出信息太多。虽然有环境变量可以控制,但是 Fastlane 在自动查找 issue、提示版本升级等信息实在是太多了。

最后

看了这两种任务管理在使用上区别以后,接下来会从源码方面分析他们的具体实现,这其中还可以学习到很多 Ruby 元编程的实践。而最终目的是想要打造一个适合自己业务的工具。