2
votes

I'm currently refactoring our Jenkins pipeline from a pipeline project to a multibranch pipeline project and trying to make some functionality more modular and trying to migrate more code to a shared pipeline project. However, this results in compile errors that feels related to groovy scoping, where it tries to use the same scope as the function when calling to a third party library instead of using the global scope.

The original function resided in my scripted pipeline jenkinsfile and looks like this:

def bootstrapPythonEnvironment( String client, String credentials, String label = "#head" ) {
        String bootstrapWorkspace = "${client}_BootstrapWorkspace"
        def p4 = p4(credential: credentials,
            workspace: manualSpec(
                charset: 'winansi',
                name: bootstrapWorkspace,
                spec: clientSpec(
                    view: "//MyGameContent/BuildScripts/Python/... //${bootstrapWorkspace}/BootstrapScripts/..." )
            ))
        p4.run('sync', "//MyGameContent/...", label)
}

However, when moving it from the Jenkinsfile to: vars/BuildTools.groovy and I call it through: BuildTools.bootstrapJenkinsEnvironment( env.MyClient, 'CSSBuildmachine', '#head' ) then I get the following error:

hudson.remoting.ProxyException: groovy.lang.MissingMethodException: No signature of method: static BuildTools.bootstrapPythonEnvironment() is applicable for argument types: (java.lang.String, java.lang.String, java.lang.String) values: [jenkins-Stekdatorn-TestWorkspace-null-0, CSSBuildmachine, ...]
Possible solutions: bootstrapPythonEnvironment(java.lang.String, java.lang.String, java.lang.String), bootstrapPythonEnvironment(java.lang.String, java.lang.String)

So I changed the function declaration to incluide static: static def bootstrapPythonEnvironment( String client, String credentials, String label = "#head" ) This helps a bit, but now the error I get is:

hudson.remoting.ProxyException: groovy.lang.MissingMethodException: No signature of method: static BuildTools.clientSpec() is applicable for argument types: (java.util.LinkedHashMap) values: [[view://MyGameContent/BuildScripts/Python/... //jenkins-Stekdatorn-TestWorkspace-null-0_BootstrapWorkspace/BootstrapScripts/...]]
    at groovy.lang.MetaClassImpl.invokeStaticMissingMethod(MetaClassImpl.java:1501)
    at groovy.lang.MetaClassImpl.invokeStaticMethod(MetaClassImpl.java:1487)
    at org.codehaus.groovy.runtime.callsite.StaticMetaClassSite.call(StaticMetaClassSite.java:53)
    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:20)
    at BuildTools.bootstrapPythonEnvironment(BuildTools.groovy:11)
    at WorkflowScript.run(WorkflowScript:18)
    at ___cps.transform___(Native Method)
    at com.cloudbees.groovy.cps.impl.ContinuationGroup.methodCall(ContinuationGroup.java:84)
    at com.cloudbees.groovy.cps.impl.FunctionCallBlock$ContinuationImpl.dispatchOrArg(FunctionCallBlock.java:113)
    at com.cloudbees.groovy.cps.impl.FunctionCallBlock$ContinuationImpl.fixArg(FunctionCallBlock.java:83)
    at sun.reflect.GeneratedMethodAccessor324.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.CollectionLiteralBlock$ContinuationImpl.dispatch(CollectionLiteralBlock.java:55)
    at com.cloudbees.groovy.cps.impl.CollectionLiteralBlock$ContinuationImpl.item(CollectionLiteralBlock.java:45)
    at sun.reflect.GeneratedMethodAccessor320.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.FunctionCallBlock$ContinuationImpl.dispatchOrArg(FunctionCallBlock.java:107)
    at com.cloudbees.groovy.cps.impl.FunctionCallBlock$ContinuationImpl.fixArg(FunctionCallBlock.java:83)
    at sun.reflect.GeneratedMethodAccessor324.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.ContinuationGroup.methodCall(ContinuationGroup.java:87)
    at com.cloudbees.groovy.cps.impl.FunctionCallBlock$ContinuationImpl.dispatchOrArg(FunctionCallBlock.java:113)
    at com.cloudbees.groovy.cps.impl.FunctionCallBlock$ContinuationImpl.fixArg(FunctionCallBlock.java:83)
    at sun.reflect.GeneratedMethodAccessor324.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:174)
    at com.cloudbees.groovy.cps.Continuable$1.call(Continuable.java:163)
    at org.codehaus.groovy.runtime.GroovyCategorySupport$ThreadCategoryInfo.use(GroovyCategorySupport.java:129)
    at org.codehaus.groovy.runtime.GroovyCategorySupport.use(GroovyCategorySupport.java:268)
    at com.cloudbees.groovy.cps.Continuable.run0(Continuable.java:163)
    at org.jenkinsci.plugins.workflow.cps.CpsThread.runNextChunk(CpsThread.java:186)
    at org.jenkinsci.plugins.workflow.cps.CpsThreadGroup.run(CpsThreadGroup.java:370)
    at org.jenkinsci.plugins.workflow.cps.CpsThreadGroup.access$200(CpsThreadGroup.java:93)
    at org.jenkinsci.plugins.workflow.cps.CpsThreadGroup$2.call(CpsThreadGroup.java:282)
    at org.jenkinsci.plugins.workflow.cps.CpsThreadGroup$2.call(CpsThreadGroup.java:270)
    at org.jenkinsci.plugins.workflow.cps.CpsVmExecutorService$2.call(CpsVmExecutorService.java:66)
    at java.util.concurrent.FutureTask.run(Unknown Source)
    at hudson.remoting.SingleLaneExecutorService$1.run(SingleLaneExecutorService.java:131)
    at jenkins.util.ContextResettingExecutorService$1.run(ContextResettingExecutorService.java:28)
    at jenkins.security.ImpersonatingExecutorService$1.run(ImpersonatingExecutorService.java:59)
    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)

