-
Bug
-
Resolution: Unresolved
-
Major
-
None
-
Jenkins 2.32.1
pipeline plugin versions:
Pipeline 2.5
Pipeline Graph Analysis Plugin 1.2
Pipeline REST API for Blue Ocean 1.0.0-b15
Pipeline: API 2.17
Pipeline: Basic Steps 2.5
Pipeline: Build Step 2.5
Pipeline: Classpath Steps 0.1.0
Pipeline: Declarative Agent API 1.1.1
Pipeline: Declarative Extension Points API 1.1.6
Pipeline: Groovy 2.34
Pipeline: Input Step 2.7
Pipeline: Job 2.11
Pipeline: Milestone Step 1.3.1
Pipeline: Model API 1.1.6
Pipeline: Model Definition 1.1.6
Pipeline: Multibranch 2.15
Pipeline: Nodes and Processes 2.11
Pipeline: REST API Plugin 2.8
Pipeline: SCM Step 2.4
Pipeline: Shared Groovy Libraries 2.8
Pipeline: Stage Step 2.2
Pipeline: Stage Tags Metadata 1.1.6
Pipeline: Stage View Plugin 2.8
Pipeline: Step API 2.11
Pipeline: Supporting APIs 2.14Jenkins 2.32.1 pipeline plugin versions: Pipeline 2.5 Pipeline Graph Analysis Plugin 1.2 Pipeline REST API for Blue Ocean 1.0.0-b15 Pipeline: API 2.17 Pipeline: Basic Steps 2.5 Pipeline: Build Step 2.5 Pipeline: Classpath Steps 0.1.0 Pipeline: Declarative Agent API 1.1.1 Pipeline: Declarative Extension Points API 1.1.6 Pipeline: Groovy 2.34 Pipeline: Input Step 2.7 Pipeline: Job 2.11 Pipeline: Milestone Step 1.3.1 Pipeline: Model API 1.1.6 Pipeline: Model Definition 1.1.6 Pipeline: Multibranch 2.15 Pipeline: Nodes and Processes 2.11 Pipeline: REST API Plugin 2.8 Pipeline: SCM Step 2.4 Pipeline: Shared Groovy Libraries 2.8 Pipeline: Stage Step 2.2 Pipeline: Stage Tags Metadata 1.1.6 Pipeline: Stage View Plugin 2.8 Pipeline: Step API 2.11 Pipeline: Supporting APIs 2.14
Hi,
First of all - I will describe what I'm trying to do:
I have multiple "library" pipeline scripts. I'm using the pipeline Load step to load them.
Some scripts need to use methods in other scripts - think of the following scenario:
pipeline Job loads script1.groovy and script2.groovy , script1.groovy loads script2.groovy as well. This results in script2.groovy being loaded twice for no good reason.
I want to achieve lazy loading, meaning if the same script has already been loaded - I want to reuse it and not load it again, meaning script1.groovy should use script2.groovy which was loaded by the job itself.
I tried to do it with the following code - in this example I do want the 2nd script to load the file on its own:
job code:
timestamps { node('some node') { //some code to get the files to the workspace - not relevant to the situation def test = load("./src/jenkins/pipeline/utils/test.groovy") test.test() } }
test.groovy
/* ***** lazy loading - BEGIN ***** */ this._scripts = [:] def getScript(def scriptName) { // lazy loading if (this._scripts."${scriptName}" == null) { this._scripts."${scriptName}" = load("${WORKSPACE}/src/jenkins/pipeline/utils/${scriptName}.groovy") this._scripts."${scriptName}".init(this._scripts) } return this._scripts."${scriptName}" } def init(def scripts) { if (scripts) this._scripts = scripts } /* ***** lazy loading - END ***** */ def test() { getScript("command_runner").runHostCommand("echo 'hello world'") } return this
command_runner.groovy
/* ***** lazy loading - BEGIN ***** */ this._scripts = [:] def getScript(def scriptName) { // lazy loading if (this._scripts."${scriptName}" == null) { this._scripts."${scriptName}" = load("${WORKSPACE}/src/jenkins/pipeline/utils/${scriptName}.groovy") this._scripts."${scriptName}".init(this._scripts) } return this._scripts."${scriptName}" } def init(def scripts) { if (scripts) this._scripts = scripts } /* ***** lazy loading - END ***** */ /** * Runs a command and returns the exit code. assumes windows [for now] * @param command * @return */ def runHostCommand(def command) { int exitCode exitCode = bat script: command, returnStatus: true return exitCode } def runHostCommandWithOutput(def command) { currentDir = pwd() println "${currentDir}>${command}" def output = bat (script: command, returnStdout: true).trim() //output contains the call the command itself so we should remove it when printing output = output.substring(output.indexOf('\n')+1); println output return output } /* *** mandatory to be able to call from another bootstrap script *** */ return this
When running it - I get the following exception
... [Pipeline] load [Pipeline] { (./src/jenkins/pipeline/utils/test.groovy) [Pipeline] } [Pipeline] // load [Pipeline] load [Pipeline] { (F:\jenkins-remote\workspace\Mor\pipeline_tests/src/jenkins/pipeline/utils/command_runner.groovy) [Pipeline] } [Pipeline] // load [Pipeline] } [Pipeline] // node [Pipeline] } [Pipeline] // timestamps [Pipeline] End of Pipeline java.lang.NullPointerException: Cannot invoke method init() on null object at org.codehaus.groovy.runtime.NullObject.invokeMethod(NullObject.java:91) 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.NullCallSite.call(NullCallSite.java:35) 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 Script1.getScript(Script1.groovy:13) at Script1.test(Script1.groovy:30) at WorkflowScript.run(WorkflowScript:26) 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.fixArg(FunctionCallBlock.java:82) at sun.reflect.GeneratedMethodAccessor270.invoke(Unknown Source) at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source) at java.lang.reflect.Method.invoke(Unknown Source) at com.cloudbees.groovy.cps.impl.ContinuationPtr$ContinuationImpl.receive(ContinuationPtr.java:72) at com.cloudbees.groovy.cps.impl.PropertyishBlock$ContinuationImpl.get(PropertyishBlock.java:76) at com.cloudbees.groovy.cps.LValueBlock$GetAdapter.receive(LValueBlock.java:30) at com.cloudbees.groovy.cps.impl.PropertyishBlock$ContinuationImpl.fixName(PropertyishBlock.java:66) at sun.reflect.GeneratedMethodAccessor355.invoke(Unknown Source) at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source) at java.lang.reflect.Method.invoke(Unknown Source) 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(Unknown Source) 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(Unknown Source) at java.util.concurrent.FutureTask.run(Unknown Source) at java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source) at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source) at java.lang.Thread.run(Unknown Source) Finished: FAILURE
According to the log, the Load command within test.groovy succeeded, but for some reason returned null.
so I'm looking for either a solution to the problem, or a better way to achieve lazy loading. And yes - I am familiar with Shared Pipeline Libraries plugin which is not suitable for my case at this stage.
Thanks in advance,
Mor
jglick would more than appreciate your feedback on this or lazy loading in general.
Thanks.