使用pipe的背景
项目中的测试包以及发布包的构建是通过Jenkins来进行的,使用的是自由风格的任务,但主要的构建任务是写在一系列的python脚本里的。比如一个Release的构建任务,基本就是设置好git的仓库地址,账号密码,然后在构建的一栏里选择执行shell
, 然后填上一句类似python /.../build.py -type adhoc
这样的语句就行。其中build.py是从项目初期的一些自动化构建脚本整合而来的,涉及的内容包括增加build号
, 构建ipa
, 上传测试包
, 上传符号
, 发邮件
等一系列操作。
这种方式虽然使用起来很简单,但是也逐渐暴露出一些弊病,比如
- 涉及到的所有步骤都是包含在build.py及其子调用里。如果想只进行其中某一个步骤,或者构建失败后,想从中间某个步骤接着重新够建,这是做不到的。必须重头再构建一遍。
- 构建错误不好排查,只能从整体的构建日志里去查,看看是哪一步出错了。
- Jenkins界面上也不太友好。一个构建步骤开始后,基本只能等着结果或者出错,或者成功。无法从界面上直观的看出进行到什么阶段,以及花费了多长时间
所以基于上面的问题,就对构建方式进行了改造,同时熟悉了一次Jenkins的pipe机制
pipe简介
pipe的机制在官网上有详细的介绍。https://www.jenkins.io/doc/book/pipeline/#。简单的理解就是某一个pipe做一项简单的工作,然后通过pipe的衔接来流程化完成一系列任务。pipe的语法有两种形式,Declarative Pipeline
和Scripted Pipeline
两种形式。
创建了pipe类型的Job之后,可以在底部流水线模块,选择Pipeline script
或者Pipeline script from SCM
。前者是将配置直接写在Jenkins上。后者是将配置写在一个文件里,并且托管到某一个代码仓库,文件一般是Jenkinsfile。后者在Job启动时,先去拉取托管代码,再根据配置的相对路径读取配置文件,继而执行配置的pipe内容。
考虑到可以将配置托管起来,编写、维护都比较方便。所以就使用了SCM的方式,将相关配置托管在一个CI脚本仓库里,并且使用了Declarative Pipeline
的语法形式。
基本的pipe结构大致是以下这样
1 | pipeline{ |
其中agent
是该任务执行的节点名称。environment
中可以配置一些环境变量.每个stage
里包含一个steps
,stage
的名称会在Jenkins的界面上出现,作为流水线中的一环。steps
中是具体该环节要执行的一系列指令。
构建流程改造Tips
由于之前的构建脚本中已经笼统包含了所有的构建内容,所以首先必须要做的就是把各个步骤拆分出来。这部分跟具体项目有关,就不用详细说了。最后达到效果是构建,改build号,该项目配置,构建.app文件,导出ipa以及邮件等许多操作,都可以通过单独的脚本来独立运行。之后就是通过pipeline把这些操作衔接起来。
主要的写法很简单,所以只简单记录一下一些注意事项,做个备忘
参数和环境变量
- 环境变量的引用形式是比如
"$env.WORKSPACE"
。环境变量可以是Jenkins预留的环境变量,也可以是节点机制配置的自定义环境变量。 - 参数的引用是比如
"$BuildNodeName"
。参数的传入,不论是直接运行job,还是通过pipe,都需要首先在当前Job配置中,勾选参数化构建过程
, 预先设置好要用的参数和类型。 - 注意都是使用
双引号
, 变量才能被正确替换,单引号
会认为是自然文本输出,不替换变量。这个地方卡了一段时间找不到问题。 environment
中,可以设置一些变量,这些变量可以在后续的steps中,以参数的形式进行引用。
work directory
在steps中,如果需要切换当前的工作目录,经实验使用sh "cd xxx"
这种方式是不起作用的。正确的方式是使用dir
语法将要在子目录执行的steps包起来,比如如下建立一个子目录,在里面获取git仓库代码
1 | sh 'mkdir -p Appcode' |
parallel
有些步骤希望并行进行。比如实际构建中,希望内测版,企业版的同时构建和分发,这时可以用parallel
语法,声明两个任务并行执行。如果是在同一个节点机器上,别忘了配置节点的并行任务数量大于1。Jenkins官网建议可以设置成CPU核的数量。语法结构大致如下
1 | stage("run all job"){ |
多个Job配合
为了让构建更加灵活,可以采用多个Job进行配合。比如纯粹的构建内测版,以及企业版可以分别建立一个单独的Job。再建立一个上层的Job, 这个Job负责升build号,之后启动多个Job完成具体构建任务,子Job都完成之后,上层Job再发邮件等。
启动一个子Job的语法如下:
1 | build job: '/Sub_Job_Name', parameters:[[$class: 'StringParameterValue', name: 'BuildNodeName', value: "$BuildNodeName"]] |
其中Sub_Job_Name
是子任务的名字。子任务有个字符参数是BuildNodeName,通过上面方式可以在启动子Job时,传递相应的参数值过去。
在step中通过build
启动子任务,默认是会一直等待该任务执行结束才继续进行后面的工作。如果这个子任务只是想触发一下,并不需要等待执行结果,可以使用wait
参数。
1 | build job: '/Sub_Job_Name', parameters:[[$class: 'StringParameterValue', name: 'BuildNodeName', value: "$BuildNodeName"]], wait:false |
构造流程改造效果
使用体验相比旧的方式还是很不错的,Jenkins上外观也有了变化。
新流程大致有如下几个优点:
- 通过pipe化的改造,后续对构建流程扩展,或者流程改造就方便很多,灵活很多。
- 目前从界面上可以清楚的看到执行到了哪个stage。通过点击看stage的log,也可以清楚的看到每个step的运行情况,
- 每个stage, step都有统计时间,方便做构建时长的优化。