【问题标题】:Jenkins Shared Library delegation errorJenkins 共享库委托错误
【发布时间】:2017-05-31 23:03:15
【问题描述】:

我有一个带有以下文件的 Jenkins shared library

vars/testlib.groovy

def foo() {
    echo 'foo'
}

def bar(body) {
    body.delegate = [:]
    body.resolveStrategy = Closure.DELEGATE_FIRST
    body()
}

还有一个流水线脚本如下:

Jenkins 文件

library 'testlib@master'

testlib.foo()
testlib.bar {
    testlib.foo()
}

我得到以下输出:

[Pipeline] echo
foo
[Pipeline] End of Pipeline
java.lang.NullPointerException: Cannot invoke method foo() on null object

由于某种原因,传递给testlib.bar 的闭包不再看到testlib。只有当决议策略有利于代表时才会发生这种情况;如果我使用 OWNER_ONLYOWNER_FIRST 它可以工作。如果我在委托中提供testlib,它也可以工作,无论是通过在地图中设置它还是通过设置body.delegate = body.owner,如果我通过在闭包中引用owner.testlib.foo来避免解决方案,它也可以工作。此外,这只发生在库代码中;如果我只是在 Jenkinsfile 中创建一个测试类,它就可以正常工作。

似乎解决策略是检查委托,而委托不提供该属性,它会立即失败,而无需费心接下来检查所有者。我做错了吗?

【问题讨论】:

  • 我注意到一般来说,带有函数的 var 文件会让你在 Jenkins 管道及其单元测试中陷入一些黑暗的角落(无法解释和错误的行为)。我也必须使用明确的this 引用来解决其中一些问题(与owner 相同,但this.foo() 看起来比owner.foo() 更干净)。

标签: jenkins groovy jenkins-pipeline


【解决方案1】:

我无法准确解释 Jenkins 管道中的 Groovy 闭包委托发生了什么,但我遇到了类似的问题,我将其修复如下:

vars/foo.groovy:

def call() {
    echo 'foo'
}

vars/bar.groovy:

//
// Something like:
//
// bar {
//    script = {
//        foo()
//        return 'Called foo'
//    }
// }
//
def call(body) {
    def config = [:]
    body.delegate = config
    body.resolveStrategy = Closure.DELEGATE_FIRST
    body()

    // In the bar DSL element
    echo 'I am bar'

    // Expecting a script element as a closure. The insanceof needs script approvals
    //assert config.script != null, 'A script element was not supplied'
    //assert config.script instanceof Closure, 'The script element supplied must be a closure'

    // Call the script closure
    config.script.delegate = this
    config.script.resolveStrategy = Closure.DELEGATE_FIRST
    def result = config.script.call()

    // Returning the script result
    return result
}

Jenkins 文件:

library 'testlib@master'

def result = bar {

    script = {

        foo()

        return 'Called foo'
    }

}

echo "result from bar: ${result}"

Jenkins 输出:

[Pipeline] echo
I am bar
[Pipeline] echo
foo
[Pipeline] echo
result from bar: Called foo
[Pipeline] End of Pipeline
Finished: SUCCESS

只需考虑作为某些配置传递的 'bar' DSL 闭包主体,这些配置以诸如“x = y”之类的赋值形式传递。因此,将其中之一设置为由 bar() 的实现执行的闭包元素,然后您可以调用已定义的其他库元素。我的 Github 上有这个示例的代码:https://github.com/macg33zr/jenkins-pipeline-experiments。您可能还想尝试在 Jenkins 之外进行单元测试 - 我在这里有一个使用库 JenkinsPipelineUnit 的示例:https://github.com/macg33zr/pipelineUnit。如果在管道中做一些复杂的工作,我推荐这种单元测试方法,因为它会保持你的理智!

【讨论】:

    猜你喜欢
    • 2018-09-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-12
    相关资源
    最近更新 更多