devops [持续交付实践] pipeline:pipeline 使用之语法详解

蒋刚毅 · 2017年09月09日 · 最后由 geons 回复于 2019年11月07日 · 2753 次阅读

一、引言

jenkins pipeline 语法的发展如此之快用日新月异来形容也不为过,而目前国内对 jenkins pipeline 关注的人还非常少,相关的文章更是稀少,唯一看到 w3c 有篇相关的估计是直接翻译软件翻的,读下来惨不忍睹。
没办法,语法详解这章我干脆把 jenkins 官网上的语法说明全部翻译了一遍,并更新了陈旧的内容(可怜了我大学四级的英语水平~),英语好的朋友也可以直接到官网阅读。

二、语法简介

Pipeline 最基本的部分是 “step”。基本上,step 告诉 Jenkins 要做什么,并且作为 Declarative Pipeline 和 Scripted Pipeline 语法的基本构建块。
Pipeline 支持两种语法:Declarative Pipeline(在 Pipeline 2.5 中引入,结构化方式)和 Scripted Pipeline,两者都支持建立连续输送的 Pipeline。
为与 BlueOcean 脚本编辑器兼容,通常建议使用 Declarative Pipeline 的方式进行编写,从 jenkins 社区的动向来看,很明显这种语法结构也会是未来的趋势。

三、Declarative Pipeline

Declarative Pipeline 是 Jenkins Pipeline 的一个相对较新的补充, 它在 Pipeline 子系统之上提出了一种更为简化和有意义的语法。
所有有效的 Declarative Pipeline 必须包含在一个 pipeline 块内,例如:
pipeline { /* insert Declarative Pipeline here */ }
Declarative Pipeline 中的基本语句和表达式遵循与 Groovy 语法相同的规则 ,但有以下例外:
a.Pipeline 的顶层必须是块,具体来说是:pipeline { }
b.没有分号作为语句分隔符。每个声明必须在自己的一行
c.块只能包含 Sections, Directives, Steps 或赋值语句。
d.属性引用语句被视为无参方法调用。所以例如,输入被视为 input()

1.Sections(章节)

Declarative Pipeline 里的 Sections 通常包含一个或多个 Directives 或 Steps

agent

该 agent 部分指定整个 Pipeline 或特定阶段将在 Jenkins 环境中执行的位置,具体取决于该 agent 部分的放置位置。该部分必须在 pipeline 块内的顶层定义 ,但 stage 级使用是可选的。

为了支持 Pipeline 可能拥有的各种用例,该 agent 部分支持几种不同类型的参数。这些参数可以应用于 pipeline 块的顶层,也可以应用在每个 stage 指令内。
参数
any
在任何可用的 agent 上执行 Pipeline 或 stage。例如:agent any
none
当在 pipeline 块的顶层使用 none 时,将不会为整个 Pipeline 运行分配全局 agent ,每个 stage 部分将需要包含其自己的 agent 部分。
label
使用提供的 label 标签,在 Jenkins 环境中可用的代理上执行 Pipeline 或 stage。例如:agent { label 'my-defined-label' }
node
agent { node { label 'labelName' } },等同于 agent { label 'labelName' },但 node 允许其他选项(如 customWorkspace)。
docker
定义此参数时,执行 Pipeline 或 stage 时会动态供应一个 docker 节点去接受 Docker-based 的 Pipelines。 docker 还可以接受一个 args,直接传递给 docker run 调用。例如:agent { docker 'maven:3-alpine' }或

docker
agent {
    docker {
        image 'maven:3-alpine'
        label 'my-defined-label'
        args  '-v /tmp:/tmp'
    }
}

dockerfile
使用从 Dockerfile 源存储库中包含的容器来构建执行 Pipeline 或 stage 。为了使用此选项,Jenkinsfile 必须从 Multibranch Pipeline 或 “Pipeline from SCM"加载。
默认是在 Dockerfile 源库的根目录:agent { dockerfile true }。如果 Dockerfile 需在另一个目录中建立,请使用以下 dir 选项:agent { dockerfile { dir 'someSubDir' } }。您可以通过 docker build ...使用 additionalBuildArgs 选项,如 agent { dockerfile { additionalBuildArgs '--build-arg foo=bar' } }。
常用选项
这些是可以应用于两个或多个 agent 的选项。除非明确定义,否则不需要。
label
一个字符串。标记在哪里运行 pipeline 或 stage
此选项适用于 node,docker 和 dockerfile,并且 node 是必需的。
customWorkspace
一个字符串。自定义运行的工作空间内。它可以是相对路径,在这种情况下,自定义工作区将位于节点上的工作空间根目录下,也可以是绝对路径。例如:

customWorkspace
agent {
    node {
        label 'my-defined-label'
        customWorkspace '/some/other/path'
    }
}

reuseNode
一个布尔值,默认为 false。如果为 true,则在同一工作空间中。
此选项适用于 docker 和 dockerfile,并且仅在 individual stage 中使用 agent 才有效。
Example

Jenkinsfile (Declarative Pipeline)
pipeline {
    //Execute all the steps defined in this Pipeline within a newly created container of the given name and tag (maven:3-alpine).
    agent { docker 'maven:3-alpine' } 
    stages {
        stage('Example Build') {
            steps {
                sh 'mvn -B clean verify'
            }
        }
    }
}
Stage-level agent section
pipeline {
    agent none 
    stages {
        stage('Example Build') {
            agent { docker 'maven:3-alpine' } 
            steps {
                echo 'Hello, Maven'
                sh 'mvn --version'
            }
        }
        stage('Example Test') {
            agent { docker 'openjdk:8-jre' } 
            steps {
                echo 'Hello, JDK'
                sh 'java -version'
            }
        }
    }
}

post

定义 Pipeline 或 stage 运行结束时的操作。post-condition 块支持 post 部件:always,changed,failure,success,unstable,和 aborted。这些块允许在 Pipeline 或 stage 运行结束时执行步骤,具体取决于 Pipeline 的状态。

conditions 项:
always
运行,无论 Pipeline 运行的完成状态如何。
changed
只有当前 Pipeline 运行的状态与先前完成的 Pipeline 的状态不同时,才能运行。
failure
仅当当前 Pipeline 处于 “失败” 状态时才运行,通常在 Web UI 中用红色指示表示。
success
仅当当前 Pipeline 具有 “成功” 状态时才运行,通常在具有蓝色或绿色指示的 Web UI 中表示。
unstable
只有当前 Pipeline 具有 “不稳定” 状态,通常由测试失败,代码违例等引起,才能运行。通常在具有黄色指示的 Web UI 中表示。
aborted
只有当前 Pipeline 处于 “中止” 状态时,才会运行,通常是由于 Pipeline 被手动中止。通常在具有灰色指示的 Web UI 中表示。

Example

post
pipeline {
    agent any
    stages {
        stage('Example') {
            steps {
                echo 'Hello World'
            }
        }
    }
    post { 
        always { 
            echo 'I will always say Hello again!'
        }
    }
}

stages

包含一个或多个 stage 的序列,Pipeline 的大部分工作在此执行。建议 stages 至少包含至少一个 stage 指令,用于连接各个交付过程,如构建,测试和部署等。

steps

steps 包含一个或多个在 stage 块中执行的 step 序列。

Example

stages
pipeline {
    agent any
    stages { 
        stage('Example') {
            steps {
                echo 'Hello World'
            }
        }
    }
}

2.Directives(指令)

environment

environment 指令指定一系列键值对,这些键值对将被定义为所有 step 或 stage-specific step 的环境变量,具体取决于 environment 指令在 Pipeline 中的位置。
该指令支持一种特殊的方法 credentials(),可以通过其在 Jenkins 环境中的标识符来访问预定义的凭据。
对于类型为 “Secret Text” 的凭据,该 credentials() 方法将确保指定的环境变量包含 Secret Text 内容;对于 “标准用户名和密码” 类型的凭证,指定的环境变量将被设置为 username:password。

Example

environment
pipeline {
    agent any
    environment { 
        CC = 'clang'
    }
    stages {
        stage('Example') {
            environment { 
                AN_ACCESS_KEY = credentials('my-prefined-secret-text') 
            }
            steps {
                sh 'printenv'
            }
        }
    }
}

options

options 指令允许在 Pipeline 本身内配置 Pipeline 专用选项。Pipeline 本身提供了许多选项,例如 buildDiscarder,但它们也可能由插件提供,例如 timestamps。

