JenkinsPipelineAsCode
Jenkins Pipeline As Code

目录
[toc]
本节实战
| 实战名称 |
|---|
| 💘 实战:第一条Pipeline-2023.4.7(测试成功) |
| 💘 实战:脚本式语法测试-2023.4.7(测试成功) |
| 💘 实战:测试脚本式语法-2023.4.7(测试成功) |
| 💘 实战:声明式语法嵌入脚本式语法-2023.4.7(测试成功) |
| 💘 实战:利用片段生成器生成执行shell命令代码-2023.4.7(测试成功) |
| 💘 实战:单引号和双引号的区别-2023.4.7(测试成功) |
| 💘 实践:使用jenkins的内置变量来显示构建名及构建分支-2023.3.23(测试成功) |
💘 实践:输出全局变量BUILD_ID和BUILD_URL(测试成功)-2023.4.8 |
| 💘 实践:agent配置的几种方法(测试成功)-2023.4.8 |
| 💘 实践:post{}测试(测试成功)-2023.4.8 |
| 💘 实践:变量测试(测试成功)-2023.4.8 |
| 💘 实践:options测试(测试成功)-2023.4.8 |
| 💘 实践:配置流水线跳过默认代码检出功能-2023.6.26(测试成功) |
| 💘 实践:parameters测试(测试成功)-2023.4.8 |
| 💘 实践:input测试(测试成功)-2023.4.8 |
| 💘 实践:when测试(测试成功)-2023.4.8 |
| 💘 实践:parallel测试(测试成功)-2023.4.10 |
| 💘 实战:groovy字符串测试-2023.4.10(测试成功) |
| 💘 实战:groovy列表测试-2023.4.10(测试成功) |
| 💘 实战:groovy if语句测试-2023.4.10(测试成功) |
| 💘 实战:groovy switch语句测试-2023.4.10(测试成功) |
| 💘 实战:groovy for语句测试-2023.4.10(测试成功) |
| 💘 实战:groovy while语句测试-2023.4.10(测试成功) |
| 💘 实战:groovy 异常处理测试-2023.4.10(测试成功) |
| 💘 实战:groovy 函数测试-2023.4.10(测试成功) |
| 💘 实践:Jenkins共享库实践(测试成功)-2023.4.11 |
一、pipeline
1、什么是Pipeline

1、Pipeline简介

- Pipeline是Jenkins 2.0 版本推出的核心功能;
- Pipeline可以实现以代码的方式定义工作流程;
- 商业价值:
- 组织级别及团队间工作流复用;
- 便于Pipeline开发与维护;
- 减少人工Web页面操作;
如果装了中文插件后,这里可能显示为流水线。

Jenkins的核心是Pipeline(流水线项目),实现了Pipeline As Code。即我们将构建部署测试等步骤全部以代码的形式写到Jenkinsfile中。Jenkins在运行Pipeline任务的时候会按照Jenkinsfile中定义的代码顺序执行。写Jenkinsfile是一项很重的工作,如果稍不注意很容易造成Jenkins的流水线任务失败。Jenkinsfile类似于Dockerfile,具有一套特定的语法。
在2年前, 18年的时候,我们一直在使用自由风格类型的项目。 每个项目中会有代码库的参数信息。 直到有一天项目的代码块从SVN迁移到了GITLAB,即需要我来操作修改Jenkins作业中的代码库URL信息。 修改的过程不复杂,即在web页面中修改然后保存即可。看似简单的过程,但是有大量的流水线作业,基本上一上午就在点点点。(操作简单,重复性动作太多了)
后来了解到Jenkins的核心特性Pipeline, 用代码的方式描述流水线。 这样我就可以维护多个Jenkinsfile来对应不同类型的项目了。 也实现了一部分项目使用统一的一个Jenkinsfile模板来管理。
2、Pipeline组成

- Jenkinsfile: 描述Pipeline的代码文件
- Agent: Pipeline的运行节点
- Stage: Pipeline的阶段
Jenkinsfile:是实现Pipeline as Code的核心功能。 该文件用于描述流水线的过程。
Agent: (是否还记得上次课程添加的JenkinsAgent节点)执行Pipeline的实际节点。
在Jenkins pipeline中,一条流水线是由多个阶段组成的,每个阶段一个stage。例如:构建、测试、部署等等。
3、第一条Pipeline
==💘 实战:第一条Pipeline-2023.4.7(测试成功)==

- 安装Pipeline插件;
- 创建Pipeline类型项目;
- 项目设置,编写Pipeline;
- 构建项目;
让我们开始编写第一条Pipeline吧: 第一条流水线不用太复杂,主要是便于理解流水线的结构。
1pipeline {
2 agent any
3 stages{
4 stage("hello"){
5 steps{
6 echo "Hello Jenkins"
7 }
8 }
9 }
10}
安装Pipeline插件
在创建Pipeline类型的作业的时候,需要提前安装好pipeline插件,不然可能会出现找不到pipeline类型的作业。
进入插件管理, 搜索关键字"pipeline" 。安装后重启一下。

创建Pipeline类型的作业,然后用我们上面编写好的代码运行一下吧。


2、Jenkinsfile语法
Jenkinsfile的基本概念和Jenkins Pipeline的语法类型。
1、Jenkinsfile是什么?

功能: 描述Jenkins Pipeline (Jenkinsfile主要是用来存储用于描述Pipeline的代码。 我们将pipeline的描述代码保存到Jenkinsfile这个文件中。 )
存放方式:
将Jenkinsfile 存放在项目设置(原生支持); (虽然实现了PipelineAsCode但是多个作业管理起来不太方便。)
将Jenkinsfile 存放在Git系统(原生支持);(推荐) (即实现了PipelineAsCode也便于统一管理。)
将Jenkinsfile 存放在制品库(需要第三方插件);(制品库上你改起来不太方便。例如一些场景,我们现在写好一些jenkinsfile,不需要子公司去改,那么我们可以把这个文件打成一个包放到制品库里面,同步给他们,然后在流水线里这么去配置。但是如果需要去改的话,那么就需要用到git,git更方便一点。)
2、Pipeline 语法类型

脚本式语法:
适合以Groovy代码的方式描述Pipeline; (对groovy的要求比较高;)
很多功能特性都需要自行写代码实现; (例如后续写一些邮件告警,想要获取流水线状态,可能都需要自己去写代码实现。)
语法简单,没有相对固定的语法,直接写groovy代码;
声明式语法:
语法简单, 固定的语法;
提供很多已经定义好的功能;(流水线成功失败捕获等等)
也可以通过script{} 嵌入脚本式语法;
jenkins语法有两种类型, 脚本式、声明式语法; 脚本式语法基本上都是通过Groovy代码来进行编写的。声明式语法有一些现成的功能可以直接用,减少脚本式语法的复杂性。但是声明式语法也不是完美的,功能固定还是需要脚本式语法来进行扩展才能实现更加灵活的Pipeline。
最佳实践是声明式语法中通过script{}标签来嵌入脚本式代码。
==💘 实战:脚本式语法测试-2023.4.7(测试成功)==
- 代码

1node("build01"){
2 stage('Build'){
3 echo 'Hello world'
4 }
5
6 stage('Test'){
7 echo 'Hello world'
8 }
9}
- 构建

==💘 实战:测试脚本式语法-2023.4.7(测试成功)==
- 代码

1pipeline {
2 agent any
3
4 stages {
5 stage('Build') {
6 steps {
7 echo 'Hello World'
8 println("devops")
9 }
10 }
11
12 stage('Test') {
13 steps {
14 echo 'Hello World'
15 }
16 }
17 }
18}
- 运行

==💘 实战:声明式语法嵌入脚本式语法-2023.4.7(测试成功)==
- 错误格式代码:

1pipeline {
2 agent any
3
4 stages {
5 stage('Build') {
6 steps {
7 echo 'Hello World'
8 println("devops")
9
10 name = "RongMei"
11 if (name == "RongMei"){
12 println("true")
13 }
14 }
15 }
16
17 stage('Test') {
18 steps {
19 echo 'Hello World'
20 }
21 }
22 }
23}
构建后,会报错的:

- 正确格式代码:

1pipeline {
2 agent any
3
4 stages {
5 stage('Build') {
6 steps {
7 echo 'Hello World'
8 println("devops")
9
10 script{
11 name = "RongMei"
12 if (name == "RongMei"){
13 println("true")
14 }
15 }
16
17 }
18 }
19
20 stage('Test') {
21 steps {
22 echo 'Hello World'
23 }
24 }
25 }
26}
以上代码运行后是正常的:

3、Pipeline开发工具


很多人看到Jenkins Pipeline的代码后,就已经被劝退了。 感觉代码的难度很高,其实难度并不高。利用好开发工具后面理解起来就更简单些。 选择任意pipeline类型的作业,点击“流水线语法”即可进入pipeline开发工具页面。

1. 片段生成器


