Clayton Keleher
Clayton Keleher

Reputation: 165

Jenkins Shared Libraries with classes reference in a Jenkinsfile

I am constructing a Shared Library which I want to have class definitions, such as:

class Kubernetes implements Serializable{
    def script
    Kubernetes(script){this.script = script}
    def someMethod(){ ... }
}

I have placed this class definition in a shared library git repo, with the path src/foo/bar/Kubernetes.groovy. I am importing the shared library using the following step at the top of my jenkinsfile, outside any node or pipeline directive:

library identifier: 'custom-lib@master', retriever: modernSCM(
[$class: 'GitSCMSource',
remote: '<my-git-remote>',
credentialsId: '<my-credentials-id>'])
import foo.bar.*

I have already validated that this library is being pulled, because I can reference custom DSL steps created in the vars/ directory. However, when I edit my pipeline file to make an instance of that class, an error is immediately thrown when the pipeline starts:

org.codehaus.groovy.control.MultipleCompilationErrorsException: startup failed:
WorkflowScript: 1276: unable to resolve class Kubernetes 
 @ line 1276, column 16.
   def kube = new Kubernetes(this)
              ^

1 error

at org.codehaus.groovy.control.ErrorCollector.failIfErrors(ErrorCollector.java:310)
at org.codehaus.groovy.control.CompilationUnit.applyToSourceUnits(CompilationUnit.java:958)
at org.codehaus.groovy.control.CompilationUnit.doPhaseOperation(CompilationUnit.java:605)
at org.codehaus.groovy.control.CompilationUnit.compile(CompilationUnit.java:554)
at groovy.lang.GroovyClassLoader.doParseClass(GroovyClassLoader.java:298)
at groovy.lang.GroovyClassLoader.parseClass(GroovyClassLoader.java:268)
at groovy.lang.GroovyShell.parseClass(GroovyShell.java:688)
at groovy.lang.GroovyShell.parse(GroovyShell.java:700)
at org.jenkinsci.plugins.workflow.cps.CpsGroovyShell.doParse(CpsGroovyShell.java:133)
at org.jenkinsci.plugins.workflow.cps.CpsGroovyShell.reparse(CpsGroovyShell.java:127)
at org.jenkinsci.plugins.workflow.cps.CpsFlowExecution.parseScript(CpsFlowExecution.java:557)
at org.jenkinsci.plugins.workflow.cps.CpsFlowExecution.start(CpsFlowExecution.java:518)
at org.jenkinsci.plugins.workflow.job.WorkflowRun.run(WorkflowRun.java:290)
at hudson.model.ResourceController.execute(ResourceController.java:97)
at hudson.model.Executor.run(Executor.java:429)
Finished: FAILURE

From what I can tell, the library pull isn't even being executed before that error is thrown. But as far as I can tell, this is exactly how this functionality is demonstrated in the Jenkins docs. Is there something I need to do differently to get the library loaded properly?

Upvotes: 3

Views: 8691

Answers (3)

fchastanet
fchastanet

Reputation: 57

For me it was caused by a typo in the name of the package in the class file that was not matching the directory name

Upvotes: -1

Szymon Stepniak
Szymon Stepniak

Reputation: 42184

Loading library dynamically does not import classes directly to the pipeline's classpath - this is why you get this exception, no matter if you refer to fully-qualified class name or if you try to import (which will fail as well).

Jenkins documentation on library step explains how you can load classes defined in the library:

You may also load classes defined in the library by selecting their fully-qualified names like properties on the return value of this step, then call static methods or call constructors as if they were a method named new:

def utils = library('mylib').com.mycorp.jenkins.Utils.new(this)
utils.handyStuff()

Source: https://jenkins.io/doc/book/pipeline/shared-libraries/#loading-libraries-dynamically

In your case it means doing something like this:

def kub = library( identifier: 'my-custom-library@master', retriever: modernSCM([
    $class: 'GitSCMSource', remote: 'file:///var/jenkins_home/libraries', credentialsId: ''
])).foo.bar.Kubernetes.new(this)

println kub

This is my local exemplary library I used for tests. When I run the pipeline it succeeds and displays following output to the console:

Started by user admin
[Pipeline] library
Loading library my-custom-library@master
Attempting to resolve master from remote references...
 > git --version # timeout=10
 > git ls-remote -h -t file:///var/jenkins_home/libraries # timeout=10
Found match: refs/heads/master revision 92e5e92f84f04293a405b2e05ec6497781ac3e47
 > git rev-parse --is-inside-work-tree # timeout=10
Fetching changes from the remote Git repository
 > git config remote.origin.url file:///var/jenkins_home/libraries # timeout=10
Fetching without tags
Fetching upstream changes from file:///var/jenkins_home/libraries
 > git --version # timeout=10
 > git fetch --no-tags --progress file:///var/jenkins_home/libraries +refs/heads/*:refs/remotes/origin/*
Checking out Revision 92e5e92f84f04293a405b2e05ec6497781ac3e47 (master)
 > git config core.sparsecheckout # timeout=10
 > git checkout -f 92e5e92f84f04293a405b2e05ec6497781ac3e47
Commit message: "commit"
 > git rev-list --no-walk 92e5e92f84f04293a405b2e05ec6497781ac3e47 # timeout=10
[Pipeline] echo
foo.bar.Kubernetes@4ae7edb1
[Pipeline] End of Pipeline
Finished: SUCCESS

Defining a global library

I don't use much dynamic library loading in my Jenkins pipeline, I actually use global library definition like:

enter image description here

In this case I can load the library with:

@Library('default_jenkins_libs@master') _

import foo.bar.Kubernetes

def kub = new Kubernetes(this)

println kub

And this example produces similar output to the one pasted above. Hope it helps.

Upvotes: 9

Javier Delgado
Javier Delgado

Reputation: 181

Are you loading the library? From Manage Jenkins » Configure System » Global Pipeline Libraries, you can add it, selecting to load implicitly one reference. Additionally, you can load at a folder level.

In case of a non-implicit load, you must add

@Library('name-of-the-shared-lib@git-reference') _

to your Jenkinsfile

The full alternatives are documented at the pipeline docs

Upvotes: 1

Related Questions