可用选项
buildDiscarder
pipeline 保持构建的最大个数。例如:options { buildDiscarder(logRotator(numToKeepStr: '1')) }
disableConcurrentBuilds
不允许并行执行 Pipeline,可用于防止同时访问共享资源等。例如:options { disableConcurrentBuilds() }
skipDefaultCheckout
默认跳过来自源代码控制的代码。例如:options { skipDefaultCheckout() }
skipStagesAfterUnstable
一旦构建状态进入了 “Unstable” 状态,就跳过此 stage。例如:options { skipStagesAfterUnstable() }
timeout
设置 Pipeline 运行的超时时间。例如:options { timeout(time: 1, unit: 'HOURS') }
retry
失败后,重试整个 Pipeline 的次数。例如:options { retry(3) }
timestamps
预定义由 Pipeline 生成的所有控制台输出时间。例如:options { timestamps() }

Example

options
pipeline {
    agent any
    options {
        timeout(time: 1, unit: 'HOURS') 
    }
    stages {
        stage('Example') {
            steps {
                echo 'Hello World'
            }
        }
    }
}

parameters

parameters 指令提供用户在触发 Pipeline 时的参数列表。这些参数值通过该 params 对象可用于 Pipeline 步骤,具体用法如下

可用参数
string
A parameter of a string type, for example: parameters { string(name: 'DEPLOY_ENV', defaultValue: 'staging', description: '') }
booleanParam
A boolean parameter, for example: parameters { booleanParam(name: 'DEBUG_BUILD', defaultValue: true, description: '') }
目前只支持 [booleanParam, choice, credentials, file, text, password, run, string] 这几种参数类型,其他高级参数化类型还需等待社区支持。
Example

params
pipeline {
    agent any
    parameters {
        string(name: 'PERSON', defaultValue: 'Mr Jenkins', description: 'Who should I say hello to?')
    }
    stages {
        stage('Example') {
            steps {
                echo "Hello ${params.PERSON}"
            }
        }
    }
}

triggers

triggers 指令定义了 Pipeline 自动化触发的方式。对于与源代码集成的 Pipeline,如 GitHub 或 BitBucket,triggers 可能不需要基于 webhook 的集成也已经存在。目前只有两个可用的触发器:cron 和 pollSCM。

cron
接受一个 cron 风格的字符串来定义 Pipeline 触发的常规间隔,例如: triggers { cron('H 4/* 0 0 1-5') }
pollSCM
接受一个 cron 风格的字符串来定义 Jenkins 检查 SCM 源更改的常规间隔。如果存在新的更改,则 Pipeline 将被重新触发。例如:triggers { pollSCM('H 4/* 0 0 1-5') }

Example

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

stage

stage 指令在 stages 部分中,应包含 stop 部分,可选 agent 部分或其他特定于 stage 的指令。实际上,Pipeline 完成的所有实际工作都将包含在一个或多个 stage 指令中。

Example

stage
pipeline {
    agent any
    stages {
        stage('Example') {
            steps {
                echo 'Hello World'
            }
        }
    }
}

tools

通过 tools 可自动安装工具,并放置环境变量到 PATH。如果 agent none,这将被忽略。

Supported Tools
maven
jdk
gradle

Example

tools
pipeline {
    agent any
    tools {
        //工具名称必须在Jenkins 管理Jenkins → 全局工具配置中预配置。
        maven 'apache-maven-3.0.1'
    }
    stages {
        stage('Example') {
            steps {
                sh 'mvn --version'
            }
        }
    }
}

when

when 指令允许 Pipeline 根据给定的条件确定是否执行该阶段。该 when 指令必须至少包含一个条件。如果 when 指令包含多个条件,则所有子条件必须为 stage 执行返回 true。这与子条件嵌套在一个 allOf 条件中相同(见下面的例子)。
更复杂的条件结构可使用嵌套条件建:not,allOf 或 anyOf。嵌套条件可以嵌套到任意深度。

内置条件
branch
当正在构建的分支与给出的分支模式匹配时执行,例如:when { branch 'master' }。请注意,这仅适用于多分支 Pipeline。
environment
当指定的环境变量设置为给定值时执行,例如: when { environment name: 'DEPLOY_TO', value: 'production' }
expression
当指定的 Groovy 表达式求值为 true 时执行,例如: when { expression { return params.DEBUG_BUILD } }
not
当嵌套条件为 false 时执行。必须包含一个条件。例如:when { not { branch 'master' } }
allOf
当所有嵌套条件都为真时执行。必须至少包含一个条件。例如:when { allOf { branch 'master'; environment name: 'DEPLOY_TO', value: 'production' } }
anyOf
当至少一个嵌套条件为真时执行。必须至少包含一个条件。例如:when { anyOf { branch 'master'; branch 'staging' } }

