Groovy 在Jenkins文件中调用变量函数会意外失败 上下文
我在Windows上运行Jenkins,编写声明性管道。我试图在一个Groovy 在Jenkins文件中调用变量函数会意外失败 上下文,groovy,jenkins-pipeline,variadic-functions,jenkins-groovy,gstring,Groovy,Jenkins Pipeline,Variadic Functions,Jenkins Groovy,Gstring,我在Windows上运行Jenkins,编写声明性管道。我试图在一个bat步骤中放置多个命令,但如果包含的任何命令失败,仍然会使步骤失败 这有两个目的 最佳实践文档建议,为每一件小事创建一个步骤可能不是最好的主意(也可以通过在批处理文件中添加更多内容来解决,但我的构建还没有那么大) 我想在Visual Studio命令提示符中执行一些命令,这是通过首先调用设置环境的.bat文件,然后执行任何必要的命令来实现的 代码 我在我的Jenkinsfile中编写了以下Groovy代码: def Exe
bat
步骤中放置多个命令,但如果包含的任何命令失败,仍然会使步骤失败
这有两个目的
- 最佳实践文档建议,为每一件小事创建一个步骤可能不是最好的主意(也可以通过在批处理文件中添加更多内容来解决,但我的构建还没有那么大)
- 我想在Visual Studio命令提示符中执行一些命令,这是通过首先调用设置环境的
文件,然后执行任何必要的命令来实现的.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(" && ")
}
问题
我不能让它可靠地工作。也就是说,在一个Jenkinsfile
中,我可以多次调用ExecuteMultipleCmdSteps()
,其中一些调用将按预期工作,而另一些调用将因java.lang.NoSuchMethodError而失败:在步骤[addBadge,
中找不到此类DSL方法“ExecuteMultipleCmdSteps”
我还没有在失败中找到任何模式。我以为它只是在warnError
块中执行时失败,但现在我在dir()
块中也遇到了问题,而在另一个Jenkinsfile
中,效果很好
此问题似乎与ExecuteMultipleCmdSteps()
是一个可变函数有关。如果我提供了一个具有正确参数数的重载,则使用该重载时不会出现问题
我在这里不知所措。欢迎你的意见
失败的解决方案
在某种程度上,我认为这可能是一个作用域/导入的问题,因此我将ExecuteMultipleCmdSteps()
封装在一个类中(下面的代码),如所建议的。现在,该方法被称为Helpers.ExecuteMultipleCmdSteps()
,这将导致出现org.jenkinsci.plugins.scriptsecurity.sandbox.RejectedAccessException:未找到此类静态方法: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:在[addBadge,addErrorBadge,
奇怪的成功例子 考虑下面的groovy脚本,它与上面失败的示例相当:当粘贴到Groovy脚本控制台(例如Jenkins提供的控制台)时,这会成功(hello = "Hello" SillyEcho("Hello") SillyEcho("${hello}" as String) SillyEcho("${hello}") def SillyEcho(String... m) { println m.join(" ") }
打印三次)Hello
<>虽然我希望这个例子能成功,但我也希望它能与失败的例子保持一致,以上,所以我对这一点有点破绽。 不确定这是否会回答你的问题,如果不是,把它当作一个更大的评论。 我喜欢你如何从C++中借用“变量函数” 然而,在groovy中有很多优雅的方法来处理这个问题 试试这个:这对我来说很好:def ExecuteMultipleCmdSteps(steps) { sh steps .collect { "echo \\> Now starting: $it && $it" } .join(" && ") } pipeline { agent any stages { stage ("test") { steps { ExecuteMultipleCmdSteps(["pwd", "pwd"]) } } } }
您可能希望像这样重写函数。 您提到的两个错误可能有不同的原因 第一个问题,“没有这样的DSL方法…”确实是一个范围问题,您找到了解决方案,但我不明白为什么重载在相同的范围内工作[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
第二个错误可以用这个方法解决。但是,对我来说,第二种方法的代码也可以很好地工作。感谢您添加失败和成功的示例。 我认为您的问题是由于和String
不兼容造成的 关于将其作为管道作业运行与在Jenkins脚本控制台中运行脚本之间的区别,我基于此假设Jenkins脚本控制台对类型引用没有那么严格,或者尝试基于函数签名强制转换参数。我基于此脚本,基于您的脚本:GString
这是我在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() }
我希望管道不会将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
在罚单中,使用集合而不是varargs描述了一个额外的解决方法。这将省去键入cast的所有需要。感谢您的反馈。您的解决方案比我的解决方案优雅得多,因此我将使用它来改进代码。不过,它对解决我的问题没有任何作用。我添加了一个“最小失败示例”以及我的问题的“奇怪的成功例子”,以进一步缩小我的问题范围。语言规范声称Groovy通常会在GString和String之间转换正如Script控制台示例中所示。流水线似乎只针对非变量函数。我开始考虑忽略一个bug。所以,现在,我想,您明确地给出的建议是我们所能做的最好的。即使我不太喜欢它混乱的代码。实际上,这似乎是詹金斯流水线中的一个错误。在我的回答中,我添加了一个到bug报告的链接。它还包含了另一个关于使用集合而不是varargs的工作。感谢您挖掘问题报告。我一直想自己做这件事