1 | node { |
测试人员的技术博客;交流请加QQ群:549576208
1 | node { |
Jenkins代码详见:
https://gitee.com/roclli/4-declarative.git
Jenkinsfile内容为:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28#!/usr/bing/env groovy
//Declarative
pipeline {
agent any //agent必需的,告诉Jenkins分配执行器和工作空间
stages { //stage必需的,
stage('Build') {
steps { //step必需的
echo 'Building....'
checkout([$class: 'GitSCM', branches: [[name: '*/master']], doGenerateSubmoduleConfigurations: false, extensions: [], submoduleCfg: [], userRemoteConfigs: [[url: 'https://gitee.com/roclli/4-declarative.git']]])\
// def username = 'Jenkins'
// echo 'Hello Mr. ${username}'
// echo "I said, Hello Mr. ${username}"
}
}
stage('Test') {
steps{
echo 'Testing....'
}
}
stage('Deploy') {
steps{
echo 'Deploying....'
}
}
}
}
Jenkins代码详见:
https://gitee.com/roclli/simple-maven-project-with-tests.git
Jenkinsfile内容为:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28#!/usr/bing/env groovy
node('master') {
echo "----------------------start git url----------------------"
checkout scm
echo "----------------------version()----------------------"
def v = version()
if (v) {
echo "---Building version ${v}---"
}
def mvnHome = tool 'M3'
echo "----------------------mvn -B -D verify----------------------"
sh "${mvnHome}/bin/mvn -B -Dmaven.test.failure.ignore verify"
}
def version() {
def matcher = readFile('pom.xml') =~ '<version>(.+)</version>'
echo "---${matcher[0][1]}---" //---1.0-SNAPSHOT---
echo "---${matcher[0][2]}---" //---null---
echo "---${matcher[0][3]}---" //---null---
echo "---${matcher[1][1]}---" //---2.18.1---
echo "---${matcher[1][2]}---" //---null---
echo "---${matcher[1][3]}---" //---null---
echo "---${matcher[2][1]}---" //---4.11---
echo "---${matcher[2][2]}---" //---null---
echo "---${matcher[2][3]}---" //---null---
matcher ? matcher[0][1] : null
}
1 | node { |
笔者是独立翻译实验,不允许转载
本节基于入门中介绍的信息, 应仅作为参考进行处理。有关如何在实际示例中使用Pipeline语法的详细信息, 请参阅本章的 Jenkinsfile 部分。在Pipeline插件的2.5 版中, Pipeline支持两个离散的语法, 下面是详细的。对于每个优点和缺点, 请参见语法比较。
正如在入门中所讨论的, Pipeline最基本的部分是步骤step。基本上, 步骤step告诉Jenkins该做什么, 并作为声明式和脚本式Pipeline语法的基本构造块。
有关可用步骤的概述, 请参阅Pipeline步骤参考, 其中包含内置的步骤的全面列表以及插件提供的步骤。
声明式Pipeline是Jenkins Pipeline的一个相对最近的加法, 它在Pipeline子系统的顶部提供了一种更简化和自以为是的语法。
所有有效的声明Pipeline必须包含在Pipeline块中, 例如:
在声明性Pipeline中有效的基本语句和表达式遵循与 Groovy 语法相同的规则, 但有以下例外情况:
声明性Pipeline中的节通常包含一个或多个指令Directive或步骤steps。
代理部分指定在Jenkins环境中执行整个Pipeline或特定阶段的位置, 具体取决于代理部分的放置位置。该节必须在Pipeline块内的 top-level 中定义, 但阶段级别的时候使用是可选的。
Required | Yes |
Parameters | Described below |
Allowed | In the top-level pipeline block and each stage block |
为了支持Pipeline作者可能有的各种用例, 代理部分支持几种不同类型的参数。这些参数可在Pipeline块的 top-level 或每个阶段指令中应用。
在任何可用的代理上执行Pipeline或阶段。例如: 代理任何agent any。
当在Pipeline块的 top-level 应用时, 将不会为整个Pipeline运行分配全局代理, 并且每个阶段部分都需要包含其自己的代理部分。例如: 代理无agent none。
使用所提供的标签, 在Jenkins环境中可用的代理上执行Pipeline或stage。例如: 代理 {标签为‘my-defined-label’}。
agent{ node {label ‘labelName’}}}和agent { label ‘labelName’}一样,node允许附加的选项,例如customWorkspace。
对给定的容器执行Pipeline或stage, 在预先配置为接受 Docker-based Pipeline的节点上或在与可选的标签参数匹配的节点上动态调配。Docker还可以选择接受参数, 其中可能包含直接传递到docker运行调用的参数, 以及一个 alwaysPull 选项, 即使图像名称已存在, 也会强制执行docker。例如 agent { docker ‘maven:3-alpine’}
使用源存储库中包含的Dockerfile生成的容器来执行Pipeline或stage。要想使用这个选项,Jenkinsfile必须通过Multibranch Pipeline或者Pipeline from SCM。通常这是源存储库根目录中的 Dockerfile:agent { dockerfile true }。如果在另一个目录中生成Dockerfile, 请使用dir选项:agent { dockerfile { dir ‘somSubDir’} }。您可以用additionalBuildArgs选项传递参数到docker build……命令中,像agent { dockerfile { additionalBuildArgs ‘–build-arg foo=bar’ } }。
这些是可以应用两个或更多agent实现的几个选项。除非明确说明, 否则不是必需的。
一个字符串。要在其上运行Pipeline或单个stage的标签。
此选项对于node,docker和dockerfile有效,并且对node是必需的。
一个字符串。运行Pipeline或单个stage,此agent在该自定义工作区中应用, 而不是默认。它可以是相对路径, 在这种情况下, 自定义工作区将位于节点的工作区根目录下, 或者是绝对路径。例如:
此选项对于node,docker和dockerfile有效。
布尔值, 默认为 false。如果为 true, 则在Pipeline的 top-level 上指定的节点上, 在同一工作区中, 而不是在新节点上运行容器。
此选项对docker和dockerfile有效, 并且仅在用于单个stage的agent时具有效果。
①在新创建的给定名称和标记(maven:3-alpine)容器中, 执行此Pipeline中定义的所有步骤。
Stage-level agent部分
在Pipeline的 top-level 定义agent none, 确保不会不必要地分配执行器。使用agent none 还强制每个stage部分包含其自己的agent部分。
使用此image在新创建的容器中执行此阶段中的步骤。
使用上一个阶段的不同image, 在新创建的容器中执行此阶段中的步骤。
post 部分定义将在Pipeline运行或阶段结束时运行的操作。post 部分中支持许多条件块: 始终(always)、已更改(changed)、失败(failure)、成功(success)、不稳定(unstable)和中止(aborted)。这些块允许在Pipeline运行或阶段的末尾执行步骤, 具体取决于管线的状态。
Required | No |
---|---|
Parameters | None |
Allowed | In the top-level pipeline block and each stage block |
无论Pipeline运行的完成状态如何, 都将运行。
changed仅当当前Pipeline运行与以前完成的Pipeline的状态不同时才运行。
failure只有在当前Pipeline出现故障状态时才运行, 通常在 web UI 中用红色指示表示。
success只有在当前Pipeline具有成功状态时才运行, 通常在 web UI 中用蓝色或绿色表示。
unstable只有在当前Pipeline的状态不稳定时才运行, 通常是由测试失败、代码违规等引起的。通常用黄色指示在 web UI 中表示。
aborted仅在当前Pipeline具有中止状态时运行, 通常是由于Pipeline被手动中止。通常在 web UI 中用灰色指示表示。
通常情况下, post部分应放置在Pipeline的末尾。
Post-condition块包含与steps部分相同的steps。
包含一个或多个stage指令的序列, stages部分是Pipeline所描述的大部分”work”的位置。至少建议stages对连续传递过程中的每个离散部分 ,如生成、测试和部署, 至少包含一个阶段指令。
Required | Yes |
---|---|
Parameters | None |
Allowed | Only once, inside the pipeline block |
steps部分在指定的stage指令中定义一系列一个或多个将要执行的steps。
Required | Yes |
---|---|
Parameters | None |
Allowed | inside each stage block |
环境environment指令指定一个键值对序列, 它将被定义为所有steps的环境变量, 或特定阶段的steps, 具体取决于环境environment指令在Pipeline中的位置。
此指令支持一种特殊的方法credentials(), 可用于通过Jenkins环境中的标识符来访问预定义的凭据。对于类型为机密文本的凭据, credentials()方法将确保指定的环境变量包含秘密文本内容。对于类型为标准用户名和密码的凭据, 指定的环境变量将被设置为用户名: 密码和另外两个环境变量将被自动定义:MYVARNAME_USR和MYVARNAME_PSW。
Required | No |
---|---|
Parameters | None |
Allowed | inside the pipeline block, or within stage directives |
Required | No |
---|---|
Parameters | None |
Allowed | Only Once, inside the pipeline block |
Required | No |
---|---|
Parameters | None |
Allowed | Only Once, inside the pipeline block |
布尔值参数,例如:parameters { booleanParam(name: ‘DEBUG_BUILD’, defaultValue: true, description: ‘’) }
提示:可供选择的综合清单尚待INFRA-1503完成。
triggers指令定义了Pipeline应 re-triggered 的自动方式。对于与源 (如 GitHub 或 BitBucket) 集成的管线, 可能不需要triggers, 因为 webhooks-based 集成可能已经存在。当前可用的触发器有 cron、pollSCM 和upstream。
Required | No |
---|---|
Parameters | None |
Allowed | Only Once, inside the pipeline block |
接受一个 cron 样式的字符串来定义Pipeline应 re-triggered 的规则间隔。例如:triggers { cron(‘H */4 1-5’) }。
接受一个 cron 样式的字符串来定义一个规则的时间间隔, 詹金斯应该检查新的源更改。例如:triggers { pollSCM(‘H */4 1-5’) }。
接受一个逗号分隔的作业字符串和一个阈值。当字符串中的任何作业以最小阈值结束时, 管线将被 re-triggered。例如:triggers { upstream(upstreamProjects: ‘job1,job2’, threshold: hudson.model.Result.SUCCESS) }。
提示:pollSCM触发器只在Jenkins版本2.22或者以后有效。。
stage指令进入stages部分,应该包含一个steps部分,一个可选的agent部分,或者其它特殊平台指令。实际上, Pipeline所完成的所有工作都将在一个或多个stage指令中进行包装。
Required | At least one |
---|---|
Parameters | One mandatory parameter, a string for the name of the stage. |
Allowed | Inside the stages section |
定义用于自动和放置PATH的工具的部分。如果agent none指定, 则忽略此项。
Required | No |
---|---|
Parameters | None |
Allowed | Inside the pipeline block or a stage block. |
when指令允许Pipeline根据给定的条件来决定是否执行stage。when指令必须包含至少一个条件。如果when指令包含多个条件时, 所有子条件必须返回 true 才能执行阶段。这与子条件嵌套在allof条件下的情况相同 (请参见下面的示例)。
可以使用嵌套条件构建更复杂的条件结构: not、allOf或anyOf。嵌套条件可以嵌套到任意深度。
Required | No |
---|---|
Parameters | None |
Allowed | Inside a stage directive |
在所构建的分支与给定的分支模式匹配时执行stage,例如:when { branch ‘master’ }。注意:这仅仅在multibranch Pipeline有效。
特殊环境变量被设置给定值以后,执行stage,例如:when { environment name: ‘DEPLOY_TO’, value: ‘production’ }。
当指定的Groovy表达式计算为真时执行stage,例如:when { expression { return params.DEBUG_BUILD } }。
当嵌套条件为假时执行stage。例如:when { not { branch ‘master’ } }。
当所有嵌套条件为真时执行stage。必须包含至少一个条件,例如:when { allOf { branch ‘master’; environment name: ‘DEPLOY_TO’, value: ‘production’ } }。
当至少一个前条件为真时执行stage。必须包含至少一个条件,例如:when { anyOf { branch ‘master’; branch ‘staging’ } }。
申明式Pipeline中有可能在里面申明许多嵌套的stages,这些stage将被并行执行。请注意, 一个stage必须有一个且只有一个steps或parallel。嵌套stage本身不能包含进一步的parallel stages, 但其他的行为与其他stages相同。包含parallel的任何stage都不能包含agent或tools, 因为没有steps,他们并不具有相关性。
此外, 您可以强制您的parallel stage全部被中止, 当其中一个失败, 通过增加’failFast true’到包含parallel的stage。
声明式Pipeline可能会使用Pipeline steps reference中记录的所有可用steps, 其中包含一个完整的steps列表, 其中添加了仅在声明性Pipeline中支持的下面列出的steps。
script step采用了一个脚本化的Pipeline块, 并在声明性Pipeline中执行。对于大多数用例, 脚本步骤在声明性Pipeline中应该是不必要的, 但它可以提供一个有用的”escape hatch”。 Script块和/或复杂性的脚本script块应该改为]共享库。
脚本化Pipeline (如声明式Pipeline) 建立在底层管线子系统的顶部。不像声明式, 脚本Pipeline是有效的通用 DSL 内置的 Groovy。Groovy 语言提供的大多数功能都可供脚本Pipeline用户使用, 这意味着它可以是一个非常富有表现力和灵活性的工具, 可以创作连续的交付Pipeline。
脚本化Pipeline从 Jenkinsfile 的顶部向下连续执行, 就像 Groovy 或其他语言中的大多数传统脚本一样。因此, 提供流控制依赖于 Groovy 表达式, 如 if/else 条件, 例如:
通过 Groovy 的异常处理支持, 可以管理另一种脚本式Pipeline流控制。当steps因任何原因而失败时, 它们抛出一个异常。错误处理行为必须使用 Groovy 中的 try/catch/finally 块, 例如:
正如在入门中所讨论的, Pipeline最基本的部分是”step”。从根本上讲, step告诉Jenkins该做什么, 并充当声明性和脚本化Pipeline语法的基本构建基块。
脚本Pipeline不引入特定于其语法的任何steps; Pipeline Steps reference, 其中包含Pipeline和插件提供的steps的全面列表。
为了提供耐用性, 这意味着运行Pipeline可以生存的Jenkins master重启, 脚本Pipeline必须序列化数据回master。由于这一设计要求, 一些 Groovy 成语如collection.each { item -> /* perform operation */ }不完全受支持。有关详细信息, 请参阅 JENKINS-27421 和 JENKINS-26481。
当Jenkins Pipeline首次创建时, Groovy 被选为基础。Jenkins长期推出了一个嵌入式的 Groovy 引擎, 为管理员和用户提供高级脚本功能。此外, Jenkins Pipeline的实现基于 Groovy 的基础上, 建立现在称为”脚本Pipeline” DSL。
由于它是一个功能完备的编程环境, 因此脚本化的管道为Jenkins用户提供了大量的灵活性和可扩展性。对于给定团队的所有成员来说, Groovy 学习曲线通常并不可取, 因此创建了声明性Pipeline, 为创作Jenkins Pipeline提供了更简单、更可用的语法。
两者基本上是相同的Pipeline子系统下面。它们都是作为”代码即管道(Pipeline as code)”的持久实现。他们都能够使用Pipeline内置的steps或由插件提供。都可以利用共享库。
然而他们不同的地方在句法和灵活性。声明性限制用户使用更严格和预先定义的结构, 使其成为更简单的连续传递Pipeline的理想选择。脚本提供的限制很少, 因为结构和语法的唯一限制往往是由 Groovy 本身定义的, 而不是任何特定于Pipeline的系统, 这使得它成为了电力用户和更复杂需求的理想选择。顾名思义, 声明性Pipeline是鼓励一个声明式编程模型。而脚本化的Pipeline遵循更命令性的编程模型。
笔者是独立翻译实验,不允许转载
Jenkins Pipeline有内嵌的文档和Snipper Generator,这些是开发Pipeline的关键资源。它们针对当前版本的Jenkins和安装的插件提供详细的帮助和信息。本节,我们讨论开发Jenkins Pipeline的其他工具和资源。
在实际运行之前,Jenkins能验证,或者lint一个申明式Pipeline。这些通常是Jenkins CLI命令或者带参数的HTTP POST请求完成的。我们推荐使用SSH interface。请参看Jenkins CLI文档怎样配置Jenkins才能安全的命令行存取。
下面是两个Pipeline Linter的例子。第一个示例显示了linter在传递无效Jenkinsfile时的输出, 该结果是缺少代理agent声明的一部分。
一般来说Pipeline是定义在经典的Jenkins Web界面上,或者通过提交Jenkinsfile到源代码库中。不幸的是, 这两种方法都不适合对Pipeline进行快速迭代或原型化。“Replay”特性允许在不更改Pipeline配置或创建新提交的情况下快速修改和执行现有Pipeline。
使用”Replay”特性:
•能被同一次运行多次调用—允许很容易的并行测试不同的特性
•能被在运行中的Pipeline调用执行—只要是包含正确Groovy语法的Pipeline,就能被重复播放。
•能引用共享库的代码同样是可修改的—如果一个引用了共享库的Pipeline运行,共享库的代码同样能被展示和修改。
•有语法错误的Pipeline不能被重新播放–这意味着无法查看其代码, 并且无法检索其中所做的任何更改。使用重播进行更重要的修改时, 请将更改保存到Jenkins外部的文件或编辑器中, 然后再运行它们。见JENKINS-37589。
•重播的Pipeline行为可能与由其他方法启动的运行不同–对于不属于多分支Pipeline的Pipeline, 提交信息可能会因原始运行和重播运行而异。见JENKINS-36453。
提示: Pipeline单元测试框架是Jenkins项目不支持的 third-party 工具。
Pipeline单元测试框架(Pipeline Unit Testing Framework)允许您在完全运行测试Pipeline和共享库之前对其进行单元检测。它提供了一个模拟执行环境, 其中实际的Pipeline步骤将替换为可用于检查预期行为的 mock 对象。新的和粗糙的边缘, 但承诺。该项目的自述文件包含示例和用法说明。
笔者是独立翻译实验,不允许转载
Pipeline被越来越多的组织和项目所使用,一些常用的模式开始出现。通常共享不同项目的Pipeline片段是非常有用的,能减少冗余,保持代码DRY(Don’t_repeat_yourself)。
Pipeline支持创建共享库,这些库能被外部源代码库定义,并能被装载到现有的Pipeline中。
共享库被一个名字,一个例如SCM的代码段定义,默认的版本是可选的。名字是一个短标识符,它能被用在脚本中。
版本可以是SCM能理解的任何东西;例如分支,标记和提交hashes等所有Git的东西你同样可以申明哪些脚本明确要求下面定义的库,或者默认是禁止的。更进一步,如果你在Jenkins配置中说明一个版本,你就能阻止脚本选择一个不同的版本。
说明SCM最好的方式就是使用SCM插件,插件已经被更新支持新的API,检查任意的命名版本。最新版本的Git和Subversion插件都支持这个模式;其他也应该这么做。
如果你的SCM插件没有被整合,你可以选择Legacy SCM,并且挑选已经提供的。在本例中,你需要在SCM配置的某个地方包含(include)${library.yourLibName.version},以便于在迁出代码阶段,插件能读取这个变量选择想要的版本。例如,对于Subversion,你能设置URI为svnserver/project/${library.yourLibName.version},然后使用例如trunk或者branches/dev或者tags/1.0。
共享库的目录结构如下:
src目录看起来像是标准的java代码目录。当执行Pipeline的时候,这个目录会被加到classpath。
vars目录保存脚本,脚本定义Pipeline可以存取的全局变量。每一个groovy的基本名应该是一个Groovy标识符,方便起见都是驼峰命名法。
这些目录中的Groovy原文件都与脚本Pipeline同样的”CPS transformation”。
resources目录允许libraryResource被外部库使用加载非Groovy文件。现在这个特性不支持内部库。
有好几个可以定义共享库的地方,取决于用户情况。Manage Jenkins » Configure System » Global Pipeline Libraries,在这里许多库都能被定义。
因为这些库是全局使用,系统中的任何Pipeline都可以用这些库中的功能。
这些库被认为是”可信任的”,他们能运行Java/Geoovy/Jenkins内部API/Jenkins插件或者第三方库的任何方法。这允许你以一个更高级别封装去包裹独立的不安全的APIs,供其他Pipeline使用。要知道,任何能提交文件到这个SCM库的人都能对Jenkins进行无限的存取。你需要全部/运行权限去配置库(通常权限被授予jenkins管理员)。
任何创建的目录都可以有和它相关联的共享库。这个机制允许特殊库对目录和子目录中的Pipeline作用范围。
目录为基础的库并不被认为是”可信任的”;它们在Groovy sandbox中的运行就像典型的Pipeline一样。
其他插件可以增加定义库的方法。例如插件github-branch-source提供一个GitHub组织目录项,这个目录项允许脚本不用任何附加配置就可以使用不受信任的库,例如github.com/someorg/somerepo。本例中,指定的GitHub库将被加载。
隐式加载的共享库允许Pipeline使用任意这样的库定义的类或者全局变量。存取其他的库,Jenkinsfile需要使用@Library标记,说明库的名字:
这些标记可以在Groovy脚本允许的任何地方。当引用类库(src目录)的时候,相应标记需要一个import表达式:
提示:对于只定义了全局变量的共享库,或者只需要一个全局变量的共享库,<a href="http://groovy-lang.org/objectorientation.html#<em>annotation">annotation</a> pattern <code>@Library('my-shared-library') _</code>
保持代码精炼是有用的。本质上,替代标记一个不必要的import
表达式,符号被标记。
不推荐import一个全局变量/函数,因为这将强迫编译器将字段和方法解释为静态(static)的。此种情况下的Groovy编译器会产生令人迷惑的错误信息。
在脚本的编译过程中, 库在开始执行之前被解析和加载。这使得 Groovy 编译器能够理解静态类型检查中使用的符号的含义, 并允许它们在脚本中的类型声明中使用, 例如
但是, 全局变量在运行时解析。
从Pipeline2.7 版: 共享的 Groovy 库插件中, 有一个新的选项用于在脚本中加载 (non-implicit) 库: 在生成过程中的任何时候动态加载库的一个library步骤。
如果你只对使用全局变脸函数感兴趣(从vars目录),语法是相当的简单:
此后, 脚本将可以访问该库中的任何全局变量。
也可以使用src/目录中的类, 但更棘手。虽然 @Library 注释在编译之前已经准备脚本的classpath, 但在遇到library step时, 脚本已经编译完毕。因此, 您无法从库中import或以其他方式,静态引用类型。
但是, 您可以动态地使用库类 (没有类型检查), 通过完全访问来自library step的返回值的限定名。可以使用类似于 Java 的语法调用静态static方法:
您还可以访问静态字段, 并调用构造函数, 就好像它们是名为new的静态方法一样:
当隐式加载被选中的时候,共享库使用默认版本,否则pipeline只通过名字引用库,例如@Library(‘my-shared-library’)。如果默认版本没有定义,Pipeline必须说明一个版本,例如@Library(‘my-shared-library@master’)。
如果共享库配置中的”允许默认版本被覆盖”被选中,@Library标记有可能覆盖库的默认版本。如果可能的话,这同样允许隐式加载的库能被加载不同的版本。
当时使用library step,你可以说明一个版本:
由于这是一个常规步骤(library step), 因此可以计算该版本而不是常量, 就像注释;例如:
将使用与 multibranch Jenkinsfile 相同的 SCM 分支加载库。另外一个例子中,你可以通过参数选择一个库:
注意:Library step可能不可以被用来覆盖一个隐式加载库的版本。它在脚本开始的时候已经加载,并且一个用名字加载的库不能被加载两次。
说明SCM最好的方式是使用SCM插件,插件已经被更新支持新的API去检查版本。在写本文时,Git和Subversion插件已经支持这个模式。
SCM插件有可能仍然通过Legacy SCM选项使用,因为插件还没有支持交心的共享库特性。本例中,包含${library.yourlibrarynamehere.version},在这里分支/tag/ref为了特殊的SCM插件被配置。这确保,在迁出库的源代码时,SCM插件能扩展它的变量去迁出合适的库版本。
如果你在Library step中仅仅指明一个库名字(可选的,在version后面@) ,Jenkins将寻找那个名字的预先配置的库。(或者加载一个github.com/owner/repo自动库)
但您也可以动态地指定检索方法, 在这种情况下, 不需要在Jenkins中预先定义库。例子如下:
最好使用Pipeline Syntax寻找你的SCM的精确的语法。
注意:在这些cases中,必须要说明库版本。
一般来说,任何合法的Groovy代码都可以使用。不同的数据结构,功能方法等,例如:
库的类不能明确的调用steps,例如sh或者git。但是, 它们可以在封闭类的范围之外实现方法, 进而调用Pipeline step,例如:
然后这个方法可以被脚本式Pipeline调用。
这个方法是有限制的,例如,它阻止一个父类的定义。
或者,在构造函数中,一套steps集合能用this被明确的传递到一个库类,或者仅仅一个方法:
当保存类的状态的时候,例如上面,类必须实现Serializable接口。
这确保在Jenkins中,一个使用类的Pipeline,像下面的例子,能挂起(suspend)和回复(resume)。
如果库需要存取全局变量,例如env,那么这些库应该被明确的传递到库类,或者方法中。
而不是将大量的变量从脚本式Pipeline传递到库中,
上面的例子展示了参数被传递到一个静态(static)方法,脚本式Pipeline按照如下方式调用:
在内部,vars目录中的脚本按要求实例化为单例。这允许在单个. groovy 文件中定义多个方法以方便使用。
声明式Pipeline不允许在脚本指令之外使用全局变量用法 (JENKINS-42360)。
① script在声明式Pipeline中访问全局变量所需的脚本指令。
注意:在Jenkins加载并使用该库作为成功的Pipeline运行的一部分之后, 在共享库中定义的变量将只显示在全局变量引用 (在Pipeline Syntax中)。
警告:
避免在全局变量中保留状态
避免使用交互或保留状态的方法来定义全局变量。改用静态类或实例化类的局部变量。
共享库还可以定义与内置步骤 (如 sh 或 gitgit) 类似的全局变量。在共享库中定义的全局变量必须用所有 lower-case 或法则命名, 以便通过管道正确加载。
例如, 要定义 sayHello, 应创建文件 vars/sayHello.groovy, 并应实现调用方法。调用方法允许以类似于步骤的方式调用全局变量:
Pipeline能引用和调用这个变量:
如果用块调用, 则调用方法将接收关闭。应显式定义该类型以阐明步骤的意图, 例如:
然后, Pipeline可以像任何接受块的内置步骤一样使用此变量:
如果您有很多类似的管道, 则全局变量机制提供了一个方便的工具来构建一个 higher-level 的 DSL 来捕获相似性。例如, 所有的詹金斯插件都是以同样的方式构建和测试的, 所以我们可以写一个名为buildPlugin的step。
假设脚本已作为全局共享库或文件夹级共享库加载, 结果 Jenkinsfile 将大大简化:
可以使用 third-party 的 Java 库, 通常是在 Maven 中心, 从受信任的库代码使用@Grab注释。有关细节, 请参阅Grape document, 但简单地把:
默认情况下, 第三方库被缓存在~/. groovy/grapes/Jenkins的master节点上。
外部库可以使用libraryResource step从resources/目录加载辅助文件。该参数是一个相对路径名, 类似于 Java 资源加载:。
该文件以字符串形式加载, 适合传递给某些 api 或使用 writeFile 保存到工作区。
最好使用一个唯一的包结构, 这样您就不会意外地与另一个库发生冲突。
如果您注意到在使用不受信任的库的生成中出现错误, 只需单击重播链接以尝试编辑其一个或多个源文件, 并查看所产生的生成是否按预期的方式工作。一旦你对结果满意,跟随Build状态页上的不同链接,应用不同的库变化,并保存到代码库中。
(即使为库请求的版本是一个分支, 而不是像标记这样的固定版本, 重播的生成将使用与原始生成完全相同的修订: 将不会再次签出库源。
受信任的库当前不支持重播。在重播过程中, 当前也不支持修改资源文件。
从2017年9月下旬发布的声明性1.2 开始, 可以定义声明性管道。也可在共享库中进行。下面是一个示例, 它将执行不同的声明管道, 具体取决于内部版本号是奇数还是偶数:
此时, 只能在共享库中定义整个Pipeline。这只能在”vars/*.groovy”,仅仅只能在一个方法调用中。在单个生成中只能执行一个声明性Pipeline, 如果尝试执行第二个Pipeline, 则生成结果将失败。
笔者是独立翻译实验,不允许转载
许多组织都是用Docker来统一编译和测试环境,提供一个发布应用的有效机制。从Pipeline 2.5版本或者更高,Pipeline内嵌支持同Jenkinsfile内Docker交互。
本章覆盖了使用Jenkinsfile内Docker基本使用,并不包括Docker的使用。关于Docker使用,请参考https://docs.docker.com/get-started/。
Pipeline较早被设计使用Docker images作为执行环境,执行一个单独的stage或者整个Pipeline,意味着用户可以定义他们的Pipeline需要使用的工具,不必手动配置代理。实践上,任何工具都能被打包到Docker container中,能被一个Jenkinsfile容易使用。
当Pipeline执行的时候,Jenkins自动开启特定的container,执行里面定义的steps。
许多编译工具会下载外部依赖,并把它们缓存在本地。因为containers初始化的时候是干净的文件系统,这能导致较小的Pipelines,因此他们并不能充分利用硬盘缓存的优势。
Pipeline支持添加定制化参数,参数能被传入到Docker中,允许用户指定特定的Docker Volumes去挂载,Volumes能被用来缓存代理上的数据。下面的例子将在Pipeline运行的时候,缓存~/.m2,可以避免重复下载依赖。
注意:实测表明
使用本地机器的maven repo,不用再单独下载的技巧,要点在于
(1).启动docker的时候就要传入repo
(2).启动编译命令的时候,要再传一次repo,否则mvn会单独下载依赖的包
现在非常普遍,代码依赖多个,不同的技术。例如一个库既有Java-based的后端API实现,也有JavaScript基础的前端实现。组合Docker和Pipeline,允许Jenkinsfile通过agent{}在不同阶段使用多种技术。
对于那些需要特定执行环境的项目,Pipeline同样支持在Dockerfile中创建和运行容器(container)。同前面使用”off-the-shell”容器相反,使用代理{dockerfile true}语法将创建新的image,而不是从Docker Hub拉取。
重新使用前面的例子,用一个定制化的Dockerfile。
Dockerfile
把这个提交到源代码库的根目录,Jenkinsfile被改变去构建一个基于这个Dockerfile的容器,用这个容器运行定义好的steps。
agent { dockerfile true}语法支持许多其他选项,关于这些选项详见Pipeline Syntax部分。
Using a Dockerfile with Jenkins Pipeline
注意:笔者添加。
其他一些使用说明,请参见:https://github.com/jenkinsci/pipeline-model-definition-plugin/wiki/Syntax-Reference
其实本例的目的:仅仅只是制作了一个新的docker image,完全可以自己手动做,也可以使用linux shell脚本和docker配合使用做出来,不一定非要通过Jenkins的插件做。此方法的一个不好的地方就是:自己设置生成的image的名字以后,通过参数加进去,最终会出来两个一样的docker image,因为再生成的时候,必须要设置一个名字,哪怕是随机的名字。
参数如下:1
2
3
4
5
6agent { dockerfile true }
agent {
dockerfile {
additionalBuildArgs '-t node-svn:7-alpine'
}
}
默认情况下,Pipeline假设任何配置的代理都能运行基于Docker的Pipeline。对于Jenkins环境,有macOS,Windows或者其他不能运行Docker代理,默认设置可能有问题。Pipeline在Manage Jenkins页面(manage Jenkins–>Configuration)上提供一个全局选项。
注意:测试表明
此label只是为了区分在哪个node上运行,本例运行的node label为:linux,其实换成master应该也可以,如果不设置,就会寻找一个默认的本地的符合条件的node执行。
在Pipeline中使用Docker是运行服务/一套测试的一个有效方式,类似于sidecar模式,Docker Pipeline能在后台运行容器。使用sidecar方法,对于每一次Pipeline运行,Pipeline能有一个干净的容器。
考虑一个依赖于本地MySQL数据库的集成测试套件。使用插件docker-workflow插件实现的withRun方法,一个能运行MySQL作为sidecar的Jenkinsfile文件:
注意:本例笔者运行失败。
(1).本地必需安装有mysqladmin工具,否则执行失败。
(2).docker run -d -e MYSQL_ROOT_PASSWORD=my-secret-pw -p 3306:3306 mysql:5这样启动以后,确实发现container里面的mysql启动成功,但是如果使用docker run -d -e MYSQL_ROOT_PASSWORD=my-secret-pw -p 3306:3306 mysql:5 /bin/sh进去以后,发现mysql没有启动,或者说启动失败。
示例可以更进一步,同时使用两个容器。一个”sidecar”运行MySQL,另一个用Docker container links提供执行环境。
上面的示例使用withRun暴露的对象,withRun有运行的容器ID。用容器ID,Pipeline能创建一个链接,通过传递客制化的Docker参数到inside()方法。
Id属性对于查看正在运行容器的log同样有用:
注意:笔者实测,
执行的sh命令都是在container里面执行的。所以起了两个container。
创建一个Docker image,插件docker-workflow同样提供一个build()方法创建新的image,在Pipeline运行的时候,从代码库中的Dockerfile文件也能创建image。
使用docker.build(“my-image-name”))语法的一个主要好处:脚本是的Pipeline能使用返回值用于后面的Docker Pipeline调用,例如:
返回值能被用来保存Docker image到Docker Hub或者私有的Registry,通过push()方法,例如:
Image的tag属性常用方法是latest标记。push()方法接受一个可选的tag参数,允许Pipeline用不同的标记存储customImage,例如:
笔者注:
此类制作image,完全可以shell和docker独立做,如有必要,可以来此参照执行。
默认情况下,插件docker-workflow会和本地的Docker交互,典型的是通过/var/run/docker.sock。
选择一个不是非默认Docker server,例如Docker Swarm,withServer()方法可以使用。
通过传递一个URI,可选的Docker Server Certificate Authentication认证信息,方法如下:
注意:inside()和build()将不能同Docker Swarm server正常工作。
对于函数inside()正常执行,Docker server和Jenkins代理必须使用同样的文件系统,以便工作空间能被挂载。
现在Jenkins插件和Docker CLI都不能自动检测远端server运行的case;一个典型的现象就是嵌套sh命令出现错误,例如
当Jenkins检测到代理运行在Docker容器中的时候,它将自动传递–volumes-from参数到inside容器,确保它能同代理共享工作空间。
另外,一些Docker Swarm版本并不支持定制化的Registry。
默认情况下,docker-workflow插件使用默认的Docker Registry—Docker Hub。
为了使用定制化的Docker Registry,脚本是的Pipeline用户可以用withRegistry方法去包含steps,传递定制化的Registry URL,例如:
对于需要认证的Docker Registry,从Jenkins主页上添加用户名/密码项,并使用认证ID作为withRegistry()的第二个参数。
注意:此类是制作docker image然后保存到私有的registry,可以shell和docker实现,如有必要,来此参照制作。
笔者是独立翻译实验,不允许转载
在上一节中,实现了Jenkinsfile,这个文件能被迁入到源代码版本控制中。这部分涵盖了 Multibranch Pipeline的概念, 建立在Jenkinsfile基础上, 提供更多的动态和自动功能。
Multibranch Pipeline项目使得在同一个项目中不同的分支需要实现不同的Jenkinsfile。在一个Multibranch Pipeline项目中,Jenkins将为不同分支的Pipelines自动发现/管理/执行,不同的分支都包含一个Jenkinsfile文件。
这将减少人工Pipeline的创建和管理。
创建一个Multibranch Pipeline:
•在Jenkins home页上点击”new Item”。
•为你的Pipeline输入名字,选择Multibranch Pipeline,然后点击OK。
注意:Jenkins用Pipeline的名字在磁盘上创建目录。包含空格的Pipeline名字可能有未查到的bug,脚本不期望路径包含空格。
•添加一个Branch Source(例如Git),输入源代码库的地址。
•保存多分支Pipeline项目。
一旦保存,Jenkins将自动扫描代码库,为库中包含Jenkinsfile的每一个分支创建合适的项。
默认情况下,Jenkins不会自动索引用于分支添加或删除的存储库 (除非使用组织文件夹), 因此将 Multibranch Pipeline配置为定期索引在配置中通常很有用:
Multibranch Pipeline暴露了关于分支的附加信息,分支可以通过全局变量env创建,例如:
BRANCH_NAME
这个Pipeline将要被执行的分支,例如master分支。
CHANGE_ID
各种改变请求的标识符,例如a pull request号码/数字。
GitHub或者Bitbucket分支源,Multibranch Pipeline能被用来验证pull/change请求。这个功能被插件github-branch-source和插件cloudbees-bitbucket-branch-source提供。请查阅相关文档得到如何使用的进一步信息。
Organization Folder使Jenkins能监控整个GitHub组织或者Bitbucket Team/Project,自动为代码库创建新的Multibranch Pipeline项目,代码库包含分支和包含Jenkinsfile的pull request。
现在,这个功能仅仅存在于GitHub和bitbucket,功能分别被插件github-branch-source和插件cloudbees-bitbucket-branch-source提供。
笔者是独立翻译实验,不允许转载
这部分基于前面Getting Started所讲过的信息,介绍更有用的步骤(steps),一般模式,一些非试用的Jenkinsfile例子。
创建一个Jenkinsfile,然后把这个文件保存到源代码版本控制中(例如svn,git等),会带来许多好处:
Pipeline支持两种语法: 声明式 (在Pipeline2.5 中引入) 和脚本Pipeline。两者都支持构建持续集成的Pipeline。都可以被用来定义一个Pipeline,不管是在web UI上还是在Jenkinsfile文件中,一般认为最好的实践是创建一个Jenkinsfile文件,并放到源代码管理库中。
像前面Getting Started章节讨论的一样,Jenkinsfile文件是一个文本文件,包含一个Jenkins Pipeline定义。考虑如下部分,实现了一个三阶段的持续集成pipeline。
并不是所有的Pipelines都有同样的三个阶段,但是对于大多数项目来说,这是一个很好的开始。下面的部分将说明一个简单的Pipeline的创建和执行。
注意:假设已经为项目创建好了源代码版本库,并且一个Pipeline已经按照前面Getting Started步骤定义好了。
用一个支持Groovy语法高亮的文本编辑器,创建一个新的Jenkinsfile文件,放到项目的根目录下。
上面声明式的Pipeline例子包含实现一个持续集成pipeline最小的必须的结构。Agent指示符是必须的,告诉Jenkins为这个pipeline分配一个执行器和工作空间。没有agent指示符,不仅申明式的Pipeline不是合法的,而且它也不能做任何工作!默认情况下,agent指示符确保源代码库代码被签出,并且是有效的可以被后面阶段的步骤(steps)使用。
对于一个有效的申明式的Pipeline,stage指示符和steps指示符同样是必须的,因为它们告诉Jenkins执行什么,哪个阶段做什么事情。
对于脚本式Pipeline的更高级用法,上面例子中的node是至关重要的第一步,因为它为Pipeline申请了执行器和工作空间。确切的说,没有node,一个Pipeline不能做任何工作!在node里面,业务的第一个命令将是迁出源代码。因为Jenkinsfile文件是直接从源代码库中迁出,所以Pipeline提供了一个快速和容易的方式存取正确的源代码版本。
① checkout步骤将从源代码库中迁出代码;scm是一个特殊的变量,它将告诉checkout克隆特定的版本,然后触发Pipeline运行。
注意,笔者实测如下:
在脚本式Pipeline中
checkout([$class: ‘GitSCM’, branches: [[name: ‘*/master’]], doGenerateSubmoduleConfigurations: false, extensions: [], submoduleCfg: [], userRemoteConfigs: [[url: ‘https://gitee.com/roclli/simple-maven-project-with-tests.git']]])。
申明式Pipeline(Jenkinsfile中),采用checkout scm,会导致checkout两次,根据说明Specify where to obtain a source code repository containing your Groovy script. It will be checked out somewhere on the Jenkins master and used to load your Pipeline script. (If you wish to use other files from the same repository during your Pipeline, you will need to check them out separately on some slave; this checkout cannot be reused.)。
许多使用Pipeline的项目开始工作第一步都是”编译(Build)”阶段。通常情况下, Pipeline的此阶段将是源代码的组装、编译或打包。Jenkinsfile文件并不是替代现有的编译工具例如GNU/Make,Maven,Gradle等等,但可以看作是一个粘合层, 以粘合绑定(bind)项目开发的多个阶段生命周期 (构建、测试、部署等) 一起。
Jenkins有许多插件,可以调用几乎所有的构建工具,但是这个例子将简单的使用shell步骤(sh)调用make命令。sh假设系统是Unix/Linux,如果是Windows系统,将会使用bat。
① sh调用make命令,将一直运行,如果命令返回0。任何非0的返回值都将使Pipeline失败。
② archiveArtifacts捕获模式匹配(**/target/*.jar)文件,并且把他们保存到Jenkins的master中。
提示:存档文件不是使用外部文件资料库 (如 Artifactory 或Nexus) 的替代品, 应该只考虑基本的报告和档案存档。
运行自动测试是任何成功持续集成的关键组件。因此Jenkins有许多测试记录,报告,可视化插件。一般来说, 当有测试失败, 它是有用的,Jenkins会记录web UI中的报告和可视化失败。下面的示例使用junit步骤, 由junit插件提供。
下面的例子,如果运行失败,Pipeline被标记不稳定”unstable”,在网页上用一个黄色的球标记。基于记录的测试报告,Jenkins同样提供历史趋势分析和可视化报告。
① 使用内置的shell条件(sh ‘make || true’)确保sh总是有一个0的返回值,这样使得junit机会去捕获和处理测试报告。或者使用下面的处理失败[handing-failures]部分。
② Junit捕获并关联Junit XML文件,模式匹配(**/target/*.xml)
注意:此处笔者测试下来是有出入的,区别在于脚本式Pipeline
部署可能意味着各种步骤, 具体取决于项目或组织的要求,可能是从将生成的文件发布到 Artifactory 服务器的任何东西, 或者将代码推送到生产系统。
在示例Pipeline的这个阶段,”Build”和”Test”阶段都已成功执行。因此发布阶段假设前面的阶段已经成功执行,否则Pipeline将退出。
① 读取currentBuild.result变量允许Pipeline判断是否有失败。这种情况之下,值是UNSTABLE。
假设示例的Jenkins Pipeline都被成功执行,每一个成功的Pipeline部分都将会关联到存档文件,测试结果报告和控制台输出。
脚本式的Pipeline能包含条件测试(像上面所示),循环(loop),try/catch/finally块,甚至函数。接下来的部分将详细讲解脚本式Pipeline语法的高级用法。
注意:笔者测试下来
(1).期间发现过无法读取currentBuild.result变量,此时提示” Cannot get property ‘currentBuild’ on null object”,故忽略此处的Deploy。env变量也是如此提示:Cannot get property ‘env’ on null object。最后实测发现:
脚本式Pipeline(web UI上面输入),可以正确输出变量值,如下方式${env.BUILD_NUMBER}
申明式Pipeline(Jenkinsfile),可以正确输出变量值,如下方式${env.BUILD_NUMBER}
可以正确用echo输出值,也可以用if判断值。代码如下:1
2
3
4
5
6
7
8echo "---${env.BUILD_NUMBER}---"
echo "---${currentBuild.result}---"
if(currentBuild.result == null || currentBuild.result == 'SUCCESS') {
echo "---currentBuild.result is:${currentBuild.result}------"
}
else {
echo "---currentBuild.result is:${currentBuild.result},so, will make publish"
}
得到的结果如下几种情况:
Cases结果 | 全局变量 | 备注 |
---|---|---|
1个skip,其他全对 | 61/null | |
1个error,其他全对 | 60/UNSTABLE | 根据前面说明,只要有失败,就是UNSTABLE |
全部正确 | 59/null |
(2).申明式Jenkins Pipeline(Jenkinsfile)中,使用steps有报错提示:
WorkflowScript: 31: Expected a step @ line 31, column 17.。解决办法:添加script符号1
2
3
4
5
6
7
8
9
10
11
12
13
14stage('Deploy'){
steps {
script{
echo "---${env.BUILD_NUMBER}---"
echo "---${currentBuild.result}---"
if(currentBuild.result == null || currentBuild.result == "SUCCESS") {
echo "---currentBuild.result is:${currentBuild.result}------"
}
else {
echo "---currentBuild.result is:${currentBuild.result},so, will make publish"
}
}
}
}
Jenkins Pipeline使用与 Groovy 相同的规则来进行字符串插值。Groovy的字符串插值规则可能会让这个语言的初学者感到混乱。因为Groovy支持申明一个字符串使用单引号或者双引号,例如:
只有后一个字符串将支持基于美元符号 ($) 的字符串插值, 例如:
运行结果是:
笔者加:测试运行结果如下:1
2Hello Mr. ${username}
I said, Hello Mr. Jenkins
对于Pipeline的高级特性,理解怎样使用字符串插值是很重要。
Jenkins Pipeline通过全局变量env暴露了许多环境变量,env可以在Jenkindfile中任何地方使用。Jenkins中完整的环境变量列表请参见:localhost:8080/pipeline-syntax/globals#env(假设Jenkins运行在本地的8080端口),环境变量包括:
当前的build ID,同BUILD_NUMBER一致。
被执行的项目的名字,例如foo或者foo/bar。
Jenkins的全URL,例如example.com:port/Jenkins/(注意:只有”System COnfiguration”中设置Jenkins URL以后才有效)
引用或使用这些环境变量可以实现, 如访问Groovy 映射, 例如:
在申明式Pipeline或者脚本式Pipeline中设置环境变量是不同的。
申明式Pipeline支持环境申明,而脚本式Pipeline必须使用withEnv。
① 一个environment申明使用在pipeline块中,将对pipeline中的所有step有效。
② 在stage中的environment申明将只对stage内的step有效。
声明式Pipeline支持现成的参数, 允许Pipeline接受用户通过参数指令在运行时指定参数。在脚本式Pipeline中配置参数,可以通过properties实现,关于properties我们可以在Snippet Generator中找到。
如果想要配置你的Pipeline接受Build With parameters的参数,那些参数是可以通过params变量读取。
假设一个名叫Greeting的字符串变量被配置在Jenkinsfile中,那么可以通过${params.Greeting}读取。
注意:笔者测试如下:本例如果是script(web UI上直接输入脚本方式)执行,会出现参数输入
如果是Jenkisnfile(声明式执行),会直接使用默认值Hello。
申明式Pipeline支持健壮的错误处理,默认通过它的post section处理,post section允许申明许多不同的”post conditions”,例如always,unstable,success,failure,和changed。Pipeline
语法(Pipeline Syntax)部分提供更多的细节,怎样使用不同的post情况。
脚本式Pipeline依赖于Groovy内嵌的try/catch/finally语法处理Pipeline执行期间出现的错误。
在上面的测试例子中,sh被修改永远不返回非0值(sh ‘make check || true’)。这个方法意味着接下来的阶段需要检查currentBuild.result去判断是否存在错误发生。
另一种处理此问题的方法是使用一系列try/finally 块, 它可以保留Pipeline中故障的早期退出行为, 同时仍使 junit 有机会捕获测试报告:
注意:笔者实测,申明式添加了post;其次mail的各参数,必须用单引号括起来。
申明式(Jenkinsfile)邮件发送失败,以下两种方式都失败(脚本式Pipeline无邮件部分):
如果前面执行成功,那么就不会执行post里面的failure部分。
在所有前面的例子中,只使用一个代理。这意味着Jenkins分配的所有执行器,只有一个是有效的,无论它是如何被标记和配置。Pipeline允许在一个Jenkinsfile中使用多个代理,这对于高级的用户案例是很帮助的,例如在多个平台上执行builds/tests。
在下面的例子中,Build阶段在一个代理上执行,build的结果将被两个子代理重用,在test阶段,标记linux和windows的两个代理是相互独立的。
① stash允许捕获模式匹配的文件(/target/.jar),只在同一个Pipeline其他地方重用。一旦Pipeline完成了执行,stash捕获的文件将被Jenkins master删除。
② 在agent/node中的参数允许任何有效的Jenkins标签表达式。查阅”Pipeline Syntax” 部分可以更详细的信息。
③ unstash 将从Jenkins主机中检索stash到Pipeline的当前工作区中。
④ bat脚本允许在windows平台上执行脚本。
注意:笔者实测
(1).首先需要配置windows slave node,关于windows slave,需要配置master上已有的需要用到的环境变量:
(2).首先分别修改两个node的label为linux(master)和windows(slave)。
Pipeline遵循Groovy语言约定, 允许在方法参数周围省略括号。
许多Pipeline steps还使用命名参数语法作为在Groovy中创建映射的简写形式, 它使用语法 [key1: value1、key2: value2]。使语句像以下功能等效:
方便起见,当参数只有一个参数的时候,参数名可以省略,例如:
脚本Pipeline是一个domain-specific语言,基于Groovy,大多数的Groovy语法都能不用修改被用在脚本Pipeline中。
上面部分中的示例在一个线性序列中跨两个不同的平台运行测试。实践中,如果make check需要花30分钟执行完,”Test”阶段将花费60分钟完成。
幸运的是,Pipeline具有内置的功能, 用于并行执行Pipeline脚本部分, 并在恰当命名的并行(parallel)步骤中实现。
重构上面的示例以使用并行(parallel)步骤:
不像原先的在linux和windows上线性执行,他们现在可以并行执行。
tag:
缺失模块。
1、请确保node版本大于6.2
2、在博客根目录(注意不是yilia根目录)执行以下命令:
npm i hexo-generator-json-content --save
3、在根目录_config.yml里添加配置:
jsonContent: meta: false pages: false posts: title: true date: true path: true text: false raw: false content: false slug: false updated: false comments: false link: false permalink: false excerpt: false categories: false tags: true