I have tried moving this file to src/com/coffeestain/build/Python.groovy and putting it in there but in the function:

class Python {
    static def bootstrap( String client, String credentials, String label = "#head" ) {
        // Snipped out code
    }
}

but it just gives me a similar message when I'm trying to import it with:

@Library('MyGameBuildtools') import com.coffeestain.build.*

and call it with:

Python.bootstrap( env.P4CLIENT, 'CSSBuildmachine', "#head" )

However, this gives me the same error in another form:

hudson.remoting.ProxyException: groovy.lang.MissingMethodException: No signature of method: static com.coffeestain.build.Python.clientSpec() is applicable for argument types: (java.util.LinkedHashMap) values: [[view://MyGameContent/BuildScripts/Python/... //jenkins-Stekdatorn-TestWorkspace-null-0_BootstrapWorkspace/BootstrapScripts/...]]

My current workaround is to add it to the file vars/bootstrapPythonEnvironment.groovy with the definition:

def call( String client, String credentials, String label = "#head" ) {
    // Snipped out code
}

But this feels unfeasible in the long run to have a file for each function I add.

1
Stuff doesnt add up for me, wheres the function clientSpec() coming from? Sems at the beginning it also needed static. Can you add the full version of your groovy files (can be redacted, but function signatures and function calls should be left intact)? - Dominik Gebhart
Wait, these are steps that might need the build context for execution. Can you try to add the parameter context to your library function, and use it like context.p4([...] context.clientSpec([...])) and fill the context with this in the call to your library function? See also jenkins.io/doc/book/pipeline/shared-libraries the Accessing Steps part. - Dominik Gebhart

1 Answers

0
votes

Thanks Dominik Gebhart for helping me solve this!

clientSpec and P4 comes from the P4 Plugin.

I solved it by changing the function to this, note how context is used:

static def bootstrapPythonEnvironment( context, String client, String credentials, String label = "#head" ) {
    String bootstrapWorkspace = "${client}_BootstrapWorkspace"
    def p4 = context.p4(credential: credentials,
        workspace: context.manualSpec(
            charset: 'winansi',
            name: bootstrapWorkspace,
            spec: context.clientSpec(
                view: "//MyGameContent/BuildScripts/Python/... //${bootstrapWorkspace}/BootstrapScripts/..." )
        ))
    p4.run('sync', "//MyGameContent/...", label)
}

and I call it through:

MyGameUtils.bootstrapPythonEnvironment( this, env.P4CLIENT, 'CSSBuildmachine' )