Jenkins Pipeline

Jenkins Pipeline

官方网址:
https://jenkins.io/zh/doc/book/pipeline/getting-started/

流水线

1、什么是Pipeline?

Pipeline是一套jenkins官方提供的插件,它可以用来在jenkins中实现和集成连续交付。

下面的流程图是一个 CD 场景的示例,在Jenkins中很容易对该场景进行建模:

Jenkins Pipeline_第1张图片

流水线概念

下面的概念是Jenkins流水线很关键的一方面 , 它与流水线语法紧密相连 (参考 overview below).

流水线

流水线是用户定义的一个CD流水线模型 。流水线的代码定义了整个的构建过程, 他通常包括构建, 测试和交付应用程序的阶段 。另外 , pipeline 块是 声明式流水线语法的关键部分.

节点

节点是一个机器 ,它是Jenkins环境的一部分 and is capable of执行流水线。另外, node块是 脚本化流水线语法的关键部分.

阶段

stage 块定义了在整个流水线的执行任务的概念性地不同的的子集(比如 “Build”, “Test” 和 “Deploy” 阶段), 它被许多插件用于可视化 或Jenkins流水线目前的 状态/进展.

步骤

本质上 ,一个单一的任务, a step 告诉Jenkins 在特定的时间点要做_what_ (或过程中的 “step”)。 举个例子,要执行shell命令 ,请使用 sh 步骤: sh 'make'。当一个插件扩展了流水线DSL, 通常意味着插件已经实现了一个新的 step

流水线语法概述

下面的流水线代码骨架说明了声明式流水线语法和 脚本化流水线语法之间的根本差异。

请注意 阶段 and 步骤 (上面的) 都是声明式和脚本化流水线语法的常见元素。

声明式流水线基础

在声明式流水线语法中, pipeline 块定义了整个流水线中完成的所有的工作。

Jenkinsfile (Declarative Pipeline)

pipeline {
    agent any  1
    stages {
        stage('Build') {  2
            steps {
                // 3
            }
        }
        stage('Test') { 4
            steps {
                // 5
            }
        }
        stage('Deploy') { 6
            steps {
                // 7
            }
        }
    }
}
1 在任何可用的代理上,执行流水线或它的任何阶段。
2 定义 “Build” 阶段。
3 执行与 “Build” 阶段相关的步骤。
4 定义"Test" 阶段。
5 执行与"Test" 阶段相关的步骤。
6 定义 “Deploy” 阶段。
7 执行与 “Deploy” 阶段相关的步骤。
脚本化流水线基础

在脚本化流水线语法中, 一个或多个 node 块在整个流水线中执行核心工作。 虽然这不是脚本化流水线语法的强制性要求, 但它限制了你的流水线的在node块内的工作做两件事:

  1. 通过在Jenkins队列中添加一个项来调度块中包含的步骤。 节点上的执行器一空闲, 该步骤就会运行。
  2. 创建一个工作区(特定为特定流水间建立的目录),其中工作可以在从源代码控制检出的文件上完成。
    Caution: 根据你的 Jenkins 配置,在一系列的空闲后,一些工作区可能不会自动清理 。参考 JENKINS-2111 了解更多信息。

Jenkinsfile (Scripted Pipeline)

node {  
    stage('Build') { 
        // 
    }
    stage('Test') { 
        // 
    }
    stage('Deploy') { 
        // 
    }
}
在任何可用的代理上,执行流水线或它的任何阶段。
定义 “Build” 阶段。 stage 块 在脚本化流水线语法中是可选的。 然而, 在脚本化流水线中实现 stage 块 ,可以清楚的显示Jenkins UI中的每个 stage 的任务子集。
执行与 “Build” 阶段相关的步骤。
定义 “Test” 阶段。
执行与 “Test” 阶段相关的步骤。
定义 “Deploy” 阶段。
执行与 “Deploy” 阶段相关的步骤。
流水线示例

这有一个使用声明式流水线的语法编写的 Jenkinsfile 文件 - 可以通过点击下面 Toggle Scripted Pipeline 链接来访问它的等效的脚本化语法:

Jenkinsfile (Declarative Pipeline)

pipeline { 1
    agent any 2
    stages {
        stage('Build') { 3
            steps { 4
                sh 'make' 5
            }
        }
        stage('Test'){
            steps {
                sh 'make check'
                junit 'reports/**/*.xml' 6
            }
        }
        stage('Deploy') {
            steps {
                sh 'make publish'
            }
        }
    }
}

Toggle Scripted Pipeline (Advanced)

1 pipeline 是声明式流水线的一种特定语法,他定义了包含执行整个流水线的所有内容和指令的 “block” 。
2 agent是声明式流水线的一种特定语法,它指示 Jenkins 为整个流水线分配一个执行器 (在节点上)和工作区。
3 stage 是一个描述 stage of this Pipeline的语法块。在 Pipeline syntax 页面阅读更多有关声明式流水线语法的stage块的信息。如 above所述, 在脚本化流水线语法中,stage 块是可选的。
4 steps 是声明式流水线的一种特定语法,它描述了在这个 stage 中要运行的步骤。
5 sh 是一个执行给定的shell命令的流水线 step (由 Pipeline: Nodes and Processes plugin提供) 。
6 junit 是另一个聚合测试报告的流水线 step (由 JUnit plugin提供)。
7 node 是脚本化流水线的一种特定语法,它指示 Jenkins 在任何可用的代理/节点上执行流水线 (和包含在其中的任何阶段)这实际上等效于 声明式流水线特定语法的agent

Jenkins Pipeline_第2张图片

我的理解就是:pipeline是一个流程,这个流程定义了完成过一个CI/CD流程的步骤,通过执行这个流程代替手工自动去完成CI/CD,这个流程是由使用者自己定义的。

整个流程主要有以下几个模块组成:

​ 1.agent

​ 2.param

​ 3.stage

首先介绍stage。pipeline实际上就是由很多个stage组成,每个stage完成一件事情。就像上图中第二个stage用来构建项目,第三个stage用来测试项目。所以定义pipeline实际上就是定义多个stage。而stage是由多个step组成,由step来定义这个stage是如何完成一个任务的。比如要执行test这个测试stage,通过step定义第一步拉取代码,第二步进行测试,第三步将测试结果发送邮件。

接着就是agent,agent是用来定义CI/CD流程环境的,比如你在构建gradle项目的时候需要gradle环境,这时候就由agent来定义。agent可以定义全局的,就是所有的stage都会在这个环境中进行,也可以放在stage中,意味这只有该stage会放在这个环境中执行。

然后就是param。param是为整个流程提供参数的。这一点是很好的,比如你使用git进行代码管理,你需要构建某个流程,这时候你只要在构建的时候指定分支参数就可以构建项目。

最后,还有其他部分,比如所有的stage都执行完成了,或者失败了,这时候你需要发送邮件,你可以在最后的部分定义发送邮件,不论构建成功与否。

2、定义pipeline

pipeline的定义被写在一个文本文件——jenkinsfile,该文件又可以提交给项目的源代码控制库, 将pipeline视为应用程序的一部分,以便像任何其他代码一样进行版本控制和审查。

创建Jenkinsfile并将其提交给源代码管理提供了许多直接的好处:

1.自动为所有分支和拉取请求创建pipeline构建过程。

2.pipeline上的代码审查/迭代(以及剩余的源代码)。

3.pipeline的审计跟踪。

4.可由项目的多个成员查看和编辑。

虽然在Web UI直接定义pipelins和使用 Jenkinsfile定义是相同的,但通常认为最佳做法是使用Jenkinsfile定义pipeline并检查其中的源代码控制。

3、为何选择Pipeline?

从根本上说,Jenkins是一个支持多种自动化模式的自动化引擎。Pipeline为Jenkins添加了一套功能强大的自动化工具,支持从简单的持续集成到全面的CD pipeline的用例。通过对一系列相关任务建模,用户可以利用Pipeline的许多功能:

代码:pipeline在代码中实现,通常检查到源代码控制,使团队能够编辑,审查和迭代其交付管道。

持久:pipeline可以在Jenkins master的计划内和计划外重启中存活。

Pausable:在继续pipeline运行之前,pipeline可以选择停止并等待人工输入或批准。

多功能:pipeline支持复杂的实际CD要求,包括并行分叉/连接,循环和执行工作的能力。

可扩展:Pipeline插件支持其DSL的自定义扩展 和多个与其他插件集成的选项。

一、流水线入门

正如 前文 提到的,Jenkins 流水线是一套插件,它支持实现和集成持续交付流水线到 Jenkins。流水线提供了一组可扩展的工具,用于通过流水线 DSL 将简单到复杂的交付流水线建模为“代码”。

本节描述了如何在 Jenkins 中开始创建你的流水线并介绍创建和存储 Jenkinsfile 的各种方式。

配置要求

为了使用 Jenkins 流水线,你需要:

  • Jenkins 2.x 或以上版本(旧版本到 1.642.3 可能可以,但不建议)
  • 流水线插件,作为“建议插件”的一部分安装(在安装 Jenkins 后,运行 Post-installation setup wizard 时指定)。

在 Managing Plugins 中阅读了解更多与安装和管理插件有关的信息。

定义流水线

声明式和脚本式流水线都是 DSL 语言,[1]用来描述软件交付流水线的一部分。 脚本式流水线是用一种限制形式的 Groovy 语法编写的。

本文档将根据需要介绍 Groovy 语法的相关部分,所以虽然理解 Groovy 对使用流水线有所帮助,但并不是必须的。

流水线可以通过以下任一方式来创建:

  • 通过 Blue Ocean - 在 Blue Ocean 中设置一个流水线项目后,Blue Ocean UI 会帮你编写流水线的 Jenkinsfile 文件并提交到源代码管理系统。
  • 通过经典 UI - 你可以通过经典 UI 在 Jenkins 中直接输入基本的流水线。
  • 在源码管理系统中定义 - 你可以手动编写一个 Jenkinsfile 文件,然后提交到项目的源代码管理仓库中。[3]

使用两种方式定义流水线的语法是相同的。尽管 Jenkins 支持在经典 UI 中直接进入流水线,但通常认为最好的实践是在 Jenkinsfile 文件中定义流水线,Jenkins 之后会直接从源代码管理系统加载。

通过 Blue Ocean

如果你刚接触 Jenkins 流水线,Blue Ocean UI 可以帮助你 设置流水线项目,并通过图形化流水线编辑器为你自动创建和编写流水线(即 Jenkinsfile)。

作为在 Blue Ocean 中设置流水线项目的一部分,Jenkins 给你项目的源代码管理仓库配置了一个安全的、经过身份验证的适当的连接。因此,你通过 Blue Ocean 的流水线编辑器在 Jenkinsfile 中做的任何更改都会自动的保存并提交到源代码管理系统。

了解更多 Blue Ocean 相关信息请前往 Blue Ocean 章节和 Blue Ocean 入门页面。

通过经典 UI

使用经典 UI 创建的 Jenkinsfile 由 Jenkins 自己保存(在 Jenkins 的主目录下)。

