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

Wrappers provided by plugins not working correctly

      I was going through how to use wrappers in Declarative Pipeline https://github.com/jenkinsci/pipeline-model-definition-plugin/wiki/Advanced looking how to use the timestamper plugin specifically. Initially I used the wrap() syntax around the classname, which provided this rather helpful error message

      org.codehaus.groovy.control.MultipleCompilationErrorsException: startup failed:
      WorkflowScript: 9: Invalid wrapper type 'wrap'. Valid wrapper types: [ansiColor, catchError, gitlabBuilds, gitlabCommitStatus, lock, node, podTemplate, retry, script, timeout, timestamps, waitUntil, withContext, withEnv, ws] @ line 9, column 9.
      wrap([$class: 'TimestamperBuildWrapper'])
      ^

      So there it was, I just needed to use 'timestamps' and it should work, as declarative pipeline identified it as an available wrapper.

      Unfortunately when trying to do that, I received the following error

      org.codehaus.groovy.control.MultipleCompilationErrorsException: startup failed:
      WorkflowScript: 9: Expected a wrapper @ line 9, column 9.
      timestamps
      ^

      To see if it was an issue with the timestamper plugin, I also tried it with ansiColor, which again showed above as an available wrapper

      org.codehaus.groovy.control.MultipleCompilationErrorsException: startup failed:
      WorkflowScript: 9: Expected a wrapper @ line 9, column 9.
      ansiColor
      ^

      It seems Declarative pipeline is identifying them as available wrappers, but unable to use them as wrappers

          [JENKINS-40721] Wrappers provided by plugins not working correctly

          Andrew Bayer added a comment -

          And the probably easier alternative would be to have better error messages - don't just say "not a wrapper, GTFO" but instead "not a wrapper, here's what wrapper syntax is - foo(), bar("arg"), baz(oneArg: 1, twoArg: 2)" or something like that.

          Andrew Bayer added a comment - And the probably easier alternative would be to have better error messages - don't just say "not a wrapper, GTFO" but instead "not a wrapper, here's what wrapper syntax is - foo(), bar("arg"), baz(oneArg: 1, twoArg: 2)" or something like that.

          Andrew Bayer added a comment -

          So...surprise! I think I got it working. PR up at https://github.com/jenkinsci/pipeline-model-definition-plugin/pull/82 but I am still wary of it.

          Andrew Bayer added a comment - So...surprise! I think I got it working. PR up at https://github.com/jenkinsci/pipeline-model-definition-plugin/pull/82 but I am still wary of it.

          Robby Pocase added a comment - - edited

          For what it's worth, timestamps() didn't seem to work (not that it should matter with the next plugin update).

          java.lang.IllegalStateException: There is no body to invoke
          	at org.jenkinsci.plugins.workflow.cps.CpsStepContext.newBodyInvoker(CpsStepContext.java:282)
          	at org.jenkinsci.plugins.workflow.cps.CpsStepContext.newBodyInvoker(CpsStepContext.java:95)
          	at hudson.plugins.timestamper.pipeline.TimestamperStep$ExecutionImpl.start(TimestamperStep.java:80)
          	at org.jenkinsci.plugins.workflow.cps.DSL.invokeStep(DSL.java:184)
          	at org.jenkinsci.plugins.workflow.cps.DSL.invokeMethod(DSL.java:126)
          	at org.jenkinsci.plugins.workflow.cps.CpsScript.invokeMethod(CpsScript.java:108)
          	at org.codehaus.groovy.runtime.callsite.PogoMetaClassSite.call(PogoMetaClassSite.java:48)
          	at org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCall(CallSiteArray.java:48)
          	at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:113)
          	at com.cloudbees.groovy.cps.sandbox.DefaultInvoker.methodCall(DefaultInvoker.java:18)
          	at org.jenkinsci.plugins.pipeline.modeldefinition.MethodsToListTranslator.methodMissing(jar:file:/data/home/jenkins/plugins/pipeline-model-definition/WEB-INF/lib/pipeline-model-definition.jar!/org/jenkinsci/plugins/pipeline/modeldefinition/MethodsToListTranslator.groovy:67)
          	at WorkflowScript.run(WorkflowScript:12)
          	at org.jenkinsci.plugins.pipeline.modeldefinition.ClosureModelTranslator.resolveClosure(jar:file:/data/home/jenkins/plugins/pipeline-model-definition/WEB-INF/lib/pipeline-model-definition.jar!/org/jenkinsci/plugins/pipeline/modeldefinition/ClosureModelTranslator.groovy:212)
          	at org.jenkinsci.plugins.pipeline.modeldefinition.ClosureModelTranslator.methodMissing(jar:file:/data/home/jenkins/plugins/pipeline-model-definition/WEB-INF/lib/pipeline-model-definition.jar!/org/jenkinsci/plugins/pipeline/modeldefinition/ClosureModelTranslator.groovy:141)
          	at WorkflowScript.run(WorkflowScript:10)
          	at org.jenkinsci.plugins.pipeline.modeldefinition.ModelInterpreter.call(jar:file:/data/home/jenkins/plugins/pipeline-model-definition/WEB-INF/lib/pipeline-model-definition.jar!/org/jenkinsci/plugins/pipeline/modeldefinition/ModelInterpreter.groovy:58)
          	at WorkflowScript.run(WorkflowScript:7)
          	at ___cps.transform___(Native Method)
          	at com.cloudbees.groovy.cps.impl.ContinuationGroup.methodCall(ContinuationGroup.java:57)
          	at com.cloudbees.groovy.cps.impl.FunctionCallBlock$ContinuationImpl.dispatchOrArg(FunctionCallBlock.java:109)
          	at com.cloudbees.groovy.cps.impl.FunctionCallBlock$ContinuationImpl.fixName(FunctionCallBlock.java:77)
          	at sun.reflect.GeneratedMethodAccessor642.invoke(Unknown Source)
          	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.FunctionCallBlock$ContinuationImpl.dispatchOrArg(FunctionCallBlock.java:103)
          	at com.cloudbees.groovy.cps.impl.FunctionCallBlock$ContinuationImpl.fixArg(FunctionCallBlock.java:82)
          	at sun.reflect.GeneratedMethodAccessor640.invoke(Unknown Source)
          	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.ContinuationGroup.methodCall(ContinuationGroup.java:60)
          	at com.cloudbees.groovy.cps.impl.FunctionCallBlock$ContinuationImpl.dispatchOrArg(FunctionCallBlock.java:109)
          	at com.cloudbees.groovy.cps.impl.FunctionCallBlock$ContinuationImpl.fixArg(FunctionCallBlock.java:82)
          	at sun.reflect.GeneratedMethodAccessor640.invoke(Unknown Source)
          	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:58)
          	at com.cloudbees.groovy.cps.Continuable.run0(Continuable.java:154)
          	at org.jenkinsci.plugins.workflow.cps.SandboxContinuable.access$001(SandboxContinuable.java:18)
          	at org.jenkinsci.plugins.workflow.cps.SandboxContinuable$1.call(SandboxContinuable.java:33)
          	at org.jenkinsci.plugins.workflow.cps.SandboxContinuable$1.call(SandboxContinuable.java:30)
          	at org.jenkinsci.plugins.scriptsecurity.sandbox.groovy.GroovySandbox.runInSandbox(GroovySandbox.java:108)
          	at org.jenkinsci.plugins.workflow.cps.SandboxContinuable.run0(SandboxContinuable.java:30)
          	at org.jenkinsci.plugins.workflow.cps.CpsThread.runNextChunk(CpsThread.java:163)
          	at org.jenkinsci.plugins.workflow.cps.CpsThreadGroup.run(CpsThreadGroup.java:324)
          	at org.jenkinsci.plugins.workflow.cps.CpsThreadGroup.access$100(CpsThreadGroup.java:78)
          	at org.jenkinsci.plugins.workflow.cps.CpsThreadGroup$2.call(CpsThreadGroup.java:236)
          	at org.jenkinsci.plugins.workflow.cps.CpsThreadGroup$2.call(CpsThreadGroup.java:224)
          	at org.jenkinsci.plugins.workflow.cps.CpsVmExecutorService$2.call(CpsVmExecutorService.java:63)
          	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:1142)
          	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
          	at java.lang.Thread.run(Thread.java:745)
          
          Jenkinsfile
          #!groovy
          def run_tests(environment) {
            sh "make ${environment}"
            publishHTML([allowMissing: false, alwaysLinkToLastBuild: false, keepAll: true, reportDir: "${environment}cov", reportFiles: 'index.html', reportName: "${environment} Coverage Report"])
          }
          
          pipeline {
            agent label:'common'
          
            properties {
              buildDiscarder(logRotator(numToKeepStr:'10'))
              timestamps()
            }
          
            stages {
              stage('Analysis') {
                steps {
                  sh 'make analysis'
                }
                post {
                  failure {
                    deleteDir()
                  }
                }
              }
          
              stage('Tests') {
                steps {
                  parallel (
                    "py27-test": {
                      script {
                        run_tests('py27-test')
                      }
                    },
                    "py3-test": {
                      script {
                        run_tests('py3-test')
                      }
                    }
                  )
                }
          
                post {
                  always {
                    junit '*results.xml'
                    deleteDir()
                  }
                }
              }
            }
          }
          

          Robby Pocase added a comment - - edited For what it's worth, timestamps() didn't seem to work (not that it should matter with the next plugin update). java.lang.IllegalStateException: There is no body to invoke at org.jenkinsci.plugins.workflow.cps.CpsStepContext.newBodyInvoker(CpsStepContext.java:282) at org.jenkinsci.plugins.workflow.cps.CpsStepContext.newBodyInvoker(CpsStepContext.java:95) at hudson.plugins.timestamper.pipeline.TimestamperStep$ExecutionImpl.start(TimestamperStep.java:80) at org.jenkinsci.plugins.workflow.cps.DSL.invokeStep(DSL.java:184) at org.jenkinsci.plugins.workflow.cps.DSL.invokeMethod(DSL.java:126) at org.jenkinsci.plugins.workflow.cps.CpsScript.invokeMethod(CpsScript.java:108) at org.codehaus.groovy.runtime.callsite.PogoMetaClassSite.call(PogoMetaClassSite.java:48) at org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCall(CallSiteArray.java:48) at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:113) at com.cloudbees.groovy.cps.sandbox.DefaultInvoker.methodCall(DefaultInvoker.java:18) at org.jenkinsci.plugins.pipeline.modeldefinition.MethodsToListTranslator.methodMissing(jar:file:/data/home/jenkins/plugins/pipeline-model-definition/WEB-INF/lib/pipeline-model-definition.jar!/org/jenkinsci/plugins/pipeline/modeldefinition/MethodsToListTranslator.groovy:67) at WorkflowScript.run(WorkflowScript:12) at org.jenkinsci.plugins.pipeline.modeldefinition.ClosureModelTranslator.resolveClosure(jar:file:/data/home/jenkins/plugins/pipeline-model-definition/WEB-INF/lib/pipeline-model-definition.jar!/org/jenkinsci/plugins/pipeline/modeldefinition/ClosureModelTranslator.groovy:212) at org.jenkinsci.plugins.pipeline.modeldefinition.ClosureModelTranslator.methodMissing(jar:file:/data/home/jenkins/plugins/pipeline-model-definition/WEB-INF/lib/pipeline-model-definition.jar!/org/jenkinsci/plugins/pipeline/modeldefinition/ClosureModelTranslator.groovy:141) at WorkflowScript.run(WorkflowScript:10) at org.jenkinsci.plugins.pipeline.modeldefinition.ModelInterpreter.call(jar:file:/data/home/jenkins/plugins/pipeline-model-definition/WEB-INF/lib/pipeline-model-definition.jar!/org/jenkinsci/plugins/pipeline/modeldefinition/ModelInterpreter.groovy:58) at WorkflowScript.run(WorkflowScript:7) at ___cps.transform___(Native Method) at com.cloudbees.groovy.cps.impl.ContinuationGroup.methodCall(ContinuationGroup.java:57) at com.cloudbees.groovy.cps.impl.FunctionCallBlock$ContinuationImpl.dispatchOrArg(FunctionCallBlock.java:109) at com.cloudbees.groovy.cps.impl.FunctionCallBlock$ContinuationImpl.fixName(FunctionCallBlock.java:77) at sun.reflect.GeneratedMethodAccessor642.invoke(Unknown Source) 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.FunctionCallBlock$ContinuationImpl.dispatchOrArg(FunctionCallBlock.java:103) at com.cloudbees.groovy.cps.impl.FunctionCallBlock$ContinuationImpl.fixArg(FunctionCallBlock.java:82) at sun.reflect.GeneratedMethodAccessor640.invoke(Unknown Source) 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.ContinuationGroup.methodCall(ContinuationGroup.java:60) at com.cloudbees.groovy.cps.impl.FunctionCallBlock$ContinuationImpl.dispatchOrArg(FunctionCallBlock.java:109) at com.cloudbees.groovy.cps.impl.FunctionCallBlock$ContinuationImpl.fixArg(FunctionCallBlock.java:82) at sun.reflect.GeneratedMethodAccessor640.invoke(Unknown Source) 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:58) at com.cloudbees.groovy.cps.Continuable.run0(Continuable.java:154) at org.jenkinsci.plugins.workflow.cps.SandboxContinuable.access$001(SandboxContinuable.java:18) at org.jenkinsci.plugins.workflow.cps.SandboxContinuable$1.call(SandboxContinuable.java:33) at org.jenkinsci.plugins.workflow.cps.SandboxContinuable$1.call(SandboxContinuable.java:30) at org.jenkinsci.plugins.scriptsecurity.sandbox.groovy.GroovySandbox.runInSandbox(GroovySandbox.java:108) at org.jenkinsci.plugins.workflow.cps.SandboxContinuable.run0(SandboxContinuable.java:30) at org.jenkinsci.plugins.workflow.cps.CpsThread.runNextChunk(CpsThread.java:163) at org.jenkinsci.plugins.workflow.cps.CpsThreadGroup.run(CpsThreadGroup.java:324) at org.jenkinsci.plugins.workflow.cps.CpsThreadGroup.access$100(CpsThreadGroup.java:78) at org.jenkinsci.plugins.workflow.cps.CpsThreadGroup$2.call(CpsThreadGroup.java:236) at org.jenkinsci.plugins.workflow.cps.CpsThreadGroup$2.call(CpsThreadGroup.java:224) at org.jenkinsci.plugins.workflow.cps.CpsVmExecutorService$2.call(CpsVmExecutorService.java:63) 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:1142) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617) at java.lang.Thread.run(Thread.java:745) Jenkinsfile #!groovy def run_tests(environment) { sh "make ${environment}" publishHTML([allowMissing: false , alwaysLinkToLastBuild: false , keepAll: true , reportDir: "${environment}cov" , reportFiles: 'index.html' , reportName: "${environment} Coverage Report" ]) } pipeline { agent label: 'common' properties { buildDiscarder(logRotator(numToKeepStr: '10' )) timestamps() } stages { stage( 'Analysis' ) { steps { sh 'make analysis' } post { failure { deleteDir() } } } stage( 'Tests' ) { steps { parallel ( "py27-test" : { script { run_tests( 'py27-test' ) } }, "py3-test" : { script { run_tests( 'py3-test' ) } } ) } post { always { junit '*results.xml' deleteDir() } } } } }

          Andrew Bayer added a comment -

          rpocase Ah, that's 'cos until 0.8, timestamps() should go in a separate wrappers section. As of 0.8, once JENKINS-40462 lands, job properties, Declarative options and "wrappers" like timestamps will all go in the options section. Sorry for the changing syntax. =)

          Andrew Bayer added a comment - rpocase Ah, that's 'cos until 0.8, timestamps() should go in a separate wrappers section. As of 0.8, once JENKINS-40462 lands, job properties, Declarative options and "wrappers" like timestamps will all go in the options section. Sorry for the changing syntax. =)

          Robby Pocase added a comment -

          Oh - right . I just now started looking at converting some old pipeline scripts to declarative and am obviously too up to date with changes and confusing what's available.

          Robby Pocase added a comment - Oh - right . I just now started looking at converting some old pipeline scripts to declarative and am obviously too up to date with changes and confusing what's available.

          Liam Newman added a comment -

          Copying in my comment on the PR:
          Unless you plan to make this change work everywhere in Declarative, do not do it now.

          It is another special case for documentation and a support. I understand this was confusing for the reporter, but just as many people will find it confusing the other way. The last thing we need is more special cases: you don't need empty parens here, but you do need them there, oh, but not that other place.

          Groovy overall makes parens optional but not for methods with no parameters. I see zero benefit to innovating away from that. Let's try to stick with either "It works just like it does in plain old Groovy or it is not allowed".

          This is something that should at best be post-1.0 - it is a non-breaking change to make the empty parens optional later. On the other hand, if you make them optional now, it is a breaking change if you find some reason why they are needed later. This feature is already coming in hot enough.

          Liam Newman added a comment - Copying in my comment on the PR: Unless you plan to make this change work everywhere in Declarative, do not do it now. It is another special case for documentation and a support. I understand this was confusing for the reporter, but just as many people will find it confusing the other way. The last thing we need is more special cases: you don't need empty parens here, but you do need them there, oh, but not that other place. Groovy overall makes parens optional but not for methods with no parameters. I see zero benefit to innovating away from that. Let's try to stick with either "It works just like it does in plain old Groovy or it is not allowed". This is something that should at best be post-1.0 - it is a non-breaking change to make the empty parens optional later. On the other hand, if you make them optional now, it is a breaking change if you find some reason why they are needed later. This feature is already coming in hot enough.

          Andrew Bayer added a comment -

          Decided not to merge the PR - as bitwiseman pointed out, this would be rather inconsistent with everything else in the syntax and with Groovy itself, so despite the potential for confusion, we're better off not doing this.

          Andrew Bayer added a comment - Decided not to merge the PR - as bitwiseman pointed out, this would be rather inconsistent with everything else in the syntax and with Groovy itself, so despite the potential for confusion, we're better off not doing this.

          Robby Pocase added a comment -

          Seems reasonable to me - I actually didn't realize Groovy required parenthesis for parameterless functions. Minor inconsistency in the base language is likely where any initial confusion came from.

          Robby Pocase added a comment - Seems reasonable to me - I actually didn't realize Groovy required parenthesis for parameterless functions. Minor inconsistency in the base language is likely where any initial confusion came from.

          Liam Newman added a comment - - edited

          Just for completeness, the documentation for this in groovy is at
          http://groovy-lang.org/style-guide.html#_omitting_parentheses.

          Liam Newman added a comment - - edited Just for completeness, the documentation for this in groovy is at http://groovy-lang.org/style-guide.html#_omitting_parentheses .

          Liam Newman added a comment -

          Bulk closing resolved issues.

          Liam Newman added a comment - Bulk closing resolved issues.

            abayer Andrew Bayer
            tomlarrow Tom Larrow
            Votes:
            1 Vote for this issue
            Watchers:
            6 Start watching this issue

              Created:
              Updated:
              Resolved: