I am looking for some guidance on the design of a Jenkins Shared Library, with respect to using a class, initializing it, and then being able to use that instance in any of my vars files.
Structure
src
- foo
- class
- Configuration.groovy
vars
- cicd.groovy
- doMore.groovy
Class
The following is a class I would like to initialize once, but then use anywhere, without having to pass it into each vars function, or reinitialize every time.
package foo.class
public class Configuration {
public String Foo
public String Bar
}
Vars
In my cicd.groovy vars file, I have something like this:
#!groovy
import foo.class.Configuration
def call () {
return initCicd()
}
def initCicd() {
configuration = new Configuration()
configuration.Foo = 'FOO'
return configuration
}
But, in other vars files like doMore.groovy, I would like to use the same configuration instance.
#!groovy
import foo.class.Configuration
def call () {
println configuration.Foo
}
Is there a Singleton pattern that that works in Jenkins Shared Library, or a way to reference an instance across vars files or steps? If possible, please share an example.
Thanks!
You can simply use Groovy's #Singleton annotation for your Configuration class and use Configuration.instance wherever you want to access configuration settings. Consider following example:
.
├── src
│ └── foo
│ └── Configuration.groovy
└── vars
├── cicd.groovy
└── doMore.groovy
src/foo/Configuration.groovy
package foo
#Singleton
class Configuration {
public String foo = 'foo_123'
public String bar = 'bar_456'
}
vars/cicd.groovy
#!groovy
import foo.Configuration
def call() {
return initCicd()
}
def initCicd() {
println Configuration.instance.foo
return Configuration.instance
}
vars/doMore.groovy
#!groovy
import foo.Configuration
def call() {
println Configuration.instance.bar
}
In the pipeline script I simply call:
cicd()
doMore()
And I get something like that in the console log:
Loading library default_jenkins_libs#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 4fa988ccde542d77d19febd72f532ef996971a5d
> 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 4fa988ccde542d77d19febd72f532ef996971a5d (master)
> git config core.sparsecheckout # timeout=10
> git checkout -f 4fa988ccde542d77d19febd72f532ef996971a5d
Commit message: "update"
> git rev-list --no-walk 39890b4ca39bf32ebde8c7ad143b110bf16cf6b3 # timeout=10
[Pipeline] echo
foo_123
[Pipeline] echo
bar_456
[Pipeline] End of Pipeline
Finished: SUCCESS
The one downside of using singletons is that they can get modified anywhere and this change is populated to all callers.
Related
The Jenkins file in my github repository is used in a Jenkins Master/Slave environment.
I need to execute a testing command on a remote Jenkins Slave Server.
In my declarative pipeline the agent is called like this:
stage("Testautomation") {
agent { label 'test-device' }
steps {
bat '''
#ECHO ON
ECHO %WORKSPACE%
... '''
}
}
Before Jenkins can even execute a remote command, it starts checking out from version control. The checkout on Jenkins Master is no problem and working fine. But on this Jenkins Slave I always receive this error message.
using credential github-enterprise:...
> git rev-parse --is-inside-work-tree # timeout=10
Fetching changes from the remote Git repository
> git config remote.origin.url https://...git # timeout=10
Fetching upstream changes from https://...git
> git --version # timeout=10
using GIT_ASKPASS to set credentials GitHub Enterprise Access Token
> git fetch --tags --force --progress --depth=1 -- https://...git +refs/heads/development:refs/remotes/origin/development # timeout=120
Checking out Revision ... (development)
> git config core.sparsecheckout # timeout=10
> git checkout -f ...
Could not checkout ...
The Declarative pipeline performs a SCM checkout on every agent by default. Check if Git is installed on the Jenkins slave.
Conversely, if you want the code to be checked out on master but not on the agent, disable the default checkout in the options directive and use the scm checkout step inside a stage.
pipeline {
agent { label 'master' }
options {
skipDefaultCheckout(true)
}
stages {
stage('Build') {
steps {
checkout scm
// do other stuff on master
}
}
stage("Testautomation") {
agent { label 'test-device' }
steps {
bat '''
#ECHO ON
ECHO %WORKSPACE%
'''
}
}
}
}
You can further customize the checkout behavior as described in this answer https://stackoverflow.com/a/42293620/8895640.
I am after a simple Jenkin application to show gcc version after each push to bitbucket.
Here is the Jenkinsfile:
pipeline {
agent { docker { image 'kernelci/build-gcc-7_arm:3.3.3' } }
stages {
stage('build') {
steps {
sh 'gcc --version'
}
}
}
}
Bitbucket
Repository Settings/Post Webhooks:
http://jenkins.SOMEADDRESS:8080/bitbucket-scmsource-hook/notify
After each push, the build does not start automatically but not a problem. I starts the builds manually.
However, The Jenkin build output result looks like this
Started by user MY_USERNAME
[Office365connector] No webhooks to notify
Building on master in workspace c:\w\PROJECTNAME
> git.exe rev-parse --is-inside-work-tree # timeout=10
Fetching changes from the remote Git repository
> git.exe config remote.origin.url http://bb.MYWEBSITE.COM/scm/bbb/MYREPO.git # timeout=10
Fetching upstream changes from http://bb.MYWEBSITE.COM/scm/bbb/MYREPO.git
> git.exe --version # timeout=10
using GIT_ASKPASS to set credentials Credentials for Jenkins in Bitbucket
> git.exe fetch --tags --progress http://bb.MYWEBSITE.COM/scm/bbb/MYREPO.git +refs/heads/*:refs/remotes/origin/*
Seen branch in repository origin/feature/LAST_BRANCH
Seen branch in repository origin/feature/BRANCH_A
Seen branch in repository origin/feature/BRANCH_B
Seen branch in repository origin/feature/BRANCH_C
Seen branch in repository origin/feature/BRANCH_D
Seen branch in repository origin/feature/BRANCH_E
Seen branch in repository origin/feature/BRANCH_F
Seen branch in repository origin/feature/BRANCH_G
Seen 8 remote branches
> git.exe show-ref --tags -d # timeout=10
Checking out Revision e3152f566543dfb813c6e33ac0d1099da7ec26a6 (origin/LAST_BRANCH)
> git.exe config core.sparsecheckout # timeout=10
> git.exe checkout -f e3152f566543dfb813c6e33ac0d1099da7ec26a6
Commit message: "Merge branch 'feature/BRANCH_A' into LAST_BRANCH"
First time build. Skipping changelog.
[Office365connector] No webhooks to notify
Finished: SUCCESS
It does not look like loading a dock in 4 seconds. I do not see any gcc version on the screen either. Where is the problem from?
Is it from Jenkinsfile?
My goal is to create a pipeline, where every stage is having the same agent, except of 1 stage (Stage E in the example below)
pipeline {
agent {
dockerfile {
filename 'Dockerfile.tester'
args '-v $HOME/.docker:/root/.docker'
}
}
stages {
stage('A') { ... }
stage('B') { ... }
stage('C') { ... }
stage('D') { ... }
stage('E') {
agent {
dockerfile {
filename 'Dockerfile.deploy'
args '-v $HOME/.docker:/root/.docker'
}
}
}
stage('F') { ... }
}
}
What I can do so far is to set globally the agent to none and then set the agent Dockerfile.tester for each stage and for the 1 other stage I set it to Dockerfile.deploy. Any idea how I can set it globally once and then just override it?
Here the new error from if I do it like above:
> git rev-parse --is-inside-work-tree # timeout=10
Fetching changes from the remote Git repository
> git config remote.origin.url https://bitbucket.org/*********/*********.git # timeout=10
Cleaning workspace
> git rev-parse --verify HEAD # timeout=10
Resetting working tree
> git reset --hard # timeout=10
> git clean -fdx # timeout=10
Fetching without tags
Fetching upstream changes from https://bitbucket.org/*********/*********.git
> git --version # timeout=10
using GIT_ASKPASS to set credentials BitBucket Login for checkout / pushing
> git fetch --no-tags --progress https://bitbucket.org/*********/********.git +refs/heads/dev:refs/remotes/origin/dev
Checking out Revision ab0d4a522d872a129f53a74f6ecadafb8fd82f11 (dev)
> git config core.sparsecheckout # timeout=10
> git checkout -f ab0d4a522d872a129f53a74f6ecadafb8fd82f11
Commit message: "chore: test"
Cleaning workspace
> git rev-parse --verify HEAD # timeout=10
Resetting working tree
> git reset --hard # timeout=10
> git clean -fdx # timeout=10
[Bitbucket] Notifying commit build result
docker login failed
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)
}
We have a few similar apps that are deployed with a scripted pipeline which is basically C&P over all apps. I would like to move the whole pipeline into a Jenkins shared lib as hinted in the Jenkins docs.
So let's suppose that I have the following "pipeline" in var/standardSpringPipeline.groovy:
#!groovy
def call() {
node {
echo "${env.BRANCH_NAME}"
}
}
Then - the Jenkins file:
#Library('my-jenkins-lib#master') _
standardSpringPipeline
echo "Bye!"
Unfortunately this does not work for a reason that I do not understand. The Jenkins output is similar:
> git fetch --no-tags --progress ssh://git#***.com:7999/log/my-jenkins-lib.git +refs/heads/*:refs/remotes/origin/*
Checking out Revision 28900d4ed5bcece9451655f6f1b9a41a76256629 (master)
> git config core.sparsecheckout # timeout=10
> git checkout -f 28900d4ed5bcece9451655f6f1b9a41a76256629
Commit message: "NOJIRA: ...."
> git rev-list --no-walk 28900d4ed5bcece9451655f6f1b9a41a76256629 # timeout=10
[Pipeline] echo
Bye!
[Pipeline] End of Pipeline
Any clue why this does not work (see the output above) and what is the correct way to do that?
For no-arg methods, you cannot use optional parenthesis. From the Groovy documentation (emphasis mine):
Method calls can omit the parentheses if there is at least one parameter and there is no ambiguity:
println 'Hello World'
def maximum = Math.max 5, 10
Parentheses are required for method calls without parameters or
ambiguous method calls:
println()
println(Math.max(5, 10))
The standardSpringPipeline behaves like a method because of how it is compiled. If you add a echo "$standardSpringPipeline" it is a bit clearer that it is a compiled class that can be invoked.
To address your issue, just add parenthesis to the call:
standardSpringPipeline()