在 Jenkinsfile 中调用可变参数函数意外失败
Calling a variadic function in a Jenkinsfile fails unpredictably
上下文
我是 运行 Jenkins Windows,正在编写声明性管道。我正在尝试将多个命令放在单个 bat
步骤中,同时如果任何包含的命令失败,仍然会使该步骤失败。
这样做有双重目的。
- 最佳实践文档建议为每一件小事创建一个步骤可能不是最好的主意(也可以通过在批处理文件中添加更多东西来解决,但我的构建还没有那么大)
- 我想在 Visual Studio 命令提示符下执行一些命令,这是通过首先
call
ing 一个设置环境的 .bat
文件,然后执行任何必要的命令来实现的.
代码
我在 Jenkinsfile
中写了以下 Groovy 代码:
def ExecuteMultipleCmdSteps(String... steps)
{
bat ConcatenateMultipleCmdSteps(steps)
}
String ConcatenateMultipleCmdSteps(String... steps)
{
String[] commands = []
steps.each { commands +="echo ^> Now starting: ${it}"; commands += it; }
return commands.join(" && ")
}
problem/question
我无法让它可靠地工作。也就是说,在单个 Jenkinsfile
中,我可以对 ExecuteMultipleCmdSteps()
进行多次调用,其中一些会按预期工作,而另一些则会失败 java.lang.NoSuchMethodError: No such DSL method 'ExecuteMultipleCmdSteps' found among steps [addBadge, ...
我还没有在失败中找到任何模式。我认为只有在 warnError
块中执行时才会失败,但现在我在 dir()
块中也遇到了问题,而在另一个 Jenkinsfile
中,它工作正常。
这个问题似乎与 ExecuteMultipleCmdSteps()
是可变参数函数有关。如果我提供具有正确数量参数的重载,则使用该重载没有问题。
我在这里不知所措。非常欢迎您的意见。
失败的解决方案
在某些时候我认为它可能是 scoping/importing 的东西,所以我按照 的建议将 ExecuteMultipleCmdSteps()
包含在 class(下面的代码)中。现在,该方法被称为 Helpers.ExecuteMultipleCmdSteps()
,结果是 org.jenkinsci.plugins.scriptsecurity.sandbox.RejectedAccessException: No such static method found: staticMethod Helpers ExecuteMultipleCmdSteps org.codehaus.groovy.runtime.GStringImpl org.codehaus.groovy.runtime.GStringImpl
public class Helpers {
public static environment
public static void ExecuteMultipleCmdSteps(String... steps)
{
environment.bat ConcatenateMultipleCmdSteps(steps)
}
public static String ConcatenateMultipleCmdSteps(String... steps)
{
String[] commands = []
steps.each { commands +="echo ^> Now starting: ${it}"; commands += it; }
return commands.join(" && ")
}
最少的失败示例
考虑以下几点:
hello = "Hello"
pipeline {
agent any
stages {
stage("Stage") {
steps {
SillyEcho("Hello")
SillyEcho("${hello}" as String)
SillyEcho("${hello}")
}
}
}
}
def SillyEcho(String... m)
{
echo m.join(" ")
}
我希望所有对 SillyEcho()
的调用都会导致 Hello
被回显。实际上,前两个成功,最后一个结果 java.lang.NoSuchMethodError: No such DSL method 'SillyEcho' found among steps [addBadge, addErrorBadge,...
奇怪的成功例子
考虑以下 groovy 脚本,它几乎等同于上面失败的示例:
hello = "Hello"
SillyEcho("Hello")
SillyEcho("${hello}" as String)
SillyEcho("${hello}")
def SillyEcho(String... m)
{
println m.join(" ")
}
当粘贴到 Groovy 脚本控制台(例如 Jenkins 提供的控制台)时,成功(Hello
打印了三次)。
尽管我希望这个示例能够成功,但我也希望它的行为与上面失败的示例一致,所以我对这个示例有点纠结。
不确定这是否能回答您的问题,如果不能,请将其视为更大的评论。
我喜欢你如何从 C++ 中借用 'variadic functions' :)
然而,在 groovy 中有很多优雅的方法来处理这个问题。
试试这个:
def ExecuteMultipleCmdSteps(steps)
{
sh steps
.collect { "echo \> Now starting: $it && $it" }
.join(" && ")
}
pipeline {
agent any
stages {
stage ("test") {
steps {
ExecuteMultipleCmdSteps(["pwd", "pwd"])
}
}
}
}
这对我来说很好用:
[Pipeline] Start of Pipeline
[Pipeline] node
Running on Jenkins in /var/lib/jenkins/workspace/TestJob
[Pipeline] {
[Pipeline] stage
[Pipeline] { (test)
[Pipeline] sh
+ echo > Now starting: pwd
> Now starting: pwd
+ pwd
/var/lib/jenkins/workspace/TestJob
+ echo > Now starting: pwd
> Now starting: pwd
+ pwd
/var/lib/jenkins/workspace/TestJob
[Pipeline] }
[Pipeline] // stage
[Pipeline] }
[Pipeline] // node
[Pipeline] End of Pipeline
Finished: SUCCESS
您可能想像这样重写您的函数。
您提到的2个错误可能有不同的原因。
第一个“没有这样的 DSL 方法……”确实是一个范围界定方法,您自己找到了解决方案,但我不明白为什么重载在同一范围内起作用。
第二个错误,可以用这个解决。但是,对我来说,第二种方法的代码也可以正常工作。
感谢您添加失败和成功的示例。
我想您的问题是由于 String
和 GString
.
不兼容造成的
关于 运行 作为管道作业与 运行 Jenkins 脚本控制台中的脚本之间的差异,我假设基于此 Jenkins 脚本控制台并不严格类型引用或尝试根据函数签名转换参数。我的假设基于这个脚本,基于你的脚本:
hello = "Hello"
hello2 = "${hello}" as String
hello3 = "${hello}"
println hello.getClass()
println hello2.getClass()
println hello3.getClass()
SillyEcho(hello)
SillyEcho(hello2)
SillyEcho(hello3)
def SillyEcho(String... m)
{
println m.getClass()
}
这是我在 Jenkins 脚本控制台中得到的输出:
class java.lang.String
class java.lang.String
class org.codehaus.groovy.runtime.GStringImpl
class [Ljava.lang.String;
class [Ljava.lang.String;
class [Ljava.lang.String;
我希望管道不会将 GString
转换为 String
,但会失败,因为没有以 Gstring
作为参数的函数。
为了调试,您可以尝试调用 .toString()
您传递给函数的所有元素。
更新
这似乎是管道解释器的一个已知问题(或至少已报告):JENKINS-56758。
在工单中描述了使用集合而不是可变参数的额外解决方法。这将省去对所有内容进行类型转换的需要。
上下文
我是 运行 Jenkins Windows,正在编写声明性管道。我正在尝试将多个命令放在单个 bat
步骤中,同时如果任何包含的命令失败,仍然会使该步骤失败。
这样做有双重目的。
- 最佳实践文档建议为每一件小事创建一个步骤可能不是最好的主意(也可以通过在批处理文件中添加更多东西来解决,但我的构建还没有那么大)
- 我想在 Visual Studio 命令提示符下执行一些命令,这是通过首先
call
ing 一个设置环境的.bat
文件,然后执行任何必要的命令来实现的.
代码
我在 Jenkinsfile
中写了以下 Groovy 代码:
def ExecuteMultipleCmdSteps(String... steps)
{
bat ConcatenateMultipleCmdSteps(steps)
}
String ConcatenateMultipleCmdSteps(String... steps)
{
String[] commands = []
steps.each { commands +="echo ^> Now starting: ${it}"; commands += it; }
return commands.join(" && ")
}
problem/question
我无法让它可靠地工作。也就是说,在单个 Jenkinsfile
中,我可以对 ExecuteMultipleCmdSteps()
进行多次调用,其中一些会按预期工作,而另一些则会失败 java.lang.NoSuchMethodError: No such DSL method 'ExecuteMultipleCmdSteps' found among steps [addBadge, ...
我还没有在失败中找到任何模式。我认为只有在 warnError
块中执行时才会失败,但现在我在 dir()
块中也遇到了问题,而在另一个 Jenkinsfile
中,它工作正常。
这个问题似乎与 ExecuteMultipleCmdSteps()
是可变参数函数有关。如果我提供具有正确数量参数的重载,则使用该重载没有问题。
我在这里不知所措。非常欢迎您的意见。
失败的解决方案
在某些时候我认为它可能是 scoping/importing 的东西,所以我按照 ExecuteMultipleCmdSteps()
包含在 class(下面的代码)中。现在,该方法被称为 Helpers.ExecuteMultipleCmdSteps()
,结果是 org.jenkinsci.plugins.scriptsecurity.sandbox.RejectedAccessException: No such static method found: staticMethod Helpers ExecuteMultipleCmdSteps org.codehaus.groovy.runtime.GStringImpl org.codehaus.groovy.runtime.GStringImpl
public class Helpers {
public static environment
public static void ExecuteMultipleCmdSteps(String... steps)
{
environment.bat ConcatenateMultipleCmdSteps(steps)
}
public static String ConcatenateMultipleCmdSteps(String... steps)
{
String[] commands = []
steps.each { commands +="echo ^> Now starting: ${it}"; commands += it; }
return commands.join(" && ")
}
最少的失败示例
考虑以下几点:
hello = "Hello"
pipeline {
agent any
stages {
stage("Stage") {
steps {
SillyEcho("Hello")
SillyEcho("${hello}" as String)
SillyEcho("${hello}")
}
}
}
}
def SillyEcho(String... m)
{
echo m.join(" ")
}
我希望所有对 SillyEcho()
的调用都会导致 Hello
被回显。实际上,前两个成功,最后一个结果 java.lang.NoSuchMethodError: No such DSL method 'SillyEcho' found among steps [addBadge, addErrorBadge,...
奇怪的成功例子
考虑以下 groovy 脚本,它几乎等同于上面失败的示例:
hello = "Hello"
SillyEcho("Hello")
SillyEcho("${hello}" as String)
SillyEcho("${hello}")
def SillyEcho(String... m)
{
println m.join(" ")
}
当粘贴到 Groovy 脚本控制台(例如 Jenkins 提供的控制台)时,成功(Hello
打印了三次)。
尽管我希望这个示例能够成功,但我也希望它的行为与上面失败的示例一致,所以我对这个示例有点纠结。
不确定这是否能回答您的问题,如果不能,请将其视为更大的评论。 我喜欢你如何从 C++ 中借用 'variadic functions' :) 然而,在 groovy 中有很多优雅的方法来处理这个问题。
试试这个:
def ExecuteMultipleCmdSteps(steps)
{
sh steps
.collect { "echo \> Now starting: $it && $it" }
.join(" && ")
}
pipeline {
agent any
stages {
stage ("test") {
steps {
ExecuteMultipleCmdSteps(["pwd", "pwd"])
}
}
}
}
这对我来说很好用:
[Pipeline] Start of Pipeline
[Pipeline] node
Running on Jenkins in /var/lib/jenkins/workspace/TestJob
[Pipeline] {
[Pipeline] stage
[Pipeline] { (test)
[Pipeline] sh
+ echo > Now starting: pwd
> Now starting: pwd
+ pwd
/var/lib/jenkins/workspace/TestJob
+ echo > Now starting: pwd
> Now starting: pwd
+ pwd
/var/lib/jenkins/workspace/TestJob
[Pipeline] }
[Pipeline] // stage
[Pipeline] }
[Pipeline] // node
[Pipeline] End of Pipeline
Finished: SUCCESS
您可能想像这样重写您的函数。 您提到的2个错误可能有不同的原因。
第一个“没有这样的 DSL 方法……”确实是一个范围界定方法,您自己找到了解决方案,但我不明白为什么重载在同一范围内起作用。
第二个错误,可以用这个
感谢您添加失败和成功的示例。
我想您的问题是由于 String
和 GString
.
关于 运行 作为管道作业与 运行 Jenkins 脚本控制台中的脚本之间的差异,我假设基于此 Jenkins 脚本控制台并不严格类型引用或尝试根据函数签名转换参数。我的假设基于这个脚本,基于你的脚本:
hello = "Hello"
hello2 = "${hello}" as String
hello3 = "${hello}"
println hello.getClass()
println hello2.getClass()
println hello3.getClass()
SillyEcho(hello)
SillyEcho(hello2)
SillyEcho(hello3)
def SillyEcho(String... m)
{
println m.getClass()
}
这是我在 Jenkins 脚本控制台中得到的输出:
class java.lang.String
class java.lang.String
class org.codehaus.groovy.runtime.GStringImpl
class [Ljava.lang.String;
class [Ljava.lang.String;
class [Ljava.lang.String;
我希望管道不会将 GString
转换为 String
,但会失败,因为没有以 Gstring
作为参数的函数。
为了调试,您可以尝试调用 .toString()
您传递给函数的所有元素。
更新
这似乎是管道解释器的一个已知问题(或至少已报告):JENKINS-56758。
在工单中描述了使用集合而不是可变参数的额外解决方法。这将省去对所有内容进行类型转换的需要。