无法在 Jenkinsfile 脚本中使用带有闭包的 Groovy 集合

问题描述 投票:0回答:3

我想在 Jenkinsfile 中使用带有集合的常规闭包:

// other parts removed for brevity
steps {
    script {
        def testList = ["item1", "item2", "item3"]
        testList.stream().map{it+".jpeg"}.each{println it}
    }
}
// other parts removed for brevity

但是,它给出了错误:

hudson.remoting.ProxyException: groovy.lang.MissingMethodException: No signature of method: java.util.stream.ReferencePipeline$Head.map() is applicable for argument types: (org.jenkinsci.plugins.workflow.cps.CpsClosure2) values: [org.jenkinsci.plugins.workflow.cps.CpsClosure2@248ba046]
Possible solutions: map(java.util.function.Function), max(java.util.Comparator), min(java.util.Comparator), wait(), dump(), grep()
    at org.jenkinsci.plugins.scriptsecurity.sandbox.groovy.SandboxInterceptor.onMethodCall(SandboxInterceptor.java:153)
    at org.kohsuke.groovy.sandbox.impl.Checker$1.call(Checker.java:161)
    at org.kohsuke.groovy.sandbox.impl.Checker.checkedCall(Checker.java:165)
    at com.cloudbees.groovy.cps.sandbox.SandboxInvoker.methodCall(SandboxInvoker.java:17)
    at WorkflowScript.run(WorkflowScript:49)
    at ___cps.transform___(Native Method)
    at com.cloudbees.groovy.cps.impl.ContinuationGroup.methodCall(ContinuationGroup.java:86)
    ...
jenkins groovy jenkins-pipeline jenkins-groovy
3个回答
0
投票

我正在解决同样的问题——我一直使用的 Groovy 中的构造,就像所有方便的收集方法一样,正在崩溃。

事实证明,在 Jenkinsfile 中,代码通过名为 Groovy CPS 的转换器传递,该转换器使 Jenkins 能够停止并重新启动某个阶段的执行。在此过程中,内容从其原始类中发生了变化,这会导致您在此处看到的错误,因为您将非 CPS 代码(Java lib 方法)与 CPS 代码(Jenkinsfile 中的任何内容)混合在一起。

这是此行为文档的链接...但我发现它不是很有帮助,所以我恢复到像在 Java(lambda 之前)中那样使用大量 for 循环:(


0
投票

虽然它不像本地定义的闭包那么方便,但在某些情况下,您可以在全局范围内定义方法,然后通过 Groovy 方法指针运算符依赖它们。

void doSomething(final String key, final String value) {
  println "${key} → ${value}"
}

pipeline {
  stages {
    stage('Process data') {
      steps {
        script {
          final Map myData = [
            "foo": "bar",
            "plop": "yo",
          ]
          myData.each(this.&doSomething)
        }
      }
    }
  }
}

0
投票

考虑 Jenkins 管道正在使用Groovy CPS 插件,它在编译程序时实现了连续传递样式转换。这可能会导致管道 CPS 方法不匹配

[ Jenkins Pipeline ]
       |
       | (executes)
       v
[ Jenkinsfile Script ]
       |
       | (transformed by)
       v
[ Groovy CPS Transformer ]
       |
       | (alters class types)
       v
[ CPS and Non-CPS Code Mixture ]

考虑到 Jenkinsfile 中的 Groovy CPS(连续传递风格)所发生的转换,调整编码风格以避免方法不匹配问题非常重要。
这种转换使 Jenkins 能够暂停和恢复阶段的执行,但可能会导致类类型更改,从而导致您看到的错误。

根据任务的性质,您可以采用三种可能的方法:

1.采用 Java(lambda 之前)风格:

恢复到更传统的 Java 编码风格,使用 for 循环,可以帮助避免这些 CPS 转换问题。

pipeline {
  stages {
    stage('Process data') {
      steps {
        script {
          final Map myData = [
            "foo": "bar",
            "plop": "yo",
          ]
          for (def entry : myData) {
            println "${entry.key} → ${entry.value}"
          }
        }
      }
    }
  }
}

2.带有方法指针运算符的全局方法使用

与之前的方法类似,全局定义方法,然后使用方法指针运算符引用它们可能是一种可行的解决方法。

void doSomething(final String key, final String value) {
  println "${key} → ${value}"
}

pipeline {
  stages {
    stage('Process data') {
      steps {
        script {
          final Map myData = [
            "foo": "bar",
            "plop": "yo",
          ]
          myData.each(this.&doSomething)
        }
      }
    }
  }
}

3.使用
@NonCPS
注释:

在某些情况下,使用

@NonCPS
注释 标记方法会有所帮助,尽管由于其局限性,它可能并不适合所有用例(即:“此类方法不得调用 CPS 转换的代码,例如 Pipeline 步骤” ).

@NonCPS
void printMap(Map myData) {
  myData.each { key, value ->
    println "${key} → ${value}"
  }
}

pipeline {
  stages {
    stage('Process data') {
      steps {
        script {
          final Map myData = [
            "foo": "bar",
            "plop": "yo",
          ]
          printMap(myData)
        }
      }
    }
  }
}
© www.soinside.com 2019 - 2024. All rights reserved.