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

Posted

技术标签:

【中文标题】Jenkinsfile Pipeline DSL:如何在作业仪表板 GUI 中显示多列 - 对于所有动态创建的阶段 - 在 PIPELINE 部分中时【英文标题】:Jenkinsfile Pipeline DSL: How to Show Multi-Columns in Jobs dashboard GUI - For all Dynamically created stages - When within PIPELINE section 【发布时间】:2020-03-12 03:03:37 【问题描述】:

Jenkins 2.89.4 滚动

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

我的要求是:

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

    我想在 jenkins 作业的仪表板中看到的 自己单独的并行动态创建阶段(GUI 列) 中运行这 N 个项目的构建。 p>

    我想分别查看(Gradle 构建/控制台)每个项目构建的输出,即 我不想混合每个项目构建的控制台输出,它们在只有一列(即名为BUILD 的列)。

    在此 URL https://jenkins.io/blog/2017/09/25/declarative-1/ 我明白了,您如何运行并行阶段/步骤,但这样做,要么将这些并行步骤的输出混合在一个列中(我的意思是在 BUILD 列)--或者,如果您希望它在单独的阶段/列下(即帖子说 在 Linux 或 Windows 上测试 单独,那么您就是仍然在 Jenkinsfile 早期硬编码所有阶段/步骤而不是只使用列表或数组哈希,我更愿意更新它以添加更多或更少的阶段/ 并行步骤,就我而言,它们都遵循相同的标准)。我想要的只是在一个地方更新有多少步骤以及一个地方的所有阶段(列表/数组) )。

    我现在使用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)
            
    

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

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

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

使用我的 JOB DSL,我创建了一个新的 Jenkins 管道作业,其中 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.')
              
    
    //---------------------

运行 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

我怎样才能在 Jenkinsfile pipeline 部分中运行它,并且仍然能够为给定项目 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.')
          

//---------------------

【问题讨论】:

【参考方案1】:

所以我对此进行了一些研究并让它发挥了作用。

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.')
          

//---------------------

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

为了清晰的消息/阶段名称,我也调整了函数,我们不会在 pipeline ... 中设置这个变量 buildStages

//---
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()
//---------------------

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

    它将为给定的第 N 步并行运行 所有 P 项目

    它将等待所有项目的第 N 步首先完成,然后跳转到下一个第 N 步。

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

    挑战:我们如何让 ALPHA 项目的第 2 步在 ALPHA 项目的第 1 步完成后立即开始,即它不会等待其他 2 个项目的第 1 步完成并且可以可能与其他项目的步骤 N(=1) 或 N+1 并行运行 ALPHA 项目 1 的步骤 #2。

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

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

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

注意:如果您想运行管道的并发构建,请轻松使用并行。有关运行parallel构建操作并发相关问题的更多信息,请参阅此处:Jenkins - java.lang.IllegalArgumentException: Last unit does not have enough valid bits & Gradle error: Task 'null' not found in root project

【讨论】:

以上是关于Jenkinsfile Pipeline DSL:如何在作业仪表板 GUI 中显示多列 - 对于所有动态创建的阶段 - 在 PIPELINE 部分中时的主要内容,如果未能解决你的问题,请参考以下文章

持续集成使用 Jenkinsfile 设计直观的 Pipeline

Jenkins Pipeline如何动态的并行任务

jenkins-使用Jenkinsfile来定义pipeline

Jenkins Pipeline - 工作流 CPS 插件 JAR 依赖项

在 Jenkins Pipeline/Jenkinsfile 中获取 git 分支名称

Jenkinsfile之语法