Jenkins shared library: Unable to import a package `No such property` - jenkins

I have a file which loads a library dynamically like this
def lib=library(identifier: 'pipeline-core#master', retriever: modernSCM(
[$class: 'GitSCMSource',
remote: 'https://scm.intra/scm/jenins/pipeline-core.git',
credentialsId: 'bitbucket.service.user'
])).ch.swisscard.pipeline.util.Utils
defaultCdPipeline {}
Whereas defaultCdPipeline is a jenkins pipeline definition which uses the Utils class like
import ch.mycompany.jenkins.pipeline.util.*
...
Utils.isRunning()
...
The file structure is this:
+- src
| +- mycompany
| +- jenkins
| +- pipeline
| +- util
| +- Utils.groovy
| +- Commons.groovy
+- vars
+- defaultCdPipeline.groovy
That works so far. When I look at the dynamic import my understanding I would use lib so lib.isRunning() rather than Utils.isRunning() but this generates the following error
No such property: lib for class: groovy.lang.Binding
Why? Going forward I would like to use both Utils.groovy and Commons.groovy. This I would "preselect the package" as this is shown in the example thus only using .ch.swisscard.pipeline.util
def lib=library(identifier: 'pipeline-core#master', retriever: modernSCM(
[$class: 'GitSCMSource',
remote: 'https://scm.intra/scm/jenins/pipeline-core.git',
credentialsId: 'bitbucket.service.user'
])).ch.swisscard.pipeline.util
defaultCdPipeline {}
This however also does not work as the call lib.Uils.isRunning()
...
stages {
stage('Deployment') {
steps {
script {
lib.Uils.isRunning()`
...
throws the same exception as mentioned above
No such property: lib for class: groovy.lang.Binding
Content if Utils
package ch.mycompany.jenkins.pipeline.util
class Utils implements Serializable {
#NonCPS
def static String isRunning() {
return "isRunning()"
}
}
Can somebody shed some light into this issue and tell me how I can properly load a package/multiple classes?

you should have only declare the library load like this:
def lib = library(
identifier: 'pipeline-core#master', retriever: modernSCM
(
[
$class: 'GitSCMSource',
remote: 'https://scm.intra/scm/jenins/pipeline-core.git',
credentialsId: 'bitbucket.service.user'
]
)
)
assuming this is the groovy class:
package ch.mycompany.jenkins.pipeline.util
class Utils implements Serializable
{
#NonCPS
def static String isRunning()
{
return "isRunning()";
}
def String isItReallyRunning()
{
return "isItReallyRunning()";
}
}
then you call a static method like this:
lib.ch.mycompany.jenkins.pipeline.util.Utils.isRunning();
and an instance method like this:
// call the constructor (you can also call a constructor with parameters)
def utils = lib.ch.mycompany.jenkins.pipeline.util.Utils.new();
// call your instance method
utils.isItReallyRunning();

Related

Add Jenkins Shared Libraries with classes reference in a Jenkinsfile dynamically

I have constructed a Shared Library in which I want to have class definitions, below is the example shared lib code.
package api
class api implements Serializable {
private final Map data
private final DSL steps
api(Map data, DSL steps) {
this.data = data
this.steps = steps
}
}
I have placed this class definition in a shared library git repo, with the path api/src/api/api.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: 'api#master', retriever: modernSCM(
scm: [
$class: 'GitSCMSource',
credentialsId: '<my-credentials-id>',
remote: '<my-git-remote>',
traits: [gitBranchDiscovery()]
],
libraryPath: 'api/'
)
import api.*
When I use the above class from the shared library it's throwing an exception saying that the class is not found.
WorkflowScript: 75: unable to resolve class api
# line 75, column 29.
api foo = new api(env.data, steps)
^
at org.codehaus.groovy.control.ErrorCollector.failIfErrors(ErrorCollector.java:309)
at org.codehaus.groovy.control.CompilationUnit.applyToSourceUnits(CompilationUnit.java:981)
at org.codehaus.groovy.control.CompilationUnit.doPhaseOperation(CompilationUnit.java:626)
at org.codehaus.groovy.control.CompilationUnit.compile(CompilationUnit.java:575)
at groovy.lang.GroovyClassLoader.doParseClass(GroovyClassLoader.java:323)
at groovy.lang.GroovyClassLoader.parseClass(GroovyClassLoader.java:293)
at org.jenkinsci.plugins.scriptsecurity.sandbox.groovy.GroovySandbox$Scope.parse(GroovySandbox.java:163)
at org.jenkinsci.plugins.workflow.cps.CpsGroovyShell.doParse(CpsGroovyShell.java:142)
at org.jenkinsci.plugins.workflow.cps.CpsGroovyShell.reparse(CpsGroovyShell.java:127)
at org.jenkinsci.plugins.workflow.cps.CpsFlowExecution.parseScript(CpsFlowExecution.java:562)
at org.jenkinsci.plugins.workflow.cps.CpsFlowExecution.start(CpsFlowExecution.java:514)
at org.jenkinsci.plugins.workflow.job.WorkflowRun.run(WorkflowRun.java:336)
at hudson.model.ResourceController.execute(ResourceController.java:107)
at hudson.model.Executor.run(Executor.java:449)
Finished: FAILURE
As i read in the doccument that loading library dynamically does not import classes directly to the pipeline's classpath.
So need a little help to get this class into my job execution.
Note: I have tested it and it's working when adding the global libraries under jenkins global settings, however i want this library setting to be dynamic from jenkins file.

Trigger Jenkins Job from shared library

This is what I have in my shared library file
build job: 'Job Name',
parameters:
[
string(name: 'ENVIRONMENT', value: 'sit'),
string(name: 'param1', value: 'value1' )
]
It is failing with below error :
hudson.remoting.ProxyException: groovy.lang.MissingMethodException: No signature of method: build.call() is applicable for argument types: (java.util.LinkedHashMap) values: [[job:**********, parameters:[#string(name=ENVIRONMENT,value=sit), ...]]]
Possible solutions: call(java.lang.Object, java.lang.Object, java.lang.Object), wait(), any(), wait(long), main([Ljava.lang.String;), any(groovy.lang.Closure)
Any help here?
Library classes cannot directly call steps such as sh or git. They can however implement methods, outside of the scope of an enclosing class, which in turn invoke Pipeline steps, for example:
// src/org/foo/Zot.groovy
package org.foo;
def checkOutFrom(repo) {
git url: "git#github.com:jenkinsci/${repo}"
}
return this
Which can then be called from a Scripted Pipeline:
def z = new org.foo.Zot()
z.checkOutFrom(repo)
This approach has limitations; for example, it prevents the declaration of a superclass.
Alternately, a set of steps can be passed explicitly using this to a library class, in a constructor, or just one method:
package org.foo
class Utilities implements Serializable {
def steps
Utilities(steps) {this.steps = steps}
def mvn(args) {
steps.sh "${steps.tool 'Maven'}/bin/mvn -o ${args}"
}
}
When saving state on classes, such as above, the class must implement the Serializable interface. This ensures that a Pipeline using the class, as seen in the example below, can properly suspend and resume in Jenkins.
#Library('utils') import org.foo.Utilities
def utils = new Utilities(this)
node {
utils.mvn 'clean package'
}
If the library needs to access global variables, such as env, those should be explicitly passed into the library classes, or methods, in a similar manner.
Instead of passing numerous variables from the Scripted Pipeline into a library,
package org.foo
class Utilities {
static def mvn(script, args) {
script.sh "${script.tool 'Maven'}/bin/mvn -s ${script.env.HOME}/jenkins.xml -o ${args}"
}
}
The above example shows the script being passed in to one static method, invoked from a Scripted Pipeline as follows:
#Library('utils') import static org.foo.Utilities.*
node {
mvn this, 'clean package'
}
For more info see jenkins shared library documentation: https://jenkins.io/doc/book/pipeline/shared-libraries/
Try adding propagate and wait like below:
build job: 'Job Name', parameters: [ string(name: 'ENVIRONMENT', value: 'sit'), string(name: 'param1', value: 'value1' ) ],propagate: true, wait: true
Ok. So I figured out the problem.
One of the shared file name was build.groovy which was causing conflicts with build pipeline step. Renamed the file and that fixed the issue.

Jenkins fail to load groovy class

I am having a groovy class in file : src/utils/versionUtil.groovy
I am trying to load it in my JenkinsFile but I got this error :
java.nio.file.NoSuchFileException: /var/lib/jenkins/jobs/TEST-web-cyclo-gitflow/workspace/src/utils/versionUtil.groovy
at sun.nio.fs.UnixException.translateToIOException(UnixException.java:86)
at sun.nio.fs.UnixException.rethrowAsIOException(UnixException.java:102)
at sun.nio.fs.UnixException.rethrowAsIOException(UnixException.java:107)
at sun.nio.fs.UnixFileSystemProvider.newByteChannel(UnixFileSystemProvider.java:214)
at java.nio.file.Files.newByteChannel(Files.java:317)
.....etc
Jenkinsfile
node {
stage('helloWorld') {
def VersionUtils = load pwd() + '/src/utils/versionUtil.groovy'
}
}
How to load properly a groovy class in jenkinsFile?
I guess you can use shared libraries for that:
#Library('somelib') import com.mycorp.pipeline.somelib.UsefulClass

Jenkins pipeline shared library can't invoke method in src directory

I want to invoke method of src directory from vars directory, which it works in IDE. But it seems not work in Jenkins.
1.project structure
├── src
│ └── foo
│ └── DemoClass.groovy
└── vars
└── varDemo.groovy
2.Content of DemoClass.groovy
#!groovy
package foo
class DemoClass {
def testDemoMethod() {
println("src DemoClass testDemoMethod")
}
}
3.Content of varDemo.groovy
#!groovy
import foo.DemoClass
def testVarsDemo() {
println("vars varDemo.groovy testVarsDemo")
}
def testVarsInvokeDemoMethod() {
println("vars varDemo.groovy testVarsInvokeDemoMethod")
def demoClass = new DemoClass()
demoClass.testDemoMethod()
println("end vars varDemo.groovy testVarsInvokeDemoMethod")
}
4.Jenkins pipeline
#Library('tools') _
varDemo.testVarsDemo()
varDemo.testVarsInvokeDemoMethod()
5.execute result in pipeline
> git checkout -f b6176268be99abe300d514e1703ff8a08e3ef8da
Commit message: "test"
> git rev-list --no-walk c1a50961228ca071d43134854548841a056e16c9 # timeout=10
[Pipeline] echo
vars varDemo.groovy testVarsDemo
[Pipeline] echo
vars varDemo.groovy testVarsInvokeDemoMethod
[Pipeline] echo
end vars varDemo.groovy testVarsInvokeDemoMethod
[Pipeline] End of Pipeline
It seem like demoClass.testDemoMethod() not work. Why can't invoke demoClass.testDemoMethod()? If I want to invoke the method in src directory, what should I do? Thank you!
Without reimplementing your code sections locally, Here are some differences I notice between yours and mine that is working.
JENKINSFILE
I don't have a space before the underscore on my #Library line
Immediately after my #Library line I am importing my shared library class that implements the methods I want to call. In your case this would be import foo.DemoClass
My call to my method is of the form (new DemoClass(config, this)).testVarsInvokeDemoMethod()
SHARED LIBRARY CLASSES
I don't have #!groovy in any of my groovy classes.
My class is public and implements Serializable
Hopefully one of these difference is the source of why its not getting called.
We need to follow the below structure
src/packagename/GroovyFile.groovy
vars/callFromJenkinsPipelne.groovy
The jenkins pipeline should be like this .
library('libracyname#Branchname')
callFromJenkinsPipelne()
Inside the vars file ( callFromJenkinsPipelne) , we can call the groovy file which is in src folder
//code in vars folder groovy file will be like this
for callFromJenkinsPipelne groovy file
def call(){
GroovyFile groovyFile = new com.jenkins.mypackagename.GroovyFile()
groovyFile.methodName(parameters)
}

How to access global variables inside groovy class files?

I have the following directory structure
(root)
+- src # Groovy source files
| +- org
| +- foo
| +- Bar.groovy # for org.foo.Bar class
+- vars
| +- foo.groovy # for global 'foo' variable
And I have the following lines of code in the following files
Bar.groovy
package org.foo
class Bar implements Serializable {
def config
def script
Bar(script, config){
this.script = script
this.config = config
}
def accessGlobalVars(){
this.script.echo "${foo.GlobalVar}" // throws an err
}
}
foo.groovy
import groovy.transform.Field
#Field GlobalVar="Hello World!"
I'm able to access the variable in the Jenkinsfile inside the script block
echo "${foo.GlobalVar}"
Is it possible to access the same variable inside the class since the vars folder is at src folder level?
The "${foo.GlobalVar}" in your example code is trying to resolve the foo against the Bar type. Since there is no foo reference it cannot be resolved and you will probably get something like a MissingPropertyException.
Jenkins Pipeline Groovy does some different compilation and binding using the Groovy compilation and runtime APIs. The global variables can also be resolved through the script object through its dynamic GroovyObject APIs (demonstrated in the source code here). The easiest way is to let the dynamic Groovy object resolution implemented by Jenkins resolve your variable.
Change your line from:
this.script.echo "${foo.GlobalVar}"
To instead lookup the foo from the script object (which I am presuming is the this or script in your Jenkins pipeline by new Foo(this, config)):
this.script.echo "${this.script.foo.GlobalVar}"

Resources