当你安装好插件之后,很多插件提供了对应的代码块。 我们可以导航到片段生成器中找到对应的代码块生成。如果没有找到相关的语法,可以检查是否安装配置了相关的插件。
这个工具可以帮助我们生成Pipeline的部分代码,降低Pipeline的开发难度。流水线代码片段生成器, 非常好用。在这里可以找到每个插件以及Jenkins内置的方法的使用方法。使用片段生成器可以根据个人需要生成方法,有些方法来源于插件,则需要先安装相关的插件才能使用哦。
范例:error报错

修改代码:

构建:


范例:利用片段生成器生成执行shell命令代码
==💘 实战:利用片段生成器生成执行shell命令代码-2023.4.7(测试成功)==
- 利用
片段生成器生成执行shell命令代码

- 代码

1pipeline {
2 agent any
3
4 stages {
5 stage('Build') {
6 steps {
7 echo 'Hello World'
8 println("devops")
9
10 script{
11 name = "RongMei"
12 if (name == "RongMei"){
13 println("true")
14 }
15
16 sh 'ls -al'
17 }
18
19 }
20 }
21
22 stage('Test') {
23 steps {
24 echo 'Hello World'
25 }
26 }
27 }
28}
- 运行

范例:单引号和双引号的区别
==💘 实战:单引号和双引号的区别-2023.4.7(测试成功)==
单引号里写变量是不生效的;(和其它编程语言差不多)
注意:这里一定要使用双引号,否则会把其当做字符串打印出的!(linux中单引号与双引号的区别)
双引号:
- 代码

1pipeline {
2 agent any
3
4 stages {
5 stage('Build') {
6 steps {
7 echo 'Hello World'
8 println("devops")
9
10 script{
11 name = "RongMei"
12 if (name == "RongMei"){
13 println("true")
14 }
15
16 sh """
17 echo ${name}
18 ls -al
19 """
20 }
21
22 }
23 }
24
25 stage('Test') {
26 steps {
27 echo 'Hello World'
28 }
29 }
30 }
31}
- 运行

单引号:
- 代码

1pipeline {
2 agent any
3
4 stages {
5 stage('Build') {
6 steps {
7 echo 'Hello World'
8 println("devops")
9
10 script{
11 name = "RongMei"
12 if (name == "RongMei"){
13 println("true")
14 }
15
16 sh '''
17 echo ${name}
18 ls -al
19 '''
20 }
21
22 }
23 }
24
25 stage('Test') {
26 steps {
27 echo 'Hello World'
28 }
29 }
30 }
31}
- 运行:

2. 声明式语法生成器

声明式语法式特有的一套语法,如果声明式语法忘记了可以导航到声明式语法生成器 生成对应的代码片段。
范例:生成阶段

编写代码:

构建:

3. 全局变量参考

常见的全局变量
全局变量
1pipeline
2env
3params
4currentBuild
5scm
有时我们会获取一些项目的参数来做数据处理, 此时可以通过Jenkins提供的内置变量来获取对应的关键信息。
例如: 获取当前项目的名称、构建ID、作业URL等等信息。
1BUILD_NUMBER //构建号
2BUILD_ID //构建号
3BUILD_DISPLAY_NAME //构建显示名称
4JOB_NAME //项目名称
5
6EXECUTOR_NUMBER //执行器数量
7NODE_NAME //构建节点名称
8WORKSPACE //工作目录
9JENKINS_HOME //Jenkins home
10JENKINS_URL //Jenkins地址
11BUILD_URL //构建地址
12JOB_URL //项目地址
–
currentbuild变量
1result currentResult //构建结果
2displayName //构建名称 #111
3description //构建描述
4duration //持续时间

范例:使用jenkins的内置变量来显示构建名及构建分支
==💘 实践:使用jenkins的内置变量来显示构建名及构建分支-2023.3.23(测试成功)==
代码:
1println(env)
2
3env.branchName = "develop"
4env.commitID = "${UUID.randomUUID().toString()}"
5println(env.commitID)
6
7env.commitID = "${env.commitID.split("-")[0]}"
8println(env.commitID)
9
10currentBuild.displayName = "#${env.branchName}-${env.commitID}"
11currentBuild.description = "Trigger by user jenkins \n branch: ${env.branchName}"
12
13pipeline {
14
15 agent { label "build"}
16
17 stages{
18 stage("test"){
19 steps{
20 script{
21 echo "${BUILD_NUMBER}"
22
23 //currentBuild.displayName = "#${env.branchName}-${env.commitID}"
24 //currentBuild.description = "Trigger by user jenkins \n branch: master"
25
26
27 echo "当前下载代码分支为: ${env.branchName}"
28 }
29 }
30 }
31 }
32}
运行效果:


全局变量定义并使用
结论:
例如 env.myName = “xyy”,myName = “xyy"等语法属于groovy语法,如果直接定义在stages/stage/steps里会报错的,需要在scripts里定义,或者在pipeline{}外面定义。
在pipeline{}外面定义的是全局变量;
在scripts里定义的变量属于局部变量,加上env标识的话,就属于全局变量了;
1、定义在pipeline{}外面
==💘 实践:输出全局变量BUILD_ID和BUILD_URL(测试成功)-2023.4.8==
- 代码

1currentBuild.displayName = "Devops:commitID"
2currentBuild.description = "Branch:master"
3
4pipeline {
5 agent any
6
7 stages {
8 stage('Build') {
9 steps {
10 echo 'Hello World'
11 println("devops")
12
13 script{
14 name = "RongMei"
15 if (name == "RongMei"){
16 println("true")
17 }
18
19 sh """
20 echo ${name}
21 ls -al
22 """
23 }
24
25 }
26 }
27
28 stage('Test') {
29 steps {
30 echo 'Hello World'
31
32 echo"build_id:${BUILD_ID}"
33 echo "build_url:${BUILD_URL}"
34 }
35 }
36 }
37}
- 构建

2、定义在scripts{}里面
- 我们首先将全局变量的定义放在
stage里面:

1pipeline{
2 //指定运行此流水线的节点
3 agent { node { label "build"}}
4
5 //管道运行选项
6 options {
7 skipStagesAfterUnstable()
8 }
9 //流水线的阶段
10 stages{
11 //阶段1 获取代码
12 stage("CheckOut"){
13 steps{
14 env.myName = "xyy"
15 script{
16 println("获取代码")
17 echo 'i love you ,xyy'
18 echo "${BUILD_ID}"
19 echo "${env.BUILD_ID}"
20 echo "$BUILD_ID"
21 echo "${myName}"
22 }
23 }
24 }
25 stage("Build"){
26 steps{
27 script{
28 println("运行构建")
29 }
30 }
31 }
32 }
33 post {
34 always{
35 script{
36 println("流水线结束后,经常做的事情")
37 }
38 }
39
40 success{
41 script{
42 println("流水线成功后,要做的事情")
43 }
44
45 }
46 failure{
47 script{
48 println("流水线失败后,要做的事情")
49 }
50 }
51
52 aborted{
53 script{
54 println("流水线取消后,要做的事情")
55 }
56
57 }
58 }
59}
结果构建失败了:

- 此时,我们修改下代码,再把其放在script里面,看能否构建成功:

1pipeline{
2 //指定运行此流水线的节点
3 agent { node { label "build"}}
4
5 //管道运行选项
6 options {
7 skipStagesAfterUnstable()
8 }
9 //流水线的阶段
10 stages{
11 //阶段1 获取代码
12 stage("CheckOut"){
13 steps{
14 script{
15 env.myName = "xyy"
16 echo "${myName}"
17 println("获取代码")
18 echo 'i love you ,xyy'
19 echo "${BUILD_ID}"
20 echo "${env.BUILD_ID}"
21 echo "$BUILD_ID"
22
23 }
24 }
25 }
26 stage("Build"){
27 steps{
28 script{
29 println("运行构建")
30 }
31 }
32 }
33 }
34 post {
35 always{
36 script{
37 println("流水线结束后,经常做的事情")
38 }
39 }
40
41 success{
42 script{
43 println("流水线成功后,要做的事情")
44 }
45
46 }
47 failure{
48 script{
49 println("流水线失败后,要做的事情")
50 }
51 }
52
53 aborted{
54 script{
55 println("流水线取消后,要做的事情")
56 }
57
58 }
59 }
60}

可以看到,本次构建成功,原来就是把命令式代码写在stage位置了,导致构建失败!因此也验证了上面的结论脚本式的语法我们都写在script里面。
- 刚才在检出代码阶段定义的全局变量,是否可以在构建阶段里面使用?(肯定可以的)这边来测试下

