Jenkinsfile 管道 DSL:如何在作业仪表板 GUI 中显示多列 - 适用于所有动态创建的阶段 - 在 PIPELINE 部分中时

Aru*_*gal 9 parallel-processing groovy dashboard jenkins-pipeline

詹金斯2.89.4 滚动

我看到了几乎所有的 stackoverflow 帖子,这些帖子展示了我们如何成功运行并行步骤/阶段(使用列表/映射等)——或者直接对它们进行硬编码——或者甚至为 Jenkinsfile 创建动态阶段(如这篇文章所示:脚本化 jenkinsfile 并行阶段)

我的要求是:

  1. 在“构建”步骤下构建 N 个项目的管道,即在这些项目中的每一个上并行构建。即它在所有 N 个项目上运行 Gradle。在这里,我有一个由声明性 JOB DSL Groovy 创建的 Jenkinsfile。这里我的 Gradle 项目没有设置为多项目,所以我不能调用顶级 gradle 并说,Gradle 请发挥你的平行魔法(在 Gradle 内)。

  2. 我想在它们自己单独的并行动态创建阶段(GUI 列)中运行这些 N 个项目的构建,如 jenkins 作业的仪表板中所见。

  3. 我想分别查看(Gradle 构建/控制台)每个项目构建的输出,即我不想混合每个项目构建的控制台输出,这些构建仅在一列(即名为 的列BUILD中并行运行

  4. 在这个 URL https://jenkins.io/blog/2017/09/25/declarative-1/我明白了,你可以如何运行并行阶段/步骤,但这样做,要么将这些并行步骤的输出混合在一列中(我的意思是在BUILD列)--OR如果你想让它在不同的阶段/列(即发帖称测试在Linux或Windows 分开,那么你仍然硬编码的所有阶段/步骤早期(在Jenkinsfile而不是使用只是一个列表或数组哈希,我更愿意更新它以添加更多或更少的阶段/并行步骤,就像我的情况一样,它们都遵循相同的标准。我想要的是只在一个地方更新多少步骤和什么所有阶段都在一个地方(列表/数组)。

  5. 我现在使用Jenkins Blue Ocean

通常,如果您在一个阶段中有并行步骤,则当您单击以查看给定并行步骤/阶段的控制台输出时,所有步骤的控制台标准输出会混合到一个控制台输出/阶段/列中;当您将鼠标悬停在作业仪表板中的 BUILD 列(假设在 BUILD 阶段中有并行步骤)时(所有这些步骤的标准输出混合在一起,很难看到单个项目步骤的控制台输出仅针对给定的步骤/阶段)。

如果我们想(动态地)创建单独的阶段,那么 Jenkins 应该能够在并行部分中显示给定步骤/动态阶段的控制台输出(即每列应该显示他们自己项目的构建控制台输出)。

使用上面的URL,我可以在尝试此脚本后执行以下操作:

// main script block
// could use eg. params.parallel build parameter to choose parallel/serial 
def runParallel = true
def buildStages

node('master') {
  stage('Initializing Parallel Dynamic Stages') {
    // Set up List<Map<String,Closure>> describing the builds
    buildStages = prepareBuildStages()
    println("Initialised pipeline.")
  }

  for (builds in buildStages) {
    if (runParallel) {
      parallel(builds)
    } else {
      // run serially (nb. Map is unordered! )
      for (build in builds.values()) {
        build.call()
      }
    }
  }

  stage('Done') {
      println('The whole SHENZI is complete.')
  }
}

// Create List of build stages to suit
def prepareBuildStages() {
  def buildList = []

  for (i=1; i<4; i++) {
    def buildStages = [:]
    for (name in [ 'Alpha', 'Tango', 'Chyarli' ] ) {
      def n = "${name} ${i}"
      buildStages.put(n, prepareOneBuildStage(n))
    }
    buildList.add(buildStages)
  }
  return buildList
}

def prepareOneBuildStage(String name) {
    def proj_name = name.split(' ')[0]    
    def proj_parallel_sub_step = name.split(' ')[1]

    //Return the whole chunkoni
    return {
            stage("Build\nProject-${proj_name}\nStep ${proj_parallel_sub_step}") {
                println("Building ${proj_name} - ${proj_parallel_sub_step}")
                sh(script:'sleep 15', returnStatus:true)
            }
    }
}
Run Code Online (Sandbox Code Playgroud)

当我将上述 Groovy 脚本(正在创建 DYNAMIC Stages)放入Pipeline ScriptPipeline Script from SCM(即 .groovy 文件中可用的相同代码)时 - 它运行成功并在 BUILD 步骤下为 3 个项目中的每一个创建动态阶段并运行对所有 3 个项目并行执行 3 个步骤(第 N 个),然后为所有 3 个项目开始下一个第 N 个步骤,依此类推。

如果您在下面看到,我们还在 Jenkins 工作仪表板中为它们提供了单独的列。

在此处输入图片说明

现在,当我将上面的脚本放在 Jenkinsfile(Pipeline DSL)中我有 pipeline { .... }部分时,它不起作用并给我以下错误。

使用我的 JOB DSL,我创建了一个新的 Jenkins Pipeline 作业,其中Pipeline Script from SCM调用了一个 groovy 文件(现在包含):

//----------------------------------------------------
// Both - Parallel Run and GUI View in JF Jenkins job.
//----------------------------------------------------
def runParallel = true
def buildStages
def wkspace = /var/lib/jenkins/workspaces/ignore_this_variale_or_its_value_for_now

// Create List of build stages to suit
def prepareBuildStages() {
  def buildList = []

  for (i=1; i<3; i++) {
    def buildStages = [:]
    for (name in [ 'Alpha', 'Tango', 'Chyarli' ] ) {
      def n = "${name} ${i}"
      buildStages.put(n, prepareOneBuildStage(n))
    }
    buildList.add(buildStages)
  }
  return buildList
}
//---
def prepareOneBuildStage(String name) {
  def proj_name = name.split(' ')[0]
  def proj_parallel_sub_step = name.split(' ')[1]
  // return the whole chunkoni (i.e. for a given stage) - will be named dynamically.
  return {
    stage("Build\nProject-${proj_name}\nStep ${proj_parallel_sub_step}") {
      println("Building ${proj_name} - ${proj_parallel_sub_step}")
      sh(script:'sleep 15', returnStatus:true)
    }
  }
}
// Set up List<Map<String,Closure>> describing the builds
buildStages = prepareBuildStages()
//---------------------




String jenkinsBaselines

// SEE NOW --- we have this section called 'pipeline'
pipeline {
    agent {
        node {
            label 'rhat6'
            customWorkspace wkspace
        }
    }

    options {
        ansiColor('xterm')
        timeout(time: 8, unit: 'HOURS')
        skipDefaultCheckout()
        timestamps()
    }

    environment {
        someEnvVar = 'aValue'

    }

    //------------- Stages
    stages {
        stage('Initializing Parallel Dynamic Stages') {
            // Set up List<Map<String,Closure>> describing the builds
            println("Initialised pipeline.")
        }

        for (builds in buildStages) {
            if (runParallel) {
                parallel(builds)
            } else {
                // run serially (nb. Map is unordered! )
                for (build in builds.values()) {
                    build.call()
                }
            }
        }

        stage('Done') {
            println('The whole SHENZI is complete.')
        }      
    }
    //---------------------
}
Run Code Online (Sandbox Code Playgroud)

现在运行 Jenkinsfile Jenkins 作业给我这个错误:

[BFA] Scanning build for known causes...
[BFA] No failure causes found
[BFA] Done. 1s
org.codehaus.groovy.control.MultipleCompilationErrorsException: startup failed:
WorkflowScript: 69: Not a valid stage section definition: "buildStages = prepareBuildStages()". Some extra configuration is required. @ line 69, column 5.
            stage('Initializing Parallel Dynamic Stages') {
       ^

WorkflowScript: 69: Unknown stage section "println". Starting with version 0.5, steps in a stage must be in a steps block. @ line 69, column 5.
            stage('Initializing Parallel Dynamic Stages') {
       ^

WorkflowScript: 75: Expected a stage @ line 75, column 5.
            for (builds in buildStages) {
       ^

WorkflowScript: 86: Unknown stage section "println". Starting with version 0.5, steps in a stage must be in a steps block. @ line 86, column 5.
            stage('Done') {
       ^

WorkflowScript: 69: No "steps" or "parallel" to execute within stage "Initializing Parallel Dynamic Stages" @ line 69, column 5.
            stage('Initializing Parallel Dynamic Stages') {
       ^

WorkflowScript: 86: No "steps" or "parallel" to execute within stage "Done" @ line 86, column 5.
            stage('Done') {
       ^

6 errors

    at org.codehaus.groovy.control.ErrorCollector.failIfErrors(ErrorCollector.java:310)
    at org.codehaus.groovy.control.CompilationUnit.applyToPrimaryClassNodes(CompilationUnit.java:1085)
    at org.codehaus.groovy.control.CompilationUnit.doPhaseOperation(CompilationUnit.java:603)
    at org.codehaus.groovy.control.CompilationUnit.processPhaseOperations(CompilationUnit.java:581)
    at org.codehaus.groovy.control.CompilationUnit.compile(CompilationUnit.java:558)
    at groovy.lang.GroovyClassLoader.doParseClass(GroovyClassLoader.java:298)
    at groovy.lang.GroovyClassLoader.parseClass(GroovyClassLoader.java:268)
    at groovy.lang.GroovyShell.parseClass(GroovyShell.java:688)
    at groovy.lang.GroovyShell.parse(GroovyShell.java:700)
    at org.jenkinsci.plugins.workflow.cps.CpsGroovyShell.doParse(CpsGroovyShell.java:133)
    at org.jenkinsci.plugins.workflow.cps.CpsGroovyShell.reparse(CpsGroovyShell.java:127)
    at org.jenkinsci.plugins.workflow.cps.CpsFlowExecution.parseScript(CpsFlowExecution.java:557)
    at org.jenkinsci.plugins.workflow.cps.CpsFlowExecution.start(CpsFlowExecution.java:518)
    at org.jenkinsci.plugins.workflow.job.WorkflowRun.run(WorkflowRun.java:290)
    at hudson.model.ResourceController.execute(ResourceController.java:97)
    at hudson.model.Executor.run(Executor.java:429)
Finished: FAILURE
Run Code Online (Sandbox Code Playgroud)

我怎样才能在 Jenkinsfilepipeline部分工作,并且仍然能够为给定的项目 N 和步骤 M 获取每个动态创建的阶段的单独列?

尝试了以下方式,仍然错误的说方式。

//------------- Stages
stages {
    stage('Initializing Parallel Dynamic Stages') {
        // Set up List<Map<String,Closure>> describing the builds
        buildStages = prepareBuildStages()
        println("Initialised pipeline.")

        // tried this way too. within a stage
        buildStages.each { bld -->
            parallel(bld)
        }  
    }

    stage('Done') {
        println('The whole SHENZI is complete.')
    }      
}
//---------------------
Run Code Online (Sandbox Code Playgroud)

Aru*_*gal 4

所以我对此做了一些研究并使其发挥作用。

Jenkinsfile 中的新代码(对于STAGES)现在是:

//------------- Stages
stages {
     stage('Start Pipeline') {
        steps {
            script {
                sh "echo HELLO moto razr!"
            }
        }  
     }

    stage('Initializing Parallel Dynamic Stages'){
        steps {
            script {
                // Run all Nth step for all Projects in Parallel. 
                buildStages.each { bs -> parallel(bs) }


                // OR uncomment the following code (if conditional on boolean variable).
                /*
                for (builds in buildStages) {
                    if (runParallel) {
                        parallel(builds)
                    } else {
                        // run serially (nb. Map is unordered! )
                        for (build in builds.values()) {
                            build.call()
                        }
                    }
                }
                */        

            }   
        }
     } 

    stage('Done') {
        println('The whole SHENZI is complete.')
    }      
}
//---------------------
Run Code Online (Sandbox Code Playgroud)

这就是工作所需要的一切。

为了清楚的消息/阶段名称,我也调整了函数,我们不会在内部设置此变量 buildStagespipeline { ... }

//---
def prepareOneBuildStage(String name) {
  def proj_name = name.split(' ')[0]
  def proj_parallel_sub_step = name.split(' ')[1]
  // return the whole chunkoni (i.e. for a given stage) - will be named dynamically.
  return {
    stage("BUILD Project-${proj_name} Parallel_Step_${proj_parallel_sub_step}") {
      println("Parallel_Step # ${proj_parallel_sub_step} of Project => ${proj_name}")
      sh(script:"echo \"Parallel_Step # ${proj_parallel_sub_step} of Project => ${proj_name}\" && sleep 20", returnStatus:true)
      // -- OR -- you can call Gradle task i.e. rpm / any other / any other command here. 
    }
  }
}

// Set up List<Map<String,Closure>> describing the builds. section now.
buildStages = prepareBuildStages()
//---------------------
Run Code Online (Sandbox Code Playgroud)

这个实现现在正在创建N号。并行阶段,即每个项目对于给定的第 N 个步骤当查看 Jenkinsfile 作业的仪表板时)P 号有一个单独的列。项目。

  1. 它将并行运行给定第N步骤的所有 P 项目。
  2. 它将首先等待所有项目完成第 N 步,然后跳转到下一个第 N 步。

    这意味着,如果 Project ALPHA Step #1 完成,它仍然会等待其他 2 个项目的所有 Step #1,然后并行启动所有项目的 Step #2。

  3. 挑战:我们如何才能在 ALPHA 项目的步骤 #1 完成后立即启动 ALPHA 项目的步骤 #2,即它不会等待其他 2 个项目的步骤 1 完成,并且可能在以下位置运行 ALPHA 项目 1 的步骤 #2与其他项目的步骤 N(=1) 或 N+1 并行。

    这假设所有项目都是相互独立的,并且项目不共享任何其他项目/阶段/步骤中给定项目/其阶段/步骤生成的内容。

根据您自己的要求,您可能需要等待(即,在所有项目的第 1 步完全完成之前不要运行所有项目的第 2 步)——或者——您可能想要运行ALPHA 项目的第 2 步,比方说- TANGO项目的第 2 步,而 CHYARLI 项目的第 1 步仍在进行中

由于这篇文章的主要范围是为每个项目获取单独的动态创建的列/阶段(在pipeline { ... }部分内并行运行),我想,我得到了我正在寻找的东西。

注意:如果您想运行管道的并发构建,请轻松使用并行。有关与同时运行 parallel构建操作相关的问题的更多信息,请参阅此处:Jenkins - java.lang.IllegalArgumentException:最后一个单元没有足够的有效位和 Gradle 错误:在根项目中找不到任务“null”