自动化打包(持续集成+持续交付+持续部署)

持续集成指的是,频繁地(一天多次)将代码集成到主干。它的好处主要有两个。

(1)快速发现错误。每完成一点更新,就集成到主干,可以快速发现错误,定位错误也比较容易。

(2)防止分支大幅偏离主干。如果不是经常集成,主干又在不断更新,会导致以后集成的难度变大,甚至难以集成。

持续集成的目的,就是让产品可以快速迭代,同时还能保持高质量。它的核心措施是,代码集成到主干之前,必须通过自动化测试。只要有一个测试用例失败,就不能集成。

Martin Fowler(重构:改善既有代码的设计的作者)说过,"持续集成并不能消除Bug,而是让它们非常容易发现和改正。

"持续交付(Continuous delivery)指的是,频繁地将软件的新版本,交付给测试和用户,以供测试和评审。如果通过,代码就进入生产阶段。

持续部署(continuous deployment)是持续交付的下一步,指的是代码通过以后,自动部署到生产环境。

主要的工具就是Jenkins+fastlane (但是我个人觉得Jenkins 维护成本高个人, 个人觉得它主要比fastlane就多一个好处,能自动检测gitlab你的代码的上传然后调用脚本 ,其实业务不复杂的话直接用fastlane就很不错,所以重点讲一下fastlane 吧)

1 Jenkins的安装 

首先你要先在本地电脑把Java环境配置好 ,https://www.jianshu.com/p/b518ce7e2bce我直接贴出来地址吧 

我们来开始安装Jenkins。从官网https://jenkins.io/ 上下载最新的pkg安装包。


一直点击继续 直到安装完成。安装完成之后,Safari可能会自动打开,如果没有自动打开,打开浏览器,输入http://localhost:8080,会出现下图的重设初始密码的界面。

按照提示,找到/Users/Shared/Jenkins/Home/ 这个目录下(也有可能不是这个目录,只需要按照它提示的目录就行),这个目录虽然是共享目录,但是有权限的,非Jenkins用户/secrets/目录是没有读写权限的。


打开initialAdminPassword文件,复制出密码,就可以填到网页上去重置密码了。如下图

点击 install suggested plugins   然后等待安装完成 如下图


一路安装过来,输入用户名,密码,邮件这些,就算安装完成了。

还是继续登录localhost:8080 ,选择“系统管理”——“管理插件”,我们要先安装一些辅助插件。

安装GitLab插件

因为我们用的是GitLab来管理源代码,Jenkins本身并没有自带GitLab插件,所以我们需要依次选择系统管理->管理插件,在“可选插件”中选中“GitLab Plugin”和“Gitlab Hook Plugin”这两项,然后安装。

安装Xcode插件

同安装GitLab插件的步骤一样,我们依次选择系统管理->管理插件,在“可选插件”中选中“Xcode integration”安装。

安装完了这个,我们就可以配置一个构建项目了。



输入项目名字,点击新建好的项目,进来配置一下General参数


接着设置源码管理

由于现在我用到的是GitLab,先配置SSH Key,在Jenkins的证书管理中添加SSH。在Jenkins管理页面,选择“Credentials”,然后选择“Global credentials (unrestricted)”,点击“Add Credentials”,如下图所示,我们填写自己的SSH信息,然后点击“Save”,这样就把SSH添加到Jenkins的全局域中去了。


如果正常的配置正确的话,是不会出现下图中的那段红色的警告。如果有下图的提示,就说明Jenkins还没有连通GitLab或者SVN,那就请再检查SSH Key是否配置正确。


构建触发器设置这里是设置自动化测试的地方。

Poll SCM(poll source code management) 轮询源码管理

需要设置源码的路径才能起到轮询的效果。一般设置为类似结果: 0/5 * * * * 每5分钟轮询一次

Build periodically(定时build)

一般设置为类似: 00 20 * * *  每天 20点执行定时build 。当然两者的设置都是一样可以通用的。


还有一些关于钥匙串和证书,描述文件的配置,但是我们主要用fastlane 脚本打包 ,所以先说怎么安装 fastlane吧

fastlane安装

确保Xcode Command Line Tools 安装了最新版

xcode-select --install

如果你单独安装过ruby(如果你能看得懂这句),去掉sudo。如果使用系统自带的ruby,需要sudo权限

[sudo] gem install fastlane

进到项目目录。在xcodeproj文件同级目录下,执行

fastlane init

如果是第一次使用 fastlane ,会要求输入你的苹果开发者账号,期间会让你输入 Apple ID 账号密码(这个信息会存在钥匙串中,后续使用无需再输入密码),会检测当前的 app identifier 是否在 Apple Dev Center 中,会检测当前 app 是否在 iTunes Connect 中,如果已经在 Apple Dev Center 和 iTunes Connect 中创建相应的信息,那么过程会很顺利

成功之后,会在你工程的根目录下创建fastlane文件夹里面内容如下,最重要的两个文件就是Appfile和Fastfile,:


其中:

Appfile, 用于存放 app ID 和你的 Apple ID。 Fastfile, 用于管理你所创建的 lane,lane 则会调用 action。

我们先看 Fastfile文件,说到Fastfile文件就要先介绍一下 fastlane组件。fastlane其实是一个工具集,包含了我们日常开发中上线时需要的大部分操作。比如gym/deliver等。主要组件包括:

deliver:自动上传截图,APP的元数据,二进制(ipa)文件到iTunes Connect

snapshot:自动截图(基于Xcode7的UI test)

frameit:可以把截的图片自动套上一层外边框

pem:自动生成、更新推送配置文件

sigh:用来创建、更新、下载、修复Provisioning Profile的工具

produce:如果你的产品还没在iTunes Connect(iTC)或者Apple Developer Center(ADC)建立,produce可以自动帮你完成这些工作

cert:自动创建管理iOS代码签名证书

pilot:管理TestFlight的测试用户,上传二进制文件

boarding:建立一个添加测试用户界面,发给测试者,可自行添加邮件地址,并同步到iTunes Connect(iTC)

gym:自动化编译打包工具

match:证书和配置文件管理工具

scan:自动运行测试工具,并且可以生成漂亮的HTML报告

Fastfile文件

Fastfile文件的主要结构如下所示:

fastlane_version "2.14.2"

default_platform :ios

platform :ios do

before_all do

  cocoapods

  end

   lane :test do

  end

   lane :beta do

  end

   lane :release do

  end

   after_all do |lane|

  end

  error do |lane, exception|

  end

end

说明:

(1)fastlane_version:指定fastlane使用的最小版本

(2)default_platform:指定当前默认的平台,可以选择ios/android/mac

(3)before_all:在执行每一个lane之前都会调用这部分的内容

(4)after_all:在每个lane执行完成之后都会执行这部分的内容

(5)error:每个lane执行出错就会执行这部分的内容

(6)desc:对lane的描述,fastlane会自动将desc的内容生成说明文档

(7)lane:定义一个lane(任务),可以理解为一个函数,我们在执行的时候使用fastlane [ios] lane名称

下面是官方提供的一个示例:

lane :beta do

  increment_build_number

  cocoapods

  match

  testflight

  sh "./customScript.sh"

  slack

end

像increment_build_number、cocoapods这样的一条命令都是一个action,由这样的一个个action组成了一个lane(lane中可以调用其他的lane)。

比如我需要完成一套发布流程:

#发布到AppStore

lane :release do

  #增加build版本号,需要先配置build setting

  increment_build_number

  #pod资源更新

  cocoapods

  #打包

  gym

  #发布到AppStore

  deliver(force: true)

  #发布testflight测试

  testflight

end

我们在项目目录下,用终端执行如下命令即可:

fastlane release

场景

随着业务的发展,产品线的增加,我们需要将APP拆分为若干个基础组件和业务组件,以便跨APP使用,并且方便管理维护(这又是另外一个大的议题,就不在此赘述了)。每个组件都由一个私有Pod来管理,Pod的发布和更新也成为了我们日常工作的一部分,对于这些Pod,一般我们团队内部的原则是:谁制作,谁管理,谁发布,Pod的负责人我们内部称之为库管,这件事也就分担到了每个库管身上,库管发布一个Pod的流程大约如下:

增加Podspec中的版本号

执行pod lib lint命令进行库验证

Git Commit代码

Git Push代码到远端

打一个Git Tag

将Tag Push到远端

执行pod repo push命令发布库到私有仓库