1pipeline{
2 //指定运行此流水线的节点
3 agent { node { label "build"}}
4
5 //管道运行选项
6 options {
7 skipStagesAfterUnstable()
8 }
9 //流水线的阶段
10 stages{
11 //阶段1 获取代码
12 stage("CheckOut"){
13 steps{
14 script{
15 env.myName = "xyy"
16 echo "${myName}"
17 env.branchName = "dev"
18 println("获取代码")
19 echo 'i love you ,xyy'
20 echo "${BUILD_ID}"
21 echo "${env.BUILD_ID}"
22 echo "$BUILD_ID"
23
24 }
25 }
26 }
27 stage("Build"){
28 steps{
29 script{
30 println("运行构建")
31 echo "${branchName}"
32 }
33 }
34 }
35 }
36 post {
37 always{
38 script{
39 println("流水线结束后,经常做的事情")
40 }
41 }
42
43 success{
44 script{
45 println("流水线成功后,要做的事情")
46 }
47
48 }
49 failure{
50 script{
51 println("流水线失败后,要做的事情")
52 }
53 }
54
55 aborted{
56 script{
57 println("流水线取消后,要做的事情")
58 }
59
60 }
61 }
62}

结论:可以看到,在某一个stage里定义的全局变量,在其他stage里也是可以使用的!(符合预期)
🍀 示例:如何使用命令式语法来定义局部变量
- 代码
1pipeline{
2 //指定运行此流水线的节点
3 agent { node { label "build"}}
4
5 //流水线的阶段
6 stages{
7 //阶段1 获取代码
8 stage("CheckOut"){
9 steps{
10 script{
11 println("获取代码")
12 envType = "test"
13
14 }
15 }
16 }
17 stage("Build"){
18 steps{
19 script{
20 println("运行构建")
21 echo "envType: ${envType}"
22 }
23 }
24 }
25 }
26}
进行构建:

结论:发现在script里定义的局部变量,在其他stage里也是可以使用的。。。😥并且,也是可以在stage里引用局部变量的,也是不会报错的。
- Jenkins web也是可以配置全局变量的,但是不推荐(使用groovy代码就好)
管理Jenkins-配置系统:


4.调试回放流水线

适合调试流水线代码,构建后点击回访可以看到上次构建运行的Pipeline代码,而我们可以通过此编辑器对代码进行修改和调试而不会影响原始的代码。
4、Pipeline核心语法
声明式+脚本式。

Pipeline核心语法1
pipeline

声明式流水线的定义, 一个pipeline{}。
agent

定义Pipeline中的运行节点(Jenkins Agent)
pipeline{ agent {} }流水线级别的节点stage{ agent{} }阶段级别的节点使用方式
label: 在特定的名称或者label节点运行; (标签=分组)
any: 在任意节点运行;
none: 当pipeline全局指定agent为none,则根据每个stage中定义的agent运行(stage必须指定)。
node:支持自定义流水线的工作目录。
pipeline里必须要有agent格式的。
1// 一
2pipeline {
3 agent any //流水线级别的节点 (常用)
4}
5
6
7
8// 二
9pipeline {
10 agent { label "labelName或者节点名称" }
11}
12
13
14//三:在阶段中定义agent
15pipeline {
16
17 agent none
18
19 stages{
20 stage('Build'){
21 agent { label "build" } //阶段级别的节点 (很少使用)
22 steps {
23 echo "building......"
24 }
25 }
26 }
27}
28
29
30
31// 四 自定义节点 node:支持自定义流水线的工作目录。
32pipeline {
33 agent {
34 node {
35 label "labelName或者节点名称"
36 customWorkspace "/opt/agent/workspace"
37 }
38 }
39}
Q:agent {node {label }} 这个语法第一次见,怎么知道有这个语法,是声明语法生成器中的吗?
但是声明式语法生成器中是没有这个配置的。
范例:agent配置的几种方法
==💘 实践:agent配置的几种方法(测试成功)-2023.4.8==
⚠️ 特别注意:
label后面可以接labelName或者节点名称,都是可以的。
测试过程如下:
- 当前测试环境节点名称及标签如下:

label后面可以接节点名称
代码:

1currentBuild.displayName = "Devops:commitID"
2currentBuild.description = "Branch:master"
3
4pipeline {
5 agent {label "build01"}
6
7 stages {
8 stage('Build') {
9 steps {
10 echo 'Hello World'
11 println("devops")
12
13 script{
14 name = "RongMei"
15 if (name == "RongMei"){
16 println("true")
17 }
18
19 sh """
20 echo ${name}
21 ls -al
22 """
23 }
24
25 }
26 }
27
28 stage('Test') {
29 steps {
30 echo 'Hello World'
31
32 echo"build_id:${BUILD_ID}"
33 echo "build_url:${BUILD_URL}"
34 }
35 }
36 }
37}
运行:

label后面可以接labelName
代码:

1currentBuild.displayName = "Devops:commitID"
2currentBuild.description = "Branch:master"
3
4pipeline {
5 agent {label "build"}
6
7 stages {
8 stage('Build') {
9 steps {
10 echo 'Hello World'
11 println("devops")
12
13 script{
14 name = "RongMei"
15 if (name == "RongMei"){
16 println("true")
17 }
18
19 sh """
20 echo ${name}
21 ls -al
22 """
23 }
24
25 }
26 }
27
28 stage('Test') {
29 steps {
30 echo 'Hello World'
31
32 echo"build_id:${BUILD_ID}"
33 echo "build_url:${BUILD_URL}"
34 }
35 }
36 }
37}

- 作业运行在任意节点:
代码:

1currentBuild.displayName = "Devops:commitID"
2currentBuild.description = "Branch:master"
3
4pipeline {
5 agent any
6
7 stages {
8 stage('Build') {
9 steps {
10 echo 'Hello World'
11 println("devops")
12
13 script{
14 name = "RongMei"
15 if (name == "RongMei"){
16 println("true")
17 }
18
19 sh """
20 echo ${name}
21 ls -al
22 """
23 }
24
25 }
26 }
27
28 stage('Test') {
29 steps {
30 echo 'Hello World'
31
32 echo"build_id:${BUILD_ID}"
33 echo "build_url:${BUILD_URL}"
34 }
35 }
36 }
37}
运行:

- 使用node来自定义流水线构建目录
代码:

1currentBuild.displayName = "Devops:commitID"
2currentBuild.description = "Branch:master"
3
4pipeline {
5 agent {
6 node{
7 label "build"
8 customWorkspace "/opt/jenkinsagent/workspace/test"
9 }
10 }
11
12 stages {
13 stage('Build') {
14 steps {
15 echo 'Hello World'
16 println("devops")
17
18 script{
19 name = "RongMei"
20 if (name == "RongMei"){
21 println("true")
22 }
23
24 sh """
25 echo ${name}
26 ls -al
27 """
28 }
29
30 }
31 }
32
33 stage('Test') {
34 steps {
35 echo 'Hello World'
36
37 echo"build_id:${BUILD_ID}"
38 echo "build_url:${BUILD_URL}"
39 }
40 }
41 }
42}
构建:

- 如果指定一个错误节点或者标签,测试的作业状态时什么样的呢?
代码:

1currentBuild.displayName = "Devops:commitID"
2currentBuild.description = "Branch:master"
3
4pipeline {
5 agent {
6 node{
7 label "build02"
8 customWorkspace "/opt/jenkinsagent/workspace/test"
9 }
10 }
11
12 stages {
13 stage('Build') {
14 steps {
15 echo 'Hello World'
16 println("devops")
17
18 script{
19 name = "RongMei"
20 if (name == "RongMei"){
21 println("true")
22 }
23
24 sh """
25 echo ${name}
26 ls -al
27 """
28 }
29
30 }
31 }
32
33 stage('Test') {
34 steps {
35 echo 'Hello World'
36
37 echo"build_id:${BUILD_ID}"
38 echo "build_url:${BUILD_URL}"
39 }
40 }
41 }
42}
构建:


- 测试:label虽然不支持正则,agent是可以加变量去选节点的
代码:

1currentBuild.displayName = "Devops:commitID"
2currentBuild.description = "Branch:master"
3
4choiceBuildNode = "build"
5pipeline {
6 agent {
7 node{
8 label "${choiceBuildNode}"
9 customWorkspace "/opt/jenkinsagent/workspace/test"
10 }
11 }
12
13 stages {
14 stage('Build') {
15 steps {
16 echo 'Hello World'
17 println("devops")
18
19 script{
20 name = "RongMei"
21 if (name == "RongMei"){
22 println("true")
23 }
24
25 sh """
26 echo ${name}
27 ls -al
28 """
29 }
30
31 }
32 }
33
34 stage('Test') {
35 steps {
36 echo 'Hello World'
37
38 echo"build_id:${BUILD_ID}"
39 echo "build_url:${BUILD_URL}"
40 }
41 }
42 }
43}
运行:

- 运行在jenins节点



stages

定义Pipeline的阶段;
1pipeline { stages {} }
2stages > stage > steps
- 关系: stages > stage > steps > script
- 定义:
- stages:包含多个stage阶段
- stage:包含多个steps步骤
- steps: 包含一组特定的脚本(加上script后就可以实现在声明式脚本中嵌入脚本式语法了)
1currentBuild.displayName = "Devops:commitID"
2currentBuild.description = "Branch:master"
3
4pipeline {
5 agent {
6 node{
7 label "build02"
8 customWorkspace "/opt/jenkinsagent/workspace/test"
9 }
10 }
11
12 stages {
13 stage('Build') {
14 steps {
15 echo 'Hello World'
16 println("devops")
17
18 script{
19 name = "RongMei"
20 if (name == "RongMei"){
21 println("true")
22 }
23
24 sh """
25 echo ${name}
26 ls -al
27 """
28 }
29
30 }
31 }
32
33 stage('Test') {
34 steps {
35 echo 'Hello World'
36
37 echo"build_id:${BUILD_ID}"
38 echo "build_url:${BUILD_URL}"
39 }
40 }
41 }
42}

- 注意:
steps下是没有step的。
代码:

1currentBuild.displayName = "Devops:commitID"
2currentBuild.description = "Branch:master"
3
4choiceBuildNode = "build"
5pipeline {
6 agent {
7 node{
8 label "${choiceBuildNode}"
9 customWorkspace "/opt/jenkinsagent/workspace/test"
10 }
11 }
12
13 stages {
14 stage('Build') {
15 steps {
16 step{
17 echo "hello"
18 }
19 echo 'Hello World'
20 println("devops")
21
22 script{
23 name = "RongMei"
24 if (name == "RongMei"){
25 println("true")
26 }
27
28 sh """
29 echo ${name}
30 ls -al
31 """
32 }
33
34 }
35 }
36
37 stage('Test') {
38 steps {
39 echo 'Hello World'
40
41 echo"build_id:${BUILD_ID}"
42 echo "build_url:${BUILD_URL}"
43 }
44 }
45 }
46}
报错:

⚠️ 注意:
- 虽然有的shell命令放在steps里也不会报错的,但是为了代码的规范性及可读性,强烈建议将shell命令都放在script里!(有可能if else放在steps里可能会报错);
- scripts里是存放groovy脚本的;


post

根据流水线的最终状态做一些操作
状态:
always: 总是执行 (例如每次执行后清理缓存动作;)
success: 仅流水线成功后执行 (执行成功后,也可触发下一动作,例如触发CD;(远程构建))
failure: 仅流水线失败后执行 (failure/aborted:可能要给开发人员发个邮件。)
aborted: 仅流水线被取消后执行
unstable:不稳定状态,单测失败等
注意:post{}是和states同级的!
1pipeline {
2
3 .....
4
5 .....
6
7 post {
8 always{
9 script{
10 println("流水线结束后,经常做的事情")
11 }
12 }
13
14 success{
15 script{
16 println("流水线成功后,要做的事情")
17 }
18
19 }
20 failure{
21 script{
22 println("流水线失败后,要做的事情")
23 }
24 }
25
26 aborted{
27 script{
28 println("流水线取消后,要做的事情")
29 }
30
31 }
32 }
33}
范例:综合脚本
==💘 实践:post{}测试(测试成功)-2023.4.8==

- 代码

1currentBuild.displayName = "Devops:commitID"
2currentBuild.description = "Branch:master"
3
4pipeline {
5 agent {
6 node{
7 label "build"
8 customWorkspace "/opt/jenkinsagent/workspace/test"
9 }
10 }
11
12 stages {
13 stage('Build') {
14 steps {
15 echo 'Hello World'
16 println("devops")
17
18 script{
19 name = "RongMei"
20 if (name == "RongMei"){
21 println("true")
22 }
23
24 sh """
25 echo ${name}
26 ls -al
27 """
28 }
29
30 }
31 }
32
33 stage('Test') {
34 steps {
35 echo 'Hello World'
36
37 echo"build_id:${BUILD_ID}"
38 echo "build_url:${BUILD_URL}"
39 }
40 }
41 }
42
43 post {
44 always{
45 script{
46 println("流水线结束后,经常做的事情")
47 }
48 }
49
50 success{
51 script{
52 println("流水线成功后,要做的事情")
53 }
54
55 }
56 failure{
57 script{
58 println("流水线失败后,要做的事情")
59 }
60 }
61
62 aborted{
63 script{
64 println("流水线取消后,要做的事情")
65 }
66
67 }
68 }
69}
- 运行:

Pipeline核心语法2
environment

以声明式脚本语法的方式定义环境变量;
局部变量优先级高于全局变量;
推荐:声明式语法里使用enironment这个语句块来写。
方式1:声明式语法
定义: 通过键值对(k-v)格式定义流水线在运行时的环境变量, 分为流水线级别和阶段级别。
1pipeline {
2 agent any
3
4 //全局变量
5 environment {
6 NAME = "RongMei"
7 VERSION = "1.1.10"
8 ENVTYPE = "DEV"
9 }
10
11 stages {
12 stage("build"){
13 //局部变量
14 environment {
15 VERSION = "1.1.20"
16 }
17 steps {
18 script {
19 echo "${VERSION}"
20 echo "${buildUser}"
21 }
22 }
23 }
24 }
25}
方式2:全局变量可以放在pipeline外面
1// 全局变量可以放在pipeline外面,也可以放在里面。
2env.lover="xyy"
3env.age=18
4
5pipeline {
6 agent any
7
8 //全局变量
9 environment {
10 NAME = "RongMei"
11 VERSION = "1.1.10"
12 ENVTYPE = "DEV"
13 }
14
15 stages {
16 stage("build"){
17 //局部变量
18 environment {
19 VERSION = "1.1.20"
20 }
21 steps {
22 script {
23 echo "${VERSION}"
24 echo "lover:${env.lover} age:${env.age}"
25 }
26 }
27 }
28 }
29}

==💘 实践:变量测试(测试成功)-2023.4.8==
- 代码

1// 全局变量可以放在pipeline外面,也可以放在里面。
2buildUser = "jenkins"
3
4pipeline {
5 agent any
6
7 //全局变量
8 environment {
9 NAME = "RongMei"
10 VERSION = "1.1.10"
11 ENVTYPE = "DEV"
12 }
13
14 stages {
15 stage("build"){
16 //局部变量
17 environment {
18 VERSION = "1.1.20"
19 }
20 steps {
21 script {
22 echo "${VERSION}"
23 echo "${buildUser}"
24 }
25 }
26 }
27 }
28}
- 运行

options

Pipeline运行时的一些选项
- 设置保存最近的记录
- 禁止并行构建
- 跳过默认的代码检出
- 设定流水线的超时时间(可用于阶段级别)
- 设定流水线的重试次数(可用于阶段级别)
- 设置日志时间输出(可用于阶段级别)
⚠️ 注意:
(以代码的方式定义的配置,需要流水线构建运行后才能看到效果)。
Jenkins需要加载一下,才能够做这个设置。
所以刚才我们定义的这些设置,都得初始化下。第一次初始化时,不用去关心它的状态是成功还是失败,它只是进行一些项目的初始化。

常用配置:
1## 设置保存最近的记录
2options { buildDiscarder(logRotator(numToKeepStr: '1')) }
3
4## 禁止并行构建
5options { disableConcurrentBuilds() }
6
7
8## 跳过默认的代码检出
9options { skipDefaultCheckout() }
10
11
12## 设定流水线的超时时间(可用于阶段级别)
13options { timeout(time: 1, unit: 'HOURS') }
14
15
16## 设定流水线的重试次数(可用于阶段级别)
17options { retry(3) }
18
19
20## 设置日志时间输出(可用于阶段级别)
21options { timestamps() }
!
1pipeline {
2 options {
3 disableConcurrentBuilds()
4 skipDefaultCheckout()
5 timeout(time: 1, unit: 'HOURS')
6 }
7
8 stages {
9 stage("build"){
10 options {
11 timeout(time: 5, unit: 'MINUTES')
12 retry(3)
13 timestamps()
14 }
15 }
16 }
17}
18
19}


范例:option选项测试
1pipeline {
2 agent any
3
4 options {
5 disableConcurrentBuilds()
6 skipDefaultCheckout()
7 timeout(time: 1, unit: 'HOURS')
8 buildDiscarder logRotator(artifactDaysToKeepStr: '', artifactNumToKeepStr: '', daysToKeepStr: '30', numToKeepStr: '5')
9 }
10
11 stages {
12 stage("build"){
13 //options既可以在stage里面,也可以写在外面。
14 options {
15 timeout(time: 5, unit: 'MINUTES')
16 retry(3)
17 timestamps()
18 }
19
20 steps{
21 echo "hello"
22 }
23 }
24 }
25}

范例:丢弃旧的构建
==💘 实践:options测试(测试成功)-2023.4.8==
- 使用片段生成器生成代码

默认这个流水线的配置这里为空:

- 代码

1// 全局变量可以放在pipeline外面,也可以放在里面。
2buildUser = "jenkins"
3
4pipeline {
5 agent any
6
7 options {
8 buildDiscarder logRotator(artifactDaysToKeepStr: '', artifactNumToKeepStr: '', daysToKeepStr: '30', numToKeepStr: '5')
9 }
10
11 //全局变量
12 environment {
13 NAME = "RongMei"
14 VERSION = "1.1.10"
15 ENVTYPE = "DEV"
16 }
17
18 stages {
19 stage("build"){
20 //局部变量
21 environment {
22 VERSION = "1.1.20"
23 }
24 steps {
25 script {
26 echo "${VERSION}"
27 echo "${buildUser}"
28 }
29 }
30 }
31 }
32}
- 运行

可以看到,运行流水线后,这里的配置被配置上了。

范例:配置流水线跳过默认代码检出功能
💘 实践:配置流水线跳过默认代码检出功能-2023.6.26(测试成功)
- 实验环境
1jenkins/jenkins:2.346.3-2-lts-jdk11
2gitlab/gitlab-ce:15.0.3-ce.0
3sonarqube:9.9.0-community
4SonarScanner 4.8.0.2856
实验软件(无)
当前现象
当给jenkins流水线配置了从git仓库拉取Jenkinsfile时(即使用共享库),编译阶段会多了一个默认代码检出阶段



- 那该如何跳过这个功能呢?
在声明式片段里生成语法下:

1options {
2 skipDefaultCheckout true
3}
- 我们来配置下共享库里的代码,提交,再次构建测试下

提交。
再次构建测试:

构建结果:

符合预期,以上问题已解决。😘
parameters{}

- 参数化构建,在jenkinsfile中以代码的方式定义构建参数;
- 注意由于是通过代码定义的,需运行一次构建后才能生成;
- String、Choice…
结论:
如果同时有
environment和parameters时,env.VERSION和VERSION默认会使用enironment的值的,因此要想使用parameters,推荐使用params.VERSION来引用。
- 定义: 流水线在运行时设置的参数,UI页面的参数。所有的参数都存储在params对象中。
- 将web ui页面中定义的参数,以代码的方式定义。 (以代码的方式定义的配置,需要流水线构建运行后才能看到效果)
1pipeline {
2 agent any
3
4 parameters {
5 string(name: 'VERSION', defaultValue: '1.1.1', description: '')
6 }
7
8 stages {
9 stage("Build"){
10 steps {
11 echo "${params.VERSION}"
12 }
13 }
14 }
15}
范例:参数构建测试
==💘 实践:parameters测试(测试成功)-2023.4.8==
- 使用片段生成器生成代码


- 代码

1// 全局变量可以放在pipeline外面,也可以放在里面。
2buildUser = "jenkins"
3
4pipeline {
5 agent any
6
7 options {
8 buildDiscarder logRotator(artifactDaysToKeepStr: '', artifactNumToKeepStr: '', daysToKeepStr: '30', numToKeepStr: '5')
9 }
10
11 parameters {
12 choice choices: ['prod', 'test', 'dev'], description: 'Please choice env name.', name: 'envName'
13 string defaultValue: '1.1.1', name: 'Version', trim: true
14 }
15
16 //全局变量
17 environment {
18 NAME = "RongMei"
19 VERSION = "1.1.10"
20 ENVTYPE = "DEV"
21 }
22
23 stages {
24 stage("build"){
25 //局部变量
26 environment {
27 VERSION = "1.1.20"
28 }
29 steps {
30 script {
31 echo "${VERSION}"
32 echo "${buildUser}"
33
34 //注意:选项参数中的变量也是全局变量,因此还可以使用如下2种方式来访问变量
35 echo "${params.envName}"
36 echo "${env.envName}"
37 echo "${envName}"
38 }
39 }
40 }
41 }
42}
- 运行


Pipeline核心语法3
trigger

1- cron 定时触发: triggers { cron('H */7 * * 1-5') } (例如代码扫描。)
2- pollSCM: triggers { pollSCM('H */7 * * 1-5') } ##被动地去检查你的代码库是否有更新,如果有更新就触发任务;

范例:cron触发
==💘 实践:trigger测试(测试成功)-2023.4.8==
常见的触发方式:

使用cron触发:

默认trigger这里为空:

- 代码

1// 全局变量可以放在pipeline外面,也可以放在里面。
2buildUser = "jenkins"
3
4pipeline {
5 agent any
6
7 options {
8 buildDiscarder logRotator(artifactDaysToKeepStr: '', artifactNumToKeepStr: '', daysToKeepStr: '30', numToKeepStr: '5')
9 }
10 parameters {
11 choice choices: ['prod', 'test', 'dev'], description: 'Please choice env name.', name: 'envName'
12 string defaultValue: '1.1.1', name: 'Version', trim: true
13 }
14 triggers {
15 // 这个代表每小时执行一次。
16 cron 'H * * * *'
17 }
18
19 //全局变量
20 environment {
21 NAME = "RongMei"
22 VERSION = "1.1.10"
23 ENVTYPE = "DEV"
24 }
25
26 stages {
27 stage("build"){
28 //局部变量
29 environment {
30 VERSION = "1.1.20"
31 }
32 steps {
33 script {
34 echo "${VERSION}"
35 echo "${buildUser}"
36
37 //注意:选项参数中的变量也是全局变量,因此还可以使用如下2种方式来访问变量
38 echo "${params.envName}"
39 echo "${env.envName}"
40 echo "${envName}"
41 }
42 }
43 }
44 }
45}
- 运行

可以看到,trigger这里出现了配置,符合预期。

范例:上游项目构建触发
1 triggers {
2 upstream 'demo2,'
3 }
我们不会用这种方式来触发,会用一个比较通用的方式来触发的,请看后文。
- 生成代码


1// 全局变量可以放在pipeline外面,也可以放在里面。
2buildUser = "jenkins"
3
4pipeline {
5 agent any
6
7 options {
8 buildDiscarder logRotator(artifactDaysToKeepStr: '', artifactNumToKeepStr: '', daysToKeepStr: '30', numToKeepStr: '5')
9 }
10
11 parameters {
12 choice choices: ['prod', 'test', 'dev'], description: 'Please choice env name.', name: 'envName'
13 string defaultValue: '1.1.1', name: 'Version', trim: true
14 }
15
16 triggers {
17 upstream 'demo2,'
18 }
19
20 //全局变量
21 environment {
22 NAME = "RongMei"
23 VERSION = "1.1.10"
24 ENVTYPE = "DEV"
25 }
26
27 stages {
28 stage("build"){
29 //局部变量
30 environment {
31 VERSION = "1.1.20"
32 }
33 steps {
34 script {
35 echo "${VERSION}"
36 echo "${buildUser}"
37
38 //注意:选项参数中的变量也是全局变量,因此还可以使用如下2种方式来访问变量
39 echo "${params.envName}"
40 echo "${env.envName}"
41 echo "${envName}"
42 }
43 }
44 }
45 }
46}
- 运行后,demo项目里就能看到,符合预期:

input

参数解析
- message: 提示信息
- ok: 表单中确认按钮的文本
- submitter: 提交人,默认所有人可以
- parameters: 交互时用户选择的参数
小demo:
1pipeline {
2 agent any
3 stages {
4 stage('Deploy') {
5 input {
6 message "是否继续发布"
7 ok "Yes"
8 submitter "zeyang,aa"
9 parameters {
10 string(name: 'ENVTYPE', defaultValue: 'DEV', description: 'env type..[DEV/STAG/PROD]')
11 }
12 }
13 steps {
14 echo "Deploy to ${ENVTYPE}, doing......."
15 }
16 }
17 }
18}

==💘 实践:input测试(测试成功)-2023.4.8==
- 使用声明式语法生成器生成代码



- 代码

1// 全局变量可以放在pipeline外面,也可以放在里面。
2buildUser = "jenkins"
3
4pipeline {
5 agent any
6
7 options {
8 buildDiscarder logRotator(artifactDaysToKeepStr: '', artifactNumToKeepStr: '', daysToKeepStr: '30', numToKeepStr: '5')
9 }
10 parameters {
11 choice choices: ['prod', 'test', 'dev'], description: 'Please choice env name.', name: 'envName'
12 string defaultValue: '1.1.1', name: 'Version', trim: true
13 }
14 triggers {
15 cron 'H * * * *'
16 }
17
18 //全局变量
19 environment {
20 NAME = "RongMei"
21 VERSION = "1.1.10"
22 ENVTYPE = "DEV"
23 }
24
25 stages {
26 stage("build"){
27 //局部变量
28 environment {
29 VERSION = "1.1.20"
30 }
31
32 input {
33 message 'Please choice your options.'
34 ok '提交'
35 parameters {
36 choice choices: ['rollback', 'stop'], name: 'runOptions'
37 }
38 }
39
40 steps {
41 script {
42 echo "${VERSION}"
43 echo "${buildUser}"
44
45 //注意:选项参数中的变量也是全局变量,因此还可以使用如下2种方式来访问变量
46 echo "${params.envName}"
47 echo "${env.envName}"
48 echo "${envName}"
49
50 //引用变量
51 echo "${runOptions}"
52
53 //这里写判断逻辑
54 if ("${runOptions}" == "rollback"){
55 println("rollback……")
56 }
57 if ("${runOptions}" == "stop"){
58 println("stop……")
59 }
60 }
61 }
62 }
63 }
64}
- 运行
选择rollback:



选择stop:


- 注意:

when

根据条件判断是否运行Stage;
判断条件
- 根据环境变量判断
- 根据表达式判断
- 根据条件判断(not/allOf/anyOf)
1pipeline {
2 agent any
3 stages {
4 stage('Build') {
5 steps {
6 echo 'build......'
7 }
8 }
9 stage('Deploy') {
10 when {
11 environment name: 'DEPLOY_TO', value: 'DEV'
12 }
13 steps {
14 echo 'Deploying.......'
15 }
16 }
17 }
18}
19
20
21
22//
23
24### allOf 条件全部成立
25 when {
26 allOf {
27 environment name: 'CAN_DEPLOY', value: 'true'
28 environment name: 'DEPLOY_ENV', value: 'dev'
29 }
30 }
31
32
33
34
35
36### anyOf 条件其中一个成立
37when {
38 anyOf {
39 environment name: 'CAN_DEPLOY', value: 'true'
40 environment name: 'DEPLOY_ENV', value: 'dev'
41 }
42 }

范例:根据环境变量去配置
- 代码:
1pipeline {
2 agent any
3 stages {
4 stage('Build') {
5 steps {
6 echo 'build......'
7 }
8 }
9 stage('Deploy') {
10 when {
11 environment name: 'DEPLOY_TO', value: 'DEV'
12 }
13 steps {
14 echo 'Deploying.......'
15 }
16 }
17 }
18}
运行:(因为默认是没这个变量的,Deploy阶段肯定跳过的)


- 添加
DEPLOY_TO变量后,再次测试
1pipeline {
2 agent any
3
4 environment {
5 DEPLOY_TO = 'DEV'
6 }
7 stages {
8 stage('Build') {
9 steps {
10 echo 'build......'
11 }
12 }
13 stage('Deploy') {
14 when {
15 environment name: 'DEPLOY_TO', value: 'DEV'
16 }
17 steps {
18 echo 'Deploying.......'
19 }
20 }
21 }
22}
再次运行:

范例
==💘 实践:when测试(测试成功)-2023.4.8==
- 代码

1// 全局变量可以放在pipeline外面,也可以放在里面。
2buildUser = "jenkins"
3
4pipeline {
5 agent any
6
7 options {
8 buildDiscarder logRotator(artifactDaysToKeepStr: '', artifactNumToKeepStr: '', daysToKeepStr: '30', numToKeepStr: '5')
9 }
10 parameters {
11 choice choices: ['prod', 'test', 'dev'], description: 'Please choice env name.', name: 'envName'
12 string defaultValue: '1.1.1', name: 'Version', trim: true
13 }
14 triggers {
15 cron 'H * * * *'
16 }
17
18 //全局变量
19 environment {
20 NAME = "RongMei"
21 VERSION = "1.1.10"
22 ENVTYPE = "DEV"
23 }
24
25 stages {
26 stage("build"){
27 //局部变量
28 environment {
29 VERSION = "1.1.20"
30 }
31
32 input {
33 message 'Please choice your options.'
34 ok '提交'
35 parameters {
36 choice choices: ['rollback', 'stop'], name: 'runOptions'
37 }
38 }
39 steps {
40 script {
41 echo "${VERSION}"
42 echo "${buildUser}"
43
44 //注意:选项参数中的变量也是全局变量,因此还可以使用如下2种方式来访问变量
45 echo "${params.envName}"
46 echo "${env.envName}"
47 echo "${envName}"
48
49 echo "${runOptions}"
50
51 if ("${runOptions}" == "rollback"){
52 println("rollback……")
53 }
54 if ("${runOptions}" == "stop"){
55 println("stop……")
56 }
57
58 env.runOptions = "${runOptions}"
59 }
60 }
61 }
62
63
64 stage("Rollback"){
65 when {
66 environment name: "runOptions", value: 'rollback'
67 }
68 steps {
69 println("rollback……")
70 }
71 }
72 }
73
74}
- 运行
当选择rollback时:Rollback阶段被执行



当选择stop时:Rollback阶段被跳过



prallel

Stage并行运行
场景: 自动化测试,多主机并行发布。
1pipeline {
2 agent any
3 stages {
4 stage('Parallel Stage') {
5 failFast true
6 parallel {
7 stage('windows') {
8 agent {
9 label "master"
10 }
11 steps {
12 echo "windows"
13 }
14 }
15 stage('linux') {
16 agent {
17 label "build"
18 }
19 steps {
20 echo "linux"
21 }
22 }
23 }
24 }
25 }
26}

==💘 实践:parallel测试(测试成功)-2023.4.10==
- 代码

1pipeline {
2 agent any
3 stages {
4 stage('Parallel Stage') {
5 failFast true
6 parallel {
7 stage('windows') {
8 agent {
9 label "master"
10 }
11 steps {
12 echo "windows"
13 }
14 }
15 stage('linux') {
16 agent {
17 label "build"
18 }
19 steps {
20 echo "linux"
21 }
22 }
23 }
24 }
25 }
26}
- 运行

FAQ:如何解决并发构建中的workspace问题?
这个workspace问题挺重要的。(已解决)
多个并行阶段使用一个工作目录肯定是有问题的;(在每次构建的时候加一个随机数)
1.build_id
2.使用随机数
或者直接禁用并行构建方法也行;
并行构建用到的场景:
情况1:如果一个项目管理多个微服务,那么就会需要用到并行构建; 情况2:多分支;
方法1:build_id
1env.nworkspace = "/opt/agent/test/build-${BUILD_ID}"
2
3
4pipeline{
5 agent{
6 node{
7 label "build"
8 customWorkspace "${env.nworkspace}"
9 }
10 }
11
12 stages{
13 stage("build"){
14
15 steps{
16 echo "${env.nworkspace}"
17 }
18 }
19
20 }
21}
多点击几次运行:


方法2:随机数
1env.nworkspace = "/opt/agent/test/${JOB_NAME}-${UUID.randomUUID().toString()}"
2
3
4pipeline{
5 agent{
6 node{
7 label "build"
8 customWorkspace "${env.nworkspace}"
9 }
10 }
11
12 stages{
13 stage("build"){
14
15 steps{
16 echo "${env.nworkspace}"
17 }
18 }
19
20 }
21}
22
23
24### 输出
25demo-fec54ca7-81a5-452e-91b5-2a187ab3562b

- 扩展:
2023.4.10-实践 - Jenkins 声明式管道中的动态并行阶段(简悦剪藏)

二、Groovy
1、Groovy简介
groovy这一块我们基本只会用到数据处理;
使用groovy的目的:
- 想让自己的pipeline更加灵活;
- 搞微服务实践时,会调api,或者做数据处理;


Groovy功能强大,可选类型和动态语言(很多语法和python类似),天生支持Java平台。
简洁且简单易学的语法。
可以与任何Java程序顺利集成、包括脚本编写功能、特定领域语言编写,运行时和编译时元编程以及函数式编程。
Groovy参考文档
http://groovy-lang.org/groovy-dev-kit.html
http://docs.groovy-lang.org/docs/latest/html/documentation/#_map_coercion

注释
- 单行注释 //
- 多行注释 /**/
groovy环境安装方法
1.直接在jenkins的pipeline里运行groovy代码即可。(推荐)
可以在回放里进行测试(回放里写的代码都是groovy代码)


2.另外:jenkins的Script Console也能执行groovy代码,同时也可以调用jenkins方法。
在系统管理-脚本命令行里打开写代码即可;
或者直接在后面加个script即可打开:
http://172.29.9.101:8080/script



3.安装本地groovy环境。
==💘 实战:《groovy配置开发环境》-泽阳-2022.4.23(测试成功)==

实验环境
1jdk版本:openjdk version "1.8.0_322" 2centos7.6虚机实验软件
链接:https://pan.baidu.com/s/1GKaCvXyU0WF2rTrWffx37A?pwd=7r9p 提取码:7r9p
2022.4.23-share-JenkinsPipelineAsCode实验代码
前置条件
1jdk环境已安装;1️⃣ 下载groovy安装包


