jenkins03-Pipeline

1、pipeline简介

  • 通常Jenkins pipeline被简称为pipeline。只有安装了pipeline插件,Jenkins才支持pipeline。pipeline代码被写在一个被命名为Jenkinsfile的文本文件中,在同一个代码项目下可以按需创建多个不同名称的Jenkinsfile

1、pipeline是什么

  • Jenkins 1.x只能通过Web界面手动操作来“描述”部署流水线。Jenkins 2.x支持“pipeline as code”了,即可以通过“代码”来描述部署流水线。
  • 使用“代码”而不是UI的意义在于:
    • 更好地版本化:将pipeline提交到版本库中进行版本控制。
    • 更好地协作:pipeline的每次修改对所有人都是可见的。除此之外,还可以对pipeline进行代码审查。
    • 更好的重用性:手动操作没法重用,但是代码可以重用。

2、pipeline语法的选择

  • Jenkins Pipeline有两种语法:
    • 脚本式(Scripted)语法
      • 写脚本式pipeline时,很像是(其实就是)在写Groovy代码。可以为用户提供巨大的灵活性和可扩展性,但语法复杂(Groovy语言的学习成本对于(不使用Groovy的)开发团队来说通常是不必要的)。
    • 声明式(Declar-ative)语法
      • 提供了更简单、更结构化(more opinionated)的语法。
      • Jenkins社区推荐使用声明式语法
  • node为根节点的是脚本式语法,pipeline为根节点的是声明式语法。
  • pipeline插件从2.5版本开始,同时支持两种格式的语法。

示例1:脚本式语法

node {
    def mvnHome
    stage('Preparation') {
        // 从GitHub存储库获取一些代码
        git 'https://github.com/jglick/simple-maven-project-with-tests.git'
        // 获取Maven工具(注意:Maven工具必须配置在全局工具配置中)
        mvnHome = tool 'maven-3.9.4'
    }
    stage('Build') {
        // 运行maven进行构建
        withEnv(["MVN_HOME=$mvnHome"]) {
            if (isUnix()) {
                sh '"$MVN_HOME/bin/mvn" -Dmaven.test.failure.ignore clean package'
            } else {
                bat(/"%MVN_HOME%\bin\mvn" -Dmaven.test.failure.ignore clean package/)
            }
        }
    }
    stage('Deploy') {
        try{
            // 执行部署
        }catch(err){
            currenBuild.result = "FAILURE"
            mail subject: "project build failed",
            body: "project build error is here: ${env.BUILD_URL}",
            from: "xxx@hh.com",
            replyTo: "yyy@hh.com",
            to: "zzz@hh.com"
            throw err
        }
    }
}

示例2:声明式语法

pipeline {
    agent any

    stages {
        stage('Build') {
            steps {
                echo 'Building...'
            }
        }
        stage('Test') {
            steps {
                echo 'Testing...'
            }
        }
        stage('Deploy') {
            steps {
                echo 'Deploying...'
            }
        }
    }
    post {
        failure {
            mail to: "xxx@hh.com", subject: "The pipeline failed :("
        }
    }
}

2、pipeline的存放位置

  • pipeline可以放在两个位置:
    • 直接放在Jenkins Web界面上
    • 使用Jenkinsfile,并将其放在相应的代码库(如GitLab的项目)中。

2.1、直接放在Jenkins Web界面上

  • 直接在Jenkins Web界面上填入pipeline。
  • 在实验时可以这么做,但是不推荐,因为这样无法做到pipeline的版本化。

2.2、使用Jenkinsfile

  • Jenkinsfile就是一个文本文件,也就是部署流水线概念在Jenkins中的表现形式。像Dockerfile之于Docker。所有部署流水线的逻辑都写在Jenkinsfile中。

1、在jenkins web界面上指定Jenkinsfile

  • 注意,我这里提前设置了链接GitLab的凭据。

2、将Jenkinsfile放到代码库中

1、Jenkisfile文件

]# cat project01-private/Jenkinsfile 
pipeline {
    agent any

    stages {
        stage('Hello') {
            steps {
                echo 'Jenkinsfile'
            }
        }
    }
}

2、jenkinsfile在代码库中的位置

]# ls -l project01-private/
-rw-r--r-- 1 root root 151 Sep  5 00:59 Jenkinsfile
-rw-r--r-- 1 root root  10 Sep  5 00:59 README.md

3、pipeline语法

3.1、必要的Groovy知识

  • 虽然学习Jenkins pipeline可以不需要任何Groovy知识,但是学习以下Groovy知识,对于写pipeline会如虎添翼。
  • (1)Groovy同时支持静态类型和动态类型。但在定义变量时,习惯使用def关键字,比如def x="abc"、def y=1。
  • (2)不像Java,Groovy语句最后的分号不是必需的。
  • (3)Groovy中的方法调用可以省略括号, 比如System.out.println "Hello world"。
  • (4)支持命名参数
//定义方法
def createName(String givenName, String familyName){
    return givenName + " " + familyName
}

//调用方法
createName familyName = "Lee", givenName = "Bruce"
  • (5)支持默认参数值
//定义方法
def sayHello(String name = "humans"){
    print "hello ${name}"
}

//调用方法
sayHello()    //此时括号不能省略
  • (6)支持单引号、双引号。双引号支持插值,单引号不支持。
def name = "world"
print "hello ${name}"    //结果:hello world
print 'hello ${name}'    //结果:hello ${name}
  • (7)支持三引号。三引号分为三单引号和三双引号。它们都支持换行,区别在于只有三双引号支持插值。
def name = 'world'

def aString = '''line one
line two
${name}
'''

def bString = """line one
line two
${name}
"""
  • (8)闭包的定义方法
//定义闭包
def codeBlock = {print "hello closure"}

//闭包可以当成函数调用
codeBlock()    //结果:hello closure
    • 还可以将闭包看作一个参数传递给另一个方法。
//定义一个pipeline函数,它会接受一个闭包作为参数
def pipeline(closure){
    closure()
}
//调用pipeline函数
pipeline(codeBlock)

//如果把闭包定义的语句去掉
pipeline({print "hello closure"})
//由于小括号是非必须的,所以...。(是不是和Jenkins pipeline很像)
pipeline {
    print "hello closure"
}

3.2、声明式pipeline的结构

  • 声明式pipeline语法说明文档:https://www.jenkins.io/doc/book/pipeline/syntax/
  • Jenkins pipeline其实就是基于Groovy语言实现的一种DSL(领域特定语言),用于描述整条流水线是如何进行的。流水线的内容包括执行编译、打包、测试、输出测试报告等步骤。
  • pipeline基本结构决定的是pipeline整体流程,但是真正“做事”的还是pipeline中的每一个步骤步骤是pipeline中已经不能再拆分的最小操作
  • Jenkins pipeline内置了很多步骤
    • 步骤是可插拔的,就像Jenkins的插件一样。并且只需要对现有的插件进行一些修改,就可以在pipeline中被当成一个步骤使用。
    • 只要安装了适配Jenkins pipeline的插件,就可以使用其提供的pipeline步骤。