如果只有两三个库的话,并且库的更新频率较低的时候,每次手动来处理还好。但是当库逐渐增多的时候这件事就变得相当麻烦,尤其是当顶层的库依赖底层库的时候,那么升级一个库,影响面将远远超过其本身,通过人工的方式处理的话,整个过程会变得相当痛苦。

那我们可以 在fastlane 中 这些写

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) # 提交到 CocoaPods

end

我们在项目目录下,用终端执行如下命令即可:

fastlane do_release_lib project:GMUtil version:0.1.4

还有在安卓由于国内Android市场众多上经常会遇到 多渠道打包,这里我们也使用Fastlane如何进行处理:

Fastlane的Action机制

Fastlane本身包含两大模块,一个是其内核部分,另外一个就是Action了。Action是Fastlane自动化流程中的最小执行单元,直观上来讲就是Fastfile脚本中的一个个命令,比如:git_pull,deliver,pod_install等等,而这些命令背后都对应一个用Ruby编写的脚本。Fastlane已经为我们提供了现成的模板,即使你对Ruby的语法不熟悉,也没有关系,Fastlane是开源的嘛,可以直接下载源码看看别人的Action是怎么写的就知道了,我们可以在这个目录下找到所有的Action文件:

fastlane/fastlane/lib/fastlane/actions/

针对pod的执行创建一个action来针对下面三种情况的执行

pod install --no-repo-update (避免master repo的每次更新耗时)

pod update --no-repo-update (避免master repo的每次更新耗时)

pod repo update XXX (私有repo的更新)

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

fastlane new_action

后根据提示,在命令行中敲入action的名字pod,然后Fastlane会在当前目录的actions文件夹中帮我们创建了一个pod.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方法:这里声明需要对外暴露出的参数,没有声明的参数在执行过程中无法使用。

在self.available_options中进行声明,在self.run方法中编写最终的业务逻辑,同时将上面的options通过params暴露出去,这样在运行pod这个action的时候,我们就可以传入对应的参数,从而Fastlane可以执行携带各种选项的完整命令,

module Fastlane

  module Actions

    module SharedValues

      POD_INSTALL_CUSTOM_VALUE = :POD_INSTALL_CUSTOM_VALUE

    end

    class PodInstallAction < Action

      def self.run(params)

        repo = "-no-repo-update"

        command = []

        command << "pod install"

        if params[:repo_update]

          repo = "--repo-update"

        end

        command << repo

        if params[:verbose]

          command << "--verbose"

        end

        result = Actions.sh(command.join(' '))

        UI.success(command.join(' ') + " Successfully ")

        return result

      end

      def self.description

        "pod install action"

      end

      def self.details

        "verbose / repo-update"

      end

      def self.available_options

        [

        FastlaneCore::ConfigItem.new(key: :verbose,

                                      description: "Allow output detail in console",

                                      optional: true,

                                      is_string: false,

                                      default_value: false),

          FastlaneCore::ConfigItem.new(key: :repo_update,

                                      description: "Allow output detail in console",

                                      optional: true,

                                      is_string: false,

                                      default_value: false)

        ]

      end

      def self.output

      end

      def self.return_value

      end

      def self.authors

        ["yang"]

      end

      def self.is_supported?(platform)

        platform == :ios

      end

    end

  end

end

最后,我们将pod.rb拷贝到iOS项目下的fastlane/actions文件夹中,然后在该项目目录下,执行如下命令:

fastlane action pod

首先,我们自定义一个Action:add_channels_to_apk,这个Action的作用就是:

拷贝最终打包生成的apk文件,并修改文件名为渠道名,如gengmei_qq_630.apk

然后将一个渠道名写入到apk文件的META-INFO目录中

其次,新建一个txt文件,里面写入所有需要打包的渠道名,如:QQ,360,Baidu...等等,渠道名之间用逗号隔开。

最后,在Fastfile中定义一个Lane来进行最终的集成处理:

desc "Package a new app version with different channels"

lane :do_package_apk do |options|

    project = "#{options[:project]}"

    target_version = options[:version]

    git_pull

    gradle(task: "clean")

    gradle(task: "assembleRelease")

    add_channels_to_apk(channels: './channels.txt')

end

接下来的事就简单多了,每次需要打包的时候,只要执行如下的命令即可:

fastlane do_package_apk project:Gengmei version:6.3.0

无论是5个渠道,还是50个渠道,1分钟内全部搞定,非常的方便。

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

推荐阅读更多精彩内容