Search code examples
jenkins-pipelinejenkins-groovyjenkins-blueocean

Parallel pipeline with 2 inline stages


Playing with Jenkins pipeline from https://www.jenkins.io/doc/pipeline/examples/#parallel-multiple-nodes

Simple two parallel steps (OK)

I made a first test pipeline this way:

pipeline {
   stages {
      stage('Build') {
         steps {
            script {
                def labels = ['precise', 'trusty'] // labels for Jenkins node types we will build on
                def builders = [:]
                for (x in labels) {
                    def label = x // Need to bind the label variable before the closure - can't do 'for (label in labels)'
                
                    // Create a map to pass in to the 'parallel' step so we can fire all the builds at once
                    builders[label] = {
                        node('JenkinsNode') {
                           sh script: 'echo build', label: 'Build on $env.NODE_NAME'
                        }
                    }
                }
                parallel builders
            }
         }
      }
   }
}

It resulted in the following expected diagram in Blue Ocean view:

enter image description here

Simple two parallel steps with two sub steps each (KO)

Attempt#1

Then I tried to split each parallel step in two inline stages (to simulate build and tests for example)

pipeline {
   stages {
      stage('Build') {
         steps {
            script {
                def labels = ['precise', 'trusty'] // labels for Jenkins node types we will build on
                def builders = [:]
                for (x in labels) {
                    def label = x // Need to bind the label variable before the closure - can't do 'for (label in labels)'
                
                    // Create a map to pass in to the 'parallel' step so we can fire all the builds at once
                    builders[label] = {
                        node('JenkinsNode') {
                            stage("build") {
                                sh script: 'echo build', label: 'Build on $env.NODE_NAME'
                            }
                            stage("test") {
                                sh script: 'echo run unit tests', label: 'Run unit tests on $env.NODE_NAME'
                            }
                        }
                    }
                }
                parallel builders
            }
         }
      }
   }
}

The Jenkins logs show both build and test stages are run for each parallel step, but the Blue Ocean view only states build stage:

enter image description here

I would expect something like:

enter image description here

I'm not very clear about the boundaries between declarative and scripted pipelines, but I suspect a misunderstanding around this.

Attempt#2

Following a suggestion in comments, I slightly changed the code to have sub-stages unique names (build1, test1, build2, test2) and it does not change the diagram. I still have build steps only.

enter image description here

Here are the Jenkins logs in this case:

enter image description here

Question: Is the pipeline invalid (leading to only "build" sub-steps instead of build + test sub-steps) or is it a limitation of Blue Ocean (1.25.3)?


Solution

  • When combining declarative and scripted syntax things become a bit tricky.
    In this specific combination case, to make it work like you expect you must create an encapsulating stage for each parallel execution code that has the same name as the parallel branch.
    This will cause the blue ocean to display the inner stages as requested.

    pipeline {
       agent any
       stages {
          stage('Build') {
             steps {
                script {
                    def labels = ['precise', 'trusty']
                    def builders = [:]
                    for (x in labels) {
                        def label = x 
                        builders[label] = {
                            stage(label) { // Encapsulating stage with same name as parallel branch
                               node('JenkinsNode') {
                                   stage("build") {
                                       sh script: 'echo build', label: 'Build on $env.NODE_NAME'
                                   }
                                   stage("test") {
                                       sh script: 'echo run unit tests', label: 'Run unit tests on $env.NODE_NAME'
                                   }
                                }
                            }
                        }
                    }
                    parallel builders
                }
             }
          }
       }
    }
    

    Or in a more Groovy way:

    pipeline {
        agent any
        stages {
            stage('Build') {
                steps {
                    script {
                        def labels = ['precise', 'trusty']
                        def builders = labels.collectEntries {
                            ["${it}" : {
                                stage(it) { // Encapsulating stage with same name as parallel branch
                                    node('JenkinsNode') {
                                        stage("build") {
                                            sh script: 'echo build', label: 'Build on $env.NODE_NAME'
                                        }
                                        stage("test") {
                                            sh script: 'echo run unit tests', label: 'Run unit tests on $env.NODE_NAME'
                                        }
                                    }
                                }
                            }]
                        }
                        parallel builders
                    }
                }
            }
        }
    }
    

    The result:

    and the result: