管道CPS方法不匹配

必威国际有限公司Jenkins Pipeline使用一个名为Groovy CPS的库来运行Pipeline脚本。虽然Pipeline使用Groovy解析器和编译器,但与常规Groovy环境不同的是,它在一个特殊的解释器中运行大部分程序。这使用延续传递样式(CPS)转换将代码转换为可以将其当前状态保存到磁盘(名为program.dat在你的构建目录中)并在Jenkins重启后继续运行。必威国际有限公司(你可以得到更多的技术背景管道:Groovy插件页面库页.)

虽然CPS转换通常对用户是透明的,但是对于可以支持的Groovy语言构造有一些限制,并且在某些情况下,它可能会导致违反直觉的行为。必威国际有限公司詹金斯- 31314使运行时尝试检测最常见的错误:从非cps转换的代码调用cps转换的代码。以下类型的东西是经过cps转换的:

  • 几乎所有您编写的Pipeline脚本(包括库中的脚本)

  • 大多数管道步骤,包括所有需要一个块的步骤

下面的事情是CPS-transformed:

  • 编译的Java字节码,包括

    • Java平台

    • 必威国际有限公司Jenkins核心和插件

    • Groovy语言的运行时

  • Pipeline脚本中的构造函数体

  • 控件标记的管道脚本中的任何方法@NonCPS注释

  • 一些流水线步骤不需要阻塞,而是立即起作用,例如回声属性

cps转换的代码可以调用非cps转换的代码或其他cps转换的代码,而非cps转换的代码可以调用其他非cps转换的但非cps转换的代码可能不叫CPS-transformed代码。如果您试图从非CPS转换的代码调用CPS转换的代码,那么CPS解释器将无法正确操作,从而导致不正确且常常令人困惑的结果。

常见问题及解决方案

管道的使用步骤从@NonCPS

有时用户将应用@NonCPS注释到方法定义,以便绕过该方法内部的CPS转换。这样做可以绕过Groovy语言覆盖中的限制(因为方法主体将使用本地Groovy语义执行),或者获得更好的性能(解释器会带来很大的开销)。但是,这些方法不能调用cps转换的代码,例如Pipeline步骤。例如,以下操作将不起作用:

@NonCPSdefcompileOnPlatforms() {linux窗户]。每个{arch ->节点(arch) {sh . sh使编译平台()

使用节点上海这种方法的步骤是非法的,行为将是异常的。运行此脚本的日志中的警告如下所示:

期望调用workflowscript . compileon平台,但捕获节点

要修复这种情况,只需删除注释——不需要它。(长期使用Pipeline的用户可能认为它是,在修复之前必威国际有限公司詹金斯- 26481.)

使用cps转换的参数调用非cps转换的方法

一些Groovy和Java方法将复杂类型作为参数来支持动态行为。一个常见的例子是排序方法,它允许调用者指定用于比较对象的方法(必威国际有限公司詹金斯- 44924).的修复之后,Groovy标准库中的许多类似方法都可以正常工作必威国际有限公司詹金斯- 26481,但有些方法仍然不固定。例如,以下操作将不起作用:

defsortByLength列表<字符串> list){列表。toSorted{a, b ->整数.valueOf (a.length ()) .compareTo (b.length ())}}def排序= sortByLength ([3331444422])回波(sorted.toString ())

闭包传递给Iterable.toSortedCPS-transformed,但Iterable.toSorted本身并没有在内部进行cps转换,所以这不会像预期的那样工作。当前行为是调用的返回值toSorted将是对闭包的第一个调用的返回值。在本例中,结果是排序被设置为-1,日志中的警告是这样的:

期望调用java.util.ArrayList.toSorted,但最终捕获了org.jenkinsci.plugins.workflow.cp必威国际有限公司s.CpsClosure2.call

要解决这种情况,传递给这些方法的任何参数都不能进行cps转换。这可以通过封装有问题的方法(Iterable.toSorted在本例中)在另一个方法内部,并用@NonCPS,或者为闭包创建显式的类定义,并用注释该类上的所有方法@NonCPS

构造函数

有时,用户可能会尝试在Pipeline脚本的构造函数中使用cps转换的代码,例如Pipeline步骤。不幸的是,通过构造对象Groovy中的操作符不是可以通过cps转换的(必威国际有限公司詹金斯- 26313),所以这是行不通的。下面是一个在构造函数中调用cps转换方法的例子:

测试defx公共Test() {setX()}私人无效对于setX () {以下方式=1;}}defx =测试()。x回声$ {x

的建设测试是否会在构造函数调用时失败Test.setX因为对于setX为cps转换方法。运行此脚本的日志中的警告如下所示:

期望调用测试。 but wound up catching Test.setX

要解决这种情况,请确保从构造函数内部调用的Pipeline脚本中定义的任何方法都带有注释@NonCPS构造函数不调用任何Pipeline步骤。如果您必须从构造函数调用cps转换代码(如Pipeline步骤),则需要将与cps转换方法相关的逻辑移出构造函数,例如移到调用cps转换代码并将结果传递给构造函数的静态工厂方法中。

非cps转换方法的覆盖

用户可以在Pipeline Script中创建一个类,该类扩展了在Pipeline脚本外部定义的现有类,例如来自Java或Groovy标准库。当这样做时,子类必须确保任何覆盖方法都被注释了@NonCPS并且不要在内部使用任何cps转换的代码。否则,如果从非cps上下文调用重写方法将失败。例如,以下操作将不起作用:

测试@Override公共字符串toString () {返回测试}}defbuilder =StringBuilder() builder.append (测试())回波(builder.toString ())

调用cps转换的重写toString从非cps转换的代码,例如StringBuilder.append不被允许,并且在大多数情况下不会像预期的那样工作。运行此脚本的日志中的警告如下所示:

期望调用java.lang.StringBuilder.append,但最终捕获了Test.toString

要修复此情况,请添加@NonCPS注释到覆盖方法,并从该方法中删除任何使用cps转换代码的地方,例如Pipeline步骤。

闭包在里面GString

在Groovy中,可以在类中使用闭包GString这样闭包每次都被求值GString被用作字符串.然而,在Pipeline脚本中,这将不会像预期的那样工作,因为GString中的闭包将被cps转换。下面是一个例子:

defx =1defs =x =$ {- > xx =2回声(s)

对象中的闭包GString如本例中所示将不起作用。运行此脚本时,来自日志的警告如下所示:

期望调用WorkflowScript。回声but wound up catching org.jenkinsci.plugins.workflow.cps.CpsClosure2.call

要解决这种情况,用一个闭包替换原始的GString,该闭包返回一个使用正常表达式而不是闭包的GString,然后在使用原始闭包的地方调用闭包GString如下:

defx =1defS = {-> x =$ {x} x =2回声(s ())

假阳性

不幸的是,有些表达式即使执行正确,也可能错误地触发此警告。如果你遇到这样的情况,请提交一个新问题(在首次检查副本之后)并将组件设置为workflow-cps



这个页面有帮助吗?

请通过此提交您对此页的反馈快速形成

或者,如果您不想填写快速表单,您可以简单地说明您是否认为此页面有帮助?

是的没有


在这里.

Baidu