Example

when
pipeline {
    agent any
    stages {
        stage('Example Build') {
            steps {
                echo 'Hello World'
            }
        }
        stage('Example Deploy') {
            when {
                allOf {
                    branch 'production'
                    environment name: 'DEPLOY_TO', value: 'production'
                }
            }
            steps {
                echo 'Deploying'
            }
        }
    }
}

3.Parallel(并行)

2017.9.25 新增 parallel stage 支持。
Declarative Pipeline 近期新增了对并行嵌套 stage 的支持,对耗时长,相互不存在依赖的 stage 可以使用此方式提升运行效率。除了 parallel stage,单个 parallel 里的多个 step 也可以使用并行的方式运行。

Example

Jenkinsfile (Declarative Pipeline)
pipeline {
    agent any
    stages {
        stage('Non-Parallel Stage') {
            steps {
                echo 'This stage will be executed first.'
            }
        }
        stage('Parallel Stage') {
            when {
                branch 'master'
            }
            parallel {
                stage('Branch A') {
                    agent {
                        label "for-branch-a"
                    }
                    steps {
                        echo "On Branch A"
                    }
                }
                stage('Branch B') {
                    agent {
                        label "for-branch-b"
                    }
                    steps {
                        echo "On Branch B"
                    }
                }
            }
        }
    }
}

4.Steps(步骤)

Declarative Pipeline 可以使用 Pipeline Steps reference 中的所有可用步骤 ,并附加以下仅在 Declarative Pipeline 中支持的步骤。

script

script 步骤需要一个 script Pipeline,并在 Declarative Pipeline 中执行。对于大多数用例,script 在 Declarative Pipeline 中的步骤不是必须的,但它可以提供一个有用的加强。

Example

script
pipeline {
    agent any
    stages {
        stage('Example') {
            steps {
                echo 'Hello World'

                script {
                    def browsers = ['chrome', 'firefox']
                    for (int i = 0; i < browsers.size(); ++i) {
                        echo "Testing the ${browsers[i]} browser"
                    }
                }
            }
        }
    }
}

四、Scripted Pipeline

Groovy 脚本不一定适合所有使用者,因此 jenkins 创建了 Declarative pipeline,为编写 Jenkins 管道提供了一种更简单、更有主见的语法。但是不可否认,由于脚本化的 pipeline 是基于 groovy 的一种 DSL 语言,所以与 Declarative pipeline 相比为 jenkins 用户提供了更巨大的灵活性和可扩展性。

1.流程控制

pipeline 脚本同其它脚本语言一样,从上至下顺序执行,它的流程控制取决于 Groovy 表达式,如 if/else 条件语句,举例如下:

Jenkinsfile (Scripted Pipeline)
node {
    stage('Example') {
        if (env.BRANCH_NAME == 'master') {
            echo 'I only execute on the master branch'
        } else {
            echo 'I execute elsewhere'
        }
    }
}

pipeline 脚本流程控制的另一种方式是 Groovy 的异常处理机制。当任何一个步骤因各种原因而出现异常时,都必须在 Groovy 中使用 try/catch/finally 语句块进行处理,举例如下:

Jenkinsfile (Scripted Pipeline)
node {
    stage('Example') {
        try {
            sh 'exit 1'
        }
        catch (exc) {
            echo 'Something failed, I should sound the klaxons!'
            throw
        }
    }
}

2.Steps

正如文档开始所言,pipeline 最核心和基本的部分就是 “step”,从根本上来说,steps 作为 Declarative pipeline 和 Scripted pipeline 语法的最基本的语句构建块来告诉 jenkins 应该执行什么操作。
Scripted pipeline 没有专门将 steps 作为它的语法的一部分来介绍,但是在 Pipeline Steps reference 这篇文档中对 pipeline 及其插件涉及的 steps 做了很详细的介绍。如有需要可参考 jenkins 官网对该部分的介绍 Pipeline Steps reference

3.Differences from plain Groovy

由于 pipeline 的一些个性化需求,比如在重新启动 jenkins 后要求 pipeline 脚本仍然可以运行,那么 pipeline 脚本必须将相关数据做序列化,然而这一点 Groovy 并不能完美的支持,例如 collection.each { item -> /* perform operation */ }

4.Declarative pipeline 和 Scripted pipeline 的比较

