我是怎么慢慢变懒的(2) : Jenkins 全自动化

jenkins

1.概要

从16年底到现在整1年多的时间一直在做公司的一个新项目,由于项目使用了 RN,并且同一套代码需要针对不同医院发布不同的 APP(icon 不同,启动图不同,应用名不同,不同 APP 通过开关控制不同模块打开与否、、、),所以在项目打包过程中使用了大量的脚本配合 jenkins 来自动化处理这些事情,通过不断的实践尝试,现在打包测试、发布完全可以脱离开发由技术工程部门的同事处理,这样不但可以将自己从繁琐的打包配置中脱离出来,也让测试部门的同事更灵活的验收各种环境、各个医院的 APP,写这篇文章的主要目的是希望将优化的过程分享出来,同时也希望大家能指出其中的不足或可以改进的地方。

2.整体项目结构

首先介绍下整体项目的结构,这样有利于大家了解接下来我为什么这么做。

1. 一份代码,可以打包 N 个医院的 APP。

2. 一个医院有 妇幼端 和 医护端 的 Android 和 iOS 平台 4个 APP。

3. 每个端的 APP 有不同的 icon,LaunchScreen,三方推送,code-push 热更新,版本号,应用名称等配置。

4. 一个 APP 分 开发环境、测试环境、预发布环境、正式环境。

大概如下图所示:

architecture

3.Jenkins 构建界面

首先看下 Jenkins 主界面。

jenkins-main

在整个自动化构建过程中,一共需要这 10 个 Jenkins 任务配合使用,下面就一个一个介绍下。

Job.1 ForkSVN

由于产品的需求文档和设计的UI(icon,launchScreen,设计图,标注)放在 SVN 中,而开发团队全是使用 git,所以通过这个 Job 定时从 SVN 仓库同步资料并提交到 git 仓库(git svn 命令),也方便打包时通过脚本到特定目录下获取特定 APP 的 icon 和 launchScreen。

Job.2 GuoKang_Create_CodePush_App

项目中使用了 RN 框架,所以集成了微软提供的 code-push 热更新框架,由于 code-push 提供了比较齐全的终端命令来创建、删除 code-push 热更新 APP,部署、删除热更新 APP,所以这个 Job 用来给技术工程团队在新建 APP 时,创建新的 code-push APP。

code-push

code-push-emai

只需要在 jenkins 构建参数界面输入需要创建的 APPID 和 CMD,最终会调用 code-push 脚本并将结果以 txt 形式通过邮件发送出来。

Job.3 GuoKang_Create_iOS_App_IDs

因为最终涉及到提交 APP 给苹果审核,所以需要在 Apple 的后台创建 APP,这里用到了 fastlane 这个神器,创建 APP(fastlane produce),生成推送证书(fastlane pem),生成描述文件(fastlane sigh),一气呵成。

创建完 APP 后,会通过邮件把推送证书发送出来(技术工程的同事需要推送证书在三方平台注册推送),同时把推送证书提交到特定的 git 仓库保存。

push-git

push-emai

Job.4 GuoKang_RN_User

这个是要重点讲的 Jenkins 任务:构建用户端。

首先看下构建参数界面:

RN_Use

1.APPID

这个参数是用来指定构建哪一个 APP(内部每一个端的 APP 都有一个唯一 APPID)。

2. ENVIRONMENT

用来指定构建 测试环境、开发环境、预发布环境 还是 正服环境。

3. iOS_isEnterprise

这个参数是后期加上的,勾选这个参数的话,就会用企业证书打包,好处就是未注册过的设备也可以安装,这样只需要临时安装看下的设备就不需要占用开发者帐号每年的100个名额了。

4. GIT_BRANCH

选择不同分支构建不同的代码。

5. DO_NOT_NOTIFICATION

不通过邮件和钉钉发送打包信息。

6. Android

是否构建 Android 版本。

7. iOS

是否构建 iOS 版本。

Jenkins 配置界面如下:

RN_User_Config

1.触发 GuoKang_RN_Update_Public_Image 任务

这个任务用来更新 SVN 中 APP 的 icon 和 启动图 到指定的 git 仓库,虽然会定时自动启动,但是为了保证每次构建 APP 时都是最新的图片,所以会优先触发这个任务。

2.更新 subtree,安装依赖库

git subtree pull --squash --prefix=.customBuild http://192.168.16.221:7990/scm/web/custombuild_rn.git master

git subtree pull –squash –prefix=public http://192.168.16.221:7990/scm/web/iconandlaunchscreen_rn.git master

npm install

3.执行打包脚本

#!/bin/bash -x

pwd

pushd .customBuild

