Groovy 在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

我在Windows上运行Jenkins,编写声明性管道。我试图在一个
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脚本,它与上面失败的示例相当:

hello = "Hello"

SillyEcho("Hello")
SillyEcho("${hello}" as String)
SillyEcho("${hello}")

def SillyEcho(String... m)
{
    println m.join(" ")
}
当粘贴到Groovy脚本控制台(例如Jenkins提供的控制台)时,这会成功(
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"])
            }
        }
    }
}
这对我来说很好:

[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
您可能希望像这样重写函数。 您提到的两个错误可能有不同的原因

第一个问题,“没有这样的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
作为参数的函数

对于调试,您可以尝试调用传递给函数的所有元素

更新

这似乎是管道解释器的已知问题(或至少已报告)


在罚单中,使用集合而不是varargs描述了一个额外的解决方法。这将省去键入cast的所有需要。

感谢您的反馈。您的解决方案比我的解决方案优雅得多,因此我将使用它来改进代码。不过,它对解决我的问题没有任何作用。我添加了一个“最小失败示例”以及我的问题的“奇怪的成功例子”,以进一步缩小我的问题范围。语言规范声称Groovy通常会在GString和String之间转换正如Script控制台示例中所示。流水线似乎只针对非变量函数。我开始考虑忽略一个bug。所以,现在,我想,您明确地给出的建议是我们所能做的最好的。即使我不太喜欢它混乱的代码。实际上,这似乎是詹金斯流水线中的一个错误。在我的回答中,我添加了一个到bug报告的链接。它还包含了另一个关于使用集合而不是varargs的工作。感谢您挖掘问题报告。我一直想自己做这件事