您可以捐助,支持我们的公益事业。

1元 10元 50元





认证码:  验证码,看不清楚?请点击刷新验证码 必填



  求知 文章 文库 Lib 视频 iPerson 课程 认证 咨询 工具 讲座 Modeler   Code  
会员   
 
   
 
 
     
   
 订阅
  捐助
Fastlane实战(二):Action和Plugin机制
 
作者 邢天宇  来源:InfOQ 发布于: 2016-12-14
  1612  次浏览      16
 

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

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

站在这个角度上,今天我们就来介绍一下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]

假设我们的项目中需要使用一个名叫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。

   
1612 次浏览       16
 
相关文章

手机软件测试用例设计实践
手机客户端UI测试分析
iPhone消息推送机制实现与探讨
Android手机开发(一)
 
相关文档

Android_UI官方设计教程
手机开发平台介绍
android拍照及上传功能
Android讲义智能手机开发
相关课程

Android高级移动应用程序
Android系统开发
Android应用开发
手机软件测试
最新课程计划
信息架构建模(基于UML+EA)3-21[北京]
软件架构设计师 3-21[北京]
图数据库与知识图谱 3-25[北京]
业务架构设计 4-11[北京]
SysML和EA系统设计与建模 4-22[北京]
DoDAF规范、模型与实例 5-23[北京]

android人机界面指南
Android手机开发(一)
Android手机开发(二)
Android手机开发(三)
Android手机开发(四)
iPhone消息推送机制实现探讨
手机软件测试用例设计实践
手机客户端UI测试分析
手机软件自动化测试研究报告
更多...   


Android高级移动应用程序
Android应用开发
Android系统开发
手机软件测试
嵌入式软件测试
Android软、硬、云整合


领先IT公司 android开发平台最佳实践
北京 Android开发技术进阶
某新能源领域企业 Android开发技术
某航天公司 Android、IOS应用软件开发
阿尔卡特 Linux内核驱动
艾默生 嵌入式软件架构设计
西门子 嵌入式架构设计
更多...