1、声明式pipeline的结构

  • 在声明式pipeline中有效的基本语句和表达式遵循与Groovy相同的语法规则,但有以下例外
    • pipeline的顶层必须是一个块(block),例如pipeline{}。
    • 没有分号作为语句分隔符,且每行只有一个语句。
    • 块只能由(Sections)、指令(Directives)、步骤容器(Steps)或赋值语句(assignment statements)组成。
    • 属性引用语句被视为无参数方法调用。例如,input被视为input()。
  • 可以使用声明式指令生成器生成sections和directives。

2、声明式pipeline最简结构

  • 列出的每一个节(sections)都是必需的,缺少一个,Jenkins都会报错。
pipeline {              //(必须)代表整条流水线,包含整条流水线的逻辑。
 
    agent any           //(必须)指定流水线的执行位置(Jenkins agent)。流水线中的每个阶段都必须在某个地方(物理机、虚拟机或Docker容器)执行。
 
    stages {            //(必须)流水线中多个阶段(stage)的容器。一个stages部分至少包含一个stage。
        stage('Hello') {              //(必须)流水线中的阶段,每个阶段都必须有名称。一个stage中有且只能有一个steps。
            steps {                   //(必须)多个具体步骤(step)的容器。一个steps中至少包含一个步骤。
                echo 'Hello World'    //(必须)具体的步骤
            }
        }
    }
}

3、声明式pipeline复杂结构

1

4、声明式pipeline中的Sections(节)

  • Sections通常包含一个或多个指令或步骤
  • 声明式pipeline中的Sections(节)有:agent、post、stages、steps。

4.1、agent(代理)

  • agent:指定整个Pipeline特定阶段的执行位置(Jenkins agent)。流水线中的每个阶段都必须在某个地方(物理机、虚拟机或Docker容器)执行。
  • agent可以定义在pipeline或stage部分,在pipeline部分是必须的,在stage部分是可选的
  • agent的参数有:
    • any:在任何可用的代理上执行整个Pipeline或特定阶段。例如,agent any
    • none:当在Pipeline块的顶层使用时,不会为整个Pipeline运行分配全局代理,并且必须为每个阶段都定义自己的代理。例如,agent none
    • label:根据所提供的标签在Jenkins环境中查找可用的代理,并在其上执行整个Pipeline或特定阶段。例如:agent { label 'my-defined-label' },{ label 'my-label1 && my-label2' },agent { label 'my-label1 || my-label2' }
    • node:agent { node { label 'labelName' } }的行为与agent { label 'labelName' }相同,但node允许额外的选项(如customWorkspace)。
    • docker:使用给定的容器执行整个Pipeline或特定阶段,该容器将动态地配置在预配置为接受基于docker的Pipeline的节点上,或者匹配可选定义的label参数的节点上。docker可以使用args参数,直接传递给docker运行调用的参数(alwaysPull将强制docker拉取,即使映像名称已经存在)。docker还可以使用registryUrl和registryCredentialsId参数指定docker镜像仓库及其凭据。
      • agent { docker 'maven:3.9.3-eclipse-temurin-17' }
        
        //或
        agent {
            docker {
                image 'maven:3.9.3-eclipse-temurin-17'
                label 'my-defined-label'
                args  '-v /tmp:/tmp'
                registryUrl 'https://myregistry.com/'
                registryCredentialsId 'myPredefinedCredentialsInJenkins'
            }
        }
    • dockerfile:使用源存储库中的Dockerfile构建的容器并在其上执行整个Pipeline或特定阶段。使用这个选项,必须从多分支Pipeline或SCM Pipeline中加载Jenkinsfile。

      • 通常Dockerfile文件在源存储库根目录下: agent { dockerfile true }。

      • 如果Dockerfile文件在其他目录中,可以使用dir选项:agent { dockerfile { dir 'someSubDir' } }。

      • 如果Dockerfile文件的名称是其他名称,可以使用filename选项指定文件名。

      • 可以通过additionalBuildArgs选项向docker build命令传递额外的参数,比如agent { dockerfile { additionalBuildArgs '--build-arg foo=bar' } }

      • //相当于"docker build -f Dockerfile.build --build-arg version=1.0.2 ./build/
        agent {
            dockerfile {
                filename 'Dockerfile.build'
                dir 'build'
                label 'my-defined-label'
                additionalBuildArgs  '--build-arg version=1.0.2'
                args '-v /tmp:/tmp'
                registryUrl 'https://myregistry.com/'
                registryCredentialsId 'myPredefinedCredentialsInJenkins'
            }
        }  
    • 在Kubernetes集群上创建一个pod,并在这个pod中执行整个Pipeline或特定阶段。使用这个选项,必须从多分支Pipeline或SCM Pipeline中加载Jenkinsfile。Pod模板在kubernetes{}块中定义。
      • //创建一个包含Kaniko容器的pod
        agent {
            kubernetes {
                defaultContainer 'kaniko'
                yaml '''
        kind: Pod
        spec:
          containers:
          - name: kaniko
            image: gcr.io/kaniko-project/executor:debug
            imagePullPolicy: Always
            command:
            - sleep
            args:
            - 99d
            volumeMounts:
              - name: aws-secret
                mountPath: /root/.aws/
              - name: docker-registry-config
                mountPath: /kaniko/.docker
          volumes:
            - name: aws-secret
              secret:
                secretName: aws-secret
            - name: docker-registry-config
              configMap:
                name: docker-registry-config
        '''
           }
        }
        View Code
  • agent的常见选项:
    • label:一个字符串。该选项对node、docker和dockerfile参数有效,对于node是必需的。
    • customWorkspace:一个字符串。自定义工作区,可以是相对路径(在这种情况下,自定义工作区将位于节点的工作区根目录下),也可以是绝对路径。该选项对node、docker和dockerfile参数有效。
      • agent {
            node {
                label 'my-defined-label'
                customWorkspace '/some/other/path'
            }
        }
    • reuseNode:一个布尔值,默认为false。如果为true,则在Pipeline顶层指定的节点上运行容器,在相同的工作空间中,而不是完全在新节点上运行容器。该选项对docker和dockerfile参数有效,只有在代理上用于单个阶段时才有效。
    • args:一个字符串。传递给docker运行的运行时参数。该选项对docker和dockerfile参数有效。
  • 当agent配合timeout使用时:
    • 定义在pipeline部分,超时计数器在分配代理后启动。
    • 定义在stage部分,超时计数器在分配代理之前启动。(即该超时包括代理分配时间,如果代理分配延迟,Pipeline可能会失败。)