共同点:
两者都是 pipeline 代码的持久实现,都能够使用 pipeline 内置的插件或者插件提供的 steps,两者都可以利用共享库扩展。
区别:
两者不同之处在于语法和灵活性。Declarative pipeline 对用户来说,语法更严格,有固定的组织结构,更容易生成代码段,使其成为用户更理想的选择。但是 Scripted pipeline 更加灵活,因为 Groovy 本身只能对结构和语法进行限制,对于更复杂的 pipeline 来说,用户可以根据自己的业务进行灵活的实现和扩展。

主帖直达:https://testerhome.com/topics/9977

如果觉得我的文章对您有用,请随意打赏。您的支持将鼓励我继续创作!
共收到 16 条回复 时间 点赞

这么好的文章,竟然一条评论都没有。。。。

trigger 只支持 cron 和 pollSCM 是吗?也就说目前 pipeline 只支持定时去检测仓库是否有代码更新是吗?没法做到 webhook 那种代码已提交到仓库就触发一次构建吗?
刚刚接触 pipeline,网上资料包括官网写的资料也不是很全,还请解答下~

Ivanli 回复

这是 declative pipeline 不灵活的地方,需要等插件语法支持,不过也可以在 pipeline job 的配置里和以前一样进行配置。

蒋刚毅 回复

好的,非常感谢!
还有个问题需要请教你,就是用 pipeline 构建 job,在 $WORKSPACE 目录下,会生成除了本项目名称的文件夹以外,还会生成一个备份文件夹,里面什么内容都没有,比如项目名称为 hello,构建 job 的时候会 workspace 目录下生成一个 hello 文件夹和一个 hello@tmp 文件夹。为什么会生成这个@tmp文件夹呢?
我用 pipeline 做个 demo,本来是要去 hello 文件夹里执行一个 shell 脚本,但 jenkins 会跑到 hello@tmp 去找这个 shell。。感觉好奇怪。。以前传统的配置方式,没出现过这种问题哎。。。
另外,pipeline 中如何切换文件夹呢?dir 貌似不好用。。。比如我 dir('/home'),构建 job 则会自动生成/home@tmp,然后 jenkins 又跑到这个@tmp里去了。。。。感觉老是跟这个@tmp过不去。。。

Ivanli 回复

这个@tmp目录的作用还真没深入研究过,猜测可能和 pipeline 使用了 sandbox 模式或者会定期保存 job 状态的特性相关吧,只是猜测,你可以再深入研究下。另外如果只是要进入工作空间可以使用 Jenkins {workspace}的变量参数。

你好,请教一下。pipeline 工程的工作空间,用什么方式可以让它可以通过前台页面查看呢?

lei___ 回复

如果进工作空间要看源代码我一般直接 git 看,可能是出于安全性的考虑 pipeline 对工作空间做了隐藏,应该可以拼接 url 访问,或者到服务器进对应目录查看,实在不爽自己写个插件把它显示出来。

蒋刚毅 回复

我试了下,sandbox 无论是否勾选,都会有@tmp生成。。待我再搜搜相关解决方法。。

蒋刚毅 回复

你好,想请教一下,如何设置 pipeline 中关于 paramter choice 选项中,增加 groovy 脚本。我找到一个链接,可是怎么试都不行 https://stackoverflow.com/questions/44570163/jenkins-dynamic-declarative-pipeline-parameters

其实我的目的就是在 choice 中,执行获取远程代码分支名字的 groovy 脚本。

未使用 Jenkins pipeline 的时候,我通过在 dynamic choice parameter 插件中执行 groovy 脚本获取。

最近主要精力在做研发协同平台的开发,好久没有关注 pipeline 的情况了,去看看 git 插件的更新情况,declarative 需要插件支持才行。

你那个帖子已经描述的相当明确了,一种是 userInput,中断式的。两一种是参数化构建,开始构建时选择参数。

w3c 的 pipline 是 google 翻译的吧。看了下确实惨不忍睹

Ivanli 回复

刚刚入 jenkins pipeline,发现遇到类似的问题,你这边现在有什么好的解决方案吗

好文章

最近在研究和使用 pipeline,易上手,简洁明了。
之前总认为 Jenkins 的 CI/CD 是很复杂难懂,一直不想入门。
其实想多了,Jenkins 是一个自动化引擎,入门上手还是比较简单。
对于我来说选择 pipeline 是因为我对于 Jenkins 的需求简单,我能满足我的测试即可,
大家多研究。

写的不错啊

需要 登录 后方可回复, 如果你还没有账号请点击这里 注册