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

.collect, then .each still results in closure being invoked only for first element

    • Icon: Bug Bug
    • Resolution: Unresolved
    • Icon: Major Major
    • workflow-cps-plugin
    • None
    • workflow-cps-plugin 2.39

      Running this Jenkinsfile

      def lines2 = [['Quick', 'Brown']]
      echo "Literal toString():" + lines2
      def result2 = ''
      lines2.each{ result2 += it }
      echo "Literal each: " + result2
      
      @NonCPS def test(){
          def lines = ['Quick Brown'].collect{ it.split('\\s+') }
          echo "NonCps toString()" + lines
          def result = ''
          lines.each{ result += it }
          echo "NonCps each: " + result
      }
      test()
      
      def lines = ['Quick Brown'].collect{ it.split('\\s+') }
      echo "Collect toString():" + lines
      def result = ''
      lines.each{ result += it }
      echo "Collect each: " + result

      Results in

      [Pipeline] echo
      Literal toString():[[Quick, Brown]]
      [Pipeline] echo
      Literal each: [Quick, Brown]
      [Pipeline] echo
      NonCps toString()[[Quick, Brown]]
      [Pipeline] echo
      NonCps each: [Quick, Brown]
      [Pipeline] echo
      Collect toString():[[Quick, Brown]]
      [Pipeline] echo
      Collect each: Quick
      [Pipeline] End of Pipeline
      Finished: SUCCESS
      

      As you can see calling .each on a literal list works correctly, but calling it on a result of .collect still fails like JENKINS-26481

          [JENKINS-46749] .collect, then .each still results in closure being invoked only for first element

          Jakub Bochenski created issue -
          Jakub Bochenski made changes -
          Link New: This issue relates to JENKINS-26481 [ JENKINS-26481 ]
          Jakub Bochenski made changes -
          Description Original: Trying to run the docs example in pipeline script fails:
          {code}def s = 'The 3 quick\nbrown 4 fox'
          def result = ''
          new StringReader(s).splitEachLine(/\d/){ parts ->
              result += "${parts[0]}_${parts[1]}|"
          }
          assert result == 'The _ quick|brown _ fox|'{code}

          This results in {code}
          hudson.remoting.ProxyException: Assertion failed:

          assert result == 'The _ quick|brown _ fox|'

          at org.codehaus.groovy.runtime.InvokerHelper.assertFailed(InvokerHelper.java:404)
          at org.codehaus.groovy.runtime.ScriptBytecodeAdapter.assertFailed(ScriptBytecodeAdapter.java:650)
          at com.cloudbees.groovy.cps.impl.AssertBlock$ContinuationImpl.fail(AssertBlock.java:47)
          at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
          at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
          at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
          at java.lang.reflect.Method.invoke(Method.java:498)
          at com.cloudbees.groovy.cps.impl.ContinuationPtr$ContinuationImpl.receive(ContinuationPtr.java:72)
          at com.cloudbees.groovy.cps.impl.ConstantBlock.eval(ConstantBlock.java:21)
          at com.cloudbees.groovy.cps.Next.step(Next.java:83)
          at com.cloudbees.groovy.cps.Continuable$1.call(Continuable.java:173)
          at com.cloudbees.groovy.cps.Continuable$1.call(Continuable.java:162)
          at org.codehaus.groovy.runtime.GroovyCategorySupport$ThreadCategoryInfo.use(GroovyCategorySupport.java:122)
          at org.codehaus.groovy.runtime.GroovyCategorySupport.use(GroovyCategorySupport.java:261)
          at com.cloudbees.groovy.cps.Continuable.run0(Continuable.java:162)
          at org.jenkinsci.plugins.workflow.cps.CpsThread.runNextChunk(CpsThread.java:174)
          at org.jenkinsci.plugins.workflow.cps.CpsThreadGroup.run(CpsThreadGroup.java:330)
          at org.jenkinsci.plugins.workflow.cps.CpsThreadGroup.access$100(CpsThreadGroup.java:82)
          at org.jenkinsci.plugins.workflow.cps.CpsThreadGroup$2.call(CpsThreadGroup.java:242)
          at org.jenkinsci.plugins.workflow.cps.CpsThreadGroup$2.call(CpsThreadGroup.java:230)
          at org.jenkinsci.plugins.workflow.cps.CpsVmExecutorService$2.call(CpsVmExecutorService.java:64)
          at java.util.concurrent.FutureTask.run(FutureTask.java:266)
          at hudson.remoting.SingleLaneExecutorService$1.run(SingleLaneExecutorService.java:112)
          at jenkins.util.ContextResettingExecutorService$1.run(ContextResettingExecutorService.java:28)
          at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511)
          at java.util.concurrent.FutureTask.run(FutureTask.java:266)
          at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
          at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
          at java.lang.Thread.run(Thread.java:748)
          Finished: FAILURE
          {code}

          The value of {{result}} is just {{The _ quick|}}
          New: Running this Jenkinsfile
          {code}def lines2 = [['Quick', 'Brown']]
          echo "Literal toString():" + lines2
          def result2 = ''
          lines2.each{ result2 += it }
          echo "Literal each: " + result2

          @NonCPS def test(){
              def lines = ['Quick Brown'].collect{ it.split('\\s+') }
              echo "NonCps toString()" + lines
              def result = ''
              lines.each{ result += it }
              echo "NonCps each: " + result
          }
          test()

          def lines = ['Quick Brown'].collect{ it.split('\\s+') }
          echo "Collect toString():" + lines
          def result = ''
          lines.each{ result += it }
          echo "Collect each: " + result{code}

          Results in {code}
          [Pipeline] echo
          Literal toString():[[Quick, Brown]]
          [Pipeline] echo
          Literal each: [Quick, Brown]
          [Pipeline] echo
          NonCps toString()[[Quick, Brown]]
          [Pipeline] echo
          NonCps each: [Quick, Brown]
          [Pipeline] echo
          Collect toString():[[Quick, Brown]]
          [Pipeline] echo
          Collect each: Quick
          [Pipeline] End of Pipeline
          Finished: SUCCESS
          {code}

          As you can see calling only {{.each}} works correctly, but calling it on a result of {{.collect}} still fails like
          Jakub Bochenski made changes -
          Description Original: Running this Jenkinsfile
          {code}def lines2 = [['Quick', 'Brown']]
          echo "Literal toString():" + lines2
          def result2 = ''
          lines2.each{ result2 += it }
          echo "Literal each: " + result2

          @NonCPS def test(){
              def lines = ['Quick Brown'].collect{ it.split('\\s+') }
              echo "NonCps toString()" + lines
              def result = ''
              lines.each{ result += it }
              echo "NonCps each: " + result
          }
          test()

          def lines = ['Quick Brown'].collect{ it.split('\\s+') }
          echo "Collect toString():" + lines
          def result = ''
          lines.each{ result += it }
          echo "Collect each: " + result{code}

          Results in {code}
          [Pipeline] echo
          Literal toString():[[Quick, Brown]]
          [Pipeline] echo
          Literal each: [Quick, Brown]
          [Pipeline] echo
          NonCps toString()[[Quick, Brown]]
          [Pipeline] echo
          NonCps each: [Quick, Brown]
          [Pipeline] echo
          Collect toString():[[Quick, Brown]]
          [Pipeline] echo
          Collect each: Quick
          [Pipeline] End of Pipeline
          Finished: SUCCESS
          {code}

          As you can see calling only {{.each}} works correctly, but calling it on a result of {{.collect}} still fails like
          New: Running this Jenkinsfile
          {code}def lines2 = [['Quick', 'Brown']]
          echo "Literal toString():" + lines2
          def result2 = ''
          lines2.each{ result2 += it }
          echo "Literal each: " + result2

          @NonCPS def test(){
              def lines = ['Quick Brown'].collect{ it.split('\\s+') }
              echo "NonCps toString()" + lines
              def result = ''
              lines.each{ result += it }
              echo "NonCps each: " + result
          }
          test()

          def lines = ['Quick Brown'].collect{ it.split('\\s+') }
          echo "Collect toString():" + lines
          def result = ''
          lines.each{ result += it }
          echo "Collect each: " + result{code}

          Results in {code}
          [Pipeline] echo
          Literal toString():[[Quick, Brown]]
          [Pipeline] echo
          Literal each: [Quick, Brown]
          [Pipeline] echo
          NonCps toString()[[Quick, Brown]]
          [Pipeline] echo
          NonCps each: [Quick, Brown]
          [Pipeline] echo
          Collect toString():[[Quick, Brown]]
          [Pipeline] echo
          Collect each: Quick
          [Pipeline] End of Pipeline
          Finished: SUCCESS
          {code}

          As you can see calling {{.each}} on a literal list works correctly, but calling it on a result of {{.collect}} still fails like JENKINS-26481

          Andrew Bayer added a comment -

          I need to nail this down a bit more, but the issue here appears to actually be the handling of a list of arrays - [['Quick', 'Brown']] is a list of lists, but {{['Quick Brown'].collect it.split('\s+') }} is a list of arrays.

          Andrew Bayer added a comment - I need to nail this down a bit more, but the issue here appears to actually be the handling of a list of arrays - [ ['Quick', 'Brown'] ] is a list of lists, but {{ ['Quick Brown'] .collect it.split('\s+') }} is a list of arrays.
          Andrew Bayer made changes -
          Assignee New: Andrew Bayer [ abayer ]
          Jesse Glick made changes -
          Component/s Original: pipeline [ 21692 ]
          Labels Original: groovy pipeline
          Andrew Bayer made changes -
          Priority Original: Critical [ 2 ] New: Major [ 3 ]
          Andrew Bayer made changes -
          Assignee Original: Andrew Bayer [ abayer ]

            Unassigned Unassigned
            jbochenski Jakub Bochenski
            Votes:
            2 Vote for this issue
            Watchers:
            6 Start watching this issue

              Created:
              Updated: