Kevinlma的博客

物来顺应,未来不迎,当时不杂,既过不恋

0%

Jenkins中pipe使用总结

使用pipe的背景

项目中的测试包以及发布包的构建是通过Jenkins来进行的,使用的是自由风格的任务,但主要的构建任务是写在一系列的python脚本里的。比如一个Release的构建任务,基本就是设置好git的仓库地址,账号密码,然后在构建的一栏里选择执行shell, 然后填上一句类似python /.../build.py -type adhoc这样的语句就行。其中build.py是从项目初期的一些自动化构建脚本整合而来的,涉及的内容包括增加build号, 构建ipa, 上传测试包, 上传符号, 发邮件等一系列操作。

这种方式虽然使用起来很简单,但是也逐渐暴露出一些弊病,比如

  1. 涉及到的所有步骤都是包含在build.py及其子调用里。如果想只进行其中某一个步骤,或者构建失败后,想从中间某个步骤接着重新够建,这是做不到的。必须重头再构建一遍。
  2. 构建错误不好排查,只能从整体的构建日志里去查,看看是哪一步出错了。
  3. Jenkins界面上也不太友好。一个构建步骤开始后,基本只能等着结果或者出错,或者成功。无法从界面上直观的看出进行到什么阶段,以及花费了多长时间

所以基于上面的问题,就对构建方式进行了改造,同时熟悉了一次Jenkins的pipe机制

pipe简介

pipe的机制在官网上有详细的介绍。https://www.jenkins.io/doc/book/pipeline/#。简单的理解就是某一个pipe做一项简单的工作,然后通过pipe的衔接来流程化完成一系列任务。pipe的语法有两种形式,Declarative PipelineScripted Pipeline两种形式。
创建了pipe类型的Job之后,可以在底部流水线模块,选择Pipeline script或者Pipeline script from SCM。前者是将配置直接写在Jenkins上。后者是将配置写在一个文件里,并且托管到某一个代码仓库,文件一般是Jenkinsfile。后者在Job启动时,先去拉取托管代码,再根据配置的相对路径读取配置文件,继而执行配置的pipe内容。

考虑到可以将配置托管起来,编写、维护都比较方便。所以就使用了SCM的方式,将相关配置托管在一个CI脚本仓库里,并且使用了Declarative Pipeline的语法形式。

基本的pipe结构大致是以下这样

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
pipeline{
agent{
label "$BuildNodeName"
}
environment{
SHARED_PARMS = ""
}
stages{
stage("stage 1"){
steps{
sh 'do simple work'
}
}
}
}

其中agent是该任务执行的节点名称。environment中可以配置一些环境变量.每个stage里包含一个stepsstage的名称会在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
2
3
4
5
6
7
sh 'mkdir -p Appcode'
dir("Appcode")
{
git branch: "$APP_GIT_BRANCH",
credentialsId: 'xxxx',
url: '$APP_GIT_URL'
}

parallel

有些步骤希望并行进行。比如实际构建中,希望内测版,企业版的同时构建和分发,这时可以用parallel语法,声明两个任务并行执行。如果是在同一个节点机器上,别忘了配置节点的并行任务数量大于1。Jenkins官网建议可以设置成CPU核的数量。语法结构大致如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
stage("run all job"){
parallel{
stage("build adhoc"){
steps{
work 1
}
}
stage("build inhouse"){
steps{
work 2
}
}
}
}

多个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上外观也有了变化。

新流程大致有如下几个优点:

  1. 通过pipe化的改造,后续对构建流程扩展,或者流程改造就方便很多,灵活很多。
  2. 目前从界面上可以清楚的看到执行到了哪个stage。通过点击看stage的log,也可以清楚的看到每个step的运行情况,
  3. 每个stage, step都有统计时间,方便做构建时长的优化。