1

语境

我在 Windows 上运行 Jenkins,编写声明性管道。我试图将多个命令放在一个bat步骤中,同时如果任何包含的命令失败,仍然会使该步骤失败。

这样做的目的是双重的。

  • 最佳实践文档表明,为每件小事创建一个步骤可能不是最好的主意(也可以通过将更多内容放入批处理文件来解决,但我的构建还没有那么大)
  • 我想在 Visual Studio 命令提示符中执行一些命令,这是通过首先设置环境call.bat文件,然后执行任何必要的命令来实现的。

代码

我在我的代码中编写了以下 Groovy 代码Jenkinsfile

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: No such DSL method 'ExecuteMultipleCmdSteps' found among steps [addBadge, ...

我还没有发现失败的任何模式。我认为它仅在从块内执行时失败warnError,但现在我在块内也遇到问题dir(),而在不同的块中Jenkinsfile,它工作正常。

这个问题似乎与ExecuteMultipleCmdSteps()可变参数函数有关。如果我提供具有正确数量的参数的重载,则使用该重载没有问题。

我在这里不知所措。非常欢迎您的意见。

失败的解决方案

在某些时候,我认为这可能是一个范围界定/导入的事情,所以我按照这个答案ExecuteMultipleCmdSteps()的建议将其包含在一个类(下面的代码)中。现在,该方法被称为,结果是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打印 3 次)。

尽管我希望这个示例能够成功,但我也希望它的行为与上面失败的示例一致,所以我对这个示例有点失望。

4

2 回答 2

1

不确定这是否会回答您的问题,如果没有,请将其视为更大的评论。我喜欢你从 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

你可能想像这样重写你的函数。您提到的 2 个错误可能有不同的原因。

第一个,“没有这样的 DSL 方法......”确实是一个范围界定,你自己找到了解决方案,但我不明白为什么重载在同一范围内工作。

第二个错误,可以用这个答案解决。但是,对我来说,第二种方法的代码也可以正常工作。

于 2021-01-13T00:03:24.007 回答
1

感谢您添加失败和成功的示例。我预计您的问题是由于 和 不兼容造成StringGString

关于将其作为管道作业运行和在 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;

我希望管道不会强制转换为GStringString但会失败,因为没有带有Gstringas 参数的函数。

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

更新

这似乎是管道解释器的一个已知问题(或至少已报告):JENKINS-56758

在票证中,使用集合而不是可变参数描述了一个额外的解决方法。这将省略对所有内容进行类型转换的需要。

于 2021-01-14T12:46:39.337 回答