原始情况:

我在詹金斯(Jenkins)有一份正在运行蚂蚁脚本的工作。我轻松地使用“多配置项目”在一个以上的软件版本上测试了该ant脚本。

这种类型的项目真的很酷,因为它允许我指定所需的两个软件的所有版本(在我的情况下为Java和Matlab),并且它将使用我的所有参数组合运行我的ant脚本。

这些参数然后用作要在我的蚂蚁要使用的可执行文件的位置的定义中串联的字符串。


示例:env.MATLAB_EXE = / usr / local / MATLAB / $ {MATLAB_VERSION} / bin / matlab


这工作得很好,但是现在我正在将该脚本迁移到它的规范版本。

管道迁移:

我设法使用Parametrized pipelines插件以管道方式实现了相同的脚本。这样,我达到了可以手动选择要使用哪个版本的软件(如果我手动触发构建)的点,并且还找到了一种执行此方法的方法,可以定期选择每次运行时所需的参数。

该解决方案似乎相当有效,但并不令人满意。

我的多配置项目具有以下某些功能:


通过一个以上的参数,我可以设置为对它们进行插值并执行每种组合
执行被清楚地分开,并且在构建历史/构建详细信息中很容易识别使用了哪些设置
只需在参数中添加新的“可能”值即可产生所需的执行


请求

因此,我想知道是否有更好的解决方案可以同时满足上述要求。

长话短说:有没有办法在詹金斯中使用管道技术来实现多配置项目?

最佳答案

我已经看到了这个问题,最近也问了很多类似的问题,所以解决这个问题似乎很有趣……

用代码可视化的矩阵/多配置作业实际上只是嵌套了几个循环,每个参数轴一个。

您可以使用一些经过硬编码的for循环来遍历一些列表来构建相当简单的东西。或者,您可能会变得更加复杂,并进行一些递归循环,因此您不必对特定循环进行硬编码。


免责声明:我做的事情远不止编写代码。我对groovy也很陌生,因此可以更简洁地完成此工作,并且可能可以完成许多groovier事情,但是无论如何,这都能完成工作。


只需做一些工作,就可以将该matrixBuilder封装在一个类中,以便您可以传递任务闭包和轴列表,并返回任务图。将其粘贴在共享库中并在任何地方使用。从多配置作业中添加其他一些功能(例如过滤器)应该很容易。

此尝试使用递归matrixBuilder函数遍历任意数量的参数轴并构建所有组合。然后,它并行执行它们(显然取决于节点的可用性)。

/*
    All the config axes are defined here
    Add as many lists of axes in the axisList as you need.
    All combinations will be built
*/
def axisList = [
    ["ubuntu","rhel","windows","osx"],           //agents
    ["jdk6","jdk7","jdk8"],                      //tools
    ["banana","apple","orange","pineapple"]      //fruit
]



def tasks = [:]
def comboBuilder
def comboEntry = []


def task = {
    // builds and returns the task for each combination

    /* Map the entries back to a more readable format
       the index will correspond to the position of this axis in axisList[] */
    def myAgent = it[0]
    def myJdk   = it[1]
    def myFruit = it[2]

    return {
        // This is where the important work happens for each combination
        node(myAgent) {
            println "Executing combination ${it.join('-')}"
            def javaHome = tool myJdk
            println "Node=${env.NODE_NAME}"
            println "Java=${javaHome}"
        }

        //We won't declare a specific agent this part
        node {
            println "fruit=${myFruit}"
        }
    }
}


/*
    This is where the magic happens
    recursively work through the axisList and build all combinations
*/
comboBuilder = { def axes, int level ->
    for ( entry in axes[0] ) {
        comboEntry[level] = entry
        if (axes.size() > 1 ) {
            comboBuilder(axes[1..-1], level + 1)
        }
        else {
            tasks[comboEntry.join("-")] = task(comboEntry.collect())
        }
    }
}

stage ("Setup") {
    node {
        println "Initial Setup"
    }
}

stage ("Setup Combinations") {
    node {
        comboBuilder(axisList, 0)
    }
}

stage ("Multiconfiguration Parallel Tasks") {
    //Run the tasks in parallel
    parallel tasks
}

stage("The End") {
    node {
        echo "That's all folks"
    }
}


您可以在http://localhost:8080/job/multi-configPipeline/[build]/flowGraphTable/(在构建页面上的Pipeline Steps链接下找到)中看到更详细的作业流程。

编辑:
您可以将阶段下移到“任务”创建中,然后更清楚地看到每个阶段的详细信息,但不能像多配置作业那样以整洁的矩阵形式查看。

...
return {
    // This is where the important work happens for each combination
    stage ("${it.join('-')}--build") {
        node(myAgent) {
            println "Executing combination ${it.join('-')}"
            def javaHome = tool myJdk
            println "Node=${env.NODE_NAME}"
            println "Java=${javaHome}"
        }
        //Node irrelevant for this part
        node {
            println "fruit=${myFruit}"
        }
    }
}
...


或者,您可以将每个node用其自己的stage包装以获取更多详细信息。

当我这样做时,我注意到我之前的代码中有一个错误(现在已在上面修复)。我正在将comboEntry引用传递给任务。我应该发送一份副本,因为尽管阶段的名称正确无误,但当它实际执行时,这些值当然是所有遇到的最后一个条目。所以我将其更改为tasks[comboEntry.join("-")] = task(comboEntry.collect())

我注意到您可以在执行并行任务时保留原始的stage ("Multiconfiguration Parallel Tasks") {}。从技术上讲,您现在已经嵌套了阶段。我不确定詹金斯应该如何处理,但它没有抱怨。但是,“父”级时序不包括并行级时序。

我还注意到,当一个新版本开始运行时,在作业的“阶段视图”上,所有以前的版本都消失了,大概是因为阶段名称不完全匹配。但是,在构建完成运行之后,它们都再次匹配,并且旧的构建再次出现。

最后,Blue Ocean似乎并没有以同样的方式生动化。它不能识别并行进程中的“阶段”,只能识别封闭阶段(如果存在),如果不能,则不能识别“并行”。然后仅显示各个并行过程,而不显示其中的各个阶段。

07-27 23:09