agent any
options {
    timeout(time: 1, unit: 'SECONDS')
}

4.2、post(附加步骤)

  • post:定义了一个或多个附加步骤,这些步骤将在整个Pipeline特定阶段运行完成时运行(取决于post的位置)。
  • post可以定义在pipeline或stage部分,post是可选的(所以并不在pipeline最简结构中)。
  • post没有参数。
  • 根据pipeline阶段完成状态,post部分可以分成多种条件块:
    • always:不论当前完成状态是什么,都执行。
    • changed:只要当前完成状态与上一次完成状态不同,就执行。
    • fixed:当前完成状态为成功,且上一次完成状态为失败或不稳定(unstable),才执行。
    • regression:当前完成状态为失败、不稳定或中止(aborted),且上一次完成状态为成功,才执行。
    • aborted:当前执行结果是中止状态(一般为人为中止)时执行。
    • failure:当前完成状态为失败时执行。
    • success:当前完成状态为成功时执行。
    • unstable:当前完成状态为不稳定时执行。
    • cleanup:清理条件块。不论当前完成状态是什么,在其他所有条件块执行完成后都执行。
  • post部分可以同时包含多种条件块。
pipeline {

    agent any

    stages {
        stage('Hello') {
            steps {
                echo 'Hello World'
            }
            post {    //阶段之后的post
                alwasy {
                    echo "stage post alwasy"
                }
            }
        }
    }

    post {            //整个pipeline之后的post
        changed {
            echo "pipeline post changed"
        }
        alwasy {
            echo "pipeline post alwasy"
        }
        success {
            echo "pipeline post success"
        }
    }
}

4.3、stages(阶段容器)

  • stages:包含一个或多个stage(阶段:指令的序列。stage至少包含一个指令),stages部分描述了pipeline中的大部分“工作”。
  • stages可以定义在pipeline部分,stages是必须的
  • stages没有参数。

4.4、steps(步骤容器)

  • steps:定义了在给定stage中执行的一个或多个步骤。
  • steps可以定义在stage部分,steps是必须的
  • steps没有参数。

5、声明式pipeline中的Directives(指令)

  • 基本结构满足不了现实多变的需求。因此,Jenkins pipeline通过各种指令(directive)来丰富自己。
  • 指令可以被理解为对Jenkins pipeline基本结构的补充
  • Jenkins pipeline支持的指令有:
    • environment:用于设置环境变量。可以定义在pipeline或stage部分。
    • tools:会自动下载并安装指定的工具,并将其加入到PATH变量中。可以定义在pipeline或stage部分。
    • input:会暂停pipeline,提示输入内容。可以定义在stage部分。
    • options:用于配置Jenkins pipeline本身的选项,比如options{retry(3)}指当pipeline失败时再重试2次。可以定义在pipeline或stage部分。
    • parameters:与input不同,parameters是执行pipeline前传入的一些参数。
    • triggers:用于定义执行pipeline的触发器。
    • when:当满足when定义的条件时,阶段才执行。
    • parallel:并行执行多个step。在pipeline插件1.2版本后,parallel开始支持对多个阶段进行并行执行。
  • 在使用指令时,需要注意的是每个指令都有自己的“作用域”。如果指令使用的位置不正确,Jenkins将会报错。

5.1、environment(环境变量)

  • environment(环境指令)指定了一系列键值对,这些键值对将被定义为所有步骤特定阶段的步骤环境变量,这取决于环境指令的位置。
    • environment指令支持一个特殊的辅助方法credentials(),它可以在Jenkins环境中通过标识符访问预定义的凭据
  • environment可以定义在pipeline或stage部分,environment是可选的(所以并不在pipeline最简结构中)。
  • environment没有参数。
  • 支持的凭证类型:
    • Secret Text:指定的环境变量将被设置为Secret Text内容。
    • Secret File:指定的环境变量将被设置为临时创建的File文件的路径。
    • Username and password:指定的环境变量将被设置为username:password,另外这两个环境变量将被自动赋值给MYVARNAME_USR和MYVARNAME_PSW。
    • SSH with Private Key:指定的环境变量将被设置为临时创建的SSH密钥文件的路径,另外这两个环境变量将被自动赋值给MYVARNAME_USR和MYVARNAME_PSW(包含密码短语)。
pipeline {
    agent any
    // 在顶级块中使用的环境指令将应用于所有步骤(steps)。
    environment { 
        CC = 'clang'
    }
    stages {
        stage('Example secret-text') {
            // 在阶段(stage)中定义的环境指令,只会应用于该阶段中的步骤。
            environment { 
                AN_ACCESS_KEY = credentials('my-predefined-secret-text') 
            }
            steps {
                sh 'printenv'
            }
        }
        stage('Example Username/Password') {
            environment {
                SERVICE_CREDS = credentials('my-predefined-username-password')
            }
            steps {
                sh 'echo "Service user is $SERVICE_CREDS_USR"'
                sh 'echo "Service password is $SERVICE_CREDS_PSW"'
                sh 'curl -u $SERVICE_CREDS https://myservice.example.com'
            }
        }
        stage('Example SSH Username with private key') {
            environment {
                SSH_CREDS = credentials('my-predefined-ssh-creds')
            }
            steps {
                sh 'echo "SSH private key is located at $SSH_CREDS"'
                sh 'echo "SSH user is $SSH_CREDS_USR"'
                sh 'echo "SSH passphrase is $SSH_CREDS_PSW"'
            }
        }
    }
}

5.2、options(pipeline选项)

  • options指令:可以在Pipeline内部配置特定于Pipeline的选项(即使用options指令配置Jenkins pipeline自身)。Pipeline提供了许多这样的选项,比如buildDiscarder,但是它们也可以由插件提供,比如timestamps(时间戳)。
  • options可以定义在pipeline或stage(有一定限制)部分,options是可选的(所以并不在pipeline最简结构中)。
  • options没有参数。
  • 注意,在stage部分定义的options指令,会在进入代理或检查任何when条件之前将调用

5.2.1、常用options指令

1、buildDiscarder

  • 保存最近历史构建记录的数量。当pipeline执行完成后,会在硬盘上保存制品和构建执行日志,如果长时间不清理会占用大量空间,设置此选项后会自动清理。
  • 可以定义在pipeline部分。
  • 例如,options { buildDiscarder(logRotator(numToKeepStr: '1')) }

2、checkoutToSubdirectory

  • Jenkins从版本控制库拉取源码时,默认检出到工作空间的根目录中,此选项可以指定检出到工作空间的子目录中。
  • 可以定义在pipeline部分。
  • 例如,options { checkoutToSubdirectory('foo') }

