We do some dynamic creation of parallel steps in some of our jobs. Thanks to this thread I found how to dynamically create the map with parameters for use in the parallel step.
However now I wanted to reuse parts of the code which is used to create those parallel steps. For this I feel that I would need to curry the closures.
However currying seems not to work correctly. Referencing the loop variable (valueCopy) inside the closure does the right thing (as mentioned here) but currying does not do what I would expect.
Am I doing something wrong, is this just not (yet) supported, are there any workarounds? Is this probably a bug in Jenkins pipeline?
Hope anybody has a clue why this doesn't work and/or how to get it working.
Jenkins: LTS (2.32.1) & latest plugin updates as of 2017/01/19.
After upgrading to Pipeline: Groovy plugin version 2.40 eveything is working as expected now.
Pipeline script executed:
def echoSome(val) {
echo val
}
def buildClosures() {
def someList = ["1", "2", "3"]
def closures = [:]
for (value in someList) {
final valueCopy = value
closures[value] = {val ->
echo valueCopy.toString()
echo val.toString()
}.curry(value)
}
closures
}
parallel buildClosures()
Output:
[Pipeline] parallel
[Pipeline] [1] { (Branch: 1)
[Pipeline] [2] { (Branch: 2)
[Pipeline] [3] { (Branch: 3)
[Pipeline] [1] echo
[1] 1
[Pipeline] [1] echo
[1] 3
[Pipeline] [1] }
[Pipeline] [2] echo
[2] 2
[Pipeline] [2] echo
[2] 3
[Pipeline] [2] }
[Pipeline] [3] echo
[3] 3
[Pipeline] [3] echo
[3] 3
[Pipeline] [3] }
[Pipeline] // parallel
[Pipeline] End of Pipeline
Finished: SUCCESS
Expected Output:
[Pipeline] parallel
[Pipeline] [1] { (Branch: 1)
[Pipeline] [2] { (Branch: 2)
[Pipeline] [3] { (Branch: 3)
[Pipeline] [1] echo
[1] 1
[Pipeline] [1] echo
[1] 1
[Pipeline] [1] }
[Pipeline] [2] echo
[2] 2
[Pipeline] [2] echo
[2] 2
[Pipeline] [2] }
[Pipeline] [3] echo
[3] 3
[Pipeline] [3] echo
[3] 3
[Pipeline] [3] }
[Pipeline] // parallel
[Pipeline] End of Pipeline
Finished: SUCCESS
I'm not sure if it's the currying, or the for loop, but this function needs to be marked as NonCPS as described here: https://github.com/jenkinsci/pipeline-examples/blob/master/docs/BEST_PRACTICES.md#groovy-gotchas
Essentially, do this:
@NonCPS
def buildClosures() {
def someList = ["1", "2", "3"]
def closures = [:]
for (value in someList) {
final valueCopy = value
closures[value] = {val ->
echo valueCopy.toString()
echo val.toString()
}.curry(value)
}
closures
}
I think it's your for loop, but regardless, anytime you don't use classic "C Style" loops, you'll need to mark your function as NonCPS.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With