- 2️⃣ 设置环境变量并配置
将安装包上传到服务器上
1(1)上传
2[root@devops ~]#ll -h apache-groovy-sdk-4.0.1.zip
3-rw-r--r-- 1 root root 57M Apr 21 08:57 apache-groovy-sdk-4.0.1.zip
4
5(2)解压
6[root@devops ~]#unzip apache-groovy-sdk-4.0.1.zip -d /usr/local/
7[root@devops ~]#cd /usr/local/groovy-4.0.1/
8[root@devops groovy-4.0.1]#ls
9bin conf doc grooid lib LICENSE licenses NOTICE src
10[root@devops groovy-4.0.1]#cd bin/
11[root@devops bin]#ls
12grape groovy.bat groovy_completion groovydoc groovysh java2groovy.bat
13grape.bat groovyc groovyConsole groovydoc.bat groovysh.bat startGroovy
14grape_completion groovyc.bat groovyConsole.bat groovydoc_completion groovysh_completion startGroovy.bat
15groovy groovyc_completion groovyConsole_completion groovy.ico java2groovy
16[root@devops bin]#
17
18(3)设置环境变量
19[root@devops ~]#vim /etc/profile
20……
21export GROOVY_HOME=/usr/local/groovy-4.0.1
22export PATH=$JAVA_HOME/bin:$GROOVY_HOME/bin:$PATH
23[root@devops groovy-4.0.1]#source /etc/profile
- 3️⃣ 验证
1[root@devops ~]#groovysh
2Apr 21, 2022 11:25:39 AM java.util.prefs.FileSystemPreferences$1 run
3INFO: Created user preferences directory.
4Groovy Shell (4.0.1, JVM: 1.8.0_322)
5Type ':help' or ':h' for help.
6groovy:000> println("hello world")
7hello world
8===> null
9groovy:000>
2、Groovy数据类型

1.String
字符串类型, 是在流水线中应用最为广泛的一种类型。
字符串定义
可以通过双引号、单引号、三引号定义;
- 如果是普通字符串: 用单引号
- 如果存在字符串插值(变量): 用双引号
- 多引号:可以往里面放字符串片段。
1//定义一个字符串变量name
2String name = "zhangsan"
3String name = 'zhangsan'
4
5//定义一个变量包含多行内容
6String zeyang = """
7devops
8"""
9println(zeyang)
⚠️ 注意:多引号里面可以对变量进行换行操作。

等价于:

字符串常见操作方法
1//String
2
3
4name = "zeyang"
5
6pipeline {
7 agent any
8 stages{
9 stage("run"){
10 steps{
11 script{
12 // script
13 println(name)
14
15 // buname-appname-type
16 job_name = "devops05-app-service_CI"
17
18 //获取元素索引值
19 println(job_name.indexOf("-"))
20
21 // ["devops05", "app", "service_CI"] 字符串分割操作
22 bu_name = job_name.split('-')[0] // -1代表 最后一个元素
23 println(bu_name) //devops05
24
25
26 // contains 是否包含CI字符串
27 println(job_name.contains("CI"))
28
29 //size/length 字符串的长度,用的很少
30 println("size: ${job_name.size()}")
31 println("length: ${job_name.length()}")
32
33 //endsWith() 判断字符串是否以CI结尾
34 println("enswith CI: ${job_name.endsWith('CI')}")
35
36
37
38
39 //字符串反转
40 String nums = "1234567"
41 println(nums.reverse())
42
43 //字符串增添操作
44 String log = "error: xxxxxx aa"
45 println(log.minus("a")) //输出 error: xxxxxx a
46 println(log - "a") //输出 error: xxxxxx a
47 println(log + "a") //输出 error: xxxxxx aaa
48 println(log.plus("aa")) //输出 error: xxxxxx aaaa
49
50 //获取元素索引值
51 println(job_name.indexOf("-")) //输出为8
52
53 //使用变量作为值
54 name = "xyy"
55 def message = "hello ${name}"
56 println(message) //输出 hello xyy
57 println(message.toString()) //输出 hello xyy
58
59 }
60 }
61 }
62 }
63}
==💘 实战:groovy字符串测试-2023.4.10(测试成功)==
- 代码

1//String
2name = "zeyang"
3
4pipeline {
5 agent any
6 stages{
7 stage("run"){
8 steps{
9 script{
10 // script
11 println(name)
12
13 // buname-appname-type
14 job_name = "devops05-app-service_CI"
15
16 // ["devops05", "app", "service_CI"]
17 bu_name = job_name.split('-')[0]
18 println(bu_name) //devops05
19
20 // contains
21 println(job_name.contains("CI"))
22
23 //size/length
24 println("size: ${job_name.size()}")
25 println("length: ${job_name.length()}")
26
27 //endsWith()
28 println("enswith CI: ${job_name.endsWith('CI')}")
29
30 }
31 }
32 }
33 }
34}
- 运行

- 官方文档:

2.List
列表常见操作方法
1//List定义
2tools = ["gitlab", "jenkins", "maven", "sonar"]
3
4pipeline {
5 agent any
6
7 stages{
8 stage("run"){
9 steps{
10 script{
11
12 // 扩展列表定义方式
13 listName = ["hg", "xyy", "happy"]
14 newListName = [1, 2, 3, 4] as int[]
15
16 println(listName)
17 println(newListName)
18
19
20 // script
21 println(tools)
22
23 // add
24 println(tools + "k8s") //不会改变原对象
25 println(tools << "ansible") //会改变原对象 **
26 println(tools - "maven") //不会改变原对象
27 println(tools)
28
29 tools.add("maven") //会改变原对象 **
30 println(tools)
31 println(tools.getClass())
32
33
34 // contains 判断列表是否包含元素
35 println(tools.contains("jenkins"))
36
37
38 // size 列表的长度
39 println(tools.size())
40 // 注意:列表是没有length()方法的!
41
42
43 // index 通过索引获取列表元素
44 println(tools[0])
45 println(tools[-1])
46
47 //判断列表是否为空
48 println(tools.isEmpty())
49
50 //列表去重
51 println(tools.unique())
52
53 //列表反转
54 println(tools.reverse())
55
56 //列表排序
57 println(tools.sort())
58
59 //计算列表中元素出现的次数
60 println(tools.count("jenkins"))
61
62 }
63 }
64 }
65 }
66}
==💘 实战:groovy列表测试-2023.4.10(测试成功)==
- 代码

1//List
2tools = ["gitlab", "jenkins", "maven", "sonar"]
3
4pipeline {
5 agent any
6
7 stages{
8 stage("run"){
9 steps{
10 script{
11 // script
12 println(tools)
13
14 // add
15 println(tools + "k8s")
16 println(tools)
17 println(tools << "ansible") //改变原对象
18 println(tools)
19 println(tools - "maven")
20 println(tools)
21
22 tools.add("maven")
23 println(tools)
24 println(tools.getClass())
25
26 // contains
27 println(tools.contains("jenkins"))
28
29 // length
30 println(tools.size())
31
32 // index
33 println(tools[0])
34 println(tools[-1])
35
36 }
37 }
38 }
39 }
40}
- 运行

- 官方文档:

3.map
map常见方法
1//Map
2
3user_info = ["id": 100, "name": "jenkins"]
4
5
6pipeline {
7 agent any
8
9 stages{
10 stage("run"){
11 steps{
12 script{
13 // script
14 println(user_info)
15
16 // get name 根据key获取value
17 println(user_info["name"])
18 println(user_info["id"])
19 println(user_info.get("id")) //这种方法也行的
20
21 // = 根据key重新赋值
22 user_info["name"] = "jenkinsX"
23 println(user_info)
24
25 // key 判断map是否包含某个key或者value
26 println(user_info.containsKey("name"))
27 println(user_info.containsValue(100))
28
29 // keys 返回map的key列表
30 println(user_info.keySet())
31
32 // remove 根据key删除元素
33 user_info.remove("name")
34 println(user_info)
35
36 }
37 }
38 }
39 }
40}
==💘 实战:groovy字典测试-2023.4.10(测试成功)==
- 代码
·
1//Map
2user_info = ["id": 100, "name": "jenkins"]
3
4pipeline {
5 agent any
6
7 stages{
8 stage("run"){
9 steps{
10 script{
11 // script
12 println(user_info)
13
14 // get name
15 println(user_info["name"])
16 println(user_info["id"])
17
18 // =
19 user_info["name"] = "jenkinsX"
20 println(user_info)
21
22 // key
23 println(user_info.containsKey("name"))
24 println(user_info.containsValue(100))
25
26 // keys
27 println(user_info.keySet())
28
29 // remove
30 user_info.remove("name")
31 println(user_info)
32
33 }
34 }
35 }
36 }
37}
- 运行

Groovy常用函数
- 打印变量类型

3、Groovy条件语句
1.if语句
1//if
2
3// dev == dev stag == master
4
5branchName = "dev"
6
7
8pipeline {
9 agent any
10
11 stages{
12 stage("run"){
13 steps{
14 script {
15 // script
16
17 currentBuild.displayName = branchName
18
19 if ( branchName == "dev"){
20 println("deploy to dev....")
21 currentBuild.description = "deploy to dev...."
22
23 } else if (branchName == "master"){
24 println("deploy to stag....")
25 currentBuild.description = "deploy to stag...."
26 } else {
27 currentBuild.description = "error..."
28 println("error...")
29 }
30
31 }
32 }
33 }
34 }
35}
==💘 实战:groovy if语句测试-2023.4.10(测试成功)==
- 代码