想要通过 Jenkins 经典 UI 创建一个基本流水线:

  1. 如果有要求的话,确保你已登录进 Jenkins。

  2. 从Jenkins 主页(即 Jenkins 经典 UI 的工作台),点击左上的 新建任务

    Jenkins Pipeline_第3张图片

  3. 输入一个任务名称字段,填写你新建的流水线项目的名称。
    **警告:**Jenkins 使用这个项目名称在磁盘上创建目录。建议不要在项目名称中使用空格,因为这样做可能会触发在脚本中不能正确处理目录路径中的空格的bug。

  4. 向下滚动并点击 流水线,然后点击页面底部的 确定 打开流水线配置页(已选中 General 选项)。

    Jenkins Pipeline_第4张图片

  5. 点击页面顶部的 流水线 选项卡让页面向下滚动到 流水线 部分。
    **注意:**如果你在源代码管理系统中定义了 Jenkinsfile, 请按照下面的在源码管理系统中定义的说明。

  6. 流水线 部分, 确保 定义 字段显示 Pipeline script 选项。

  7. 将你的流水线代码输入到 脚本 文本区域。
    例如,复制并粘贴下面的声明式示例流水线代码(在 Jenkinsfile ( …​ ) 标题下)或者它的脚本化的版本到 脚本 文本区域。(下面的声明式示例将在整个过程的其余部分使用。)

    Jenkinsfile (Declarative Pipeline)

    pipeline {
        agent any 
        stages {
            stage('Stage 1') {
                steps {
                    echo 'Hello world!' 
                }
            }
        }
    }
    

    Toggle Scripted Pipeline (Advanced)

    agent 指示 Jenkins 为整个流水线分配一个执行器(在 Jenkins 环境中的任何可用代理/节点上)和工作区。
    echo 写一个简单的字符串到控制台输出。
    node 与上面的 agent 做了同样的事情。

    Jenkins Pipeline_第5张图片

    **注意:**你也可以从 脚本 文本区域右上方的 try sample Pipeline… 选项选择脚本式流水线的示例。注意该区域没有可用的声明式流水线示例。

  8. 点击 保存 打开流水线项目视图页面。

  9. 在该页面, 点击左侧的 立即构建 Build Now 运行流水线。

    Jenkins Pipeline_第6张图片

  10. 在左侧的 Build History 下面,点击 #1 来访问这个特定流水线运行的详细信息。

  11. 点击 Console Output 来查看流水线运行的全部输出。下面的输出显示你的流水线已成功运行。

    Jenkins Pipeline_第7张图片

    注意:

    • 你也可以通过点击构建号左边的彩色地球仪从工作台直接访问控制台输出(例如 #1)。
    • 通过经典的 UI 定义流水线可以很方便的测试流水线代码片段,也可以处理简单的或不需要从源代码仓库中检出/克隆的流水线。正如上面提到的,和通过 Blue Ocean(上面)或在版本管理系统中(下面)定义的 Jenkinsfile 不同,在流水线项目的 脚本 文本区域输入的 Jenkinsfile 由 Jenkins 自己存储在 Jenkins 主目录下。因此,为了更好地控制和扩展你的流水线,尤其是源代码管理系统中那些复杂的项目,建议使用 Blue Ocean 或 源码管理系统来定义你的 Jenkinsfile 文件。
在源码管理系统中

复杂的流水线很难在流水线配置页面 经典 UI 的脚本文本区域进行编写和维护。

为简化操作,流水线的 Jenkinsfile 可以在文本编辑器或集成开发环境(IDE)中进行编写并提交到源码管理系统 [3](可选择性地与需要 Jenkins 构建的应用程序代码放在一起)。然后 Jenkins 从源代码管理系统中检出 Jenkinsfile 文件作为流水线项目构建过程的一部分并接着执行你的流水线。

要使用来自源代码管理系统的 Jenkinsfile 文件配置流水线项目:

  1. 按照 通过经典 UI上面的步骤定义你的流水线直到第5步(在流水线配置页面访问流水线部分)。
  2. 定义 字段选择 Pipeline script from SCM 选项。
  3. SCM 字段,选择包含 Jenkinsfile 文件的仓库的源代码管理系统的类型。
  4. 填充对应仓库的源代码管理系统的字段。
    Tip: 如果你不确定给定字段应填写什么值,点击它右侧的 ? 图标以获取更多信息。
  5. 脚本路径 字段,指定你的 Jenkinsfile 文件的位置(和名称)。这个位置是 Jenkins 检出/克隆包括 Jenkinsfile 文件的仓库的位置,它应该与仓库的文件结构匹配。该字段的默认值采取名称为 “Jenkinsfile” 的 Jenkinsfile 文件并位于仓库的根路径。

当你更新指定的仓库时,只要流水线配置了版本管理系统的轮询触发器,就会触发一个新的构建。

由于流水线代码(特别是脚本式流水线)是使用类似 Groovy 的语法编写的, 如果你的IDE不能正确的使用语法高亮显示你的 Jenkinsfile,可以尝试在 Jenkinsfile 文件的顶部插入行 #!/usr/bin/env groovy 纠正这个问题。
内置文档

流水线拥有内置文档的特性可以让创建各种复杂的流水线变得更容易。该内置文档基于 Jenkins 实例中安装的插件自动生成和更新。

该内置文档可以在 ${YOUR_JENKINS_URL}/pipeline-syntax 全局地找到。对于任何已配置的流水线项目,这个文档也被链接到侧栏的流水线语法

Jenkins Pipeline_第8张图片

片段生成器

内置的“片段生成器”工具有助于为各个步骤创建代码段,发现插件提供的新步骤,或者为特定的步骤尝试不同的参数。

片段生成器由 Jenkins 实例中可用的步骤动态添加。可用的步骤的数量依赖于安装的插件,这些插件显式地公开了流水线中使用的步骤。

要使用代码生成器生成一个步骤的片段:

  1. 从已配置好的流水线导航到 流水线语法 链接(见上),或访问 ${YOUR_JENKINS_URL}/pipeline-syntax
  2. 示例步骤 下拉菜单中选择需要的步骤。
  3. 使用 示例步骤 下拉菜单的动态填充区来配置已选的步骤。
  4. 点击 生成流水线脚本 生成一个能够被复制并粘贴到流水线中的流水线片段。

Jenkins Pipeline_第9张图片

要访问所选步骤的附加信息和/或文档,请点击帮助图标(上图中的红色箭头所示)。

全局变量参考

对于只展示步骤的片段生成器的补充,流水线还提供了一个内置的“全局变量参考”。和片段生成器一样,它也是由插件动态添加。但和片段生成器不一样的是,全局变量参考只包含由流水线或插件提供的可用于流水线的变量文档。

流水线默认提供的变量是:

  • env

    可以从脚本式流水线中访问的环境变量,例如: env.PATHenv.BUILD_ID。 访问内置的全局变量参考页面 ${YOUR_JENKINS_URL}/pipeline-syntax/globals 以获取完整的,最新的,可用于流水线的环境变量列表。

  • params

    将为流水线定义的所有参数作为 Map,例如:params.MY_PARAM_NAME

  • currentBuild

    可用于发现当前正在执行的流水线的信息, 比如 currentBuild.resultcurrentBuild.displayName 等属性。参考内置的全局变量参考页面 ${YOUR_JENKINS_URL}/pipeline-syntax/globals 以获取完整的,最新的,currentBuild 的属性列表。

声明式指令生成器

片段生成器可以帮助生成脚本式流水线的步骤或者声明式流水线的 stage 中的 steps 代码块,但是其并没有包含用于定义声明式流水线的 section(节段)和 directive(指令)。声明式指令生成器(Declarative Directive Generator)这个工具可以做到这点。和 片段生成器类似,指令生成器允许你选择声明式的指令,对其以一种方式进行配置,然后生成这个指令的配置,让你将其用于声明式流水线。

要使用声明式指令生成器生成一个声明式的指令:

  1. 从已配置好的流水线导航到 Pipeline Syntax/流水线语法 链接(见上),然后点击侧栏的 Declarative Directive Generator,或直接访问 ${YOUR_JENKINS_URL}/directive-generator
  2. 在下拉菜单中选择需要的指令。
  3. 使用下拉菜单下面动态生成的区域配置已选的指令。
  4. 点击 Generate Declarative Directive 生成一个能够被复制到流水线中的指令配置。

指令生成器可以生成嵌套的指令配置,比如在 when 指令内的条件,但是它不能生成流水线步骤。对于包含步骤的指令内容,比如 stage 内的 stepspost 内的条件如 alwaysfailure,指令生成器添加一个占位符注释。你仍然需要手动添加步骤到流水线中。

Jenkinsfile (Declarative Pipeline)

stage('Stage 1') {
    steps {
        // One or more steps need to be included within the steps block.
    }
}

二、流水线语法

本节是建立在 流水线入门内容的基础上,而且,应当被当作一个参考。 对于在实际示例中如何使用流水线语法的更多信息, 请参阅本章在流水线插件的2.5版本中的 使用 Jenkinsfile部分, 流水线支持两种离散的语法,具体如下对于每种的优缺点, 参见语法比较。

正如 本章开始讨论的, 流水线最基础的部分是 “步骤”。基本上, 步骤告诉 Jenkins 要做什么,以及作为声明式和脚本化流水线语法的基本构建块。

对于可用步骤的概述, 请参考 流水线步骤引用,它包含了一个构建到流水线的步骤和 插件提供的步骤的全面的列表。

声明式流水线

声明式流水线是最近添加到 Jenkins 流水线的 ,它在流水线子系统之上提供了一种更简单,更有主见的语法。

所有有效的声明式流水线必须包含在一个 pipeline 块中, 比如:

pipeline {
    /* insert Declarative Pipeline here */
}

在声明式流水线中有效的基本语句和表达式遵循与 Groovy的语法同样的规则, 有以下例外:

  • 流水线顶层必须是一个 block, 特别地: pipeline { }
  • 没有分号作为语句分隔符,,每条语句都必须在自己的行上。
  • 块只能由 节段, 指令, 步骤, 或赋值语句组成。 *属性引用语句被视为无参方法调用。 例如, input被视为 input()

节段

声明式流水线中的节段通常包含一个或多个 指令 或 步骤。

代理

agent 部分指定了整个流水线或特定的部分, 将会在Jenkins环境中执行的位置,这取决于 agent 区域的位置。该部分必须在 pipeline 块的顶层被定义, 但是 stage 级别的使用是可选的。

Required Yes
Parameters Described below
Allowed In the top-level pipeline block and each stage block.
参数

为了支持作者可能有的各种各样的用例流水线, agent 部分支持一些不同类型的参数。这些参数应用在pipeline块的顶层, 或 stage 指令内部。

  • any

    在任何可用的代理上执行流水线或阶段。例如: agent any

  • none

    当在 pipeline 块的顶部没有全局代理, 该参数将会被分配到整个流水线的运行中并且每个 stage 部分都需要包含他自己的 agent 部分。比如: agent none

  • label

    在提供了标签的 Jenkins 环境中可用的代理上执行流水线或阶段。 例如: agent { label 'my-defined-label' }

  • node

    agent { node { label 'labelName' } }agent { label 'labelName' } 一样, 但是 node 允许额外的选项 (比如 customWorkspace )。

  • docker

    使用给定的容器执行流水线或阶段。该容器将在预置的 node上,或在匹配可选定义的label 参数上,动态的供应来接受基于Docker的流水线。 docker 也可以选择的接受 args 参数,该参数可能包含直接传递到 docker run 调用的参数, 以及 alwaysPull 选项, 该选项强制 docker pull ,即使镜像名称已经存在。 比如: agent { docker 'maven:3-alpine' }agent { docker { image 'maven:3-alpine' label 'my-defined-label' args '-v /tmp:/tmp' } }

  • dockerfile

    执行流水线或阶段, 使用从源代码库包含的 Dockerfile 构建的容器。为了使用该选项, Jenkinsfile 必须从多个分支流水线中加载, 或者加载 “Pipeline from SCM.” 通常,这是源代码仓库的根目录下的 Dockerfile : agent { dockerfile true }. 如果在另一个目录下构建 Dockerfile , 使用 dir 选项: agent { dockerfile {dir 'someSubDir' } }。如果 Dockerfile 有另一个名称, 你可以使用 filename 选项指定该文件名。你可以传递额外的参数到 docker build ... 使用 additionalBuildArgs 选项提交, 比如 agent { dockerfile {additionalBuildArgs '--build-arg foo=bar' } }。 例如, 一个带有 build/Dockerfile.build 的仓库,期望一个构建参数 version:agent { // Equivalent to "docker build -f Dockerfile.build --build-arg version=1.0.2 ./build/ dockerfile { filename 'Dockerfile.build' dir 'build' label 'my-defined-label' additionalBuildArgs '--build-arg version=1.0.2' } }

常见选项

有一些应用于两个或更多 agent 的实现的选项。他们不被要求,除非特别规定。

  • label

    一个字符串。该标签用于运行流水线或个别的 stage。该选项对 node, dockerdockerfile 可用, node要求必须选择该选项。

  • customWorkspace

    一个字符串。在自定义工作区运行应用了 agent 的流水线或个别的 stage, 而不是默认值。 它既可以是一个相对路径, 在这种情况下,自定义工作区会存在于节点工作区根目录下, 或者一个绝对路径。比如:agent { node { label 'my-defined-label' customWorkspace '/some/other/path' } }该选项对 node, dockerdockerfile 有用 。

  • reuseNode

    一个布尔值, 默认为false。 如果是true, 则在流水线的顶层指定的节点上运行该容器, 在同样的工作区, 而不是在一个全新的节点上。这个选项对 dockerdockerfile 有用, 并且只有当 使用在个别的 stageagent 上才会有效。

示例

Jenkinsfile (Declarative Pipeline)

pipeline {
    agent { docker 'maven:3-alpine' }    1
    stages {
        stage('Example Build') {
            steps {
                sh 'mvn -B clean verify'
            }
        }
    }
}
1 在一个给定名称和标签(maven:3-alpine)的新建的容器上执行定义在流水线中的所有步骤 。
阶段级别的 agent 部分

Jenkinsfile (Declarative Pipeline)

pipeline {
    agent none   1
    stages {
        stage('Example Build') {
            agent { docker 'maven:3-alpine' }      2
            steps {
                echo 'Hello, Maven'
                sh 'mvn --version'
            }
        }
        stage('Example Test') {
            agent { docker 'openjdk:8-jre' }       3
            steps {
                echo 'Hello, JDK'
                sh 'java -version'
            }
        }
    }
}
1 在流水线顶层定义 agent none 确保 an Executor 没有被分配。 使用 agent none 也会强制 stage 部分包含他自己的 agent 部分。
2 使用镜像在一个新建的容器中执行该阶段的该步骤。
3 使用一个与之前阶段不同的镜像在一个新建的容器中执行该阶段的该步骤。
post

post 部分定义一个或多个steps ,这些阶段根据流水线或阶段的完成情况而 运行(取决于流水线中 post 部分的位置). post 支持以下 post-condition 块中的其中之一: always, changed, failure, success, unstable, 和 aborted。这些条件块允许在 post 部分的步骤的执行取决于流水线或阶段的完成状态。

Required No
Parameters None
Allowed In the top-level pipeline block and each stage block.
Conditions
  • always

    无论流水线或阶段的完成状态如何,都允许在 post 部分运行该步骤。

  • changed

    只有当前流水线或阶段的完成状态与它之前的运行不同时,才允许在 post 部分运行该步骤。

  • failure

    只有当前流水线或阶段的完成状态为"failure",才允许在 post 部分运行该步骤, 通常web UI是红色。

  • success

    只有当前流水线或阶段的完成状态为"success",才允许在 post 部分运行该步骤, 通常web UI是蓝色或绿色。

  • unstable

    只有当前流水线或阶段的完成状态为"unstable",才允许在 post 部分运行该步骤, 通常由于测试失败,代码违规等造成。通常web UI是黄色。

  • aborted

    只有当前流水线或阶段的完成状态为"aborted",才允许在 post 部分运行该步骤, 通常由于流水线被手动的aborted。通常web UI是灰色。

示例

Jenkinsfile (Declarative Pipeline)

pipeline {
    agent any
    stages {
        stage('Example') {
            steps {
                echo 'Hello World'
            }
        }
    }
    post {    1
        always {    2
            echo 'I will always say Hello again!'
        }
    }
}
1 按照惯例, post 部分应该放在流水线的底部。
2 Post-condition 块包含与 steps 部分相同的steps。
stages

包含一系列一个或多个 stage 指令, stages 部分是流水线描述的大部分"work" 的位置。 建议 stages 至少包含一个 stage 指令用于连续交付过程的每个离散部分,比如构建, 测试, 和部署。

Required Yes
Parameters None
Allowed Only once, inside the pipeline block.
示例

Jenkinsfile (Declarative Pipeline)

pipeline {
    agent any
    stages { 1
        stage('Example') {
            steps {
                echo 'Hello World'
            }
        }
    }
}
1 stages 部分通常会遵循诸如 agent, options 等的指令。
steps

steps 部分在给定的 stage 指令中执行的定义了一系列的一个或多个steps。

Required Yes
Parameters None
Allowed Inside each stage block.
示例

Jenkinsfile (Declarative Pipeline)

pipeline {
    agent any
    stages {
        stage('Example') {
            steps { 1
                echo 'Hello World'
            }
        }
    }
}
1 steps 部分必须包含一个或多个步骤。

指令

environment

environment 指令制定一个 键-值对序列,该序列将被定义为所有步骤的环境变量,或者是特定于阶段的步骤, 这取决于 environment 指令在流水线内的位置。

该指令支持一个特殊的助手方法 credentials() ,该方法可用于在Jenkins环境中通过标识符访问预定义的凭证。对于类型为 "Secret Text"的凭证, credentials() 将确保指定的环境变量包含秘密文本内容。对于类型为 "SStandard username and password"的凭证, 指定的环境变量指定为 username:password ,并且两个额外的环境变量将被自动定义 :分别为 MYVARNAME_USRMYVARNAME_PSW

Required No
Parameters None
Allowed Inside the pipeline block, or within stage directives.
示例

Jenkinsfile (Declarative Pipeline)

pipeline {
    agent any
    environment { 1
        CC = 'clang'
    }
    stages {
        stage('Example') {
            environment { 2
                AN_ACCESS_KEY = credentials('my-prefined-secret-text') 3
            }
            steps {
                sh 'printenv'
            }
        }
    }
}
1 顶层流水线块中使用的 environment 指令将适用于流水线中的所有步骤。
2 在一个 stage 中定义的 environment 指令只会将给定的环境变量应用于 stage 中的步骤。
3 environment 块有一个 助手方法 credentials() 定义,该方法可以在 Jenkins 环境中用于通过标识符访问预定义的凭证。
options

options 指令允许从流水线内部配置特定于流水线的选项。 流水线提供了许多这样的选项, 比如 buildDiscarder,但也可以由插件提供, 比如 timestamps.

Required No
Parameters None
Allowed Only once, inside the pipeline block.
可用选项
  • buildDiscarder

    为最近的流水线运行的特定数量保存组件和控制台输出。例如: options { buildDiscarder(logRotator(numToKeepStr: '1')) }

  • disableConcurrentBuilds

    不允许同时执行流水线。 可被用来防止同时访问共享资源等。 例如: options { disableConcurrentBuilds() }

  • overrideIndexTriggers

    允许覆盖分支索引触发器的默认处理。 如果分支索引触发器在多分支或组织标签中禁用, options { overrideIndexTriggers(true) } 将只允许它们用于促工作。否则, options { overrideIndexTriggers(false) } 只会禁用改作业的分支索引触发器。

  • skipDefaultCheckout

    agent 指令中,跳过从源代码控制中检出代码的默认情况。例如: options { skipDefaultCheckout() }

  • skipStagesAfterUnstable

    一旦构建状态变得UNSTABLE,跳过该阶段。例如: options { skipStagesAfterUnstable() }

  • checkoutToSubdirectory

    在工作空间的子目录中自动地执行源代码控制检出。例如: options { checkoutToSubdirectory('foo') }

  • timeout

    设置流水线运行的超时时间, 在此之后,Jenkins将中止流水线。例如: options { timeout(time: 1, unit: 'HOURS') }

  • retry

    在失败时, 重新尝试整个流水线的指定次数。 For example: options { retry(3) }

  • timestamps

    预谋所有由流水线生成的控制台输出,与该流水线发出的时间一致。 例如: options { timestamps() }

Example

Jenkinsfile (Declarative Pipeline)

pipeline {
    agent any
    options {
        timeout(time: 1, unit: 'HOURS') 1
    }
    stages {
        stage('Example') {
            steps {
                echo 'Hello World'
            }
        }
    }
}
1 指定一个小时的全局执行超时, 在此之后,Jenkins 将中止流水线运行。
一个完整的可用选项列表正在等待完成第 INFRA-1503次。
阶段选项

stageoptions 指令类似于流水线根目录上的 options 指令。然而, stage -级别 options 只能包括 retry, timeout, 或 timestamps 等步骤, 或与 stage 相关的声明式选项,如 skipDefaultCheckout

stage, options 指令中的步骤在进入 agent 之前被调用或在 when 条件出现时进行检查。

可选的阶段选项
  • skipDefaultCheckout

    agent 指令中跳过默认的从源代码控制中检出代码。例如: options { skipDefaultCheckout() }

  • timeout

    设置此阶段的超时时间, 在此之后, Jenkins 会终止该阶段。 例如: options { timeout(time: 1, unit: 'HOURS') }

  • retry

    在失败时, 重试此阶段指定次数。 例如: options { retry(3) }

  • timestamps

    预谋此阶段生成的所有控制台输出以及该行发出的时间一致。例如: options { timestamps() }

示例

Jenkinsfile (Declarative Pipeline)

pipeline {
    agent any
    stages {
        stage('Example') {
            options {
                timeout(time: 1, unit: 'HOURS') 1
            }
            steps {
                echo 'Hello World'
            }
        }
    }
}
1 指定 Example 阶段的执行超时时间, 在此之后,Jenkins 将中止流水线运行。
参数

parameters 指令提供了一个用户在触发流水线时应该提供的参数列表。这些用户指定参数的值可通过 params 对象提供给流水线步骤, 了解更多请参考示例。

Required No
Parameters None
Allowed Only once, inside the pipeline block.
可用参数
  • string

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

  • booleanParam

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

示例

Jenkinsfile (Declarative Pipeline)

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}"
            }
        }
    }
}
一份完整的可用参数列表正在等待 INFRA-1503的完成。
触发器

triggers 指令定义了流水线被重新触发的自动化方法。对于集成了源( 比如 GitHub 或 BitBucket)的流水线, 可能不需要 triggers ,因为基于 web 的集成很肯能已经存在。 当前可用的触发器是 cron, pollSCMupstream

Required No
Parameters None
Allowed Only once, inside the pipeline block.
  • cron

    接收 cron 样式的字符串来定义要重新触发流水线的常规间隔 ,比如: triggers { cron('H */4 * * 1-5') }

  • pollSCM

    接收 cron 样式的字符串来定义一个固定的间隔,在这个间隔中,Jenkins 会检查新的源代码更新。如果存在更改, 流水线就会被重新触发。例如: triggers { pollSCM('H */4 * * 1-5') }

  • upstream

    接受逗号分隔的工作字符串和阈值。 当字符串中的任何作业以最小阈值结束时,流水线被重新触发。例如: triggers { upstream(upstreamProjects: 'job1,job2', threshold: hudson.model.Result.SUCCESS) }

pollSCM 只在Jenkins 2.22 及以上版本中可用。
示例

Jenkinsfile (Declarative Pipeline)

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

stage 指令在 stages 部分进行,应该包含一个 实际上, 流水巷所做的所有实际工作都将封装进一个或多个 stage 指令中。

Required At least one
Parameters One mandatory parameter, a string for the name of the stage.
Allowed Inside the stages section.
示例

Jenkinsfile (Declarative Pipeline)

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

定义自动安装和放置 PATH 的工具的一部分。如果 agent none 指定,则忽略该操作。

Required No
Parameters None
Allowed Inside the pipeline block or a stage block.
支持工具
  • maven
  • jdk
  • gradle
示例

Jenkinsfile (Declarative Pipeline)

pipeline {
    agent any
    tools {
        maven 'apache-maven-3.0.1'    1
    }
    stages {
        stage('Example') {
            steps {
                sh 'mvn --version'
            }
        }
    }
}
1 The tool name must be pre-configured in Jenkins under Manage JenkinsGlobal Tool Configuration.
input

stageinput 指令允许你使用 input step提示输入。 在应用了 options 后,进入 stageagent 或评估 when 条件前, stage 将暂停。 如果 input 被批准, stage 将会继续。 作为 input 提交的一部分的任何参数都将在环境中用于其他 stage

配置项
  • message

    必需的。 这将在用户提交 input 时呈现给用户。

  • id

    input 的可选标识符, 默认为 stage 名称。

  • ok

    input表单上的"ok" 按钮的可选文本。

  • submitter

    可选的以逗号分隔的用户列表或允许提交 input 的外部组名。默认允许任何用户。

  • submitterParameter

    环境变量的可选名称。如果存在,用 submitter 名称设置。

  • parameters

    提示提交者提供的一个可选的参数列表。 更多信息参见 [parameters]。

示例

Jenkinsfile (Declarative Pipeline)

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."
            }
        }
    }
}
when

when 指令允许流水线根据给定的条件决定是否应该执行阶段。 when 指令必须包含至少一个条件。 如果 when 指令包含多个条件, 所有的子条件必须返回True,阶段才能执行。 这与子条件在 allOf 条件下嵌套的情况相同 (参见下面的示例)。

使用诸如 not, allOf, 或 anyOf 的嵌套条件可以构建更复杂的条件结构 can be built 嵌套条件刻意潜逃到任意深度。

Required No
Parameters None
Allowed Inside a stage directive
内置条件
  • branch

    当正在构建的分支与模式给定的分支匹配时,执行这个阶段, 例如: when { branch 'master' }。注意,这只适用于多分支流水线。

  • environment

    当指定的环境变量是给定的值时,执行这个步骤, 例如: when { environment name: 'DEPLOY_TO', value: 'production' }

  • expression

    当指定的Groovy表达式评估为true时,执行这个阶段, 例如: when { expression { return params.DEBUG_BUILD } }

  • not

    当嵌套条件是错误时,执行这个阶段,必须包含一个条件,例如: when { not { branch 'master' } }

  • allOf

    当所有的嵌套条件都正确时,执行这个阶段,必须包含至少一个条件,例如: when { allOf { branch 'master'; environment name: 'DEPLOY_TO', value: 'production' } }

  • anyOf

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

在进入 stageagent 前评估 when

默认情况下, 如果定义了某个阶段的代理,在进入该stageagent 后该 stagewhen 条件将会被评估。但是, 可以通过在 when 块中指定 beforeAgent 选项来更改此选项。 如果 beforeAgent 被设置为 true, 那么就会首先对 when 条件进行评估 , 并且只有在 when 条件验证为真时才会进入 agent

示例

Jenkinsfile (Declarative Pipeline)

pipeline {
    agent any
    stages {
        stage('Example Build') {
            steps {
                echo 'Hello World'
            }
        }
        stage('Example Deploy') {
            when {
                branch 'production'
            }
            steps {
                echo 'Deploying'
            }
        }
    }
}

Jenkinsfile (Declarative Pipeline)

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

Jenkinsfile (Declarative Pipeline)

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'
            }
        }
    }
}

Jenkinsfile (Declarative Pipeline)

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

Jenkinsfile (Declarative Pipeline)

pipeline {
    agent any
    stages {
        stage('Example Build') {
            steps {
                echo 'Hello World'
            }
        }
        stage('Example Deploy') {
            when {
                expression { BRANCH_NAME ==~ /(production|staging)/ }
                anyOf {
                    environment name: 'DEPLOY_TO', value: 'production'
                    environment name: 'DEPLOY_TO', value: 'staging'
                }
            }
            steps {
                echo 'Deploying'
            }
        }
    }
}

Jenkinsfile (Declarative Pipeline)

pipeline {
    agent none
    stages {
        stage('Example Build') {
            steps {
                echo 'Hello World'
            }
        }
        stage('Example Deploy') {
            agent {
                label "some-label"
            }
            when {
                beforeAgent true
                branch 'production'
            }
            steps {
                echo 'Deploying'
            }
        }
    }
}

并行

声明式流水线的阶段可以在他们内部声明多隔嵌套阶段, 它们将并行执行。 注意,一个阶段必须只有一个 stepsparallel 的阶段。 嵌套阶段本身不能包含进一步的 parallel 阶段, 但是其他的阶段的行为与任何其他 stage 相同。任何包含 parallel 的阶段不能包含 agenttools 阶段, 因为他们没有相关 steps

另外, 通过添加 failFast true 到包含 parallelstage 中, 当其中一个进程失败时,你可以强制所有的 parallel 阶段都被终止。

示例

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'
            }
            failFast true
            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"
                    }
                }
            }
        }
    }
}

步骤

声明式流水线可能使用在 流水线步骤引用中记录的所有可用的步骤, 它包含一个完整的步骤列表, 其中添加了下面列出的步骤,这些步骤只在声明式流水线中 only supported

脚本

script 步骤需要 [scripted-pipeline]块并在声明式流水线中执行。 对于大多数用例来说,应该声明式流水线中的“脚本”步骤是不必要的, 但是它可以提供一个有用的"逃生出口"。 非平凡的规模和/或复杂性的 script 块应该被转移到 共享库 。

示例

Jenkinsfile (Declarative Pipeline)

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"
                    }
                }
            }
        }
    }
}

脚本化流水线

脚本化流水线, 与[declarative-pipeline]一样的是, 是建立在底层流水线的子系统上的。与声明式不同的是, 脚本化流水线实际上是由 Groovy构建的通用 DSL。 Groovy 语言提供的大部分功能都可以用于脚本化流水线的用户。这意味着它是一个非常有表现力和灵活的工具,可以通过它编写持续交付流水线。

流控制

脚本化流水线从 Jenkinsfile 的顶部开始向下串行执行, 就像 Groovy 或其他语言中的大多数传统脚本一样。 因此,提供流控制取决于 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'
        }
    }
}

另一种方法是使用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
        }
    }
}

步骤

正如 本章开始所讨论的, 流水线最基础的部分是"步骤"。从根本上说, 步骤告诉 Jenkins要做 what ,并作为声明式和脚本化流水线已发的基本构建块。

脚本化流水线 not 不引入任何特定于其语法的步骤; 流水线步骤引用 包括流水线和插件提供的步骤的完整列表。

区别普通 Groovy

为了提供 durability, 这意味着运行流水线可以在Jenkins master 重启后继续运行,脚本化的流水线序列化数据到主服务器。由于这个设计需求, 一些Groovy 习惯用语,比如 collection.each { item -> /* perform operation */ } 都不完全支持。详情参见 JENKINS-27421 和 JENKINS-26481。

语法比较

当Jenkins 流水线第一次构建时, Groovy 被选为基础。 Jenkins长期使用嵌入式 Groovy引擎来为管理员和用户提供 高级脚本功能。另外, Jenkins流水线的实现者发现 Groovy是 构建现在成为 “脚本化流水线” DSL的坚实基础 [2]。

由于它是一个功能齐全的编程环境, 脚本化流水线为Jenkins用户提供了 大量的灵活性性和可扩展性。 Groovy学习曲线通常不适合给定团队的所有成员, 因此创造了声明式流水线来为编写Jenkins流水线提供一种更简单、更有主见的语法。

两者本质上是相同的流水线子系统。 underneath. 他们都是 “流水线即代码” 的持久实现。它们都能够使用构建到流水线中或插件提供的步骤。它们都能够使用 共享库

但是它们的区别在于语法和灵活性。 声明式限制了用户使用更严格和预定义的结构, 使其成为更简单的持续交付流水线的理想选择。 脚本化提供了很少的限制, 以至于对脚本和语法的唯一限制往往是由Groovy子集本身定义的,而不是任何特定于流水线的系统, 这使他成为权利用户和那些有更复杂需求的人的理想选择。 顾名思义, 声明式流水线鼓励 声明式编程模型。 而脚本化流水线遵循一个更命令式的编程模型 。

三、使用 Jenkinsfile

本节基于 流水线入门 所涵盖的信息,介绍更多有用的步骤、常见的模式,并且演示 Jenkinsfile 的一些特例。

创建一个检入到源码管理系统中的 Jenkinsfile 带来了一些直接的好处:

  • 流水线上的代码评审/迭代
  • 对流水线进行审计跟踪
  • 流水线的单一可信数据源,能够被项目的多个成员查看和编辑。

流水线支持 两种语法:声明式(在 Pipeline 2.5 引入)和脚本式流水线。 两种语法都支持构建持续交付流水线。两种都可以用来在 web UI 或 Jenkinsfile 中定义流水线,不过通常认为创建一个 Jenkinsfile 并将其检入源代码控制仓库是最佳实践。

创建 Jenkinsfile

正如 在 SCM 中定义流水线中所讨论的,Jenkinsfile 是一个文本文件,它包含了 Jenkins 流水线的定义并被检入源代码控制仓库。下面的流水线实现了基本的三阶段持续交付流水线。

Jenkinsfile (Declarative Pipeline)

pipeline {
    agent any

    stages {
        stage('Build') {
            steps {
                echo 'Building..'
            }
        }
        stage('Test') {
            steps {
                echo 'Testing..'
            }
        }
        stage('Deploy') {
            steps {
                echo 'Deploying....'
            }
        }
    }
}

Toggle Scripted Pipeline (Advanced)

不是所有的流水线都有相同的三个阶段,但为大多数项目定义这些阶段是一个很好的开始。下面这一节将在 Jenkins 的测试安装中演示一个简单流水线的创建和执行。

假设已经为项目设置了一个源代码控制仓库并在 Jenkins 下的 these instructions中定义了一个流水线。

使用文本编辑器,最好支持 Groovy 语法高亮,在项目的根目录下创建一个 Jenkinsfile

上面的声明式流水线示例包含了实现持续交付流水线的最小必要结构。agent指令是必需的,它指示 Jenkins 为流水线分配一个执行器和工作区。没有 agent 指令的话,声明式流水线不仅无效,它也不可能完成任何工作!默认情况下,agent 指令确保源代码仓库被检出并在后续阶段的步骤中可被使用。

一个合法的声明式流水线还需要 stages 指令和 steps 指令,因为它们指示 Jenkins 要执行什么,在哪个阶段执行。

想要使用脚本式流水线的更高级用法,上面例子中的 node 是关键的第一步,因为它为流水线分配了一个执行者和工作区。实际上,没有 node,流水线无法工作!在 node 内,业务的第一步是检出这个项目的源代码。由于 Jenkinsfile 已经从源代码控制中直接拉取出来,流水线提供了一个快速且简单的方式来访问正确的源代码修订版本。

Jenkinsfile (Scripted Pipeline)

node {
    checkout scm 1
    /* .. snip .. */
}
1 checkout 步骤将会从源代码控制中检出代码;scm 是一个特殊的变量, 它指示 checkout 步骤克隆触发流水线运行的特定修订版本。

构建

对于许多项目来说,流水线“工作”的开始就是“构建”阶段。通常流水线的这个阶段包括源代码的组装、编译或打包。Jenkinsfile 文件不能替代现有的构建工具,如 GNU/Make、Maven、Gradle 等,而应视其为一个将项目的开发生命周期的多个阶段(构建、测试、部署等)绑定在一起的粘合层。

Jenkins 有许多插件可以用于调用几乎所有常用的构建工具,不过这个例子只是从 shell 步骤(sh)调用 makesh 步骤假设系统是基于 Unix/Linux 的,对于基于 Windows 的系统可以使用 bat 替代。

Jenkinsfile (Declarative Pipeline)

pipeline {
    agent any

    stages {
        stage('Build') {
            steps {
                sh 'make' 1
                archiveArtifacts artifacts: '**/target/*.jar', fingerprint: true 2
            }
        }
    }
}

Toggle Scripted Pipeline (Advanced)

1 sh 步骤调用 make 命令,只有命令返回的状态码为零时才会继续。任何非零的返回码都将使流水线失败。
2 archiveArtifacts 捕获符合模式(**/target/*.jar)匹配的交付件并将其保存到 Jenkins master 节点以供后续获取。
制品归档不能替代外部制品库(例如 Artifactory 或 Nexus),而只应当认为用于基本报告和文件存档。

测试

运行自动化测试是任何成功的持续交付过程的重要组成部分。因此,Jenkins 有许多测试记录,报告和可视化工具,这些都是由各种插件提供的。最基本的,当测试失败时,让 Jenkins 记录这些失败以供汇报以及在 web UI 中可视化是很有用的。下面的例子使用由 JUnit 插件提供的 junit 步骤。

在下面的例子中,如果测试失败,流水线就会被标记为“不稳定”,这通过 web UI 中的黄色球表示。基于测试报告的记录,Jenkins 还可以提供历史趋势分析和可视化。

Jenkinsfile (Declarative Pipeline)

pipeline {
    agent any

    stages {
        stage('Test') {
            steps {
                /* `make check` 在测试失败后返回非零的退出码;
                * 使用 `true` 允许流水线继续进行
                */
                sh 'make check || true' 1
                junit '**/target/*.xml' 2
            }
        }
    }
}

Toggle Scripted Pipeline (Advanced)

