写点什么

Fastlane 实战(二):Action 和 Plugin 机制

  • 2016-10-17
  • 本文字数:5739 字

    阅读完需:约 19 分钟

作为架构师的我们常常要面临的一个难题就是技术选型。现在无论是商业项目也好,开源项目也好,可供选择的方案实在是太多,其中优秀的方案也是层出不穷,这就要求我们在做技术选型的时候,需要从多个维度进行考量,其中良好的扩展性是我们重点考量的对象。

任何一个优秀的框架或平台都应该具有良好的扩展性,以满足不断变化的业务场景和个性化要求,而这种扩展性的其中一个方面则体现在:是否能够提供一种机制,这种机制既能满足二次开发的便捷性,又最小化甚至不会对原有的系统产生任何的侵入或破坏。

站在这个角度上,今天我们就来介绍一下 Fastlane 的两种扩展机制,Action 和 Plugin。

Fastlane 的 Action 机制

Fastlane 本身包含两大模块,一个是其内核部分,另外一个就是 Action 了。Action 是 Fastlane 自动化流程中的最小执行单元,直观上来讲就是 Fastfile 脚本中的一个个命令,比如:git_pull,deliver,pod_install 等等,而这些命令背后都对应一个用 Ruby 编写的脚本。

我猜想,Fastlane 的作者们在项目的早期甚至规划的阶段,应该就考虑到了这一点:在移动开发中,自动化的业务场景太多,每个团队都有自己的特殊要求,单靠一两个人的力量是无法满足的,所以如何将涉及到实际业务的功能开发,用优雅的方式交给开源社区中庞大工程师们来维护,成为 Fastlane 架构中需要重点考虑的内容。

于是经过不断的探索,讨论和实践,Action 这种扩展机制应运而生。我们可以理解为 Fastlane 建立了一套完整的规则,这个规则是如此的简单易行,无论是官方的工程师还是开源社区的工程师们,大家都在这个规则里进行游戏,这样不但降低了扩展的门槛,吸引工程师们来完善 Fastlane 本身;同时又增强了约束,减少不必要的沟通和代码检查成本。所以我们可以看到无论是官方贡献的,还是 Github 社区贡献的 Action 们,无一例外都隶属于 Action 扩展的一部分。

到目前为止 Fastlane 包含大约 170 多个 Action,大约分为如下几类:

  1. 和移动端持续交付相关的 15 个核心的工具链:如:deliver(上传 ipa,截屏和 meta 信息到 ITC),supply(上传 apk,截屏和 meta 信息到 Google Play),sigh(iOS Provisioning 文件管理)等等,详情如下: https://github.com/fastlane/fastlane#fastlane-toolchain
  2. 和 iOS 相关的,如:ipa,xcode_install 等等
  3. 和 Android 相关的,如:gradle,adb 等等
  4. 和版本控制相关的,如 git_pull,hg_push 等等
  5. 和 iOS 依赖库管理相关的,如:cocoapods,carthage 等等
  6. 第三方平台对接相关的,如:hipchat,jira,twitter,slack 等等

这些 Action 的详情和使用方法可以查看这个链接: https://docs.fastlane.tools/actions/Actions/

应该说几乎涵盖了所有常见的场景,但是如果仍然无法完全满足你的要求的话,就得自己来动手自定义一个了。

场景分析

那么如何来自定义一个 Action 呢?按照习惯,为了便于大家理解,我们还是先从一个业务场景入手。在上一篇文章中,我曾经举过一个例子:私有 Pod 的发布,其步骤如下:

  1. 增加 Podspec 中的版本号
  2. 执行 pod lib lint 命令进行库验证
  3. Git Commit 代码
  4. Git Push 代码到远端
  5. 打一个 Git Tag
  6. 将 Tag Push 到远端
  7. 执行 pod repo push 命令发布库到私有仓库

然后对应以上的几个步骤,我们都可以找到现成的 Action 来实现,所以我们可以在 Fastfile 中增加如下 Lane:

复制代码
desc "Release new private pod version"
lane :do_release_lib do |options|
target_version = options[:version]
project = options[:project]
path = "#{project}.podspec"
git_pull
ensure_git_branch # 确认 master 分支
pod_install
pod_lib_lint(verbose: true, allow_warnings: true, sources:
SOURCES, use_bundle_exec: true, fail_fast: true)
version_bump_podspec(path: path, version_number: target_version) # 更新 podspec
git_commit_all(message: "Bump version to #{target_version}") # 提交版本号修改
add_git_tag(tag: target_version) # 设置 tag
push_to_git_remote # 推送到 git 仓库
pod_push(path: path, repo: "GMSpecs", allow_warnings: true, sources: SOURCES) # 提交到私有仓库
end

