-
Bug
-
Resolution: Not A Defect
-
Minor
-
None
-
Powered by SuggestiMate
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
So this is a little dumb, but if you do timestamps() rather than just timestamps, it works. I know, I know. I'll see if I can make it smarter.
Note to myself - if it's viable to solve this, then we need to do two things:
- First, get the parser and validator to detect MethodASTMethodCall without args without parentheses in the right cases.
- Second, get the runtime to probably do a propertyMissing implementation that will return script."barething"() in that case as well.
Not 100% sure that's possible but I'll see what I can do.
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.
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.
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)
#!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() } } } } }
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. =)
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.
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.
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.
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.
Just for completeness, the documentation for this in groovy is at
http://groovy-lang.org/style-guide.html#_omitting_parentheses.
As another data point, timestamps does work within a steps block.