When you’re using a system with multiple teams/users, there is always a trade off between having a shared codebase and the ability to override certain parts for a specific need.
Using Jenkins and the modern scripted or declarative pipelines with multiple users or teams is no exception to this, as there will always be an edge case which differs subtly from the norm. It might differ for any reason, even if it is just the stubbornness of a certain individual or a team.
Please note this does not apply to the more traditional Freestyle jobs, as they do not rely on code to run, but are defined in XML files on the Jenkins Master.
This post is part of a Series in which I elaborate on my best practices for running Jenkins at scale which might benefit Agile teams and CI/CD efforts.
So, we have at least two criteria for our Jenkins set up:
- Having a shared code base for your declarative or scripted pipelines
- Being able to override behaviour as needed
Jenkins has created a feature just for this purpose: Shared Libraries.
It enables you to create a collection of reusable components for your pipelines, but you’ll have to play by the rules that have been set.
Creating your Shared Library
First off, a Shared Library lives in a repository in your VCS. This can be in any type of VCS, as long as Jenkins knows how to obtain the contents. I’ll be assuming git for now.
Your git repository should live on your SCM-server and be accessible by Jenkins. This may be your organisations GitLab instance, publicly or privately shared Github.com repo, or even your very own vanilla git host.
Your repository should at least have a branch, preferrably called master.
The contents should adhere to this standard:
(root) +- src # Groovy source files | +- org | +- foo | +- Bar.groovy # for org.foo.Bar class +- vars | +- foo.groovy # for global 'foo' variable | +- foo.txt # help for 'foo' variable +- resources # resource files (external libraries only) | +- org | +- foo | +- bar.json # static helper data for org.foo.Bar
Setting up your Shared Library
The right way
There are several places where Shared Libraries can be defined, depending on the use-case. I’ll assume a Global Shared Library. Navigate to Manage Jenkins » Configure System » Global Pipeline Libraries where as many libraries as necessary can be configured.
Since these libraries will be globally usable, any Pipeline in the system can utilize functionality implemented in these libraries if needed.
These libraries are considered “trusted:” they can run any methods in Java, Groovy, Jenkins internal APIs, Jenkins plugins, or third-party libraries. This allows you to define libraries which encapsulate individually unsafe APIs in a higher-level wrapper safe for use from any Pipeline. Beware that anyone able to push commits to this SCM repository could obtain unlimited access to Jenkins. You need the Overall/RunScripts permission to configure these libraries (normally this will be granted to Jenkins administrators).
I created a library by the name of ‘ci-cd’, which refers to a git repository as its source.
Please note that the checkboxes Allow default version to be overridden is checked and Load implicitly is unchecked. This way, you need to explicitly import a Library in your pipeline, and you can choose a specific version if required.
The old-fashioned way
Back in 2016 , Jenkins did not have the ability to use an external repository as the source for a Shared Library. It instead relied on a built in ‘library’, called workflowLibs. As it is this old (three whole years!) no documentation is left, just circumstantial stuff.
It had some major drawbacks as well, as it basically required you to strip all authentication from Jenkins (at least temporarily) and also required ssh-access to the machine the Jenkins Master node ran on. Also: this workflowLibs library is included in all pipelines by default and can therefore be used by any malicious actor to do really evil stuff without anyone noticing by overriding ‘vanilla’ pipeline steps.
If you really want to you can follow the steps in this manual, but I wouldn’t recommend it. Also, I didn’t test it on a recent (2.150+) version of Jenkins, so you’ll be on your own on that part!
Using your Shared Library in a pipeline
In order to include your library, your Jenkinsfile or pipeline script must include something like the lines below.
@Library('ci-cd') _ /* Using a version specifier, such as branch, tag, etc */ @Library('ci-cd@development') _
This import line ends with an underscore ( _ ) as Groovy needs this to work properly as an annotation.
Now that we know how to create and include a Shared Library, we can add some code to this.
In the other posts in this series, I’ll share information on how to create common code and even create tests for it!