Search code examples
jenkinsjenkins-pipelinecicd

Jenkins parallel pipeline triggers same build


Issue: Jenkins pipeline triggers only single job from upstream job

I have a list of jenkins job in array and from it, I'm trying to build object of build jobs and run them in parallel.

E.g.

def testJobs = [
  'pipeline1',
  'pipeline2'
]
def buildList = [:]


for (jobName in testJobs){
  buildList[jobName] = {
    build job: jobName,  propagate: false
  }
}

def buildResults = parallel (buildList)

For above snippet, I can see only single build for all pipelines as shown below. (and it always points to last pipeline from list... e.g. pipeline2)

[pipeline1] Starting building: pipeline2 #5

[pipeline2] Starting building: pipeline2 #5

I have also tried with dummy parameter to make each built different (even though it already has different job name), but that didn't work.

build job: jobName, parameters: [string(name: "Dummy", value: "${jobName}")], propagate: false

But instead of for loop, if I creates object manually, that works perfectly (shown as below).

E.g.

def buildList = [:]

buildList[pipeline1] = {
  build job: pipeline1,  propagate: false
}

buildList[pipeline2] = {
  build job: pipeline2,  propagate: false
}

def buildResults = parallel (buildList)

Output:

[pipeline1] Starting building: pipeline1 #3

[pipeline2] Starting building: pipeline2 #6

Assigning value manually is not feasible solution for large number of pipeline. Did anyone had same problem? or what is the issue with first approach with fora loop?


Solution

  • The problem is related on how closures works inside a for loop.

    for instantiates only one variable which is reassigned in each iteration, in your case the jobName variable, therefore when the loops ends the jobName for all closures created in the for has the same value for this variable, the value is the last assigned to it.

    Groovy closures instead creates a new variable for each iteration, so if you change the for by each it'll work as expected:

    def testJobs = [
      'pipeline1',
      'pipeline2'
    ]
    def buildList = [:]
    
    // use each instead of for
    testJobs.each{ jobName -> 
      buildList[jobName] = {
        build job: jobName,  propagate: false
      }
    }
    
    def buildResults = parallel (buildList)