Reuse a Pipeline with Jenkins Shared Library

In this post, we’ll learn how to use a shared library in Jenkins Pipeline.

· Prerequisites
· Overview
∘ What are Shared Libraries?
∘ Why Use Jenkins Shared Library?
∘ Jenkins Shared Library Repository structure
· Real-world examples
∘ Creating a Jenkins Shared Library
∘ Set up a Shared Library in Jenkins
∘ Use the Shared Library in a Pipeline
· Conclusion
· References


Prerequisites

This is the list of all the prerequisites:

  • Jenkins server
  • A GitHub repository
  • Git
  • Maven 3.+ / Spring Boot 3+ / Java 17

Overview

What are Shared Libraries?

A shared library in Jenkins is a collection of reusable Groovy scripts that allow common functionalities to be shared across multiple Jenkins pipelines. Pipeline has support for creating “Shared Libraries” which can be defined in external source control repositories and loaded into existing Pipelines.

Within an organization, there are usually several different types of projects with the same stack ( e.g. microservices architecture). Each project has its pipeline script, with the most likely to be very similar. It can be not easy to manage and maintain these multiple scripts. It’s often useful to share parts of pipelines between different projects to reduce redundancies and keep the code “DRY”. Jenkins Shared Libraries help your team be more productive by reducing duplicate code and improving maintainability.

Why Use Jenkins Shared Library?

  • It eliminates duplicating code, fostering a more efficient and maintainable development process.
  • It ensures that all pipelines meet the same logic, best practices, coding conventions, and security measures across all pipelines.
  • With version control, the team can efficiently manage changes and rollbacks to the shared library code. So when a change or improvement is made to a function in the library, all pipelines using that function can instantly benefit from the update.
  • It can be organized into functions and classes making pipeline scripts cleaner and easier to maintain.

Jenkins Shared Library Repository structure

The directory structure of a Shared Library repository is as follows:

(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
+- *
  • The src directory should look like a standard Java source directory structure. This directory is added to the classpath when executing Pipelines.
  • The vars directory hosts script files that are exposed as variables in Pipelines. The name of the file is the name of the variable in the Pipeline.
  • resources directory allows the libraryResource step to be used from an external library to load associated non-Groovy files. Currently, this feature is not supported for internal libraries.
  • Other directories under the root are reserved for future enhancements.

Real-world examples

The following code shows an integration pipeline for a Spring Boot application.

pipeline {
agent any

environment {
MAVEN_ARGS=" -B -e -U"
}

stages {

stage('Build') {
steps {
withMaven(maven: 'MAVEN_ENV') {
sh "mvn clean install -DskipTests=true -Dmaven.javadoc.skip=true -Dcheckstyle.skip=true ${MAVEN_ARGS}"
}
}
}

stage('Unit tests') {
steps {
withMaven(maven: 'MAVEN_ENV') {
sh "mvn clean test-compile surefire:test ${MAVEN_ARGS}"
}
}
}

stage('Integration tests') {
steps {
withMaven(maven: 'MAVEN_ENV') {
sh "mvn clean verify -Dskip.unit.tests=true ${MAVEN_ARGS}"
}
}
post {
success {
junit allowEmptyResults: true, testResults: '**/*-reports/*.xml'
}
}
}

stage('Code quality: sonar') {
steps {
sh """
echo "Running sonar Analysis"
"""
}
}

stage('OWASP Dependency-Check Vulnerabilities') {
steps {
sh """
echo "OWASP Dependency-Check Vulnerabilities"
"""
}
}
}
}

For more information, see my previous story:

Building CI Multibranch pipeline with Jenkins and GitHub

The goal is to reuse this pipeline in other projects by organizing the code into a shared library.

Creating a Jenkins Shared Library

We’ll create a shared library project with Jenkins’s shared library structure.

├── resources
│ └── com
│ └── bootlabs
├── src
│ └── com
│ └── bootlabs
└── vars

This project has to contain Groovy scripts and save them to a platform like Git, GitHub, GitLab, etc.

Then, create a file called mvnBuild.groovy inside the vars folder.

#!/bin/groovy

import com.bootlabs.MavenLifecycle
import com.bootlabs.Constants

def call(Map buildParams) {

def mvn = new MavenLifecycle()


pipeline {
agent any

options {
// Keep the 10 most recent builds
buildDiscarder(logRotator(numToKeepStr: '25'))
timeout(time: 10, unit: 'MINUTES')
ansiColor('xterm')
timestamps()
}

stages {

stage('Checkout code') {
steps {
script {
log.info 'Starting Checkout code'
}
git branch: buildParams.gitBranch , url: buildParams.gitUrl
}
}

stage('Build') {
steps {
script {
log.info Constants.MVN_BUILD

mvn.mavenBuildGoal(buildParams.mvnBuildArgs)
}
}
}

stage('Unit tests') {
steps {
script {
log.info Constants.MVN_TEST

mvn.mavenTestGoal(buildParams.mvnTestArgs)
}
}
}

stage('Integration tests') {
steps {
script {
log.info Constants.MVN_IT

mvn.mavenIntegrationTestGoal(buildParams.mvnITArgs)
}
}
post {
success {
junit allowEmptyResults: true, testResults: '**/*-reports/*.xml'
}
}
}

stage('Code quality - sonar') {
steps {
script {
log.info Constants.MVN_SONAR
}
}
}

stage('OWASP Dependency-Check Vulnerabilities') {
steps {
script {
log.info Constants.MVN_OWASP
}
}
}
}
}

}

The file defined the entire declarative Pipeline that will be used to build different Spring Boot applications. By default, Jenkins calls the call() function in our Groovy file when the shared library is referenced in a Jenkins job. Think of the Java main() method as analogous to the call() function.

Like Java, we add import to use some methods and constants from other classes in the src directory. All the code-related maven Lifecycle inMavenLifecycle.groovy and project constants inConstants.groovy

Set up a Shared Library in Jenkins

Now it’s time to configure the shared library in the Jenkins instance.

Log in to Jenkins and navigate to Manage Jenkins > System

Navigate to the Global Pipeline Libraries section and click the Add button.

  • Name: It refers to this shared library from Jenkinsfile using this name.
  • Default version (branch name of our shared library git repo).
  • The best way is to specify the Modern SCM option with Git. Then, enter the Pipeline Shared Libraries repo URL under.
  • Click on the Save button.

Use the Shared Library in a Pipeline

To access shared libraries, the Jenkinsfile needs to use the @Library annotation, specifying the library’s name.

In our case: @Library(lab_shared_lib) _

Let’s create a new Jenkins pipeline job with the Maven project.

In the Jenkinsfile project we can use the following piece of code:

@Library('lab_shared_lib') _

mvnBuild(
mvnBuildArgs: '-DskipTests=true -Dmaven.javadoc.skip=true -Dcheckstyle.skip=true -B -e -U',
mvnTestArgs: '-X',
mvnITArgs: '-Dskip.unit.tests=true -B -e -U -X'
)

As we see, the Jenkinsfile code is tiny. It can be used in the different Maven applications for continuous integration.

Let’s try to run the pipeline:

Conclusion

Well done !!. In this post, we learned how to reuse a Jenkins shared library in Jenkinsfile applications that are built similarly. Jenkins shared library works for Declarative and Scripted Pipelines as well.

The complete source code is available on GitHub.

Support me through GitHub Sponsors.

Thank you for Reading !! See you in the next story.

References

👉 Link to Medium blog

Related Posts