1//if
2// dev == dev stag == master
3branchName = "dev"
4
5pipeline {
6 agent any
7
8 stages{
9 stage("run"){
10 steps{
11 script {
12 // script
13 currentBuild.displayName = branchName
14
15 if ( branchName == "dev"){
16 println("deploy to dev....")
17 currentBuild.description = "deploy to dev...."
18
19 } else if (branchName == "master"){
20 println("deploy to stag....")
21 currentBuild.description = "deploy to stag...."
22 } else {
23 currentBuild.description = "error..."
24 println("error...")
25 }
26 }
27 }
28 }
29 }
30}
- 运行

2.switch语句
1//switch
2
3// dev == dev stag == master
4
5branchName = "dev"
6
7
8pipeline {
9 agent any
10
11 stages{
12 stage("run"){
13 steps{
14 script {
15 // script
16
17 currentBuild.displayName = branchName
18
19 switch(branchName) {
20 case "dev":
21 println("deploy to dev....")
22 currentBuild.description = "deploy to dev...."
23 break
24
25 case "master":
26 println("deploy to stag....")
27 currentBuild.description = "deploy to stag...."
28 break
29
30 default:
31 currentBuild.description = "error..."
32 println("error...")
33 break
34
35 }
36 }
37 }
38 }
39 }
40}
==💘 实战:groovy switch语句测试-2023.4.10(测试成功)==
- 代码

1//switch
2// dev == dev stag == master
3branchName = "dev"
4
5pipeline {
6 agent any
7
8 stages{
9 stage("run"){
10 steps{
11 script {
12 // script
13 currentBuild.displayName = branchName
14
15 switch(branchName) {
16 case "dev":
17 println("deploy to dev....")
18 currentBuild.description = "deploy to dev...."
19 break
20
21 case "master":
22 println("deploy to stag....")
23 currentBuild.description = "deploy to stag...."
24 break
25
26 default:
27 currentBuild.description = "error..."
28 println("error...")
29 break
30 }
31 }
32 }
33 }
34 }
35}
- 运行

3.for循环
1//for
2users = [
3 ["name": "zeyang", "role": "dev"],
4 ["name": "zeyang1", "role": "admin"],
5 ["name": "zeyang2", "role": "ops"],
6 ["name": "zeyang3", "role": "test"]
7 ]
8
9pipeline {
10 agent any
11
12 stages{
13 stage("test"){
14 steps{
15 script{
16 //遍历0-9,打印
17 for (i=1; i<10; i++){
18 println(i)
19 }
20 }
21 }
22 }
23 stage("run"){
24 steps{
25 script {
26 // script
27 // i = ["name": "zeyang", "role": "dev"]
28 //方法1:
29 user_names = []
30 for (i in users){
31 println(i["name"])
32 user_names << i["name"]
33 }
34
35 println(user_names) // [zeyang, zeyang1, zeyang2, zeyang3]
36
37
38
39 // times 方法2:
40 10.times {
41 println('hello')
42 }
43
44 10.times { i ->
45 println(i)
46 }
47
48 }
49 }
50 }
51 }
52}
==💘 实战:groovy for语句测试-2023.4.10(测试成功)==
- 代码

1//for
2users = [
3 ["name": "zeyang", "role": "dev"],
4 ["name": "zeyang1", "role": "admin"],
5 ["name": "zeyang2", "role": "ops"],
6 ["name": "zeyang3", "role": "test"]
7 ]
8
9pipeline {
10 agent any
11
12 stages{
13 stage("run"){
14 steps{
15 script {
16 // script
17 // i = ["name": "zeyang", "role": "dev"]
18
19 user_names = []
20 for (i in users){
21 println(i["name"])
22 user_names << i["name"]
23 }
24
25 println(user_names) // [zeyang, zeyang1, zeyang2, zeyang3]
26
27 // times
28 10.times {
29 println('hello')
30 }
31
32 10.times { i ->
33 println(i)
34 }
35 }
36 }
37 }
38 }
39}
- 运行

4.while循环
1// while 这个用的很少,不如用input。
2
3
4sleeps = true
5
6pipeline {
7 agent any
8
9 stages{
10 stage("run"){
11 steps{
12 script {
13
14 // script
15
16 while(sleeps){
17 println("sleep....")
18 }
19
20 }
21 }
22 }
23 }
24}
==💘 实战:groovy while语句测试-2023.4.10(测试成功)==
- 代码
1// while
2sleeps = true
3
4pipeline {
5 agent any
6
7 stages{
8 stage("run"){
9 steps{
10 script {
11 // script
12 while(sleeps){
13 println("sleep....")
14 }
15
16 }
17 }
18 }
19 }
20}

- 运行

4、Groovy异常处理与函数
1.异常处理

try
catch
finally
1// try catch
2
3/*
4如果println(a)失败(肯定失败,因为有语法错误)
5catch捕获错误,并打印错误。
6finally 总是执行。
7error关键字可以抛出异常。
8*/
9
10
11pipeline{
12 agent any
13
14 stages{
15 stage("run"){
16 steps{
17 script{
18 // script
19
20 try {
21 println(a) // not define a error
22 } catch(Exception e){
23 println(e)
24 //error "error..."
25 } finally {
26 println("always....")
27 }
28
29 }
30 }
31 }
32 }
33}
==💘 实战:groovy 异常处理测试-2023.4.10(测试成功)==
代码:
1// try catch
2
3/*
4如果println(a)失败(肯定失败,因为有语法错误)
5catch捕获错误,并打印错误。
6finally 总是执行。
7error关键字可以抛出异常。
8*/
9
10
11pipeline{
12 agent any
13
14 stages{
15 stage("run"){
16 steps{
17 script{
18 // script
19
20 println(a) //肯定会报错的
21
22 }
23 }
24 }
25 }
26}
运行:(肯定会报错的)

加上异常处理代码后,再次运行:(就能看到会捕获并抛出异常,流水线执行成功,符合预期)
1// try catch
2
3/*
4如果println(a)失败(肯定失败,因为有语法错误)
5catch捕获错误,并打印错误。
6finally 总是执行。
7error关键字可以抛出异常。
8*/
9
10
11pipeline{
12 agent any
13
14 stages{
15 stage("run"){
16 steps{
17 script{
18 // script
19
20 try {
21 println(a) // not define a error
22 } catch(Exception e){
23 println(e)
24 //error "error..."
25 } finally {
26 println("always....")
27 }
28
29 }
30 }
31 }
32 }
33}

2.函数

函数定义
函数传参
函数返回值
函数调用
1//function
2/*
3def关键字 定义函数名为GetUserNameByID, 带有一个参数id;
4函数体内的for循环遍历users的列表,如果id与参数id一致则返回用户的name;否则返回null;
5*/
6
7users = [
8 ["id": 1, "name": "jenkins1"],
9 ["id": 2, "name": "jenkins2"],
10 ["id": 3, "name": "jenkins3"],
11 ]
12
13
14pipeline{
15 agent any
16
17 stages{
18 stage("run"){
19 steps{
20 script {
21 //script
22 // 调用函数并打印返回值
23 name = GetUserNameByID(1)
24 println(name) //jenkins1
25 }
26 }
27 }
28 }
29}
30
31
32// define GetUserName
33def GetUserNameByID(id){
34 for (i in users){
35 if (i["id"] == id){
36 return i["name"]
37 }
38 }
39 return "null"
40}
==💘 实战:groovy 函数测试-2023.4.10(测试成功)==
- 代码

1//function
2/*
3def关键字 定义函数名为GetUserNameByID, 带有一个参数id;
4函数体内的for循环遍历users的列表,如果id与参数id一致则返回用户的name;否则返回null;
5*/
6
7users = [
8 ["id": 1, "name": "jenkins1"],
9 ["id": 2, "name": "jenkins2"],
10 ["id": 3, "name": "jenkins3"],
11 ]
12
13
14pipeline{
15 agent any
16
17 stages{
18 stage("run"){
19 steps{
20 script {
21 //script
22 // 调用函数并打印返回值
23 name = GetUserNameByID(1)
24 println(name) //jenkins1
25 }
26 }
27 }
28 }
29}
30
31
32// define GetUserName
33def GetUserNameByID(id){
34 for (i in users){
35 if (i["id"] == id){
36 return i["name"]
37 }
38 }
39 return "null"
40}
- 运行

关于我
我的博客主旨:
- 排版美观,语言精炼;
- 文档即手册,步骤明细,拒绝埋坑,提供源码;
- 本人实战文档都是亲测成功的,各位小伙伴在实际操作过程中如有什么疑问,可随时联系本人帮您解决问题,让我们一起进步!
🍀 微信二维码 x2675263825 (舍得), qq:2675263825。

🍀 微信公众号 《云原生架构师实战》

🍀 语雀
https://www.yuque.com/xyy-onlyone

🍀 csdn https://blog.csdn.net/weixin_39246554?spm=1010.2135.3001.5421

🍀 知乎 https://www.zhihu.com/people/foryouone

最后
好了,关于本次就到这里了,感谢大家阅读,最后祝大家生活快乐,每天都过的有意义哦,我们下期见!

1