3、disableConcurrentBuilds

  • 同一个pipeline,Jenkins默认是可以同时执行多次的。此选项是为了禁止同一个pipeline同时被执行。
  • 可以定义在pipeline部分。
//当已经有一个正在执行的构建时,新的构建会排队
options {
    disableConcurrentBuilds()
}

//中止正在运行的构建并开始新的构建。
options {
    disableConcurrentBuilds(abortPrevious: true)
}

4、newContainerPerStage

  • 当agent为docker或dockerfile时,指定在同一个Jenkins节点上,每个stage都分别运行在一个新的容器中,而不是所有stage都运行在同一个容器中。
  • 可以定义在pipeline部分。
  • 例如,options { newContainerPerStage() }

5、retry

  • 当发生失败时进行重试,可以指定整个pipeline的重试次数。需要注意的是,这个次数是指总次数,包括第1次失败
  • 可以定义在pipeline或stage部分
  • 例如,options { retry(3) }

6、timeout

  • 如果pipeline执行时间过长,超出了设置的timeout时间,Jenkins将中止pipeline。单位有:SECONDS(秒)、MINUTES(分)、HOURS(小时)
  • 可以定义在pipeline或stage部分
  • 例如,options { timeout(time: 1, unit: 'HOURS') }

5.2.2、其他options指令

1、disableResume

  • 如果控制器重新启动,不允许管道恢复。
  • 可以定义在pipeline部分。
  • 例如,options { disableResume() }

2、overrideIndexTriggers

  • 允许重写分支索引触发器的默认处理。
  • 可以定义在pipeline部分。
// 如果在多分支或组织标签处禁用分支索引触发器,可以仅为此作业启用分支索引触发器。
options { overrideIndexTriggers(true) }
// 否则,仅为该作业禁用分支索引触发器。
options { overrideIndexTriggers(false) }

3、preserveStashes

  • 保留完成构建的存储,用于阶段重新启动。
  • 可以定义在pipeline部分。
// 保存最近完成的构建的存储
options { preserveStashes() }
// 保存最近完成的五个构建的存储
options { preserveStashes(buildCount: 5) }

4、quietPeriod

  • 为Pipeline设置安静周期(以秒为单位),覆盖全局默认值。
  • 可以定义在pipeline部分。
  • 例如,options { quietPeriod(30) }

5、skipDefaultCheckout

  • 默认情况下,在代理指令(agent directive)中跳过从源代码控制签出代码。
  • 可以定义在pipeline或stage部分
  • 示例,options { skipDefaultCheckout() }

6、skipStagesAfterUnstable

  • 一旦构建状态变为不稳定(UNSTABLE),就跳过各个阶段。
  • 可以定义在pipeline部分。
  • 示例,options { skipStagesAfterUnstable() }

7、timestamps

  • 在Pipeline运行生成的所有控制台输出前加上该行发出的时间。
  • 可以定义在pipeline或stage部分
  • 示例,options { timestamps() }

8、parallelsAlwaysFailFast

  • 将管道中所有后续并行阶段的failfast设置为true。
  • 可以定义在pipeline部分。
  • 示例,options { parallelsAlwaysFailFast() }

9、disableRestartFromStage

  • 完全禁用在经典Jenkins UI和蓝海中可见的“重新启动”选项。
  • 可以定义在pipeline部分,不能定义在stage部分。
  • 例如,options { disableRestartFromStage() }

5.3、parameters(参数)

  • parameters指令:提供了一个参数列表,用户在触发Pipeline时应该提供这些参数。这些用户指定参数的值可以通过params对象提供给管道步骤。
    • 根据参数类型,每个参数都有一个Name和Value。当构建开始时,这些信息被导出为环境变量,从而允许构建配置的后续部分访问这些值。例如,在POSIX shell(如bash和ksh)中使用${PARAMETER_NAME}语法,在PowerShell中使用${Env:PARAMETER_NAME}语法,或在Windows cmd.exe中使用%PARAMETER_NAME%语法。
  • parameters可以定义在pipeline部分,parameters是可选的(所以并不在pipeline最简结构中)。且parameters只能出现一次
  • parameters没有参数。

1、string

  • 字符串类型的参数。例如,parameters { string(name: 'DEPLOY_ENV', defaultValue: 'staging', description: '') }

