任何 Groovy 有效的代码都可以。例如不同的数据结构、工具方法:
// src/org/foo/Point.groovy package org.foo // point in 3D space class Point { float x,y,z }
在库类中(src/),不能直接调用步骤(比如 ssh、git 等等)。然而他们可以实现方法,需要在封闭的类的范围外部,在其中调用步骤:
// src/org/foo/Zot.groovy package org.foo def checkOutFrom(repo) { git url: "git@github.com:jenkinsci/${repo}" } return this
然后在 Pipeline Script 中调用:
def z = new org.foo.Zot() z.checkOutFrom(repo)
这种方法有局限性;例如,它阻止父类的声明。
另外可以通过 this 关键字将步骤传递到类中。可以在构造器中,也可以是一个方法:
// src/org/foo/Utilities.grovvy package org.foo class Utilities implements Serializable { def steps Utilities(steps) { this.steps = steps } def mvn(args) { steps.sh "${steps.tool 'Maven'}/bin/mvn -o ${args}" } }
在类上保存状态时,像上面那样,类必须实现 Serializable 接口,这保证使用该类的 Pipeline 可以在 Jenkins 中休眠和恢复。
调用定义的类:
@Library('utils') import org.foo.Utilities def utils = new Utilities(this) node { utils.mvn 'clean package' }
如果要使用全局变量(env),应该明确的“传入类”(使用构造器)或“传入方法”(使用方法参数)中。手段是类似的:
package org.foo class Utilities { static def mvn(script, args) { script.sh "${script.tool 'Maven'}/bin/mvn -s ${script.env.HOME}/jenkins.xml -o ${args}" } }
上面的代码将环境变量传入静态的方法中,然后在下面的脚本化Pipeline中调用:
@Library('utils') import static org.foo.Utilities.* node { mvn this, 'clean package' }
注意,不建议将env中的参数取出来挨个传入函数中。
在内部,在 vars/ 中的脚本“按需要”实例化为单例。
为方便起见,这允许在单个.groovy文件中定义多个方法。例如:
// vars/log.groovy def info(message) { echo "INFO: ${message}" } def warning(message) { echo "WARNING: ${message}" } // In Jenkinsfile @Library('utils') _ log.info 'Starting' log.warning 'Nothing to do!'
注意,如果您希望在全局中使用某个字,用于保存状态,请将其注解为:
@groovy.transform.Field def yourField = [:] def yourFunction....
声明式的 Pipeline 不允许在 script 块外进行对象的方法调用(JENKINS-42360),需要放在 script 中使用:
// In Jenkinsfile @Library('utils') _ pipeline { agent none stage ('Example') { steps { // log.info 'Starting' // 该命令会失败,因为它在 script 块之外 script { // 需要使用 script 块来访问全局变量 log.info 'Starting' log.warning 'Nothing to do!' } } } } // 定义在共享库中的变量要在 Global Variables Reference (在 Pipeline Syntax 中)中显 // 示,前提是Jenkins在一次成功的Pipeline运行中加载并使用该库 // 最佳实践:避免在全局变量中保留状态 // 避免定义“与方法交互或保留状态的”全局变量。应该使用“静态类”或“实例化类的局部变量”
共享库还可以定义全局变量,其行为类似于内置步骤(例如 sh、git 等等)。在共享库中定义的全局变量必须使用所有全小写或驼峰命名,以便通Pipeline 正确加载。
例如,要定义sayHello()步骤,应创建vars/sayHello.groovy文件,并应实现call()方法。call()方法允许以类似于步骤的方式调用全局变量:
// vars/sayHello.groovy def call(String name = 'human') { // Any valid steps can be called from this code, just like in other // Scripted Pipeline echo "Hello, ${name}." }
然后 Pipeline 将能够引用和调用此变量:
sayHello 'Joe' sayHello() /* invoke with default arguments */
如果使用块调用,则call()方法将接收 Closure 对象。应明确定义类型,以阐明该步骤的意图,例如:
// vars/windows.groovy def call(Closure body) { node('windows') { body() } }
然后 Pipeline 可以使用此变量,如同接受块的任何内置步骤一样:
windows { bat "cmd /?" }
如果你有很多大致相似的 Pipeline,全局变量机制提供了便利的工具,用于构建一个捕获相似性的更高级别的 DSL。
例如,所有 Jenkins Plugin 都以相同的方式构建和测试,因此我们可以编写名为 buildPlugin 的步骤:
// vars/buildPlugin.groovy def call(Map config) { node { git url: "https://github.com/jenkinsci/${config.name}-plugin.git" sh 'mvn install' mail to: '...', subject: "${config.name} plugin build", body: '...' } }
假设脚本已作为全局共享库(或文件夹级共享库)加载,最后产生的 Jenkinsfile 将变得非常简单:
// Jenkinsfile (Scripted Pipeline) buildPlugin name: 'git'
还有使用 Groovy 的 Closure.DELEGATE_FIRST 的“构建器模式”技巧,它允许 Jenkinsfile 看起来更像配置文件而不是程序,但这更复杂且容易出错,不建议使用。
可以从信任的库代码中使用第三方的 Java 库,一般可以在 Maven Central 中找到,从受信库代码中使用@Grab注解。
有关详细信息,请参阅 Grape 文档,但只需输入:
@Grab('org.apache.commons:commons-math3:3.4.1') import org.apache.commons.math3.primes.Primes void parallelize(int count) { if (!Primes.isPrime(count)) { error "${count} was not prime" } // … }
在默认情况下,第三方库会被缓存,在 Jenkins 主节点的~/.groovy/grapes/中。
外部库可使用 libraryResource 来加载在 resources/ 中的资源。参数是相对路径,类似于在 Java 中资源加载:
def request = libraryResource 'com/mycorp/pipeline/somelib/request.json'
该文件内容作为字符串加载,可以传入某些 API 中,或者使用 writeFile 保存到工作目录中。
建议,在 resources/ 中使用唯一的包结构来保存文件,防止和其他的类库冲突。
如果发现使用不受信任的库在构建中出现错误,只需单击“Replay”链接以尝试编辑其一个或多个源文件,并查看生成的构建是否按预期运行。对结果感到满意后,请从构建的状态页面中按照 diff 链接,将 diff 应用到库存储库并提交。
(即使为库请求的版本是分支,而不是像标记这样的固定版本,Replay版本将使用与原始版本完全相同的版本:库源码不会再次检出。)
目前,受信任的库不支持 Replay 。Replay 期间当前也不支持修改资源文件。
从2017年9月下旬发布的 Declarative 1.2 开始,也可以在共享库中定义 Declarative Pipelines 。这是一个示例,它将执行不同的声明性管道,具体取决于构建号是奇数还是偶数:
// vars/evenOrOdd.groovy def call(int buildNumber) { if (buildNumber % 2 == 0) { pipeline { agent any stages { stage('Even Stage') { steps { echo "The build number is even" } } } } } else { pipeline { agent any stages { stage('Odd Stage') { steps { echo "The build number is odd" } } } } } } // Jenkinsfile @Library('my-shared-library') _ evenOrOdd(currentBuild.getNumber())
到目前为止,只能在共享库中定义整个 Pipeline 。这只能在 vars/*.groovy 中完成,并且只能在 call() 中完成。在单个构建中只能执行单个 Declarative Pipeline ,如果您尝试执行第二个,那么构建将因此失败。
「Jenkins Pipeline」- 定义共享库
「Jenkins Pipeline」- 在 Jenkinsfile 中使用共享库