自定义 Action

讲到这里,大家可能会问,这不都写完了吗,哪里还需要自定义 Action 啊?别急,其实大约 3 个月前,笔者编写这个 Fastfile 的时候,Fastlane 正好缺少一个 Action 能够支持 Cocoapods 的这个命令,即:

pod lib lint这个命令是用来验证私有的 Pod 库是否正确,所以当时无奈之下,只能自己动手写一个了。写完后发现,这个工作也并没有想象中的那么困难,Fastlane 已经为我们提供了现成的模板,即使你对 Ruby 的语法不熟悉,也没有关系,Fastlane 是开源的嘛,可以直接下载源码看看别人的 Action 是怎么写的就知道了,我们可以在这个目录下找到所有的 Action 文件:

fastlane/fastlane/lib/fastlane/actions/自定义 Action 的流程大约如下,首先,我们在终端中执行命令:

fastlane new_action然后根据提示,在命令行中敲入 action 的名字 pod_lib_lint,然后 Fastlane 会在当前目录的 actions 文件夹中帮我们创建了一个 pod_lib_lint.rb 的 Ruby 文件,内容大致如下(省略了非重点部分):

复制代码
module Fastlane
module Actions
class PodLibLintAction < Action
def self.run(params)
UI.message "Parameter API Token: #{params[:api_token]}"
end
......
def self.available_options
[
FastlaneCore::ConfigItem.new(key: :api_token,
env_name: "FL_POD_LIB_LINT_API_TOKEN", # The name of the environment variable
description: "API Token for PodLibLintAction", # a short description of this parameter
verify_block: proc do |value|
UI.user_error!("No API token for PodLibLintAction given,
pass using `api_token: 'token'`") unless (value and not value.empty?)
end),
......
]
end
end
end

大家可以看到,自定义的 Action 都是隶属于 Fastlane/Actions 这个 module,并且继承自 Action 这个父类。虽然模板中的内容还挺多,不过不用担心,大部分内容都是一些简单的文本描述,对于我们来说只需要重点关注这两个方法就行:

  1. self.run 方法:这里放置的是实际的业务处理代码。
  2. self.available_options 方法:这里声明需要对外暴露出的参数,没有声明的参数在执行过程中无法使用。

在开始编写实际的业务代码之前,我们需要了解清楚这个 Action 具体包含的业务逻辑,所以我们首先来分析一下 Cocoapods 的 pod lib lint 命令,在终端执行

pod lib lint --help终端打印出(只保留重点部分)

复制代码
Usage:
$ pod lib lint
Validates the Pod using the files in the working directory.
Options:
--quick Lint skips checks that would
require to download and build
the spec
--allow-warnings Lint validates even if warnings
......

可以看出这个命令包含了不少选项(Options),而我们需要做的是将这些选项映射到 action 中的参数,所以接下来我们根据选项的类型,在 self.available_options 中进行声明,代码如下(只保留重点部分):

复制代码
def self.available_options
[
FastlaneCore::ConfigItem.new(key: :use_bundle_exec,
description: "Use bundle exec when there is a Gemfile presented",
is_string: false,
default_value: true),
FastlaneCore::ConfigItem.new(key: :verbose,
description: "Allow ouput detail in console",
optional: true,
is_string: false)
......
]
end

声明完毕后,在 self.run 方法中编写最终的业务逻辑,同时将上面的 options 通过 params 暴露出去,这样在运行 pod_lib_lint 这个 action 的时候,我们就可以传入对应的参数,从而 Fastlane 可以执行携带各种选项的完整命令,代码如下(只保留重点部分):

复制代码
def self.run(params)
command = []
command << "bundle exec" if File.exist?("Gemfile") && params[:use_bundle_exec]
command << "pod lib lint"
command << "--verbose" if params[:verbose]
command << "--allow-warnings" if params[:allow_warnings]
......
result = Actions.sh(command.join(' '))
UI.success("Pod lib lint Successfully")
return result
end

从这段代码可以看出,关键点在于 Actions.sh() 这句话,所以我们要保证这里的 sh 方法执行的 command 和 pod lib lint 命令在终端中输出的一致,例如:

pod lib lint --quick --verbose --allow-warnings --use-libraries最后,我们将 pod_lib_lint.rb 拷贝到 iOS 项目下的 fastlane/actions 文件夹中,然后在该项目目录下,执行如下命令:

fastlane action pod_lib_lint如果没有错误的话,终端中会输出如下内容:

(点击放大图像)

其实,最初写这个Action,我只是打算在团队内部使用,并没有贡献到Github 上的计划,所以只实现了一部分参数。我们自己使用了一段时间,感觉比较稳定的时候,才将所有参数都补齐,然后贡献到了Fastlane 的主仓库中,地址如下:

https://github.com/fastlane/fastlane/blob/master/fastlane/lib/fastlane/actions/pod_lib_lint.rb

这里说一个题外话
对于开源项目的代码提交,整个过程会比较严格,除了功能无 Bug,单元测试需要完全覆盖之外,对于语法格式等软指标也有一定的要求。当提交 pull request 的时候,Github 会先使用自动化工具(HoundCI 和 CircleCI)进行全面的检查,通过后才会交给 Code Review 团队人工 Check,所以平常代码习惯不好的同学需要多加注意。

Fastlane 的 Plugin 机制

我们在使用 Fastlane 的时候常常会遇到这样的场景:

  1. 我的自定义 Action 需要在多个内部项目中使用
  2. 我觉得这个自定义 Action 很不错,想共享给其他的团队使用

此时,拷贝粘贴虽然可以解决问题,但并不是一个聪明的方案。将 Action 发布到 Fastlane 的官方仓库倒是一个不错的选择,但是官方仓库本身对 Action 的要求比较高,并不会接收非通用性的 Action,即使接收了,整个发布周期也会比较长,而且以后无论是升级还是 Bug 修复,都依赖 Fastlane 本身的发版,大大降低了灵活性。

所以从 1.93 开始,Fastlane 提供了一种 Plugin 的机制来解决这种问题。大家可以理解为:Plugin 就是在 Action 的基础上做了一层包装,这个包装巧妙的利用了 RubyGems 这个相当成熟的 Ruby 库管理系统,所以其可以独立于 Fastlane 主仓库进行查找,安装,发布和删除。

我们甚至可以简单的认为:Plugin 就是 RubyGem 封装的 Action,我们可以像管理 RubyGems 一样来管理 Fastlane 的 Plugin。

安装 Plugin

到目前为止,大约有 30 个 Plugin 发布到了 RubyGems 下,我们可以通过如下命令来查找:

fastlane search_plugins [query]详情可以看这里
AvailablePlugins

假设我们的项目中需要使用一个名叫 version_from_last_tag,用于获取 git 的最近一个 tag,那么我们在终端的项目目录下执行:

fastlane add_plugin version_from_last_tag添加完成后,项目中会多出一个 Gemfile,Gemfile.lock,fastlane/Pluginfile 三个文件,其中这个 Pluginfile 实际上就是一个 Gemfile,里面包含对于 Plugin 的引用,格式如下:

复制代码
# Autogenerated by fastlane
#
# Ensure this file is checked in to source control!
gem 'fastlane-plugin-version_from_last_tag'

而 Pluginfile 本身又被 Gemfile 引用,所以又印证上上文中的那句话:对 Plugin 的管理其实就是对 RubyGem 的管理。

此后的 Plugin 是实际用法和使用 Action 是一致的,所以就不在此赘述了。

发布 Plugin

如果你想发布一个 Plugin,可以选择直接作为一个 Gem 发布到 RubyGems 上,这样大家就可以通过 search_plugins 命令搜索到了;也可以选择只提交代码到 Github 上,然后提供一个 github 的地址给其它项目或团队使用,这时需要在 Pluginfile 中这样声明:

复制代码
gem "fastlane-plugin-version_from_last_tag", git: "https://github.com/jeeftor/fastlane-plugin-version_from_last_tag"

发布之前,为了本地调试方便,可以将 gem 指向本地,在 Pluginfile 这样声明:

复制代码
gem "fastlane-plugin-version_from_last_tag", path: "../fastlane-plugin-version_from_last_tag"

有了 Plugin 之后,Fastlane 的更新频率大大降低,主仓库上 Action 的数量将维持在目前的水平上,取而代之的是 Plugin 的不断增多。企业和团队可以选择适合自己的 Plugin,也可以随时随地发布 Plugin 给别的团队使用。

结语

有了 Action,Fastlane 的可扩展性大大的增强,我们可以非常方便的编写适合自己业务场景的工具;Plugin 的出现,又在扩展性的基础之上大大增强了其灵活性,两者结合在一起使用可以将 Fastlane 的优势充分的的发挥出来。

通常情况下,如果一个工具只打算在一个项目中使用,那么建议直接用 Action,毕竟一个 Ruby 脚本就能解决,成本比较低;如果打算在多个项目中甚至跨团队使用,那么则建议使用 Plugin。

关于 Action 和 Plugin 更为详细的描述可以查看官方提供的文档:

Action: https://docs.fastlane.tools/actions/Actions/
Plugin: https://docs.fastlane.tools/plugins/CreatePlugin/

目前的两篇文章中的内容和场景都和 iOS 相关,接下来的一篇文章中,我将详细讲解一下如何将 Fastlane 应用于 Andriod 平台。


感谢徐川对本文的审校。

给InfoQ 中文站投稿或者参与内容翻译工作,请邮件至 editors@cn.infoq.com 。也欢迎大家通过新浪微博( @InfoQ @丁晓昀),微信(微信号: InfoQChina )关注我们。

2016-10-17 17:123212

评论

发布
暂无评论
发现更多内容

XPath攻略:从入门到精通,告别查找困难!

测吧(北京)科技有限公司

测试

高效运维|AIRIOT智慧电力运维解决方案

AIRIOT

数据分析 物联网平台 电力 智慧电力 智能控制

CAE科普!电池仿真的必要性

智造软件

CAE CAE软件 有限元技术

10大开源文档管理系统

爱吃小舅的鱼

开源 文档管理 文档管理工具

听完这期音视频发展史,才更清楚元宇宙到底还行不行|编码人声

声网

软件测试学习笔记丨软件测试基础概念

测试人

软件测试

TikTok直播专线的优势

Ogcloud

海外直播专线 tiktok直播 tiktok直播专线 海外直播网络 tiktok直播网络

RowHammer 攻击:内存的隐形威胁

EquatorCoco

攻击 RAM

提高LED显示屏安全性的关键措施

Dylan

安全 事故 消费者 LED显示屏 led显示屏厂家

玩转云端|演唱会一票难求?快用天翼云边缘安全加速平台AccessOne!

天翼云开发者社区

云计算 安全

如何构建一台机器学习服务器

EquatorCoco

机器学习 数据库 服务器

黑盒Prompt优化:提升大模型反馈效果的新思路

百度开发者中心

大模型 Prompt

Dynamic Wallpaper for Mac(视频动态壁纸) 17.1免激活版

iMac小白

SD-WAN如何适应运营商网络

Ogcloud

SD-WAN 企业组网 SD-WAN组网 SD-WAN服务商 SDWAN

对话 Mines of Dalarnia: Web3 游戏创新,社区驱动与公链共建

Footprint Analytics

gamefi #Web3

华为云&华为终端云创新峰会:华为阅读再迎三个内容伙伴,携手繁荣阅读行业生态

最新动态

TikTok直播专线:解决海外直播稳定问题的关键

Ogcloud

海外直播专线 tiktok直播 tiktok直播专线 海外直播网络 tiktok直播网络

文心千帆:从PPT制作到数字人主播,ERNIE-Bot|BLOOMZ大模型调优与RLHF训练全攻略

百度开发者中心

大模型 千帆

人大金仓助力吉林省属国企核心财务系统批量上线

科技热闻

GraphPad Prism 10 for Mac(统计分析绘图软件) v10.1.1注册版

iMac小白

ETLCloud结合Oracle实现CDC

RestCloud

oracle 数据同步 ETL CDC

Lightroom Classic 2022 for Mac(LrC中文版) 11.5激活版

iMac小白

NTFS Disk by Omi NTFS for Mac(NTFS 磁盘管理器) v1.1.4激活版

iMac小白

What’s the future ofIPQ9574 with QCN9274 Solution in Industrial Communication?

wallyslilly

软件测试学习笔记丨HttpRunnerV4 用例转换

测试人

软件测试

MCtalk·CEO对话×e签宝丨如何修好SaaS生态建设这门CEO必修课?

ToB行业头条

Abaqus模拟新能源汽车电池理论概念

思茂信息

abaqus abaqus软件 abaqus有限元仿真

Linux服务器部署Web版VSCode,在window下使用浏览器在linux环境下编写代码

快乐非自愿限量之名

Linux 运维 服务器

云原生最佳实践系列 5:基于函数计算 FC 实现阿里云 Kafka 消息内容控制 MongoDB DML 操作

阿里巴巴云原生

kafka 阿里云 云原生

Fastlane实战(二):Action和Plugin机制_语言 & 开发_邢天宇_InfoQ精选文章