Jenkins 管道将 vars/mavenBuildSpike.groovy 中的方法调用替换为赋值 "new NullObject"

Jenkins pipeline replaces method call in vars/mavenBuildSpike.groovy with assignment "new NullObject"

我在 vars/mavenBuildSpike.groovy 中有此代码:

@NonCPS
def createSqBuilder(SqBuildConfig config) {
    System.out.println("createSqBuilder=${config}")
    // The constructor contains code which the CPS transformer can't handle.
    def result = new SqBuilder(config)
    System.out.println("result=${result}")
    return result
}

def call(Closure body) {

    echo 'Creating ConfigBuilderWrapper'
    def wrapper = new ConfigBuilderWrapper()
    echo 'Calling apply()'
    wrapper.apply(body)
    echo 'Done processing closure'

    def config = wrapper.builder.build()
    echo "config=${config.dump()}"

    echo 'Creating builder'
    def builder = createSqBuilder(config)    // <<--- This doesn't work.
    echo "builder=${builder}"

    echo builder.dump()
    ...

输出为:

...everything looks good...
[Pipeline] echo
Creating builder
[Pipeline] echo
builder=null
[Pipeline] End of Pipeline
java.lang.NullPointerException: Cannot invoke method hashCode() on null object
    at org.codehaus.groovy.runtime.NullObject.hashCode(NullObject.java:174)
    at org.codehaus.groovy.runtime.DefaultGroovyMethods.dump(DefaultGroovyMethods.java:291)
    ...
    at org.kohsuke.groovy.sandbox.impl.Checker.checkedCall(Checker.java:159)
    at com.cloudbees.groovy.cps.sandbox.SandboxInvoker.methodCall(SandboxInvoker.java:17)
    at mavenBuildSpike.call(...\branches\master\builds\libs\sq-pipeline-library-spike\vars\mavenBuildSpike.groovy:33)
    at WorkflowScript.run(WorkflowScript:4)
    at ___cps.transform___(Native Method)
    ....

也就是说,方法 createSqBuilder 从未被调用,只是被赋值替换:def builder = new NullObject().

为什么会这样,我该如何解决?

在 运行 代码之前,Jenkins 将执行一个名为 "CPS transformation" 的 AST 转换。这个转换器不支持 Groovy 可以做的所有事情,它不会告诉你什么时候不能做 - 你只会得到奇怪或无用的错误消息 运行 结果代码,有时没有错误完全 - 构建将简单地失败,没有任何错误消息或任何堆栈跟踪。

CPS 转换似乎不喜欢用参数调用构造函数。这对我有用:

@Field // groovy.transform.Field
SqBuilder builder = new SqBuilder()

def call(Closure body) {
    ...
    //def builder = createSqBuilder(config)  // Doesn't work!!!
    builder.init(config) // This works; move the code from the constructor to the init() method.
    ...

需要 @Field 注释才能将局部变量 builder 转换为 Groovy 将在运行时创建的 class 的字段。这个 class 的名字是 WorkflowScript.

您也可以只键入 builder = new SqBuilder()(在变量名称前不键入或 def)。但这会将 builder 放入全局变量池(在 Groovy 中称为 "Binding")。 Jenkins 把它自己的东西放在那里(比如 envscm)所以当你安装更多插件时可能会导致奇怪的问题。

另请参阅: