我想在 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)
...
我正在解决同样的问题——我一直使用的 Groovy 中的构造,就像所有方便的收集方法一样,正在崩溃。
事实证明,在 Jenkinsfile 中,代码通过名为 Groovy CPS 的转换器传递,该转换器使 Jenkins 能够停止并重新启动某个阶段的执行。在此过程中,内容从其原始类中发生了变化,这会导致您在此处看到的错误,因为您将非 CPS 代码(Java lib 方法)与 CPS 代码(Jenkinsfile 中的任何内容)混合在一起。
这是此行为文档的链接...但我发现它不是很有帮助,所以我恢复到像在 Java(lambda 之前)中那样使用大量 for 循环:(
虽然它不像本地定义的闭包那么方便,但在某些情况下,您可以在全局范围内定义方法,然后通过 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)
}
}
}
}
}
考虑 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 能够暂停和恢复阶段的执行,但可能会导致类类型更改,从而导致您看到的错误。
根据任务的性质,您可以采用三种可能的方法:
恢复到更传统的 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}"
}
}
}
}
}
}
与之前的方法类似,全局定义方法,然后使用方法指针运算符引用它们可能是一种可行的解决方法。
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)
}
}
}
}
}
@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)
}
}
}
}
}