共享库开发
任何 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 关键字
另外可以通过 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/)
在内部,在 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 /?"
}
定义更加结构化的 DSL
如果你有很多大致相似的 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/中。
加载资源文件(resources)
外部库可使用 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 中使用共享库