IFS=’,’ read -r -a array <<< “$APPID”

echo “$array ”

for id in “${array[@]}”

do

python3 ./package.py –appId $id –environment $ENVIRONMENTAL –platform “resident” –isEnterprise “$iOS_isEnterprise” –doNotNotification “$DO_NOT_NOTIFICATION” –android $Android –iOS $iOS

done

popd

大概流程如下图:

RN_User_Flo

而这里最重要的流程就是将构建参数传递给打包脚本后一系列的流程了,接下来会重点讲一下整个打包脚本的流程:

1. 根据 APPID 和 ENVIRONMENT 从后台请求配置参数:

上文中也讲到,每个端的 APP 配置都不一样,一开始是通过自己在项目中手动维护一个 JSON 文件,新增一个医院时,手动将各种配置添加到 JSON 文件中,随着医院慢慢增加,这种方式的效率越来越低,而且很容易出错,所以就拉上管理后台和后台的人,将 APP 所需要的参数都录入到后台,每次打包时通过接口请求对应 APP 的参数。

2. 根据特定规则校验请求到的参数:

有一次新打一个医院的 APP 时,微信分享突然不能用了,后来查到原因是在管理后台录入数据的同事粗心将我们自己的 APPID 当成 微信的 APPID 录入进去了,正是因为这次低级的错误,我们加上了有规则参数的校验。

比如 微信 APPID 都是以 wx 开头的,我们自己的 APPID 都是以 gk 开头的,凡是有特定规则的都加上了校验,这样在打包前就能发现问题,并给 Jenkins 抛出打包异常信息。

3. 更新 APP 项目配置:

从后台请求到 APP 对应的参数后,需要将对应的信息写到对应的地方。

这里以 iOS 为例,需要更新 :

(1)Info.plist 里面的 Bundle 版本号(CFBundleVersion),应用名称(CFBundleDisplayName),应用 Identifier(CFBundleIdentifier)等等。。

(2)一些第三方 SDK 初始需要的 key 写到 iOS 项目特定的 json 文件中以便原生代码读取。

(3)从 icon 和 launchScreen 仓库中取到当前打包 APP 的图片,放到原生项目对应的图片目录中。

4.编译 iOS 应用。

所有数据都修改好后,就可以编译 APP 了。

说到这里,就要顺便介绍下第二台 Jenkins 构建机器了,因为我们在编译 iOS 的应用里集成了 fastlane 自动提交给 itc 并进入审核状态的选项,如下图:

RN_User_Config_10001

对于这种操作不能跟测试期间的 Jenkins 放在一起,以免造成不必要的麻烦,所以才有了第二台 Jenkins 构建服务的产生,主要用来操作跟正服相关的操作,比如最终发布。

为了区分两台机器,我在 Jenkins 系统设置中加上了一个环境变量:MACHINE_NO,这样在代码中只要通过 MACHINE_NO 这个环境变量的值来判断是哪个 Jenkins 在调用打包脚本。

jenkins-config

因为我们内部开发的代码管理流程按照 git-flow 流程,所以为了安全起见,加了更多验证,比如:

如果打包环境是 正服,并且勾选了直接提交到 itc 后台,那么如果构建分支不是 master,那么直接报错。