1 使用内联的 shell 条件(sh 'make || true')确保 sh 步骤总是看到退出码是零,使 junit 步骤有机会捕获和处理测试报告。在下面处理故障一节中,对它的替代方法有更详细的介绍。
2 junit 捕获并关联与包含模式(\**/target/*.xml)匹配的 JUnit XML 文件。

部署

部署可以隐含许多步骤,这取决于项目或组织的要求,并且可能是从发布构建的交付件到 Artifactory 服务器,到将代码推送到生产系统的任何东西。 在示例流水线的这个阶段,“Build(构建)” 和 “Test(测试)” 阶段都已成功执行。从本质上讲,“Deploy(部署)” 阶段只有在之前的阶段都成功完成后才会进行,否则流水线会提前退出。

Jenkinsfile (Declarative Pipeline)

pipeline {
    agent any

    stages {
        stage('Deploy') {
            when {
              expression {
                currentBuild.result == null || currentBuild.result == 'SUCCESS' 1
              }
            }
            steps {
                sh 'make publish'
            }
        }
    }
}

Toggle Scripted Pipeline (Advanced)

1 流水线访问 currentBuild.result 变量确定是否有任何测试的失败。在这种情况下,值为 UNSTABLE

假设在示例的 Jenkins 流水线中所有的操作都执行成功,那么每次流水线的成功运行都会在 Jenkins 中存档相关的交付件、上面报告的测试结果以及所有控制台输出。

脚本式流水线包含条件测试(如上所示),循环,try/catch/finally 块甚至函数。下一节将会详细的介绍这个高级的脚本式流水线语法。

使用 Jenkinsfile 工作

接下来的章节提供了处理以下事项的细节:

  • Jenkinsfile 中的流水线特有语法
  • 流水线语法的特性和功能,这对于构建应用程序或流水线项目非常重要。

字符串插值

Jenkins 使用与 Groovy 相同的规则进行字符串插值。 Groovy 的字符串插值支持可能会使很多新手感到困惑。尽管 Groovy 支持使用单引号或双引号声明一个字符串,例如:

def singlyQuoted = 'Hello'
def doublyQuoted = "World"

只有后面的字符串才支持基于美元符($)的字符串插值,例如:

def username = 'Jenkins'
echo 'Hello Mr. ${username}'
echo "I said, Hello Mr. ${username}"

其结果是:

Hello Mr. ${username}
I said, Hello Mr. Jenkins

理解如何使用字符串插值对于使用一些流水线的更高级特性是至关重要的。

使用环境变量

Jenkins 流水线通过全局变量 env 提供环境变量,它在 Jenkinsfile 文件的任何地方都可以使用。Jenkins 流水线中可访问的完整的环境变量列表记录在 ${YOUR_JENKINS_URL}/pipeline-syntax/globals#env,并且包括:

  • BUILD_ID

    当前构建的 ID,与 Jenkins 版本 1.597+ 中创建的构建号 BUILD_NUMBER 是完全相同的。

  • BUILD_NUMBER

    当前构建号,比如 “153”。

  • BUILD_TAG

    字符串 jenkins-${JOB_NAME}-${BUILD_NUMBER}。可以放到源代码、jar 等文件中便于识别。

  • BUILD_URL

    可以定位此次构建结果的 URL(比如 http://buildserver/jenkins/job/MyJobName/17/ )

  • EXECUTOR_NUMBER

    用于识别执行当前构建的执行者的唯一编号(在同一台机器的所有执行者中)。这个就是你在“构建执行状态”中看到的编号,只不过编号从 0 开始,而不是 1。

  • JAVA_HOME

    如果你的任务配置了使用特定的一个 JDK,那么这个变量就被设置为此 JDK 的 JAVA_HOME。当设置了此变量时,PATH 也将包括 JAVA_HOME 的 bin 子目录。

  • JENKINS_URL

    Jenkins 服务器的完整 URL,比如 https://example.com:port/jenkins/ (注意:只有在“系统设置”中设置了 Jenkins URL 才可用)。

  • JOB_NAME

    本次构建的项目名称,如 “foo” 或 “foo/bar”。

  • NODE_NAME

    运行本次构建的节点名称。对于 master 节点则为 “master”。

  • WORKSPACE

    workspace 的绝对路径。

引用或使用这些环境变量就像访问 Groovy Map 的 key 一样, 例如:

Jenkinsfile (Declarative Pipeline)

pipeline {
    agent any
    stages {
        stage('Example') {
            steps {
                echo "Running ${env.BUILD_ID} on ${env.JENKINS_URL}"
            }
        }
    }
}

Toggle Scripted Pipeline (Advanced)

设置环境变量

在 Jenkins 流水线中,取决于使用的是声明式还是脚本式流水线,设置环境变量的方法不同。

声明式流水线支持 environment 指令,而脚本式流水线的使用者必须使用 withEnv 步骤。

Jenkinsfile (Declarative Pipeline)

pipeline {
    agent any
    environment { 1
        CC = 'clang'
    }
    stages {
        stage('Example') {
            environment { 2
                DEBUG_FLAGS = '-g'
            }
            steps {
                sh 'printenv'
            }
        }
    }
}

Toggle Scripted Pipeline (Advanced)

1 用在最高层的 pipeline 块的 environment 指令适用于流水线的所有步骤。
2 定义在 stage 中的 environment 指令只适用于 stage 中的步骤。

动态设置环境变量

环境变量可以在运行时设置,然后给 shell 脚本(sh)、Windows 批处理脚本(batch)和 Powershell 脚本(powershell)使用。各种脚本都可以返回 returnStatusreturnStdout

下面是一个使用 sh(shell)的声明式脚本的例子,既有 returnStatus 也有 returnStdout

Jenkinsfile (Declarative Pipeline)

pipeline {
    agent any 1
    environment {
        // 使用 returnStdout
        CC = """${sh(
                returnStdout: true,
                script: 'echo "clang"'
            )}""" 2
        // 使用 returnStatus
        EXIT_STATUS = """${sh(
                returnStatus: true,
                script: 'exit 1'
            )}"""
    }
    stages {
        stage('Example') {
            environment {
                DEBUG_FLAGS = '-g'
            }
            steps {
                sh 'printenv'
            }
        }
    }
}
1 agent 必须设置在流水线的最高级。如果设置为 agent none 会失败。
2 使用 returnStdout 时,返回的字符串末尾会追加一个空格。可以使用 .trim() 将其移除。

处理凭据

Jenkins 中配置的凭据可以在流水线中处理以便于立即使用。请前往 使用凭据页面阅读更多关于在 Jenkins 中使用凭据的信息。

Secret 文本,带密码的用户名,Secret 文件

Jenkins 的声明式流水线语法有一个 credentials() 辅助方法(在environment 指令中使用),它支持 secret 文本,带密码的用户名,以及 secret 文件凭据。如果你想处理其他类型的凭据,请参考其他凭据类型一节(见下)。

Secret 文本

下面的流水线代码演示了如何使用环境变量为 secret 文本凭据创建流水线的示例。

在该示例中,将两个 secret 文本凭据赋予各自的环境变量来访问 Amazon Web 服务(AWS)。这些凭据已在 Jenkins 中配置了各自的凭据 ID jenkins-aws-secret-key-idjenkins-aws-secret-access-key

Jenkinsfile (Declarative Pipeline)

pipeline {
    agent {
        // 此处定义 agent 的细节
    }
    environment {
        AWS_ACCESS_KEY_ID     = credentials('jenkins-aws-secret-key-id')
        AWS_SECRET_ACCESS_KEY = credentials('jenkins-aws-secret-access-key')
    }
    stages {
        stage('Example stage 1') {
            steps {
                // 1
            }
        }
        stage('Example stage 2') {
            steps {
                // 2
            }
        }
    }
}
1 你可以在该阶段的步骤中用语法 $AWS_ACCESS_KEY_ID$AWS_SECRET_ACCESS_KEY 来引用两个凭据环境变量(定义在流水线的 environment 指令中)。比如,在这里,你可以使用分配给这些凭据变量的 secret 文本凭据对 AWS 进行身份验证。 为了保持这些凭据的安全性和匿名性,如果任务试图从流水线中显示这些凭据变量的值(如 echo $AWS_SECRET_ACCESS_KEY),Jenkins 只会返回 “” 来降低机密信息被写到控制台输出和任何日志中的风险。凭据 ID 本身的任何敏感信息(如用户名)也会以 “” 的形式返回到流水线运行的输出中。 这只能降低意外暴露的风险。它无法阻止恶意用户通过其他方式获取凭据的值。使用凭据的流水线也可能泄漏这些凭据。不要允许不受信任的流水线任务使用受信任的凭据。
2 在该流水线示例中,分配给两个 AWS_... 环境变量的凭据在整个流水线的全局范围内都可访问,所以这些凭据变量也可以用于该阶段的步骤中。然而,如果流水线中的 environment 指令被移动到一个特定的阶段(比如下面的 带密码的用户名流水线示例),那么这些 AWS_... 环境变量就只能作用于该阶段的步骤中。
带密码的用户名

下面的流水线代码片段展示了如何创建一个使用带密码的用户名凭据的环境变量的流水线。

在该示例中,带密码的用户名凭据被分配了环境变量,用来使你的组织或团队以一个公用账户访问 Bitbucket 仓库;这些凭据已在 Jenkins 中配置了凭据 ID jenkins-bitbucket-common-creds

当在 environment 指令中设置凭据环境变量时:

environment {
    BITBUCKET_COMMON_CREDS = credentials('jenkins-bitbucket-common-creds')
}

这实际设置了下面的三个环境变量:

  • BITBUCKET_COMMON_CREDS - 包含一个以冒号分隔的用户名和密码,格式为 username:password
  • BITBUCKET_COMMON_CREDS_USR - 附加的一个仅包含用户名部分的变量。
  • BITBUCKET_COMMON_CREDS_PSW - 附加的一个仅包含密码部分的变量。
按照惯例,环境变量的变量名通常以大写字母中指定,每个单词用下划线分割。 但是,你可以使用小写字母指定任何合法的变量名。请记住, credentials() 方法(见上)所创建的附加环境变量总是会有后缀 _USR_PSW(即以下划线后跟三个大写字母的格式)。

下面的代码片段完整的展示了示例流水线:

Jenkinsfile (Declarative Pipeline)

pipeline {
    agent {
        // 此处定义 agent 的细节
    }
    stages {
        stage('Example stage 1') {
            environment {
                BITBUCKET_COMMON_CREDS = credentials('jenkins-bitbucket-common-creds')
            }
            steps {
                // 1
            }
        }
        stage('Example stage 2') {
            steps {
                // 2
            }
        }
    }
}
1 下面的凭据环境变量(定义在流水线的 environment 指令中)可以在该阶段的步骤中使用,并且可以使用下面的语法引用:$BITBUCKET_COMMON_CREDS``$BITBUCKET_COMMON_CREDS_USR``$BITBUCKET_COMMON_CREDS_PSW比如,在这里你可以使用分配给这些凭据变量的用户名和密码向 Bitbucket 验证身份。 为了维护这些凭据的安全性和匿名性,如果任务试图从流水线中显示这些凭据变量的值,那么上面的 Secret 文本 描述的行为也同样适用于这些带密码的用户名凭据变量类型。 同样,这只能降低意外暴露的风险。它无法阻止恶意用户通过其他方式获取凭据的值。使用凭据的流水线也可能泄漏这些凭据。不要允许不受信任的流水线任务使用受信任的凭据。
2 在该流水线示例中,分配给三个 COMMON_BITBUCKET_CREDS... 环境变量的凭据仅作用于 Example stage 1,所以在 Example stage 2 阶段的步骤中这些凭据变量不可用。然而,如果马上把流水线中的 environment 指令移动到 pipeline 块中(正如上面的 Secret 文本流水线示例一样),这些 COMMON_BITBUCKET_CREDS... 环境变量将应用于全局并可以在任何阶段的任何步骤中使用。
Secret 文件

就流水线而言,secret 文件的处理方式与 Secret 文本 完全相同。

实际上,secret 文本和 secret 文件凭据之间的唯一不同是,对于 secret 文本,凭据本身直接输入到 Jenkins 中,而 secret 文件的凭据则原样保存到一个文件中,之后将传到 Jenkins。

与 secret 文本不同的是,secret 文件适合:

  • 太笨拙而不能直接输入 Jenkins
  • 二进制格式,比如 GPG 文件
其他凭据类型

如果你需要在流水线中设置除了 secret 文本、带密码的用户名、secret 文件(见上)以外的其他凭据——即 SSH 秘钥或证书,那么请使用 Jenkins 的片段生成器特性,你可以通过 Jenkins 的经典 UI 访问它。

要从你的流水线项目访问片段生成器

  1. 从 Jenkins 主页(即 Jenkins 的经典 UI 工作台)点击流水线项目的名字。
  2. 在左侧,点击流水线语法并确保 Snippet Generator/片段生成器的链接在右上角粗体显示(如果没有,点击它的链接)。
  3. 示例步骤字段中,选择 withCredentials: Bind credentials to variables
  4. 绑定下面,点击新增并从下拉框中选择:
    • SSH User Private Key - 要处理 SSH 公私钥对凭据,你可以提供:
      • Key 文件变量 - 将要绑定到这些凭据的环境变量的名称。Jenkins 实际上将此临时变量分配给 SSH 公私钥对身份验证过程中所需的私钥文件的安全位置。
      • 密码变量可选)- 将要被绑定到与 SSH 公私钥对相关的 密码 的环境变量的名称。
      • 用户名变量可选)- 将要绑定到与 SSH 公私钥对相关的用户名的环境变量的名称。
      • 凭据 - 选择存储在 Jenkins 中的 SSH 公私钥对证书。该字段的值是凭据 ID,Jenkins 将其写入生成的代码片段中。
    • Certificate - 要处理 PKCS#12 证书,你可以提供:
      • 密钥库变量 - 将要绑定到这些凭据的环境变量的名称。Jenkins 实际上将这个临时变量分配给要求进行身份验证的证书密钥库的安全位置。
      • 密码变量可选) - 将会被绑定到与证书相关的密码的环境变量的名称。
      • 别名变量可选) - 将会被绑定到与证书相关的唯一别名的环境变量的名称。
      • 凭据 - 选择存储在 Jenkins 中的证书。该字段的值是凭据 ID,Jenkins 将其写入生成的代码片段中。
    • Docker client certificate - 用于处理 Docker 主机证书的身份验证。
  5. 点击 生成流水线脚本,Jenkins 会为你指定的凭据生成一个 withCredentials( ... ) { ... } 的流水线步骤片段,你可以将其复制并粘贴到你的声明式或脚本化流水线代码中。
    注意:
    • 凭据 字段(见上)显示的是 Jenkins 中配置的证书的名称。然而,这些值在点击 生成流水线脚本 之后会被转换成证书 ID。
    • 要在一个 withCredentials( ... ) { ... } 流水线步骤组合多个证书,请查看 在一个步骤中组合使用凭据(见下)的详细信息。

SSH User Private Key 示例

withCredentials(bindings: [sshUserPrivateKey(credentialsId: 'jenkins-ssh-key-for-abc', \
                                             keyFileVariable: 'SSH_KEY_FOR_ABC', \
                                             passphraseVariable: '', \
                                             usernameVariable: '')]) {
  // some block
}

可选的 passphraseVariableusernameVariable 定义可以在最终的流水线代码中删除。

Certificate 示例

withCredentials(bindings: [certificate(aliasVariable: '', \
                                       credentialsId: 'jenkins-certificate-for-xyz', \
                                       keystoreVariable: 'CERTIFICATE_FOR_XYZ', \
                                       passwordVariable: 'XYZ-CERTIFICATE-PASSWORD')]) {
  // some block
}

可选的 aliasVariablepasswordVariable 变量定义可以在最终的流水线代码中删除。

下面的代码片段展示了一个完整的示例流水线,实现了上面的 SSH User Private KeyCertificate 片段:

Jenkinsfile (Declarative Pipeline)

pipeline {
    agent {
        // define agent details
    }
    stages {
        stage('Example stage 1') {
            steps {
                withCredentials(bindings: [sshUserPrivateKey(credentialsId: 'jenkins-ssh-key-for-abc', \
                                                             keyFileVariable: 'SSH_KEY_FOR_ABC')]) {
                  // 1
                }
                withCredentials(bindings: [certificate(credentialsId: 'jenkins-certificate-for-xyz', \
                                                       keystoreVariable: 'CERTIFICATE_FOR_XYZ', \
                                                       passwordVariable: 'XYZ-CERTIFICATE-PASSWORD')]) {
                  // 2
                }
            }
        }
        stage('Example stage 2') {
            steps {
                // 3
            }
        }
    }
}
1 在该步骤中,你可以使用语法 $SSH_KEY_FOR_ABC 引用凭据环境变量。比如,在这里你可以使用配置的 SSH 公私钥对证书对 ABC 应用程序进行身份验证,它的 SSH User Private Key 文件被分配给 $SSH_KEY_FOR_ABC
2 在该步骤中,你可以使用语法 $CERTIFICATE_FOR_XYZ$XYZ-CERTIFICATE-PASSWORD 引用凭据环境变量。比如,在这里你可以使用配置的证书凭据对 XYZ 应用程序进行身份验证。证书 Certificate 的秘钥存储文件和密码分别被分配给 $CERTIFICATE_FOR_XYZ$XYZ-CERTIFICATE-PASSWORD 变量。
3 在流水线示例中,分配给 $SSH_KEY_FOR_ABC$CERTIFICATE_FOR_XYZ$XYZ-CERTIFICATE-PASSWORD 的环境变量的凭据只适用于它们各自 withCredentials( ... ) { ... } 步骤中,所以这些凭据变量在 Example stage 2 阶段的步骤中不可用。

为了维护这些证书的安全性和匿名性,如果你试图从 withCredentials( ... ) { ... } 步骤中检索这些凭据变量的值,在 Secret 文本 示例(见上)中的相同行为也适用于这些 SSH 公私钥对证书和凭据变量类型。

片段生成器示例步骤中使用 withCredentials: Bind credentials to variables 选项时,只有当前流水线项目有访问权限的凭据才可以从凭据字段中选择。 虽然你可以为你的流水线手动编写 withCredentials( ... ) { ... } 步骤( 如上所示),但更建议使用 片段生成器 来防止指定超出该流水线访问范围的证书,可以避免运行步骤时失败。你也可以用 片段生成器 来生成处理 secret 文本,带密码的用户名以及 secret 文件的 withCredentials( ... ) { ... } 步骤。但是,如果你只需要处理这些类型的证书的话,为了提高你流水线代码的可读性,更建议你使用在上面一节中描述的相关过程。在 Groovy 中使用单引号而不是双引号来定义脚本(sh 的隐式参数)。单引号将使 secret 被 shell 作为环境变量展开。双引号可能不太安全,因为这个 secret 是由 Groovy 插入的,所以一般操作系统的进程列表(以及 Blue Ocean 和经典 UI 中的流水线步骤树)会意外地暴露它:node { withCredentials([string(credentialsId: 'mytoken', variable: 'TOKEN')]) { sh /* 错误! */ """ set +x curl -H 'Token: $TOKEN' https://some.api/ """ sh /* 正确 */ ''' set +x curl -H 'Token: $TOKEN' https://some.api/ ''' } }
在一个步骤中组合使用凭据

使用 片段生成器,你可以在单个 withCredentials( ... ) { ... } 步骤中提供多个可用凭据,操作如下:

  1. 从 Jenkins 的主页中(即 Jenkins 的经典 UI 工作台)点击流水线项目的名称。
  2. 在左侧,点击 流水线语法 确保片段生成器链接在左上加粗显示(如果没有,点击该链接)。
  3. 示例步骤 字段,选择 withCredentials: Bind credentials to variables
  4. 点击 绑定 下的 新增
  5. 从下拉列表中选择要添加到 withCredentials( ... ) { ... } 步骤的凭据类型。
  6. 指定凭据绑定的细节。请在操作过程中阅读其他凭据类型。
  7. 重复“点击 新增 …”将每个(组)凭据添加到 withCredentials( ... ) { ... } 步骤。
  8. 点击 生成流水线脚本 生成最终的 withCredentials( ... ) { ... } 步骤片段。

处理参数

声明式流水线支持参数开箱即用,允许流水线在运行时通过parameters 指令接受用户指定的参数。配置脚本式流水线的参数是通过 properties 步骤实现的,可以在代码生成器中找到。

如果你使用 Build with Parameters 选项将流水线配置为接受参数,这些参数将作为 params 变量的成员被访问。

假设在 Jenkinsfile 中配置了名为 “Greeting” 的字符串参数,它可以通过 ${params.Greeting} 访问该参数:

Jenkinsfile (Declarative Pipeline)

pipeline {
    agent any
    parameters {
        string(name: 'Greeting', defaultValue: 'Hello', description: 'How should I greet the world?')
    }
    stages {
        stage('Example') {
            steps {
                echo "${params.Greeting} World!"
            }
        }
    }
}

Toggle Scripted Pipeline (Advanced)

处理故障

声明式流水线默认通过 post 节段支持强大的故障处理,它允许声明许多不同的 “post 条件”,比如: alwaysunstablesuccessfailurechanged。流水线语法 提供了关于如何使用各种 post 条件的更多细节。

Jenkinsfile (Declarative Pipeline)

pipeline {
    agent any
    stages {
        stage('Test') {
            steps {
                sh 'make check'
            }
        }
    }
    post {
        always {
            junit '**/target/*.xml'
        }
        failure {
            mail to: [email protected], subject: 'The Pipeline failed :('
        }
    }
}

Toggle Scripted Pipeline (Advanced)

然而脚本化的流水线依赖于 Groovy 的内置的 try/catch/finally 语义来处理流水线运行期间的故障。

在上面的测试示例中, sh 步骤被修改为永远不会返回非零的退出码(sh 'make check || true')。虽然这种方法合法,但意味着接下来的阶段需要检查 currentBuild.result 来了解测试是否失败。

该问题的另一种处理方式是使用一系列的 try/finally 块,它保留了流水线中前面的失败退出的行为,但仍然给了 junit 捕获测试报告的机会。

使用多个代理

在之前所有的示例中都只使用了一个代理。这意味着 Jenkins 会分配一个可用的执行者而无论该执行者是如何打标签或配置的。流水线不仅可以覆盖这种行为,还允许在 Jenkins 环境中使用 同一个 Jenkinsfile 中的多个代理,这将有助于更高级的用例,例如跨多个平台的执行构建/测试。

在下面的示例中,“Build” 阶段将会在一个代理中执行,并且构建结果将会在后续的 “Test” 阶段被两个分别标记为 “linux” 和 “windows” 的代理重用。

Jenkinsfile (Declarative Pipeline)

pipeline {
    agent none
    stages {
        stage('Build') {
            agent any
            steps {
                checkout scm
                sh 'make'
                stash includes: '**/target/*.jar', name: 'app' 1
            }
        }
        stage('Test on Linux') {
            agent { 2
                label 'linux'
            }
            steps {
                unstash 'app' 3
                sh 'make check'
            }
            post {
                always {
                    junit '**/target/*.xml'
                }
            }
        }
        stage('Test on Windows') {
            agent {
                label 'windows'
            }
            steps {
                unstash 'app'
                bat 'make check' 4
            }
            post {
                always {
                    junit '**/target/*.xml'
                }
            }
        }
    }
}

Toggle Scripted Pipeline (Advanced)

1 stash 步骤允许捕获与包含模式(\**/target/*.jar)匹配的文件,以便在同一个流水线中重用。一旦流水线执行完成,就会从 Jenkins master 中删除暂存文件。
2 agent/node 中的参数允许使用任何可用的 Jenkins 标签表达式。参考 流水线语法 部分了解更多信息。
3 unstash 将会从 Jenkins master 中取回命名的 “stash” 到流水线的当前工作区中。
4 bat 脚本允许在基于 Windows 的平台上执行批处理脚本。

可选的步骤参数

流水线遵循 Groovy 语言允许在方法周围省略括号的惯例。

许多流水线步骤也使用命名参数语法作为在 Groovy 中创建的 Map(使用语法 [key1: value1, key2: value2] )的简写 。下面的语句有着相同的功能:

git url: 'git://example.com/amazing-project.git', branch: 'master'
git([url: 'git://example.com/amazing-project.git', branch: 'master'])

为了方便,当调用只有一个参数的步骤时(或仅一个强制参数),参数名称可以省略,例如:

sh 'echo hello' /* short form  */
sh([script: 'echo hello'])  /* long form */

高级脚本式流水线

脚本式流水线是一种基于 Groovy 的领域特定语言 [3] ,大多数 Groovy 语法都可以无需修改,直接在脚本式流水线中使用。

并行执行

上面这节中的示例跨两个不同的平台串联地运行测试。在实践中,如果执行 make check 需要30分钟来完成,那么 “Test” 阶段就需要 60 分钟来完成!

幸运的是,流水线有一个内置的并行执行部分脚本式流水线的功能,通过贴切的名为 parallel 的步骤实现。

使用 parallel 步骤重构上面的示例:

Jenkinsfile (Scripted Pipeline)

stage('Build') {
    /* .. snip .. */
}

stage('Test') {
    parallel linux: {
        node('linux') {
            checkout scm
            try {
                unstash 'app'
                sh 'make check'
            }
            finally {
                junit '**/target/*.xml'
            }
        }
    },
    windows: {
        node('windows') {
            /* .. snip .. */
        }
    }
}

测试不再在标记为 “linux” 和 “windows” 节点中串联地执行,而是并行执行。

你可能感兴趣的:(jenkins,pipeline,Jenkins,Pipeline,pipeline语法,Jenkins,Pipeline,语法)