2、text

  • 文本类型的参数,可以包含多行。例如,parameters { text(name: 'DEPLOY_TEXT', defaultValue: 'One\nTwo\nThree\n', description: '') 

3、booleanParam

  • 布尔类型的参数。例如,parameters { booleanParam(name: 'DEBUG_BUILD', defaultValue: true, description: '') }

4、choice(选择)

  • 一个枚举的参数列表,第一个值是默认值。例如,parameters { choice(name: 'CHOICES', choices: ['one', 'two', 'three'], description: '') }

5、password

  • 密码参数。例如,parameters { password(name: 'PASSWORD', defaultValue: 'SECRET', description: 'A secret password') }
pipeline {
    agent any
    parameters {
        string(name: 'PERSON', defaultValue: 'Mr Jenkins', description: 'Who should I say hello to?')
        text(name: 'BIOGRAPHY', defaultValue: '', description: 'Enter some information about the person')
        booleanParam(name: 'TOGGLE', defaultValue: true, description: 'Toggle this value')
        choice(name: 'CHOICE', choices: ['One', 'Two', 'Three'], description: 'Pick something')
        password(name: 'PASSWORD', defaultValue: 'SECRET', description: 'Enter a password')
    }
    stages {
        stage('Example') {
            steps {
                echo "Hello ${params.PERSON}"
                echo "Biography: ${params.BIOGRAPHY}"
                echo "Toggle: ${params.TOGGLE}"
                echo "Choice: ${params.CHOICE}"
                echo "Password: ${params.PASSWORD}"
            }
        }
    }
}

5.4、triggers(触发器)

  • triggers指令:定义了自动触发管道的方式。对于与GitHub或BitBucket等源集成的管道,可能不需要触发器,因为基于webhook的集成可能已经存在。
  • 目前可用的触发器有:
    • cron:定时执行pipeline
    • pollSCM:定时轮询代码仓库,如果有变化就执行pipeline
    • upstream:由上游任务触发
  • triggers可以定义在pipeline部分,triggers是可选的(所以并不在pipeline最简结构中)。且triggers只能出现一次
  • triggers没有参数。
// 定时触发器
triggers { cron('H */4 * * 1-5') }.
// 定时轮询代码仓库的触发器
triggers { pollSCM('H */4 * * 1-5') }
// 由上游任务触发的触发器(接受以逗号分隔的作业字符串和阈值)
triggers { upstream(upstreamProjects: 'job1,job2', threshold: hudson.model.Result.SUCCESS) }

示例:

pipeline {
    agent any
    triggers {
        cron('H */4 * * 1-5')
    }
    stages {
        stage('Example') {
            steps {
                echo 'Hello World'
            }
        }
    }
}

5.5、stage(阶段)

  • 每个stages中至少有一个stage,每个stage中应该包含一个steps节一个可选代理(agent)节其他特定于stage的指令
  • stage可以定义在stages部分,stage是必须的
  • stage有一个必选参数,即该阶段的名称。
  • 实际上,管道完成的所有实际工作都将被封装在一个或多个阶段(stage)指令中。
pipeline {
    agent any
    stages {
        stage('Example') {
            steps {
                echo 'Hello World'
            }
        }
    }
}

5.6、tools(工具)

  • 定义要自动安装并放在PATH上的工具。如果指定代理none,则忽略此选项。
  • tools可以定义在pipeline或stage部分,tools是可选的(所以并不在pipeline最简结构中)。
  • tools没有参数。
  • 目前支持的工具有:
    • maven
    • jdk
    • gradle
pipeline {
    agent any
    tools {
        maven 'apache-maven-3.0.1'
    }
    stages {
        stage('Example') {
            steps {
                sh 'mvn --version'
            }
        }
    }
}

5.7、input(输入语句)

  • 执行input步骤会暂停pipeline,直到用户输入参数。
  • input的参数:
    • message(必须的):提示信息。
    • id:输入的可选标识符。默认值是基于阶段(stage)名称的。
    • ok:自定义确定按钮的文本。
    • submitter:可以进行操作的用户ID或用户组名,使用“,”分隔,在“,”左右不允许有空格。这在做input步骤的权限控制方面很实用。默认允许任何用户。
    • submitterParameter:保存input步骤的实际操作者的用户名的变量名。
    • parameters:手动输入的参数列表。
pipeline {
    agent any
    stages {
        stage('Example') {
            input {
                message "Should we continue?"
                ok "Yes, we should."
                submitter "alice,bob"
                parameters {
                    string(name: 'PERSON', defaultValue: 'Mr Jenkins', description: 'Who should I say hello to?')
                }
            }
            steps {
                echo "Hello, ${PERSON}, nice to meet you."
            }
        }
    }
}

5.8、when(条件判断)

  • when指令允许管道根据给定的条件决定是否执行阶段。when指令必须包含至少一个条件。
    • 如果when指令包含多个条件,则所有子条件必须返回true才能执行阶段。这与将子条件嵌套在allOf条件中的情况相同。
    • 如果使用anyOf条件,请注意,一旦发现第一个条件为“true”,就会跳过剩余的测试。
  • 可以使用嵌套条件来构建更复杂的条件结构:not、allOf或anyOf。嵌套条件可以嵌套到任意深度。
  • when可以定义在stage部分,when是可选的(所以并不在pipeline最简结构中)。
  • when没有参数。

示例:

pipeline {
    agent any
    stages {
        stage('Example 1') {
            steps {
                echo 'Hello World 111'
            }
        }
        stage('Example 2') {
            when {
                branch 'production'
            }
            steps {
                echo 'Hello World 222'
            }
        }
        stage('Example 3') {
            when {
                branch 'production'
                environment name: 'DEPLOY_TO', value: 'production'
            }
            steps {
                echo 'Hello World 333'
            }
        }
        stage('Example 4') {
            when {
                allOf {
                    branch 'production'
                    environment name: 'DEPLOY_TO', value: 'production'
                }
            }
            steps {
                echo 'Hello World 444'
            }
        }
        stage('Example 5') {
            when {
                branch 'production'
                anyOf {
                    environment name: 'DEPLOY_TO', value: 'production'
                    environment name: 'DEPLOY_TO', value: 'staging'
                }
            }
            steps {
                echo 'Hello World 555'
            }
        }
        stage('Example 666') {
            when {
                expression { BRANCH_NAME ==~ /(production|staging)/ }
                anyOf {
                    environment name: 'DEPLOY_TO', value: 'production'
                    environment name: 'DEPLOY_TO', value: 'staging'
                }
            }
            steps {
                echo 'Hello World 666'
            }
        }
        stage('Example 7') {
            agent {
                label "some-label"
            }
            when {
                beforeAgent true
                branch 'production'
            }
            steps {
                echo 'Hello World 777'
            }
        }
        stage('Example 8') {
            when {
                beforeInput true
                branch 'production'
            }
            input {
                message "Deploy to production?"
                id "simple-input"
            }
            steps {
                echo 'Hello World 888'
            }
        }
        stage('Example 9') {
            when {
                beforeOptions true
                branch 'testing'
            }
            options {
                lock label: 'testing-deploy-envs', quantity: 1, variable: 'deployEnv'
            }
            steps {
                echo "Deploying to ${deployEnv}"
            }
        }
    }
}
View Code

5.8.1、内置条件

1、branch

  • 执行正在构建的分支与给定的分支模式(ANT风格的通配符)匹配时,则执行该阶段。例如,when { branch 'master' }。注意,这只适用于多分支管道。
  • 可以使用参数comparator指定匹配模式:
    • EQUALS:简单的字符串比较
    • GLOB(默认值):ANT风格的通配符
    • REGEXP:正则表达式
when { branch pattern: "release-\\d+", comparator: "REGEXP"}

2、buildingTag

  • 当正在构建是构建标记时才执行该阶段。例如,when { buildingTag() }

3、changelog

  • 如果构建的SCM变更日志包含给定的正则表达式模式,则执行该阶段。例如,when { changelog '.*^\\[DEPENDENCY\\] .+$' }

4、changeset

  • 如果构建的SCM变更集包含一个或多个与给定模式匹配的文件,则执行该阶段。示例,when { changeset "**/*.js" }
  • 可以使用参数comparator指定匹配模式:
    • EQUALS:简单的字符串比较
    • GLOB(默认值):ANT风格的通配符,不区分大小写(可以使用caseSensitive参数关闭)。
    • REGEXP:正则表达式
when { changeset pattern: ".TEST\\.java", comparator: "REGEXP" }
when { changeset pattern: "*/*TEST.java", caseSensitive: true }

5、changeRequest

  • 如果当前构建是针对“变更请求”(也称为GitHub和Bitbucket上的Pull request, GitLab上的Merge request, Gerrit中的change,等等),则执行该阶段。当不传递任何参数时,阶段在每个更改请求上运行。例如, when { changeRequest() }。
  • 通过向变更请求添加带有参数的过滤器属性,可以使阶段仅在匹配的变更请求上运行。可能的属性有id、target、branch、fork、url、title、author、authorDisplayName和authorremail。每一个都对应一个CHANGE_*环境变量。例如,when { changeRequest target: 'master' }
  • 可以使用参数comparator指定匹配模式:
    • EQUALS(默认值):简单的字符串比较
    • GLOB:ANT风格的通配符
    • REGEXP:正则表达式
when { changeRequest authorEmail: "[\\w_-.]+@example.com", comparator: 'REGEXP' }

6、environment

  • 当指定的环境变量设置为给定值时,则执行该阶段。例如,when { environment name: 'DEPLOY_TO', value: 'production' }。

7、equals

  • 当期望值等于实际值时,则执行的阶段。例如,when { equals expected: 2, actual: currentBuild.number }

8、expression

  • 当指定的Groovy表达式求值为true时,则执行该阶段。例如,when { expression { return params.DEBUG_BUILD } }
  • 注意,当从表达式返回字符串时,必须将它们转换为布尔值或返回null以求值为false。简单地返回“0”或“false”仍然会计算为“true”。

9、tag

  • 如果TAG_NAME变量与给定的模式匹配,则执行阶段。例如,when { tag "release-*" }提供一个空模式时,如果TAG_NAME变量存在,就会执行该阶段(与buildingTag()相同)。
  • 可以使用参数comparator指定匹配模式:
    • EQUALS:简单的字符串比较
    • GLOB(默认值):ANT风格的通配符
    • REGEXP:正则表达式
when { tag pattern: "release-\\d+", comparator: "REGEXP"}

10、not

  • 嵌套条件为false时,则执行该阶段。必须包含一个条件。例如,when { not { branch 'master' } }

11、allOf

  • 所有嵌套条件为真时,则执行该阶段。必须包含至少一个条件。例如,when { allOf { branch 'master'; environment name: 'DEPLOY_TO', value: 'production' } }

12、anyOf

  • 当至少有一个嵌套条件为真时,则执行该阶段。必须包含至少一个条件。例如,when { anyOf { branch 'master'; branch 'staging' } }

13、triggeredBy

  • 由给定的参数触发当前构建时,则执行该阶段。
when { triggeredBy 'SCMTrigger' }
when { triggeredBy 'TimerTrigger' }
when { triggeredBy 'BuildUpstreamCause' }
when { triggeredBy cause: "UserIdCause", detail: "vlinde" }

5.8.2、when与其他指令

1、when与agent

  • 如果在某个阶段定义了when条件,默认情况下会先在进入该阶段的代理,再计算when条件
    • 可以在when块中指定beforeAgent选项来更改。如果beforeAgent设置为true,则先计算when条件,并且只有当when的条件值为true时才会进入代理。

2、when与input

  • 如果定义了某个阶段的when条件,默认情况下先执行input指令,再计算when条件
    • 可以在when块中指定beforeInput选项来更改。如果beforeInput设置为true,则先计算when条件,并且只有当when条件值为true时才会执行输入。
  • beforeInput true优先于beforeAgent true。

3、when与options

  • 如果定义了某个阶段的when条件,默认情况下会先执行options指令,再计算when条件
    • 可以在when块中指定beforeOptions选项来更改。如果beforeOptions设置为true,则先计算when条件,并且只有当when条件值为true时才会执行options指令。
  • beforeOptions true优先于beforeInput true和beforeAgent true。

6、声明式pipeline中的Steps(步骤)

6.1、pipeline内置基础步骤

  • pipeline步骤:https://www.jenkins.io/doc/pipeline/steps

6.1.1、基础步骤(Basic Steps)

  • echo:将信息输出到控制台。
    • 支持的参数有:
      • message(String):要输出的信息
  • deleteDir:删除当前目录
    • deleteDir是一个无参步骤。通常它与dir步骤一起使用,用于删除指定目录下的内容。
  • fileExists:判断文件或目录是否存在,返回true或false。
    • 支持的参数有:
      • file(String):文件路径
        • 支持绝对路径和相对路径。当使用相对路径时,它是相对于当前工作目录的。
        • Unix和Windows路径分割符都可以使用:/
//脚本语法
stage ('Check for existence of index.html') {
    agent any # Could be a top-level directive or a stage level directive
    steps {
        script {
            if (fileExists('src/main/resources/index.html')) {
                echo "File src/main/resources/index.html found!"
            }
        }
    }
}

//分隔符
node('Linux') {
    if (fileExists('/usr/local/bin/jenkins.sh')) {
        sh '/usr/local/bin/jenkins.sh'
    }
}
node('Windows') {
    if (fileExists('C:/jenkins.exe')) {
        bat 'C:/jenkins.exe'
    }
}
View Code
  • isUnix:判断是否为类UNIX系统
    • 如果当前pipeline运行在一个类UNIX系统上,则返回true。
  • pwd:返回当前目录
    • pwd步骤与Linux的pwd命令一样,返回当前目录。
    • 支持的参数有:
      • tmp(boolean, 可选):默认值是false。如果参数值为true,则返回与当前目录关联的临时目录。
  • writeFile:将内容写入指定文件中
    • 支持的参数有:
      • file(String):文件路径,可以是绝对路径,也可以是相对路径。
      • text(String):要写入的文件内容。
      • encoding(String, 可选):目标文件的编码。默认使用操作系统默认的编码。如果是Base64的数据,则可以使用Base64编码。
  • readFile:读取文件内容
    • 支持的参数有:
      • file(String):文件路径,可以是绝对路径,也可以是相对路径。
      • encoding(String, 可选):读取文件时使用的编码。
  • stash:保存临时文件
    • stash步骤可以保存一组文件以供在同一Pipeline中的其他步骤或阶段使用(任何节点/工作空间)。如果整个pipeline的所有阶段在同一台机器上执行,stash步骤是多余的。所以,stash通常是要跨Jenkins node使用的。
    • 默认情况下,在Pipeline运行结束时丢弃存储的文件。其他插件可能会更改此行为以延长存储时间。例如,声明式管道包含一个preserveStashes()选项,允许保留运行时的存储,并在重新启动运行时使用。
    • 一个Pipeline运行的缓存在其他Pipeline或其他作业中不可用。
    • stash步骤会将文件存储在tar文件中,对于大文件的stash操作将会消耗Jenkins master的计算资源。Jenkins官方文档推荐,当文件大小为5∼100MB时,应该考虑使用其他替代方案。
    • 支持的参数有:
      • name(String):保存文件的集合的唯一标识。
      • allowEmpty(boolean, 可选):允许stash内容为空。
      • excludes(String, 可选):将哪些文件排除(ant风格)。如果排除多个文件,则使用逗号分隔。如果为空代表不排除任何文件。
      • includes(String, 可选):包含哪些文件(ant风格)。如果要添加多个表达式,可以使用逗号分隔。如果为空代表当前文件夹下的所有文件。
      • useDefaultExcludes(boolean, 可选):默认为true。如果为true,则代表使用Ant风格路径默认排除文件列表。
  • unstash:取出之前stash的文件
    • 支持的参数有:
      • name(String):先前保存的存储库的名称。
//stash步骤在master节点上执行,而unstash步骤在node1节点上执
pipeline {
    agent none
    stages {
        stage('stash') {
            agent {label "master"}
            steps {
                writeFile(file: "a.txt", text: "${env.BUILD_NUMBER}")
                stash(name: "abc", includes: "a.txt")
            }
        }
        stage('unstash') {
            agent {label "node1"}
            steps {
                unstash("abc")
                script {
                    def content = readFile("a.txt")
                    echo "${content}"
                }
            }
        }
    }
}
View Code
  • error:主动报错,中止当前pipeline
    • error步骤的执行类似于抛出一个异常。
    • 支持的参数有:
      • message(String)
    • 通常省略参数:error("there's an error")。
  • tool:使用预定义的工具
    • 如果在Global Tool Configuration(全局工具配置)中配置了工具,可以通过tool步骤得到工具路径。
    • 支持的参数有:
      • name(Strin):工具名称。
      • type(String, 可选):工具类型,指该工具安装类的全路径类名
    • 每个插件的type值都不一样,而且绝大多数插件的文档根本不写type值。除了到该插件的源码中查找,还有一种方法可以快速找到type值,就是前往Jenkins pipeline代码片段生成器中生成该tool步骤的代码即可。
  • timeout:代码块超时时间
    • 为timeout步骤内运行的代码设置超时时间限制。如果超时,将抛出一个org.jenkinsci.plugins.workflow.steps.FlowInterruptedException异常,导致中止构建(除非它被捕获并以某种方式处理)。。
    • 支持如下参数:
      • time(int):超时时间。
      • unit(可选):时间单位,支持的值有NANOSECONDS、MICROSECONDS、MILLISECONDS、SECONDS、MINUTES(默认)、HOURS、DAYS。
      • activity(boolean, 可选):如果值为true,则只有当日志没有活动后,才真正算作超时。
  • waitUntil:等待条件满足
    • 不断重复waitUntil内的代码,直到条件为true。但waitUntil不负责处理块内代码的异常,遇到异常时直接向外抛出。waitUntil步骤最好与timeout步骤共同使用,避免死循环。
    • 支持如下参数:
      • initialRecurrencePeriod(long, 可选):设置重试之间的初始等待时间(以毫秒为单位)。默认为250ms。每次失败将减少尝试之间的延迟,最多可达15秒。
      • quiet(Boolean, 可选):如果为true,则该步骤不会在每次检查条件时记录一条消息。默认为false。
timeout(time: 30, unit: 'SECONDS') {
    waitUntil {
        script {
            def r = sh(script: 'curl http://exmaple', returnStatus: true)
            return(r == 0)
        }
    }
}
View Code
  • retry:重复执行块
    • 如果在其代码块执行期间发生任何异常,则重试该块(最多N次)。如果在最后一次尝试时发生异常,那么它将导致终止构建(除非它被捕获并以某种方式处理)。构建的用户中止不会被捕获。同时,在执行retry的过程
    • 中,用户是无法中止pipeline的。
    • 支持如下参数:
      • count(int):可以重试的最多次数。
      • conditions(可选):应该重试该块的条件。如果没有匹配,块将失败。如果没有指定的条件,除了用户中止的情况外,该块将总是被重试。
  • sleep:让pipeline休眠一段时间
    • 支持的参数有:
      • time(int):休眠时间。
      • unit(可选):时间单位,支持的值有NANOSECONDS、MICROSECONDS、MILLISECONDS、SECONDS(默认)、MINUTES、HOURS、DAYS

6.2.2、节点与进程(Nodes and Processes)

  • dir:切换目录
    • pipeline默认工作在工作空间目录下,dir步骤可以切换到其他目录。
    • dir块中的任何步骤都将使用该目录作为当前目录,而且任何相对路径都会使用它作为基础路径。
    • 支持的参数有:
      • path(String):目录路径
dir("/var/logs") {
    deleteDir
}
  • sh:执行shell命令
    • 支持的参数有:
      • script(String):要执行的shell脚本,可以是多行。通常在类UNIX系统上。
        • 可以指定解释器(如#!/usr/bin/perl),默认使用-xe标志运行系统默认的shell(您可以指定set +e和/或set +x来禁用它们)。
      • encoding(String, 可选):脚本执行后输出日志的编码,默认是脚本运行所在系统的编码。
      • returnStatus(boolean, 可选):脚本默认返回的是状态码,如果是一个非零的状态码,将导致该步骤失败,并引发pipeline执行失败。如果returnStatus为true,则不论状态码是什么,pipeline的执行都不会受影响。
      • returnStdout(boolean, 可选):如果为true,则任务的标准输出将作为步骤的返回值,而不是打印到构建日志中(如果有错误,则依然会打印到日志中)。
      • label(String, 可选):要在pipeline步骤视图中显示的标签和步骤的详细信息,而不是步骤类型。
    • returnStatus与returnStdout参数一般不会同时使用,因为返回值只能有一个。如果同时使用,则只有returnStatus参数生效。
  • bat、powershell
    • bat步骤执行的是Windows的批处理命令。powershell步骤执行的是PowerShell脚本,支持3+版本。
    • 这两个步骤支持的参数与sh步骤的一样。

6.2、在声明式pipeline中使用脚本

  • 不能直接在steps块中写if-else,或者定义一个变量,否则Jenkins都会报错。也就是不能直接在steps块中写Groovy代码。
    • 为了解决这个问题,Jenkins pipeline专门提供了一个script步骤,可以在script步骤中像写代码一样写pipeline逻辑。(在script块中的其实就是Groovy代码)
  • 大多数时候,是不需要使用script步骤的。如果在script步骤中写了大量的逻辑,则说明应该把这些逻辑拆分到不同的阶段,或者放到共享库中。
pipeline {
    agent any
    stages {
        stage('Hello') {
            steps {
			    script {    //在script步骤中写Groovy代码
					def browsers = ["chrome", "firefox"]
					for (int i = 0; i < browsers.size(); ++i) {
						echo "Testing the ${browsers[i]} browser."
					}
				}
            }

        }
    }
}

1

4、环境变量

  • 环境变量可以分为Jenkins内置变量和自定义变量。

4.1、Jenkins内置变量

  • 在pipeline执行时,可以通过一个名为env的全局变量,将Jenkins内置环境变量暴露出来。
  • 使用内置环境变量的三种方法:
    • 默认env的属性可以直接在pipeline中引用。所以,三种方法都是合法的。但是不推荐方法三,因为出现变量冲突时,非常难查问题。
pipeline {
    agent any
    stages {
        stage('Example') {
            steps {
				echo "Running ${env.BUILD_NUMBER} on ${env.JENKINS_URL}"
				echo "Running $env.BUILD_NUMBER on $env.JENKINS_URL"
				echo "Running $BUILD_NUMBER on $JENKINS_URL"
            }
        }
    }
}
  • env变量都有哪些可用属性呢?通可以过访问<Jenkins master的地址>/pipeline-syntax/globals来获取完整列表。在列表中,当一个变量被声明为“For a multibranch project”时,代表只有多分支项目才会有此变量。
  • 几个常用的内置变量(即全局变量env的属性):
    • BUILD_NUMBER:当前构建的构建号,一个累加的数字。在打包时,它可作为制品名称的一部分,比如server-2.jar。
    • BRANCH_NAME:对于多分支项目,这将被设置为正在构建的分支的名称。当需要根据不同的分支做不同的事情时就会用到,比如通过代码将release分支发布到生产环境中、master分支发布到测试环境中。
    • BUILD_URL:当前构建的页面URL。如果构建失败,则需要将失败的构建链接放在邮件通知中,这个链接就可以是BUILD_URL。
    • GIT_BRANCH:通过git拉取的源码构建的项目才会有该变量。
  • 在使用env变量时,需要注意不同类型的项目,env变量所包含的属性及其值是不一样的。比如普通pipeline任务中的GIT_BRANCH变量的值为origin/master,而在多分支pipeline任务中GIT_BRANCH变量的值为master。
  • 小技巧:在调试pipeline时,可以在pipeline的开始阶段将env变量的属性值都打印出来:sh 'printenv'

4.2、自定义全局环境变量

  • 自定义的全局环境变量可以让Jenkins的所有pipeline项目使用。
  • Jenkins --> Manage Jenkins --> Configure System --> Global properties --> 勾选“Environment variables” --> 单击“Add”按钮
    • 通过单击“Add”按钮,可以添加多个全局环境变量。

  • 自定义全局环境变量会被加入env属性列表中,因此使用自定义全局环境变量与使用Jenkins内置变量的方法一样:${env.myName}。

4.3、自定义pipeline环境变量

  • 当pipeline变得复杂时,可能就要定义自己的环境变量。声明式pipeline提供可以通过environment指令自定义自己的变量。
  • environment指令可以定义在pipeline中,变量的作用域是整个pipeline;也可以定义在stage中,此时变量的作用域仅是该阶段。
    • 但是这些变量都不可以跨pipeline,比如pipeline A访问不到pipeline B的变量。在pipeline之间共享变量可以通过参数化pipeline来实现。
pipeline {
    agent any

    environment {    //作用范围是整个pipeline
	    myName = "hangha_pipeline"
	}

    stages {
        stage('Example') {

			environment {    //作用范围仅是这个stage
				myAge = "12"
			}

            steps {
				echo "my name is ${myName}, my age is ${myAge}"
				sh 'printenv'
            }
        }
    }
}
  • 在实际工作中,还会遇到一个环境变量引用另一个环境变量的情况。在environment中可以这样定义:
environment {
    myName = "hangha"
	myAge = "12"
	myNameAge = "${myName}-${myAge}"
}
  • 如果在environment中定义的变量与env中的变量重名,那么env中的变量值将会被覆盖掉
  • 小技巧:为避免变量名冲突,可以根据实际情况,在变量名前加上前缀,比如__server_name,__就是前缀。

5、tools(构建工具)

  • 构建是指将源码转换成一个可以使用的二进制程序的过程。这个过程可以包括但不限于这几个环节:下载依赖、编译、打包。
  • 构建过程的输出,比如一个jar包,我们称为制品(有些书籍也称之为产出物)。而管理制品的仓库,称为制品库
  • 当Jenkins执行到tools时,tools指令可以自动下载并安装所指定的构建工具,并将其加入PATH变量中。这样,就可以在sh步骤里直接使用工具了。但在agent none的情况下不会生效
  • tools指令默认支持3种工具:JDK、Maven、Gradle。

5.1、使用tools段

  • Jenkins pipeline的tools指令默认就支持Maven。所以,使用Maven只需要两步。
    • (1)进入Jenkins --> Manage Jenkins --> Global Tool Configuration --> Maven页,配置maven工具。
      • 请注意Name的值为mvn-3.9.4,接下来会在pipeline中用到这个值。
    • (2)在pipeline中指定Maven版本,并使用mvn命令。
pipeline {
    agent any

    tools {
	    maven "maven-3.9.4"
	}

    stages {
        stage('Example') {
            steps {
			    sh 'mvn -v'
				sh 'printenv'
            }
        }
    }
}

5.2、利用环境变量支持更多的构建工具

  • 一般搭建开发环境的做法是:首先在机器上安装好构建工具,然后将这个构建工具所在目录加入PATH环境变量中。
  • 如果想让Jenkins支持更多的构建工具,也是同样的做法:在Jenkins agent上安装构建工具,然后将该构建工具的可执行命令的目录添加到Jenkins pipeline的PATH环境变量中。示例如下:
//方法一
pipeline {
    agent any

    environment {
	    PATH = "/usr/local/maven/bin/:${PATH}"    //将maven的bin目录添加到PATH中
	}

    stages {
        stage('Example') {
            steps {
			    sh 'mvn -v'    //
				sh 'printenv'
            }
        }
    }
}

//方法二
pipeline {
    agent any

    environment {
	    MAVEN_HOME = "/usr/local/maven/bin"    //将mavem的bin目录的路径定义成一个变量
	}

    stages {
        stage('Example') {
            steps {
			    sh '${MAVEN_HOME}/mvn -v'    //直接引用maven的bin目录
				sh 'printenv'
			}
        }
    }
}

5.3、利用tools作用域实现多版本编译

  • 在实际工作中,有时需要对同一份源码使用多个版本的编译器进行编译。tools指令除了支持pipeline作用域,还支持stage作用域。所以,可以在同一个pipeline中实现多版本编译。
pipeline {
    agent any
    stages {
        stage('Example-3.9.4') {
			tools {
				maven "maven-3.9.4"
			}
            steps {
			    sh 'mvn -v'
				sh 'printenv'
            }
        }
        stage('Example-3.9.5') {
			tools {
				maven "maven-3.9.5"
			}
            steps {
			    sh 'mvn -v'
				sh 'printenv'
            }
        }
    }
}

1

#                                                                                                                       #
EQUALS(默认值):简单的字符串比较GLOB:ANT风格的通配符REGEXP:正则表达式
posted @ 2023-09-03 23:08  麦恒  阅读(179)  评论(0编辑  收藏  举报