构建完后,会将 ipa 和 xcarchive 文件移动到 Jenkins 机器上 Tomcat 中的目录下(Tomcat 这个目录已映射到外网,ipa 对应的地址为:http://host/guokang/${APPID}/${BUILD_NUMBER}.ipa, 这个地址会在后续的邮件中发出来)

tomcat

5.安装包上传三方平台,并发送通知:

上传安装包我选择了蒲公英平台,整体用下来感觉还行,有一点不足的是一个 APP 只有一个二维码,也就是扫描二维码安装的永远是最新的 build,要安装老的只能切换到对应的build,点击「点击安装」按钮,所以上传到蒲公英后,我们自己根据返回的安装包地址生成了二维码,放到 tomcat 目录里备用。

发送通知包括了:

a.发送邮件。

emai

首先,邮件的主题会根据不同通知类型设置不同主题,如开发环境打包的主题就是『国康-开发环境』,正式环境热更新部署的主题就是『热更新部署-正服环境』,以便收件人按照邮件主题格式分类。

邮件内容很简单,就把放到 tomcat 目录下的 ipa 和 apk 外网地址发送出来,方便下载安装,还要有构建分支,构建改动,以及构建时从后台请求的配置文件也发出来,方便检查配置的正确性。

b.发送钉钉通知。

dingding-1

dingding-2

钉钉里面也根据不同的构建环境通知到不同的钉钉群里,不同的人可以关注不同群里面的消息。

上传蒲公英后生成的二维码用在了钉钉里面,直接扫描特定消息的二维码,就能直接安装特定的 build。

Job.5 GuoKang_RN_Doctor、GuoKang_RN_Doctor_CodePush、GuoKang_RN_User_CodePush

这三个 Job 其实用的是一套打包脚本,所以就不介绍了。

Job.6 GuoKang_RN_Update_Dynamic_Params

这个 Job 很简单,就是用来定时从后台拉取所有 APPID 列表,并按照特定格式写入指定文件,这样参数化构建的插件可以实时更新 APPID 列表了。

Job.7 GuoKang_RN_Update_Public_Image

这个 Job 用来定时从 ForkSVN 仓库里同步 APP 用到的 icon 和 launchScreen 到特定的 git 仓库,在 GuoKang_RN_User 这个项目构建时,会优先触发 GuoKang_RN_Update_Public_Image 这个 job,这样能保证每次构建时,APP 的 icon 和 launchScreen 都是最新的。

Job.8 update_profile

这个用来刷新开发者帐号下所有的描述文件,因为有时如果在开发者帐号下面添加了一个设备,那么所有的描述文件都需要手动重新生成一遍,这里用 fastlane 批量刷新所有的描述文件,省时省力。

4. Tips

所有 Job 处理的事情大概都概括了一遍,这里我总结了一些 Tips,供大家参考:

Tips.1 Execute shell 中的坑

Execute shell 这项是 Jenkins 提供给我们执行脚本的地方,在脚本里如果 exit 非 0 值,则 Jenkins 会将构建标记为 failure。

但是这种方式 jenkins 不会将构建标记为 failure:

1. Execute shell 里调用一个 sh 脚本。

tips1

2. sh 脚本里面执行 2 个 python 脚本。

tips1.1

3. test.py 脚本内容如下:

tips1.2

4. test2.py 脚本内容如下:

tips1.3

因为在 test.py 脚本中调用了 sys.exit 了 非 0 值,所以这时候会终止当前进程,并将 1 返回给父进程写入 $? 中,如果这时候我们不对 $? 进行处理,shell 进程会继续执行 test2.py 这行脚本,而 test2.py 执行完后会给父进程返回 0,这时候 $? 变成 0,shell 脚本执行完,将 $? 返回给 jenkins,虽然我们在 test.py 中 返回了 非 0 值,但是这个值最终并没有传递给 jenkins,所以我们需要在 shell 中调用每一个命令后对 $? 进行处理:

tips1.6

tips1.5

Tips.2 尽量加上规则限制:

由于涉及到多个 APP,多种环境的配置,而且这其中需要人为参与配置,所以难免会出错,想要尽量避免这种错误的发生,需要我们尽量加上各种规则限制,比如:

1. 检测微信 AppID 是否以 wx 开头。

2. 检测 icon 和 launchScreen 各种尺寸是否齐全,命名是否规范。

3. 检测直接通过 fastlane 发布到 itc 时,是否选择的 master 分支。

4. 。。。

Tips.3 尽可能添加更多构建信息

由于构建完全自动化后,所以所有人都可以构建打包(产品,测试,后端),这时构建信息就显得很重要了,比如: 构建分支、构建人、构建环境、构建版本号、构建机器(多台 jenkins 服务器),这样在安装的时候才能对应到特定的版本。

Tips.4 巧用环境变量

jenkins 本身会使用环境变量来存储一些数据。

想要查看 jenkins 包含哪些环境变量可以通过简单的几行 Python 脚本打印出来:

tips4.1

tips4.2

Tips.5 Jenkins 插件

jenkins提供了很多有用的插件,可以到 系统管理 -> 插件管理 -> 可选插件 逛逛,说不定会有意外的收获。

这里推荐几个我常用的 jenkins 插件:

1. Dynamic Parameter Plug-in

动态生成 jenkins 构建参数

2. Git Parameter Plug-In

可以将 git 仓库的 branch 或者 tag 设置到环境变量中,在需要的地方使用。

这个插件很有用,因为大多数 jobs 我们都需要在 『源码管理』里面配置 git 仓库,有了这个插件后,每次构建都可以手动选择 branch 或者 tag,不需要不断修改配置来构建不同分支的代码了。

tips5.1

3. Publish Over FTP

将文件通过 FTP 发送到指定的服务器上。

4. ThinBackup

备份 jenkins 配置。

回顾

写到这里大体都讲完了,单纯都是技术总结,所以显得比较乏味,但是这里涵盖了这一年我们在整个自动化流程中的摸索,写这篇文章也是为了跟大家交流,学习,探索更好的解决方案。