Skip to content

Jenkins Pipeline As Code

image-20230404072431734

目录

[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

image-20230407065712398

1、Pipeline简介

image-20230406074003731

  • Pipeline是Jenkins 2.0 版本推出的核心功能;
  • Pipeline可以实现以代码的方式定义工作流程;
  • 商业价值:
    • 组织级别及团队间工作流复用;
    • 便于Pipeline开发与维护;
    • 减少人工Web页面操作;

如果装了中文插件后,这里可能显示为流水线

image-20230406073610528

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组成

image-20230406074604526

  • Jenkinsfile: 描述Pipeline的代码文件
  • Agent: Pipeline的运行节点
  • Stage: Pipeline的阶段

Jenkinsfile:是实现Pipeline as Code的核心功能。 该文件用于描述流水线的过程。

Agent: (是否还记得上次课程添加的JenkinsAgent节点)执行Pipeline的实际节点。

在Jenkins pipeline中,一条流水线是由多个阶段组成的,每个阶段一个stage。例如:构建、测试、部署等等。

3、第一条Pipeline

==💘 实战:第一条Pipeline-2023.4.7(测试成功)==

image-20230407065143482

  • 安装Pipeline插件;
  • 创建Pipeline类型项目;
  • 项目设置,编写Pipeline;
  • 构建项目;

让我们开始编写第一条Pipeline吧: 第一条流水线不用太复杂,主要是便于理解流水线的结构。

groovy
pipeline {
	agent any
    stages{
        stage("hello"){
            steps{
                echo "Hello Jenkins"
            }
        }
    }
}

安装Pipeline插件

在创建Pipeline类型的作业的时候,需要提前安装好pipeline插件,不然可能会出现找不到pipeline类型的作业。

进入插件管理, 搜索关键字"pipeline" 。安装后重启一下。

tstmp_20230407065322

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

image-20230407065558986

image-20230407065527767

2、Jenkinsfile语法

Jenkinsfile的基本概念和Jenkins Pipeline的语法类型。

1、Jenkinsfile是什么?

image-20230407071153536

  • 功能: 描述Jenkins Pipeline (Jenkinsfile主要是用来存储用于描述Pipeline的代码。 我们将pipeline的描述代码保存到Jenkinsfile这个文件中。 )

  • 存放方式:

    • 将Jenkinsfile 存放在项目设置(原生支持); (虽然实现了PipelineAsCode但是多个作业管理起来不太方便。)

    • 将Jenkinsfile 存放在Git系统(原生支持);(推荐) (即实现了PipelineAsCode也便于统一管理。)

    • 将Jenkinsfile 存放在制品库(需要第三方插件);(制品库上你改起来不太方便。例如一些场景,我们现在写好一些jenkinsfile,不需要子公司去改,那么我们可以把这个文件打成一个包放到制品库里面,同步给他们,然后在流水线里这么去配置。但是如果需要去改的话,那么就需要用到git,git更方便一点。)

2、Pipeline 语法类型

image-20230407074622164

  • 脚本式语法:

    • ​ 适合以Groovy代码的方式描述Pipeline; (对groovy的要求比较高;)

    • 很多功能特性都需要自行写代码实现; (例如后续写一些邮件告警,想要获取流水线状态,可能都需要自己去写代码实现。)

    • ​ 语法简单,没有相对固定的语法,直接写groovy代码;

  • 声明式语法:

    • ​ 语法简单, 固定的语法;

    • ​ 提供很多已经定义好的功能;(流水线成功失败捕获等等

    • 也可以通过script{} 嵌入脚本式语法

jenkins语法有两种类型, 脚本式、声明式语法; 脚本式语法基本上都是通过Groovy代码来进行编写的。声明式语法有一些现成的功能可以直接用,减少脚本式语法的复杂性。但是声明式语法也不是完美的,功能固定还是需要脚本式语法来进行扩展才能实现更加灵活的Pipeline。

最佳实践是声明式语法中通过script{}标签来嵌入脚本式代码。

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

  • 代码

image-20230407080031554

groovy
node("build01"){
    stage('Build'){
        echo 'Hello world'
    }

    stage('Test'){
        echo 'Hello world'
    }
}
  • 构建

image-20230407080010546

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

  • 代码

image-20230407121741408

groovy
pipeline {
    agent any

    stages {
        stage('Build') {
            steps {
                echo 'Hello World'
                println("devops")
            }
        }
        
        stage('Test') {
            steps {
                echo 'Hello World'
            }
        }        
    }
}
  • 运行

image-20230407121950605

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

  • 错误格式代码:

image-20230407123203731

groovy
pipeline {
    agent any

    stages {
        stage('Build') {
            steps {
                echo 'Hello World'
                println("devops")

                name = "RongMei"
                if (name == "RongMei"){
                    println("true")
                }
            }
        }
        
        stage('Test') {
            steps {
                echo 'Hello World'
            }
        }        
    }
}

构建后,会报错的:

image-20230407122711717

  • 正确格式代码:

image-20230407123436607

groovy
pipeline {
    agent any

    stages {
        stage('Build') {
            steps {
                echo 'Hello World'
                println("devops")

                script{
                    name = "RongMei"
                    if (name == "RongMei"){
                        println("true")
                    }
                }

            }
        }
        
        stage('Test') {
            steps {
                echo 'Hello World'
            }
        }        
    }
}

以上代码运行后是正常的:

image-20230407123416348

3、Pipeline开发工具

image-20230412055616825

image-20230407123812957

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

image-20230408085100210

1. 片段生成器

image-20230408085142996

image-20230408090652345

当你安装好插件之后,很多插件提供了对应的代码块。 我们可以导航到片段生成器中找到对应的代码块生成。如果没有找到相关的语法,可以检查是否安装配置了相关的插件。

这个工具可以帮助我们生成Pipeline的部分代码,降低Pipeline的开发难度。流水线代码片段生成器, 非常好用。在这里可以找到每个插件以及Jenkins内置的方法的使用方法。使用片段生成器可以根据个人需要生成方法,有些方法来源于插件,则需要先安装相关的插件才能使用哦。

范例:error报错

image-20240616160746465

修改代码:

image-20240616160823008

构建:

image-20240616160849884

image-20240616160907195

范例:利用片段生成器生成执行shell命令代码

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

  • 利用片段生成器生成执行shell命令代码

image-20230408085328044

  • 代码

image-20230408085627114

groovy
pipeline {
    agent any

    stages {
        stage('Build') {
            steps {
                echo 'Hello World'
                println("devops")

                script{
                    name = "RongMei"
                    if (name == "RongMei"){
                        println("true")
                    }

                    sh 'ls -al'
                }

            }
        }
        
        stage('Test') {
            steps {
                echo 'Hello World'
            }
        }        
    }
}
  • 运行

image-20230408085440213

范例:单引号和双引号的区别

==💘 实战:单引号和双引号的区别-2023.4.7(测试成功)==

单引号里写变量是不生效的;(和其它编程语言差不多)

注意:这里一定要使用双引号,否则会把其当做字符串打印出的!(linux中单引号与双引号的区别)

双引号:

  • 代码

image-20230408090024425

groovy
pipeline {
    agent any

    stages {
        stage('Build') {
            steps {
                echo 'Hello World'
                println("devops")

                script{
                    name = "RongMei"
                    if (name == "RongMei"){
                        println("true")
                    }

                    sh """
                        echo ${name}
                        ls -al
                    """
                }

            }
        }
        
        stage('Test') {
            steps {
                echo 'Hello World'
            }
        }        
    }
}
  • 运行

image-20230408090126426

单引号:

  • 代码

image-20230408090238579

groovy
pipeline {
    agent any

    stages {
        stage('Build') {
            steps {
                echo 'Hello World'
                println("devops")

                script{
                    name = "RongMei"
                    if (name == "RongMei"){
                        println("true")
                    }

                    sh '''
                        echo ${name}
                        ls -al
                    '''
                }

            }
        }
        
        stage('Test') {
            steps {
                echo 'Hello World'
            }
        }        
    }
}
  • 运行:

image-20230408090312697

2. 声明式语法生成器

image-20230408090737971

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

范例:生成阶段

image-20240616161513539

编写代码:

image-20240616161645677

构建:

image-20240616161709198

3. 全局变量参考

image-20230408091720527

常见的全局变量

全局变量

bash
pipeline
env
params
currentBuild
scm

有时我们会获取一些项目的参数来做数据处理, 此时可以通过Jenkins提供的内置变量来获取对应的关键信息。

例如: 获取当前项目的名称、构建ID、作业URL等等信息。

bash
BUILD_NUMBER          //构建号
BUILD_ID              //构建号
BUILD_DISPLAY_NAME    //构建显示名称
JOB_NAME              //项目名称
              
EXECUTOR_NUMBER       //执行器数量
NODE_NAME             //构建节点名称
WORKSPACE             //工作目录
JENKINS_HOME          //Jenkins home
JENKINS_URL           //Jenkins地址
BUILD_URL             //构建地址
JOB_URL               //项目地址

--

currentbuild变量

bash
result  currentResult   //构建结果
displayName      //构建名称  #111
description      //构建描述
duration         //持续时间

image-20230408091930307

范例:使用jenkins的内置变量来显示构建名及构建分支

==💘 实践:使用jenkins的内置变量来显示构建名及构建分支-2023.3.23(测试成功)==

代码:

groovy
println(env)

env.branchName = "develop"
env.commitID = "${UUID.randomUUID().toString()}"
println(env.commitID)

env.commitID = "${env.commitID.split("-")[0]}"
println(env.commitID)

currentBuild.displayName = "#${env.branchName}-${env.commitID}"
currentBuild.description = "Trigger by user jenkins \n branch: ${env.branchName}"

pipeline {

    agent { label "build"}

    stages{
        stage("test"){
            steps{
                script{
                    echo "${BUILD_NUMBER}"
         
                    //currentBuild.displayName = "#${env.branchName}-${env.commitID}"
                    //currentBuild.description = "Trigger by user jenkins \n branch: master"


                    echo "当前下载代码分支为: ${env.branchName}"
                }
            }
        }
    }
}

运行效果:

image-20240616163615437

image-20240616163700274

全局变量定义并使用

结论:

例如 env.myName = "xyy",myName = "xyy"等语法属于groovy语法,如果直接定义在stages/stage/steps里会报错的,需要在scripts里定义,或者在pipeline{}外面定义。

在pipeline{}外面定义的是全局变量;

在scripts里定义的变量属于局部变量,加上env标识的话,就属于全局变量了;

1、定义在pipeline{}外面

==💘 实践:输出全局变量BUILD_ID和BUILD_URL(测试成功)-2023.4.8==

  • 代码

image-20230408093537366

groovy
currentBuild.displayName = "Devops:commitID"
currentBuild.description = "Branch:master"

pipeline {
    agent any

    stages {
        stage('Build') {
            steps {
                echo 'Hello World'
                println("devops")

                script{
                    name = "RongMei"
                    if (name == "RongMei"){
                        println("true")
                    }

                    sh """
                        echo ${name}
                        ls -al
                    """
                }

            }
        }
        
        stage('Test') {
            steps {
                echo 'Hello World'

                echo"build_id:${BUILD_ID}"
                echo "build_url:${BUILD_URL}"
            }
        }        
    }
}
  • 构建

image-20230408093516103

2、定义在scripts{}里面

  • 我们首先将全局变量的定义放在stage里面:

image-20220420075702149

groovy
pipeline{
    //指定运行此流水线的节点
    agent { node { label "build"}}
    
    //管道运行选项
    options {
        skipStagesAfterUnstable()
    }
    //流水线的阶段
    stages{
        //阶段1 获取代码
        stage("CheckOut"){
            steps{
                env.myName = "xyy"
                script{
                    println("获取代码")
                    echo 'i love you ,xyy'
                    echo "${BUILD_ID}"
                    echo "${env.BUILD_ID}"
                    echo "$BUILD_ID"
                    echo "${myName}"
                }
            }
        }
        stage("Build"){
            steps{
                script{
                    println("运行构建")
                }
            }
        }
    }
    post {
        always{
            script{
                println("流水线结束后,经常做的事情")
            }
        }
        
        success{
            script{
                println("流水线成功后,要做的事情")
            }
        
        }
        failure{
            script{
                println("流水线失败后,要做的事情")
            }
        }
        
        aborted{
            script{
                println("流水线取消后,要做的事情")
            }
        
        }
    }
}

结果构建失败了:

image-20220420075636291

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

image-20220420080240591

bash
pipeline{
    //指定运行此流水线的节点
    agent { node { label "build"}}
    
    //管道运行选项
    options {
        skipStagesAfterUnstable()
    }
    //流水线的阶段
    stages{
        //阶段1 获取代码
        stage("CheckOut"){
            steps{
                script{
                    env.myName = "xyy"
                    echo "${myName}"
                    println("获取代码")
                    echo 'i love you ,xyy'
                    echo "${BUILD_ID}"
                    echo "${env.BUILD_ID}"
                    echo "$BUILD_ID"
                    
                }
            }
        }
        stage("Build"){
            steps{
                script{
                    println("运行构建")
                }
            }
        }
    }
    post {
        always{
            script{
                println("流水线结束后,经常做的事情")
            }
        }
        
        success{
            script{
                println("流水线成功后,要做的事情")
            }
        
        }
        failure{
            script{
                println("流水线失败后,要做的事情")
            }
        }
        
        aborted{
            script{
                println("流水线取消后,要做的事情")
            }
        
        }
    }
}

image-20220420080225322

可以看到,本次构建成功,原来就是把命令式代码写在stage位置了,导致构建失败!因此也验证了上面的结论脚本式的语法我们都写在script里面

  • 刚才在检出代码阶段定义的全局变量,是否可以在构建阶段里面使用?(肯定可以的)这边来测试下

image-20220420080653894

bash
pipeline{
    //指定运行此流水线的节点
    agent { node { label "build"}}
    
    //管道运行选项
    options {
        skipStagesAfterUnstable()
    }
    //流水线的阶段
    stages{
        //阶段1 获取代码
        stage("CheckOut"){
            steps{
                script{
                    env.myName = "xyy"
                    echo "${myName}"
                    env.branchName = "dev"
                    println("获取代码")
                    echo 'i love you ,xyy'
                    echo "${BUILD_ID}"
                    echo "${env.BUILD_ID}"
                    echo "$BUILD_ID"
                    
                }
            }
        }
        stage("Build"){
            steps{
                script{
                    println("运行构建")
                    echo "${branchName}"
                }
            }
        }
    }
    post {
        always{
            script{
                println("流水线结束后,经常做的事情")
            }
        }
        
        success{
            script{
                println("流水线成功后,要做的事情")
            }
        
        }
        failure{
            script{
                println("流水线失败后,要做的事情")
            }
        }
        
        aborted{
            script{
                println("流水线取消后,要做的事情")
            }
        
        }
    }
}

image-20220420080729187

结论:可以看到,在某一个stage里定义的全局变量,在其他stage里也是可以使用的!(符合预期)

🍀 示例:如何使用命令式语法来定义局部变量

  • 代码
bash
pipeline{
    //指定运行此流水线的节点
    agent { node { label "build"}}
    
    //流水线的阶段
    stages{
        //阶段1 获取代码
        stage("CheckOut"){
            steps{
                script{
                    println("获取代码")
                    envType = "test"                    
                    
                }
            }
        }
        stage("Build"){
            steps{
                script{
                    println("运行构建")
                    echo "envType: ${envType}"
                }
            }
        }
    }
}

进行构建:

image-20230412143311392

结论:发现在script里定义的局部变量,在其他stage里也是可以使用的。。。😥并且,也是可以在stage里引用局部变量的,也是不会报错的。

  • Jenkins web也是可以配置全局变量的,但是不推荐(使用groovy代码就好)

管理Jenkins-配置系统:

image-20230622084806614

image-20230622084850879

4.调试回放流水线

image-20230408093800204

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

4、Pipeline核心语法

声明式+脚本式

image-20240616164534245

Pipeline核心语法1

pipeline{}

image-20230408094054613

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

agent{}

image-20230408100953612

  • 定义Pipeline中的运行节点(Jenkins Agent)

  • pipeline{ agent {} } 流水线级别的节点

  • stage{ agent{} } 阶段级别的节点

  • 使用方式

    • label: **在特定的名称或者label节点**运行; (标签=分组)
      
    • any: 在任意节点运行; 
      
    • none:  当pipeline全局指定agent为none,则根据每个stage中定义的agent运行(stage必须指定)。
      
    • node:**支持自定义流水线的工作目录**。
      

pipeline里必须要有agent格式的。

groovy
// 一
pipeline {
	agent any //流水线级别的节点  (常用)
}



// 二
pipeline {
	agent { label "labelName或者节点名称" }
}


//三:在阶段中定义agent
pipeline {

  agent none 
  
  stages{
  	stage('Build'){
    	agent { label "build" } //阶段级别的节点  (很少使用)
        steps {
            echo "building......"
        }
     }
  }
}



// 四 自定义节点 node:支持自定义流水线的工作目录。
pipeline {
  agent { 
     node {
        label "labelName或者节点名称"
        customWorkspace "/opt/agent/workspace"
     }
  }
}

Q:agent {node {label }} 这个语法第一次见,怎么知道有这个语法,是声明语法生成器中的吗?

但是声明式语法生成器中是没有这个配置的。

范例:agent配置的几种方法

==💘 实践:agent配置的几种方法(测试成功)-2023.4.8==

⚠️ 特别注意:label后面可以接labelName或者节点名称,都是可以的。

测试过程如下:

  • 当前测试环境节点名称及标签如下:

image-20230408102331115

  • label后面可以接节点名称

代码:

image-20230408102439076

groovy
currentBuild.displayName = "Devops:commitID"
currentBuild.description = "Branch:master"

pipeline {
    agent {label "build01"}

    stages {
        stage('Build') {
            steps {
                echo 'Hello World'
                println("devops")

                script{
                    name = "RongMei"
                    if (name == "RongMei"){
                        println("true")
                    }

                    sh """
                        echo ${name}
                        ls -al
                    """
                }

            }
        }
        
        stage('Test') {
            steps {
                echo 'Hello World'

                echo"build_id:${BUILD_ID}"
                echo "build_url:${BUILD_URL}"
            }
        }        
    }
}

运行:

image-20230408102544526

  • label后面可以接labelName

代码:

image-20230408102612091

groovy
currentBuild.displayName = "Devops:commitID"
currentBuild.description = "Branch:master"

pipeline {
    agent {label "build"}

    stages {
        stage('Build') {
            steps {
                echo 'Hello World'
                println("devops")

                script{
                    name = "RongMei"
                    if (name == "RongMei"){
                        println("true")
                    }

                    sh """
                        echo ${name}
                        ls -al
                    """
                }

            }
        }
        
        stage('Test') {
            steps {
                echo 'Hello World'

                echo"build_id:${BUILD_ID}"
                echo "build_url:${BUILD_URL}"
            }
        }        
    }
}

image-20230408102653525

  • 作业运行在任意节点:

代码:

image-20230408102746227

groovy
currentBuild.displayName = "Devops:commitID"
currentBuild.description = "Branch:master"

pipeline {
    agent any

    stages {
        stage('Build') {
            steps {
                echo 'Hello World'
                println("devops")

                script{
                    name = "RongMei"
                    if (name == "RongMei"){
                        println("true")
                    }

                    sh """
                        echo ${name}
                        ls -al
                    """
                }

            }
        }
        
        stage('Test') {
            steps {
                echo 'Hello World'

                echo"build_id:${BUILD_ID}"
                echo "build_url:${BUILD_URL}"
            }
        }        
    }
}

运行:

image-20230408102832203

  • 使用node来自定义流水线构建目录

代码:

image-20230408102923728

groovy
currentBuild.displayName = "Devops:commitID"
currentBuild.description = "Branch:master"

pipeline {
    agent {
        node{
            label "build"
            customWorkspace "/opt/jenkinsagent/workspace/test"
        }
    }

    stages {
        stage('Build') {
            steps {
                echo 'Hello World'
                println("devops")

                script{
                    name = "RongMei"
                    if (name == "RongMei"){
                        println("true")
                    }

                    sh """
                        echo ${name}
                        ls -al
                    """
                }

            }
        }
        
        stage('Test') {
            steps {
                echo 'Hello World'

                echo"build_id:${BUILD_ID}"
                echo "build_url:${BUILD_URL}"
            }
        }        
    }
}

构建:

image-20230408103006298

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

代码:

image-20230408103402172

groovy
currentBuild.displayName = "Devops:commitID"
currentBuild.description = "Branch:master"

pipeline {
    agent {
        node{
            label "build02"
            customWorkspace "/opt/jenkinsagent/workspace/test"
        }
    }

    stages {
        stage('Build') {
            steps {
                echo 'Hello World'
                println("devops")

                script{
                    name = "RongMei"
                    if (name == "RongMei"){
                        println("true")
                    }

                    sh """
                        echo ${name}
                        ls -al
                    """
                }

            }
        }
        
        stage('Test') {
            steps {
                echo 'Hello World'

                echo"build_id:${BUILD_ID}"
                echo "build_url:${BUILD_URL}"
            }
        }        
    }
}

构建:

image-20230408103345416

image-20230408103443265

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

代码:

image-20230408110601473

groovy
currentBuild.displayName = "Devops:commitID"
currentBuild.description = "Branch:master"

choiceBuildNode = "build"
pipeline {
    agent {
        node{
            label "${choiceBuildNode}"
            customWorkspace "/opt/jenkinsagent/workspace/test"
        }
    }

    stages {
        stage('Build') {
            steps {
                echo 'Hello World'
                println("devops")

                script{
                    name = "RongMei"
                    if (name == "RongMei"){
                        println("true")
                    }

                    sh """
                        echo ${name}
                        ls -al
                    """
                }

            }
        }
        
        stage('Test') {
            steps {
                echo 'Hello World'

                echo"build_id:${BUILD_ID}"
                echo "build_url:${BUILD_URL}"
            }
        }        
    }  
}

运行:

image-20230408110534206

  • 运行在jenins节点

image-20240616213357643

image-20240616213304909

image-20240616212028492

stages{}

image-20230408104418979

定义Pipeline的阶段;

bash
pipeline { stages {} } 
stages > stage > steps
  • 关系: stages > stage > steps > script
  • 定义:
    • stages:包含多个stage阶段
    • stage:包含多个steps步骤
    • steps: 包含一组特定的脚本(加上script后就可以实现在声明式脚本中嵌入脚本式语法了)
groovy
currentBuild.displayName = "Devops:commitID"
currentBuild.description = "Branch:master"

pipeline {
    agent {
        node{
            label "build02"
            customWorkspace "/opt/jenkinsagent/workspace/test"
        }
    }

    stages {
        stage('Build') {
            steps {
                echo 'Hello World'
                println("devops")

                script{
                    name = "RongMei"
                    if (name == "RongMei"){
                        println("true")
                    }

                    sh """
                        echo ${name}
                        ls -al
                    """
                }

            }
        }
        
        stage('Test') {
            steps {
                echo 'Hello World'

                echo"build_id:${BUILD_ID}"
                echo "build_url:${BUILD_URL}"
            }
        }        
    }
}

  • 注意:steps下是没有step的。

代码:

image-20230408161337707

groovy
currentBuild.displayName = "Devops:commitID"
currentBuild.description = "Branch:master"

choiceBuildNode = "build"
pipeline {
    agent {
        node{
            label "${choiceBuildNode}"
            customWorkspace "/opt/jenkinsagent/workspace/test"
        }
    }

    stages {
        stage('Build') {
            steps {
                step{
                    echo "hello"
                }
                echo 'Hello World'
                println("devops")

                script{
                    name = "RongMei"
                    if (name == "RongMei"){
                        println("true")
                    }

                    sh """
                        echo ${name}
                        ls -al
                    """
                }

            }
        }
        
        stage('Test') {
            steps {
                echo 'Hello World'

                echo"build_id:${BUILD_ID}"
                echo "build_url:${BUILD_URL}"
            }
        }        
    }  
}

报错:

image-20230408161319014

⚠️ 注意:

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

image-20220420072705011

image-20240616215803124

post{}

image-20230408105423408

  • 根据流水线的最终状态做一些操作

  • 状态:

    • always: 总是执行 (例如每次执行后清理缓存动作;)

    • success: 仅流水线成功后执行 (执行成功后,也可触发下一动作,例如触发CD;(远程构建))

    • failure: 仅流水线失败后执行 (failure/aborted:可能要给开发人员发个邮件。)

    • aborted: 仅流水线被取消后执行

    • unstable:不稳定状态,单测失败等

注意:post{}是和states同级的!

groovy
pipeline {
    
    .....
        
    .....
        
    post {
        always{
            script{
                println("流水线结束后,经常做的事情")
            }
        }
        
        success{
            script{
                println("流水线成功后,要做的事情")
            }
        
        }
        failure{
            script{
                println("流水线失败后,要做的事情")
            }
        }
        
        aborted{
            script{
                println("流水线取消后,要做的事情")
            }
        
        }
    }
}

范例:综合脚本

==💘 实践:post{}测试(测试成功)-2023.4.8==

image-20230408110226859

  • 代码

image-20230408105817125

groovy
currentBuild.displayName = "Devops:commitID"
currentBuild.description = "Branch:master"

pipeline {
    agent {
        node{
            label "build"
            customWorkspace "/opt/jenkinsagent/workspace/test"
        }
    }

    stages {
        stage('Build') {
            steps {
                echo 'Hello World'
                println("devops")

                script{
                    name = "RongMei"
                    if (name == "RongMei"){
                        println("true")
                    }

                    sh """
                        echo ${name}
                        ls -al
                    """
                }

            }
        }
        
        stage('Test') {
            steps {
                echo 'Hello World'

                echo"build_id:${BUILD_ID}"
                echo "build_url:${BUILD_URL}"
            }
        }        
    }

    post {
        always{
            script{
                println("流水线结束后,经常做的事情")
            }
        }
        
        success{
            script{
                println("流水线成功后,要做的事情")
            }
        
        }
        failure{
            script{
                println("流水线失败后,要做的事情")
            }
        }
        
        aborted{
            script{
                println("流水线取消后,要做的事情")
            }
        
        }
    }    
}
  • 运行:

image-20230408105942304

Pipeline核心语法2

environment{}

image-20230408161647214

以声明式脚本语法的方式定义环境变量;

局部变量优先级高于全局变量;

推荐:声明式语法里使用enironment这个语句块来写。

方式1:声明式语法

定义: 通过键值对(k-v)格式定义流水线在运行时的环境变量, 分为流水线级别和阶段级别。

groovy
pipeline {
    agent any
    
    //全局变量
    environment {
     	NAME = "RongMei"
        VERSION = "1.1.10"
        ENVTYPE = "DEV"
    }    

    stages {
        stage("build"){
            //局部变量
            environment {
                VERSION = "1.1.20"
            }
            steps {
                script {
                    echo "${VERSION}"
                    echo "${buildUser}"
                }
            }
        }
    }
}

方式2:全局变量可以放在pipeline外面

groovy
// 全局变量可以放在pipeline外面,也可以放在里面。
env.lover="xyy"
env.age=18

pipeline {
    agent any
    
    //全局变量
    environment {
     	NAME = "RongMei"
        VERSION = "1.1.10"
        ENVTYPE = "DEV"
    }    

    stages {
        stage("build"){
            //局部变量
            environment {
                VERSION = "1.1.20"
            }
            steps {
                script {
                    echo "${VERSION}"
                    echo "lover:${env.lover} age:${env.age}"
                }
            }
        }
    }
}

image-20240617213646579


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

  • 代码

image-20230408200457069

groovy
// 全局变量可以放在pipeline外面,也可以放在里面。
buildUser = "jenkins"

pipeline {
    agent any
    
    //全局变量
    environment {
     	NAME = "RongMei"
        VERSION = "1.1.10"
        ENVTYPE = "DEV"
    }    

    stages {
        stage("build"){
            //局部变量
            environment {
                VERSION = "1.1.20"
            }
            steps {
                script {
                    echo "${VERSION}"
                    echo "${buildUser}"
                }
            }
        }
    }
}
  • 运行

image-20230408200422720

options{}

image-20230408201331230

Pipeline运行时的一些选项

  • 设置保存最近的记录
  • 禁止并行构建
  • 跳过默认的代码检出
  • 设定流水线的超时时间(可用于阶段级别)
  • 设定流水线的重试次数(可用于阶段级别)
  • 设置日志时间输出(可用于阶段级别)

⚠️ 注意:

(以代码的方式定义的配置,需要流水线构建运行后才能看到效果)。

Jenkins需要加载一下,才能够做这个设置。

所以刚才我们定义的这些设置,都得初始化下。第一次初始化时,不用去关心它的状态是成功还是失败,它只是进行一些项目的初始化。

image-20230408201526495

常用配置:

groovy
## 设置保存最近的记录
options { buildDiscarder(logRotator(numToKeepStr: '1')) }

## 禁止并行构建
options { disableConcurrentBuilds() }


## 跳过默认的代码检出
options { skipDefaultCheckout() }


## 设定流水线的超时时间(可用于阶段级别)
options { timeout(time: 1, unit: 'HOURS') }


## 设定流水线的重试次数(可用于阶段级别)
options { retry(3) }


## 设置日志时间输出(可用于阶段级别)
options { timestamps() }

groovy
pipeline {
    options {
        disableConcurrentBuilds()
        skipDefaultCheckout()
        timeout(time: 1, unit: 'HOURS')
    }
    
    stages {
        stage("build"){
            options {
                timeout(time: 5, unit: 'MINUTES')
                retry(3)
                timestamps()
            }
        }
    }
}
    
}

image-20230408201846465

image-20230408201904360


范例:option选项测试
groovy
pipeline {
    agent any

    options {
        disableConcurrentBuilds()
        skipDefaultCheckout()
        timeout(time: 1, unit: 'HOURS')
        buildDiscarder logRotator(artifactDaysToKeepStr: '', artifactNumToKeepStr: '', daysToKeepStr: '30', numToKeepStr: '5')
    }
    
    stages {
        stage("build"){
            //options既可以在stage里面,也可以写在外面。
            options {
                timeout(time: 5, unit: 'MINUTES')
                retry(3)
                timestamps()
            }

            steps{
                echo "hello"
            }
        }
    }
}

范例:丢弃旧的构建

==💘 实践:options测试(测试成功)-2023.4.8==

  • 使用片段生成器生成代码

image-20230408202026247

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

image-20230408202213092

  • 代码

image-20230408202401996

groovy
// 全局变量可以放在pipeline外面,也可以放在里面。
buildUser = "jenkins"

pipeline {
    agent any
    
    options {
        buildDiscarder logRotator(artifactDaysToKeepStr: '', artifactNumToKeepStr: '', daysToKeepStr: '30', numToKeepStr: '5')
    }    
    
    //全局变量
    environment {
     	NAME = "RongMei"
        VERSION = "1.1.10"
        ENVTYPE = "DEV"
    }    

    stages {
        stage("build"){
            //局部变量
            environment {
                VERSION = "1.1.20"
            }
            steps {
                script {
                    echo "${VERSION}"
                    echo "${buildUser}"
                }
            }
        }
    }
}
  • 运行

image-20230408202257729

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

image-20230408202328242

范例:配置流水线跳过默认代码检出功能

💘 实践:配置流水线跳过默认代码检出功能-2023.6.26(测试成功)

  • 实验环境
bash
jenkins/jenkins:2.346.3-2-lts-jdk11
gitlab/gitlab-ce:15.0.3-ce.0
sonarqube:9.9.0-community
SonarScanner 4.8.0.2856
  • 实验软件(无)

  • 当前现象

当给jenkins流水线配置了从git仓库拉取Jenkinsfile时(即使用共享库),编译阶段会多了一个默认代码检出阶段

image-20230626061053348

image-20230626061121243

image-20230626061234609

  • 那该如何跳过这个功能呢?

在声明式片段里生成语法下:

image-20230626061351314

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

image-20230626061717927

提交。

再次构建测试:

image-20230626061624466

构建结果:

image-20230626061810886

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

parameters{}

image-20230408202952276

  • 参数化构建,在jenkinsfile中以代码的方式定义构建参数;
  • 注意由于是通过代码定义的,需运行一次构建后才能生成
  • String、Choice…

结论:

如果同时有environmentparameters时,env.VERSIONVERSION默认会使用enironment的值的,因此要想使用parameters,推荐使用params.VERSION来引用。

  • 定义: 流水线在运行时设置的参数,UI页面的参数。所有的参数都存储在params对象中。
  • 将web ui页面中定义的参数,以代码的方式定义。 (以代码的方式定义的配置,需要流水线构建运行后才能看到效果)
groovy
pipeline {
    agent any
    
	parameters { 
        string(name: 'VERSION', defaultValue: '1.1.1', description: '') 
    }
    
    stages {
        stage("Build"){
            steps {
                echo "${params.VERSION}"
            }
        }
    }
}

范例:参数构建测试

==💘 实践:parameters测试(测试成功)-2023.4.8==

  • 使用片段生成器生成代码

image-20230408203554237

image-20230408203610827

  • 代码

image-20230408204206552

groovy
// 全局变量可以放在pipeline外面,也可以放在里面。
buildUser = "jenkins"

pipeline {
    agent any

    options {
        buildDiscarder logRotator(artifactDaysToKeepStr: '', artifactNumToKeepStr: '', daysToKeepStr: '30', numToKeepStr: '5')
    }    
    
    parameters {
        choice choices: ['prod', 'test', 'dev'], description: 'Please choice env name.', name: 'envName'
        string defaultValue: '1.1.1', name: 'Version', trim: true
    }

    //全局变量
    environment {
     	NAME = "RongMei"
        VERSION = "1.1.10"
        ENVTYPE = "DEV"
    }    

    stages {
        stage("build"){
            //局部变量
            environment {
                VERSION = "1.1.20"
            }
            steps {
                script {
                    echo "${VERSION}"
                    echo "${buildUser}"

                    //注意:选项参数中的变量也是全局变量,因此还可以使用如下2种方式来访问变量
                    echo "${params.envName}"
                    echo "${env.envName}"
                    echo "${envName}"
                }
            }
        }
    }
}
  • 运行

image-20230408203931736

image-20230408204117656

Pipeline核心语法3

trigger{}

image-20230408205131493

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

image-20230409070419804


范例:cron触发

==💘 实践:trigger测试(测试成功)-2023.4.8==

常见的触发方式:

image-20230408205449796

使用cron触发:

image-20230408205713818

默认trigger这里为空:

image-20230408220210643

  • 代码

image-20230408220610566

groovy
// 全局变量可以放在pipeline外面,也可以放在里面。
buildUser = "jenkins"

pipeline {
    agent any

    options {
        buildDiscarder logRotator(artifactDaysToKeepStr: '', artifactNumToKeepStr: '', daysToKeepStr: '30', numToKeepStr: '5')
    }    
    parameters {
        choice choices: ['prod', 'test', 'dev'], description: 'Please choice env name.', name: 'envName'
        string defaultValue: '1.1.1', name: 'Version', trim: true
    }
    triggers {
        // 这个代表每小时执行一次。
        cron 'H * * * *'
    }

    //全局变量
    environment {
     	NAME = "RongMei"
        VERSION = "1.1.10"
        ENVTYPE = "DEV"
    }    

    stages {
        stage("build"){
            //局部变量
            environment {
                VERSION = "1.1.20"
            }
            steps {
                script {
                    echo "${VERSION}"
                    echo "${buildUser}"

                    //注意:选项参数中的变量也是全局变量,因此还可以使用如下2种方式来访问变量
                    echo "${params.envName}"
                    echo "${env.envName}"
                    echo "${envName}"
                }
            }
        }
    }
}
  • 运行

image-20230408220450123

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

image-20230408220425622

范例:上游项目构建触发
groovy
    triggers {
        upstream 'demo2,'
    }

我们不会用这种方式来触发,会用一个比较通用的方式来触发的,请看后文。

  • 生成代码

image-20240618071439475

image-20240618071515059

groovy
// 全局变量可以放在pipeline外面,也可以放在里面。
buildUser = "jenkins"

pipeline {
    agent any

    options {
        buildDiscarder logRotator(artifactDaysToKeepStr: '', artifactNumToKeepStr: '', daysToKeepStr: '30', numToKeepStr: '5')
    }    
    
    parameters {
        choice choices: ['prod', 'test', 'dev'], description: 'Please choice env name.', name: 'envName'
        string defaultValue: '1.1.1', name: 'Version', trim: true
    }
    
    triggers {
        upstream 'demo2,'
    }

    //全局变量
    environment {
     	NAME = "RongMei"
        VERSION = "1.1.10"
        ENVTYPE = "DEV"
    }    

    stages {
        stage("build"){
            //局部变量
            environment {
                VERSION = "1.1.20"
            }
            steps {
                script {
                    echo "${VERSION}"
                    echo "${buildUser}"

                    //注意:选项参数中的变量也是全局变量,因此还可以使用如下2种方式来访问变量
                    echo "${params.envName}"
                    echo "${env.envName}"
                    echo "${envName}"
                }
            }
        }
    }
}
  • 运行后,demo项目里就能看到,符合预期:

image-20240618071751631

input{}

image-20230409071232385

参数解析

  • message: 提示信息
  • ok: 表单中确认按钮的文本
  • submitter: 提交人,默认所有人可以
  • parameters: 交互时用户选择的参数

小demo:

groovy
pipeline {
    agent any
    stages {
        stage('Deploy') {
            input {
                message "是否继续发布"
                ok "Yes"
                submitter "zeyang,aa"
                parameters {
                    string(name: 'ENVTYPE', defaultValue: 'DEV', description: 'env type..[DEV/STAG/PROD]')
                }
            }
            steps {
                echo "Deploy to  ${ENVTYPE}, doing......."
            }
        }
    }
}

image-20240618073444988


==💘 实践:input测试(测试成功)-2023.4.8==

  • 使用声明式语法生成器生成代码

image-20230409071805539

image-20230409071818912

image-20230409071830287

  • 代码

image-20230409072814735

groovy
// 全局变量可以放在pipeline外面,也可以放在里面。
buildUser = "jenkins"

pipeline {
    agent any

    options {
        buildDiscarder logRotator(artifactDaysToKeepStr: '', artifactNumToKeepStr: '', daysToKeepStr: '30', numToKeepStr: '5')
    }    
    parameters {
        choice choices: ['prod', 'test', 'dev'], description: 'Please choice env name.', name: 'envName'
        string defaultValue: '1.1.1', name: 'Version', trim: true
    }
    triggers {
        cron 'H * * * *'
    }

    //全局变量
    environment {
     	NAME = "RongMei"
        VERSION = "1.1.10"
        ENVTYPE = "DEV"
    }    

    stages {
        stage("build"){
            //局部变量
            environment {
                VERSION = "1.1.20"
            }

            input {
                message 'Please choice your options.'
                ok '提交'
                parameters {
                    choice choices: ['rollback', 'stop'], name: 'runOptions'
                }
            }
            
            steps {
                script {
                    echo "${VERSION}"
                    echo "${buildUser}"

                    //注意:选项参数中的变量也是全局变量,因此还可以使用如下2种方式来访问变量
                    echo "${params.envName}"
                    echo "${env.envName}"
                    echo "${envName}"
					
                    //引用变量
                    echo "${runOptions}"

                    //这里写判断逻辑
                    if ("${runOptions}" == "rollback"){
                        println("rollback……")
                    }
                    if ("${runOptions}" == "stop"){
                        println("stop……")
                    }                    
                }
            }
        }
    }
}
  • 运行

选择rollback:

image-20230409072908555

image-20230409072929321

image-20230409072950748

选择stop:

image-20230409073044534

image-20230409073104669

  • 注意:

image-20230413064637483

when{}

image-20230409074107470

根据条件判断是否运行Stage;

判断条件

  • 根据环境变量判断
  • 根据表达式判断
  • 根据条件判断(not/allOf/anyOf)
groovy
pipeline {
    agent any
    stages {
        stage('Build') {
            steps {
                echo 'build......'
            }
        }
        stage('Deploy') {
            when {
                environment name: 'DEPLOY_TO', value: 'DEV'
            }
            steps {
                echo 'Deploying.......'
            }
        }
    }
}



//

###  allOf 条件全部成立
 when {
     allOf {
         environment name: 'CAN_DEPLOY', value: 'true'
         environment name: 'DEPLOY_ENV', value: 'dev'
     }
 }





### anyOf 条件其中一个成立
when {
     anyOf {
         environment name: 'CAN_DEPLOY', value: 'true'
         environment name: 'DEPLOY_ENV', value: 'dev'
     }
 }

tstmp_20230409074231


范例:根据环境变量去配置
  • 代码:
groovy
pipeline {
    agent any
    stages {
        stage('Build') {
            steps {
                echo 'build......'
            }
        }
        stage('Deploy') {
            when {
                environment name: 'DEPLOY_TO', value: 'DEV'
            }
            steps {
                echo 'Deploying.......'
            }
        }
    }
}

运行:(因为默认是没这个变量的,Deploy阶段肯定跳过的)

image-20240618074001311

image-20240618074018910

  • 添加DEPLOY_TO变量后,再次测试
groovy
pipeline {
    agent any
    
    environment {
        DEPLOY_TO = 'DEV'
    }
    stages {
        stage('Build') {
            steps {
                echo 'build......'
            }
        }
        stage('Deploy') {
            when {
                environment name: 'DEPLOY_TO', value: 'DEV'
            }
            steps {
                echo 'Deploying.......'
            }
        }
    }
}

再次运行:

image-20240618074136152

范例

==💘 实践:when测试(测试成功)-2023.4.8==

  • 代码

image-20230409075514494

groovy
// 全局变量可以放在pipeline外面,也可以放在里面。
buildUser = "jenkins"

pipeline {
    agent any

    options {
        buildDiscarder logRotator(artifactDaysToKeepStr: '', artifactNumToKeepStr: '', daysToKeepStr: '30', numToKeepStr: '5')
    }    
    parameters {
        choice choices: ['prod', 'test', 'dev'], description: 'Please choice env name.', name: 'envName'
        string defaultValue: '1.1.1', name: 'Version', trim: true
    }
    triggers {
        cron 'H * * * *'
    }

    //全局变量
    environment {
     	NAME = "RongMei"
        VERSION = "1.1.10"
        ENVTYPE = "DEV"
    }    

    stages {
        stage("build"){
            //局部变量
            environment {
                VERSION = "1.1.20"
            }

            input {
                message 'Please choice your options.'
                ok '提交'
                parameters {
                    choice choices: ['rollback', 'stop'], name: 'runOptions'
                }
            }
            steps {
                script {
                    echo "${VERSION}"
                    echo "${buildUser}"

                    //注意:选项参数中的变量也是全局变量,因此还可以使用如下2种方式来访问变量
                    echo "${params.envName}"
                    echo "${env.envName}"
                    echo "${envName}"

                    echo "${runOptions}"

                    if ("${runOptions}" == "rollback"){
                        println("rollback……")
                    }
                    if ("${runOptions}" == "stop"){
                        println("stop……")
                    } 

                    env.runOptions = "${runOptions}"          
                }
            }
        }


        stage("Rollback"){
            when {
                environment name: "runOptions", value: 'rollback'
            }
            steps {
                println("rollback……")
            }
        }
    }
 
}
  • 运行

当选择rollback时:Rollback阶段被执行

image-20230409075614942

image-20230409075600645

image-20230409075642818

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

image-20230409075331319

image-20230409075353348

image-20230409075304539

prallel{}

image-20230410053439916

Stage并行运行

场景: 自动化测试,多主机并行发布。

groovy
pipeline {
    agent any
    stages {
        stage('Parallel Stage') {
            failFast true
            parallel {
                stage('windows') {
                    agent {
                        label "master"
                    }
                    steps {
                        echo "windows"
                    }
                }
                stage('linux') {
                    agent {
                        label "build"
                    }
                    steps {
                        echo "linux"
                    }
                }
            }
        }
    }
}

tstmp_20230410053537


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

  • 代码

image-20230410053958804

groovy
pipeline {
    agent any
    stages {
        stage('Parallel Stage') {
            failFast true
            parallel {
                stage('windows') {
                    agent {
                        label "master"
                    }
                    steps {
                        echo "windows"
                    }
                }
                stage('linux') {
                    agent {
                        label "build"
                    }
                    steps {
                        echo "linux"
                    }
                }
            }
        }
    }
}
  • 运行

image-20230410053923552

FAQ:如何解决并发构建中的workspace问题?

这个workspace问题挺重要的。(已解决)

多个并行阶段使用一个工作目录肯定是有问题的;(在每次构建的时候加一个随机数)

1.build_id

2.使用随机数

或者直接禁用并行构建方法也行;

image-20240618080618682

并行构建用到的场景:

情况1:如果一个项目管理多个微服务,那么就会需要用到并行构建; 情况2:多分支;

方法1:build_id

groovy
env.nworkspace = "/opt/agent/test/build-${BUILD_ID}"


pipeline{
	agent{
  	node{
    	label "build"
      customWorkspace "${env.nworkspace}"
    }
  }
 
  stages{
  	stage("build"){
    
    	steps{
      	echo "${env.nworkspace}"
      }
    }
  
  }
}

多点击几次运行:

image-20240618080005846

image-20240618080115808

方法2:随机数

groovy
env.nworkspace = "/opt/agent/test/${JOB_NAME}-${UUID.randomUUID().toString()}"


pipeline{
	agent{
  	node{
    	label "build"
      customWorkspace "${env.nworkspace}"
    }
  }
 
  stages{
  	stage("build"){
    
    	steps{
      	echo "${env.nworkspace}"
      }
    }
  
  }
}


### 输出
demo-fec54ca7-81a5-452e-91b5-2a187ab3562b

image-20240618080314145

  • 扩展:

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

image-20230410055254149

二、Groovy

1、Groovy简介

groovy这一块我们基本只会用到数据处理;

使用groovy的目的:

  • 想让自己的pipeline更加灵活;
  • 搞微服务实践时,会调api,或者做数据处理;

image-20230413065952829

image-20230410061156284

Groovy功能强大,可选类型和动态语言(很多语法和python类似)天生支持Java平台

简洁且简单易学的语法。

可以与任何Java程序顺利集成、包括脚本编写功能、特定领域语言编写,运行时和编译时元编程以及函数式编程。

Groovy参考文档

http://groovy-lang.org/groovy-dev-kit.html

http://docs.groovy-lang.org/docs/latest/html/documentation/#_map_coercion

image-20230410061437762

注释

  • 单行注释 //
  • 多行注释 /**/

groovy环境安装方法

1.直接在jenkins的pipeline里运行groovy代码即可。(推荐)

可以在回放里进行测试(回放里写的代码都是groovy代码)

image-20230410062155480

image-20220421131040322

2.另外:jenkins的Script Console也能执行groovy代码,同时也可以调用jenkins方法。

系统管理-脚本命令行里打开写代码即可;

或者直接在后面加个script即可打开:

http://172.29.9.101:8080/script

image-20220421131208009

image-20220421131235941

image-20220421131247087

3.安装本地groovy环境。

==💘 实战:《groovy配置开发环境》-泽阳-2022.4.23(测试成功)==

image-20220421084226461

  • 实验环境

    bash
    jdk版本:openjdk version "1.8.0_322"
    centos7.6虚机
  • 实验软件

    链接:https://pan.baidu.com/s/1GKaCvXyU0WF2rTrWffx37A?pwd=7r9p 提取码:7r9p

    2022.4.23-share-JenkinsPipelineAsCode实验代码

    image-20220423175636767

  • 前置条件

    bash
    jdk环境已安装;
  • 1️⃣ 下载groovy安装包

https://groovy.apache.org/

image-20220421084920233

image-20220421085004773

  • 2️⃣ 设置环境变量并配置

将安装包上传到服务器上

bash
(1)上传
[root@devops ~]#ll -h apache-groovy-sdk-4.0.1.zip 
-rw-r--r-- 1 root root 57M Apr 21 08:57 apache-groovy-sdk-4.0.1.zip

(2)解压
[root@devops ~]#unzip apache-groovy-sdk-4.0.1.zip -d /usr/local/
[root@devops ~]#cd /usr/local/groovy-4.0.1/
[root@devops groovy-4.0.1]#ls
bin  conf  doc  grooid  lib  LICENSE  licenses  NOTICE  src
[root@devops groovy-4.0.1]#cd bin/
[root@devops bin]#ls
grape             groovy.bat          groovy_completion         groovydoc             groovysh             java2groovy.bat
grape.bat         groovyc             groovyConsole             groovydoc.bat         groovysh.bat         startGroovy
grape_completion  groovyc.bat         groovyConsole.bat         groovydoc_completion  groovysh_completion  startGroovy.bat
groovy            groovyc_completion  groovyConsole_completion  groovy.ico            java2groovy
[root@devops bin]#

(3)设置环境变量
[root@devops ~]#vim /etc/profile
……
export GROOVY_HOME=/usr/local/groovy-4.0.1
export PATH=$JAVA_HOME/bin:$GROOVY_HOME/bin:$PATH
[root@devops groovy-4.0.1]#source /etc/profile
  • 3️⃣ 验证
groovy
[root@devops ~]#groovysh
Apr 21, 2022 11:25:39 AM java.util.prefs.FileSystemPreferences$1 run
INFO: Created user preferences directory.
Groovy Shell (4.0.1, JVM: 1.8.0_322)
Type ':help' or ':h' for help.
groovy:000> println("hello world")
hello world
===> null
groovy:000>

2、Groovy数据类型

image-20230410063006631

1.String

字符串类型, 是在流水线中应用最为广泛的一种类型。

字符串定义

可以通过双引号、单引号、三引号定义;

  • 如果是普通字符串: 用单引号
  • 如果存在字符串插值(变量): 用双引号
  • 多引号:可以往里面放字符串片段。
groovy
//定义一个字符串变量name
String name = "zhangsan"
String name = 'zhangsan'

//定义一个变量包含多行内容
String zeyang = """
devops
"""
println(zeyang)

⚠️ 注意:多引号里面可以对变量进行换行操作。

image-20230624213345583

等价于:

image-20230624213550783

字符串常见操作方法

groovy
//String


name = "zeyang"

pipeline {
	agent any
	stages{
		stage("run"){
			steps{
				script{
					// script 
					println(name)

					// buname-appname-type
					job_name = "devops05-app-service_CI"
                    
                    //获取元素索引值
                    println(job_name.indexOf("-"))

					// ["devops05", "app", "service_CI"] 字符串分割操作
					bu_name = job_name.split('-')[0] // -1代表 最后一个元素
					println(bu_name)  //devops05
                  

					// contains 是否包含CI字符串
					println(job_name.contains("CI"))

					//size/length 字符串的长度,用的很少
					println("size: ${job_name.size()}")
					println("length: ${job_name.length()}")

					//endsWith() 判断字符串是否以CI结尾
					println("enswith CI: ${job_name.endsWith('CI')}")
                    
                    
                    
                    
                    //字符串反转
                    String nums = "1234567"
                    println(nums.reverse())                      
                   
                    //字符串增添操作
                    String log = "error: xxxxxx aa" 
                    println(log.minus("a")) //输出 error: xxxxxx a
                    println(log - "a") //输出 error: xxxxxx a
                    println(log + "a") //输出 error: xxxxxx aaa
                    println(log.plus("aa")) //输出 error: xxxxxx aaaa
                    
                    //获取元素索引值
                    println(job_name.indexOf("-"))    //输出为8
                    
                    //使用变量作为值
                    name = "xyy"
                    def message = "hello ${name}"       
                    println(message) //输出 hello xyy
                    println(message.toString())     //输出 hello xyy                   

				}
			}
		}
	}
}

==💘 实战:groovy字符串测试-2023.4.10(测试成功)==

  • 代码

image-20230410063435618

groovy
//String
name = "zeyang"

pipeline {
	agent any
	stages{
		stage("run"){
			steps{
				script{
					// script 
					println(name)

					// buname-appname-type
					job_name = "devops05-app-service_CI"

					// ["devops05", "app", "service_CI"]
					bu_name = job_name.split('-')[0]
					println(bu_name)  //devops05

					// contains
					println(job_name.contains("CI"))

					//size/length
					println("size: ${job_name.size()}")
					println("length: ${job_name.length()}")

					//endsWith()
					println("enswith CI: ${job_name.endsWith('CI')}")

				}
			}
		}
	}
}
  • 运行

image-20230410063406654

  • 官方文档:

image-20230410070115263

2.List

列表常见操作方法

groovy
//List定义
tools = ["gitlab", "jenkins", "maven", "sonar"]

pipeline {
    agent any

    stages{
        stage("run"){
            steps{
                script{

                    // 扩展列表定义方式
                    listName = ["hg", "xyy", "happy"]
                    newListName = [1, 2, 3, 4] as int[]

                    println(listName)
                    println(newListName)

                    
                    // script
                    println(tools)

                    // add 
                    println(tools + "k8s")   //不会改变原对象
                    println(tools << "ansible") //会改变原对象 **
                    println(tools - "maven") //不会改变原对象
                    println(tools)

                    tools.add("maven") //会改变原对象  **
                    println(tools)
                    println(tools.getClass())


                    // contains 判断列表是否包含元素
                    println(tools.contains("jenkins"))


                    // size 列表的长度
                    println(tools.size())
                    // 注意:列表是没有length()方法的!


                    // index 通过索引获取列表元素
                    println(tools[0])
                    println(tools[-1])
                    
                    //判断列表是否为空
                    println(tools.isEmpty())
                    
                    //列表去重
                    println(tools.unique())
                    
                    //列表反转
                    println(tools.reverse())
                    
                    //列表排序
                    println(tools.sort())
                    
                    //计算列表中元素出现的次数
                    println(tools.count("jenkins"))
                    
                }
            }
        }
    }
}

==💘 实战:groovy列表测试-2023.4.10(测试成功)==

  • 代码

image-20230410070617700

groovy
//List
tools = ["gitlab", "jenkins", "maven", "sonar"]

pipeline {
    agent any

    stages{
        stage("run"){
            steps{
                script{
                    // script
                    println(tools)

                    // add
                    println(tools + "k8s")
                    println(tools)
                    println(tools << "ansible") //改变原对象
                    println(tools)
                    println(tools - "maven")
                    println(tools)

                    tools.add("maven")
                    println(tools)
                    println(tools.getClass())

                    // contains
                    println(tools.contains("jenkins"))

                    // length
                    println(tools.size())

                    // index
                    println(tools[0])
                    println(tools[-1])

                }
            }
        }
    }
}
  • 运行

image-20230410070637619

  • 官方文档:

image-20230410070659399

3.map

map常见方法

groovy
//Map

user_info = ["id": 100, "name": "jenkins"]


pipeline {
	agent any

	stages{
		stage("run"){
			steps{
				script{
					// script 
					println(user_info)

					// get name 根据key获取value
					println(user_info["name"])
					println(user_info["id"])
                    println(user_info.get("id")) //这种方法也行的

					// =  根据key重新赋值
					user_info["name"] = "jenkinsX"
					println(user_info)

					// key 判断map是否包含某个key或者value
					println(user_info.containsKey("name"))
				    println(user_info.containsValue(100))

				    // keys 返回map的key列表
				    println(user_info.keySet())

				    // remove 根据key删除元素
				    user_info.remove("name")
				    println(user_info)

				}
			}
		}
	}
}

==💘 实战:groovy字典测试-2023.4.10(测试成功)==

  • 代码

image-20230410071337459·

groovy
//Map
user_info = ["id": 100, "name": "jenkins"]

pipeline {
	agent any

	stages{
		stage("run"){
			steps{
				script{
					// script 
					println(user_info)

					// get name
					println(user_info["name"])
					println(user_info["id"])

					// = 
					user_info["name"] = "jenkinsX"
					println(user_info)

					// key
					println(user_info.containsKey("name"))
				    println(user_info.containsValue(100))

				    // keys
				    println(user_info.keySet()) 

				    // remove
				    user_info.remove("name")
				    println(user_info)

				}
			}
		}
	}
}
  • 运行

image-20230410071326270

Groovy常用函数

  • 打印变量类型

image-20230628075457515

3、Groovy条件语句

1.if语句

groovy
//if 

// dev == dev  stag == master

branchName = "dev"


pipeline {
	agent any

	stages{
		stage("run"){
			steps{
				script {
					// script

					currentBuild.displayName = branchName

					if ( branchName == "dev"){
						println("deploy to dev....")
						currentBuild.description = "deploy to dev...."

					} else if (branchName == "master"){
						println("deploy to stag....")
						currentBuild.description = "deploy to stag...."
					} else {
						currentBuild.description = "error..."
						println("error...")
					}

				}
			}
		}
	}
}

==💘 实战:groovy if语句测试-2023.4.10(测试成功)==

  • 代码

image-20230410072545433

groovy
//if 
// dev == dev  stag == master
branchName = "dev"

pipeline {
	agent any

	stages{
		stage("run"){
			steps{
				script {
					// script
					currentBuild.displayName = branchName

					if ( branchName == "dev"){
						println("deploy to dev....")
						currentBuild.description = "deploy to dev...."

					} else if (branchName == "master"){
						println("deploy to stag....")
						currentBuild.description = "deploy to stag...."
					} else {
						currentBuild.description = "error..."
						println("error...")
					}
				}
			}
		}
	}
}
  • 运行

image-20230410072533919

2.switch语句

groovy
//switch

// dev == dev  stag == master

branchName = "dev"


pipeline {
	agent any

	stages{
		stage("run"){
			steps{
				script {
					// script

					currentBuild.displayName = branchName

					switch(branchName) {
						case "dev":
							println("deploy to dev....")
							currentBuild.description = "deploy to dev...."	
							break

						case "master":
							println("deploy to stag....")
							currentBuild.description = "deploy to stag...."
							break

						default:
							currentBuild.description = "error..."
							println("error...")
							break

					}
				}
			}
		}
	}
}

==💘 实战:groovy switch语句测试-2023.4.10(测试成功)==

  • 代码

image-20230410072851279

groovy
//switch
// dev == dev  stag == master
branchName = "dev"

pipeline {
	agent any

	stages{
		stage("run"){
			steps{
				script {
					// script
					currentBuild.displayName = branchName

					switch(branchName) {
						case "dev":
							println("deploy to dev....")
							currentBuild.description = "deploy to dev...."	
							break

						case "master":
							println("deploy to stag....")
							currentBuild.description = "deploy to stag...."
							break

						default:
							currentBuild.description = "error..."
							println("error...")
							break
					}
				}
			}
		}
	}
}
  • 运行

image-20230410072839175

3.for循环

groovy
//for 
users = [ 
			["name": "zeyang", "role": "dev"], 
			["name": "zeyang1", "role": "admin"], 
			["name": "zeyang2", "role": "ops"], 
			["name": "zeyang3", "role": "test"]
		]

pipeline {
	agent any

	stages{
        stage("test"){
            steps{
                script{
                       //遍历0-9,打印
                        for (i=1; i<10; i++){
                        println(i)
                    } 
                }
            }
        }
        stage("run"){
			steps{
				script {
					// script
					// i = ["name": "zeyang", "role": "dev"]
					//方法1:
					user_names = []
					for (i in users){
						println(i["name"])
						user_names << i["name"]
					}

					println(user_names)  // [zeyang, zeyang1, zeyang2, zeyang3]

                    
                    
					// times 方法2:
					10.times {
						println('hello')
					}

					10.times { i ->
						println(i)
					}         

				}
			}
		}
	}
}

==💘 实战:groovy for语句测试-2023.4.10(测试成功)==

  • 代码

image-20230410073026444

groovy
//for 
users = [ 
			["name": "zeyang", "role": "dev"], 
			["name": "zeyang1", "role": "admin"], 
			["name": "zeyang2", "role": "ops"], 
			["name": "zeyang3", "role": "test"]
		]

pipeline {
	agent any

	stages{
		stage("run"){
			steps{
				script {
					// script
					// i = ["name": "zeyang", "role": "dev"]

					user_names = []
					for (i in users){
						println(i["name"])
						user_names << i["name"]
					}

					println(user_names)  // [zeyang, zeyang1, zeyang2, zeyang3]

					// times
					10.times {
						println('hello')
					}

					10.times { i ->
						println(i)
					}
				}
			}
		}
	}
}
  • 运行

image-20230410073104757

4.while循环

groovy
// while 这个用的很少,不如用input。


sleeps = true

pipeline {
	agent any 

	stages{
		stage("run"){
			steps{
				script {

					// script

					while(sleeps){
						println("sleep....")
					}

				}
			}
		}
	}
}

==💘 实战:groovy while语句测试-2023.4.10(测试成功)==

  • 代码
groovy
// while 
sleeps = true

pipeline {
	agent any 

	stages{
		stage("run"){
			steps{
				script {
					// script
					while(sleeps){
						println("sleep....")
					}

				}
			}
		}
	}
}

image-20230410073235411

  • 运行

image-20230410073305305

4、Groovy异常处理与函数

1.异常处理

image-20230410073956288

try

catch

finally

groovy
// try catch

/*
如果println(a)失败(肯定失败,因为有语法错误)
catch捕获错误,并打印错误。
finally 总是执行。
error关键字可以抛出异常。
*/


pipeline{
	agent any

	stages{
		stage("run"){
			steps{
				script{
					// script

					try {
						println(a)   // not define a  error
					}  catch(Exception e){
						println(e)
						//error "error..."
					} finally {
						println("always....")
					}

				}
			}
		}
	}
}

==💘 实战:groovy 异常处理测试-2023.4.10(测试成功)==

代码:

groovy
// try catch

/*
如果println(a)失败(肯定失败,因为有语法错误)
catch捕获错误,并打印错误。
finally 总是执行。
error关键字可以抛出异常。
*/


pipeline{
	agent any

	stages{
		stage("run"){
			steps{
				script{
					// script

					println(a) //肯定会报错的

				}
			}
		}
	}
}

运行:(肯定会报错的)

image-20240618095131817

加上异常处理代码后,再次运行:(就能看到会捕获并抛出异常,流水线执行成功,符合预期)

groovy
// try catch

/*
如果println(a)失败(肯定失败,因为有语法错误)
catch捕获错误,并打印错误。
finally 总是执行。
error关键字可以抛出异常。
*/


pipeline{
	agent any

	stages{
		stage("run"){
			steps{
				script{
					// script

					try {
						println(a)   // not define a  error
					}  catch(Exception e){
						println(e)
						//error "error..."
					} finally {
						println("always....")
					}

				}
			}
		}
	}
}

image-20240618095247759

2.函数

image-20230410075122108

函数定义

函数传参

函数返回值

函数调用

groovy
//function
/*
def关键字 定义函数名为GetUserNameByID, 带有一个参数id;
函数体内的for循环遍历users的列表,如果id与参数id一致则返回用户的name;否则返回null;
*/

users = [
			["id": 1, "name": "jenkins1"],
			["id": 2, "name": "jenkins2"],
			["id": 3, "name": "jenkins3"],
		]


pipeline{
	agent any 

	stages{
		stage("run"){
			steps{
				script {
					//script
                    // 调用函数并打印返回值
					name = GetUserNameByID(1)
					println(name)   //jenkins1
				}
			}
		}
	}
}


// define GetUserName
def GetUserNameByID(id){
	for (i in users){
		if (i["id"] == id){
			return i["name"]
		}
	}
	return "null"
}

==💘 实战:groovy 函数测试-2023.4.10(测试成功)==

  • 代码

image-20230410075330365

groovy
//function
/*
def关键字 定义函数名为GetUserNameByID, 带有一个参数id;
函数体内的for循环遍历users的列表,如果id与参数id一致则返回用户的name;否则返回null;
*/

users = [
			["id": 1, "name": "jenkins1"],
			["id": 2, "name": "jenkins2"],
			["id": 3, "name": "jenkins3"],
		]


pipeline{
	agent any 

	stages{
		stage("run"){
			steps{
				script {
					//script
                    // 调用函数并打印返回值
					name = GetUserNameByID(1)
					println(name)   //jenkins1
				}
			}
		}
	}
}


// define GetUserName
def GetUserNameByID(id){
	for (i in users){
		if (i["id"] == id){
			return i["name"]
		}
	}
	return "null"
}
  • 运行

image-20230410075418144

关于我

我的博客主旨:

  • 排版美观,语言精炼;
  • 文档即手册,步骤明细,拒绝埋坑,提供源码;
  • 本人实战文档都是亲测成功的,各位小伙伴在实际操作过程中如有什么疑问,可随时联系本人帮您解决问题,让我们一起进步!

🍀 微信二维码 x2675263825 (舍得), qq:2675263825。

image-20230107215114763

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

image-20230107215126971

🍀 语雀

https://www.yuque.com/xyy-onlyone

image-20230306221144511

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

image-20230107215149885

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

image-20230107215203185

最后

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

image-20230413064932461

1