Uploaded image for project: 'Jenkins'
  1. Jenkins
  2. JENKINS-68646

Delegate of cloned closure cannot be modified

    • Icon: Bug Bug
    • Resolution: Unresolved
    • Icon: Minor Minor
    • workflow-cps-plugin
    • None

      I experienced a rather strange and, as far as I can tell, erroneous behavior when cloning a closure. To reproduce, create a Pipeline job with the following simple Pipeline script:

      node {
          Closure c1 = { echo "someValue: ${someValue}" }
          c1.delegate = [someValue:1]
          c1.call()
          Closure c2 = c1.clone()
          c2.delegate = [someValue:2]
          c2.call()
      } 

      Closure c1 refers to variable someValue which is resolved through its delegate. However, cloning c1 and setting a different delegate produces the same output:

      Started by user admin
      [Pipeline] Start of Pipeline
      [Pipeline] node
      Running on Jenkins in /var/jenkins_home/workspace/ClosureClone
      [Pipeline] {
      [Pipeline] echo
      someValue: 1
      [Pipeline] echo
      someValue: 1
      [Pipeline] }
      [Pipeline] // node
      [Pipeline] End of Pipeline
      Finished: SUCCESS

      Running this code directly with Groovy (versions >= 2.x) produces the correct result, which is why I assume that it must be caused by Jenkins:

      someValue: 1
      someValue: 2

          [JENKINS-68646] Delegate of cloned closure cannot be modified

          Jesse Glick added a comment -

          Not surprising that use of Groovy exotica turns up bugs. https://github.com/jenkinsci/workflow-cps-plugin/#known-limitations Just avoid clone.

          Jesse Glick added a comment - Not surprising that use of Groovy exotica turns up bugs. https://github.com/jenkinsci/workflow-cps-plugin/#known-limitations Just avoid clone .

          Well, it's not just us trying to be fancy We have our own little Jenkins shared library that provides a very simple DSL. We stumbled upon this because the closure passed is being run multiple times in parallel. We couldn't think of another way to make sure that every execution of the closure gets its own, separate delegate. In the end, we worked around it by passing the delegate explicitly to the closure - which kind of destroys the DSL illusion, but gets the job done.

          Philipp Neuner added a comment - Well, it's not just us trying to be fancy We have our own little Jenkins shared library that provides a very simple DSL. We stumbled upon this because the closure passed is being run multiple times in parallel. We couldn't think of another way to make sure that every execution of the closure gets its own, separate delegate. In the end, we worked around it by passing the delegate explicitly to the closure - which kind of destroys the DSL illusion, but gets the job done.

          Jesse Glick added a comment -

          I would also strongly advise avoiding delegate. You should not really need these special language features; try something like

          Closure make(someValue) {
            return {echo "someValue: $someValue"}
          }
          node {
              Closure c1 = make(1)
              c1.call()
              Closure c2 = make(2)
              c2.call()
          } 
          

          Jesse Glick added a comment - I would also strongly advise avoiding delegate . You should not really need these special language features; try something like Closure make(someValue) { return {echo "someValue: $someValue" } } node { Closure c1 = make(1) c1.call() Closure c2 = make(2) c2.call() }

            Unassigned Unassigned
            p9r Philipp Neuner
            Votes:
            0 Vote for this issue
            Watchers:
            2 Start watching this issue

              Created:
              Updated: