Jenkins共享库委派错误

Jenkins共享库委派错误,jenkins,groovy,jenkins-pipeline,Jenkins,Groovy,Jenkins Pipeline,我有一个Jenkins,文件如下: vars/testlib.groovy 和管道脚本,如下所示: 詹金斯档案 我得到以下输出: [Pipeline] echo foo [Pipeline] End of Pipeline java.lang.NullPointerException: Cannot invoke method foo() on null object 由于某种原因,传递给testlib.bar的闭包不再看到testlib。只有当解决策略有利于代表时,才会发生这种情况;如果我只

我有一个Jenkins,文件如下:

vars/testlib.groovy 和管道脚本,如下所示:

詹金斯档案 我得到以下输出:

[Pipeline] echo
foo
[Pipeline] End of Pipeline
java.lang.NullPointerException: Cannot invoke method foo() on null object
由于某种原因,传递给
testlib.bar
的闭包不再看到
testlib
。只有当解决策略有利于代表时,才会发生这种情况;如果我只使用
OWNER\u
或先使用
OWNER\u
它会工作。如果我在委托中提供
testlib
,它也可以工作,方法是在映射中设置它,或者只设置
body.delegate=body.owner
,如果我只引用闭包中的
owner.testlib.foo
来避免解析,它也可以工作。此外,这只发生在库代码中;如果我只是在Jenkins文件中创建一个测试类,它就可以正常工作


似乎如果解决策略是检查委托,而委托没有提供该属性,那么它会立即失败,而不必检查下一个所有者。我做错了什么吗?

我无法确切解释Jenkins管道中Groovy闭包委托的情况,但我遇到了类似的问题,我这样修复了它:

vars/foo.groovy:

def call() {
    echo 'foo'
}
//
// 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
}
library 'testlib@master'

def result = bar {

    script = {

        foo()

        return 'Called foo'
    }

}

echo "result from bar: ${result}"
[Pipeline] echo
I am bar
[Pipeline] echo
foo
[Pipeline] echo
result from bar: Called foo
[Pipeline] End of Pipeline
Finished: SUCCESS
vars/bar.groovy:

def call() {
    echo 'foo'
}
//
// 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
}
library 'testlib@master'

def result = bar {

    script = {

        foo()

        return 'Called foo'
    }

}

echo "result from bar: ${result}"
[Pipeline] echo
I am bar
[Pipeline] echo
foo
[Pipeline] echo
result from bar: Called foo
[Pipeline] End of Pipeline
Finished: SUCCESS
Jenkinsfile:

def call() {
    echo 'foo'
}
//
// 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
}
library 'testlib@master'

def result = bar {

    script = {

        foo()

        return 'Called foo'
    }

}

echo "result from bar: ${result}"
[Pipeline] echo
I am bar
[Pipeline] echo
foo
[Pipeline] echo
result from bar: Called foo
[Pipeline] End of Pipeline
Finished: SUCCESS
詹金斯输出:

def call() {
    echo 'foo'
}
//
// 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
}
library 'testlib@master'

def result = bar {

    script = {

        foo()

        return 'Called foo'
    }

}

echo "result from bar: ${result}"
[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上有此示例的代码:。您可能还想尝试在Jenkins之外进行单元测试-我这里有一个使用库JenkinsPipelineUnit的示例:。如果在管道中做一些复杂的工作,我建议使用这种单元测试方法,因为它将保持您的理智

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