1

Question on using Libraries directive in declarative pipelines

I have a shared-library function defined as follows:

shared-libraries/src/org/conf$ cat Utilities.groovy
package org.foo
class Utilities {
  static def gbuild(script, args) {
    script.sh "${script.tool 'gradle32'}/bin/gradle ${args}"
  }
}

I have Jenkins configured to know where the shared library is and download it. 

Then I have a declarative pipeline where I use the Libraries directive to load this.

pipeline {

...

    Libraries {

              lib('Utilities@1.3')

   }

  Later on in my pipeline, I try to call the gbuild function from the Utilities lib

    gbuild ...

But Jenkins can never find it.  If I import org.conf.Utilities above the pipeline block, then Jenkins seems to be able to find it. What's the right way to call a function like this in a declarative pipeline after you've loaded the library with the libraries directive and without having to include the import?

Thanks

5 comments

  • 0
    Avatar
    Patrick Wolf

    Unless you need to create one class ("Utilities") with a bunch of static methods it is easier to use global vars in the /vars directory instead of classes in the /src directory.

    You would need to create a separate file for each method, in this case /vars/gbuild.groovy, but you can this call this directly from your Declarative Pipeline.

    def call(args) {
    sh "${tool 'gradle32'}/bin/gradle ${args}"
    }

    Global variables have script context so you shouldn't need to pass in the `script` variable.

    From the docs on using classes in a dynamic library:

    "Using classes from the src/ directory is also possible, but trickier. Whereas the @Library annotation prepares the “classpath” of the script prior to compilation, by the time a library step is encountered the script has already been compiled. Therefore you cannot import or otherwise “statically” refer to types from the library.

    However you may use library classes dynamically (without type checking), accessing them by fully-qualified name from the return value of the library step. static methods can be invoked using a Java-like syntax:

    library('my-shared-library').com.mycorp.pipeline.Utils.someStaticMethod()

     

    Edited by Patrick Wolf
  • 0
    Avatar
    Brent Laster

    Thanks very much for the quick reply and details!

  • 0
    Avatar
    Brent Laster

    Hmm, trying to use the FQN still seems problematic.  Given this simple test program::

    pipeline {
        agent any
        stages {
            stage ('build') {
              steps {
                  library('Utilities@1.5').org.conf.Utilities.gbuild(this,'clean')
                  
              }
            }
        }
    }

    I'm getting these errors:

    org.codehaus.groovy.control.MultipleCompilationErrorsException: startup failed:
    WorkflowScript: 6: Expected a symbol @ line 6, column 15.
                     library('Utilities@1.5').org.conf.Utilities.gbuild(this,'clean')
                     ^

    WorkflowScript: 6: "error" should have 1 arguments but has 2 arguments instead. @ line 6, column 15.
                     library('Utilities@1.5').org.conf.Utilities.gbuild(this,'clean')
                     ^

    2 errors

        at org.codehaus.groovy.control.ErrorCollector.failIfErrors(ErrorCollector.java:310)
        at

    I presume the "expected a symbol" is analgous to a syntax error and Jenkins not recognizing the FQN form here.   Suggestions on how to make this work?

     

  • 0
    Avatar
    Andrew Bayer

    This syntax:

    library('my-shared-library').com.mycorp.pipeline.Utils.someStaticMethod()

     doesn't work in Declarative, just in Scripted (or within a "script { ... }" block in Declarative). I'd strongly advise going in the direction of global variables, as Patrick suggested.

  • 0
    Avatar
    Brent Laster

    Thanks for the follow up!

Please sign in to leave a comment.