Jenkins Shared Libraries â Reusable Pipeline Code Guide
In this tutorial, you'll learn about Jenkins Shared Libraries. We cover key concepts, practical examples, and best practices to help you understand and apply this topic effectively.
Jenkins Shared Libraries let you define reusable pipeline functions, steps, and variables in a separate Repository, allowing teams to share standardized CI/CD logic without duplicating code across hundreds of Jenkinsfiles.
What You'll Learn
Why It Matters
When 50 Microservices each copy-paste the same build-and-deploy logic into their Jenkinsfiles, updating a single step requires touching every Repository. Shared Libraries consolidate pipeline logic into a single source of truth â update one function, and every pipeline using it gets the change immediately. DodaTech reduced pipeline maintenance effort by 80% after moving common build, test, and deploy logic into shared libraries.
Real-World Use
DodaZIP's shared library defines standard stages for Docker image building, container scanning, Kubernetes deployment, and Slack notifications. Any team's Jenkinsfile imports this library and calls dodatechBuild() to get a production-ready pipeline with zero configuration.
flowchart TD
A[Shared Library Repo] --> B[vars/dodatechBuild.groovy]
A --> C[vars/sendSlackNotification.groovy]
A --> D[vars/dockerPublish.groovy]
A --> E[src/org/dodatech/PipelineUtils.groovy]
B --> F[Team A Jenkinsfile]
B --> G[Team B Jenkinsfile]
B --> H[Team C Jenkinsfile]
C --> F
C --> G
D --> H
style A fill:#D24939,color:#fff
style B fill:#4CAF50,color:#fff
Prerequisites: Understanding of Jenkins Pipeline syntax. Admin access to a Jenkins instance to configure shared libraries.
Repository Structure
Shared libraries follow a standard directory layout:
dodatech-pipeline-lib/
src/ # Helper classes
org/dodatech/
PipelineUtils.groovy
DockerUtils.groovy
vars/ # Global DSL variables
dodatechBuild.groovy
dodatechTest.groovy
sendSlackNotification.groovy
dockerPublish.groovy
deployKubernetes.groovy
resources/ # Static resources
org/dodatech/
templates/
build.sh
deploy.sh
Configuring the Library in Jenkins
// Jenkins configuration (Manage Jenkins > Configure System > Global Pipeline Libraries)
// Name: dodatech-lib
// Default version: main
// Retrieval method: Modern SCM
// Source Code Management: Git
// Project Repository: https://github.com/dodatech/pipeline-lib.git
Writing a Simple Library Step
// vars/dodatechBuild.groovy
def call(Map config = [:]) {
def nodeVersion = config.nodeVersion ?: '20'
def buildCommand = config.buildCommand ?: 'npm run build'
pipeline {
agent any
tools {
nodejs "Node-${nodeVersion}"
}
stages {
stage('Install Dependencies') {
steps {
sh 'npm ci'
}
}
stage('Lint') {
steps {
sh 'npm run lint'
}
}
stage('Build') {
steps {
sh buildCommand
}
}
stage('Test') {
steps {
sh 'npm test'
}
post {
always {
junit '**/junit.xml'
}
}
}
}
}
}
Expected behavior: Any Jenkinsfile can call dodatechBuild(nodeVersion: '22', buildCommand: 'npm run build:prod') to get a complete build pipeline.
Using the Library in a Jenkinsfile
// Jenkinsfile at root of any microservice repository
@Library('dodatech-lib@v2.5') _
dodatechBuild(
nodeVersion: '22',
buildCommand: 'npm run build:prod'
)
dockerPublish(
registry: 'registry.dodatech.com',
imageName: 'user-service',
scanEnabled: true
)
deployKubernetes(
environment: 'staging',
namespace: 'user-service',
replicas: 3
)
Expected output: Jenkins resolves the library from the configured Repository at version v2.5, imports the global variables, and executes the three pipeline steps sequentially.
Global Variables with Multiple Methods
// vars/dockerPublish.groovy
def call(Map config) {
stage('Docker Build') {
sh "docker build -t ${config.registry}/${config.imageName}:${BUILD_NUMBER} ."
}
if (config.scanEnabled) {
stage('Container Scan') {
sh "trivy image ${config.registry}/${config.imageName}:${BUILD_NUMBER}"
}
}
stage('Docker Push') {
withDockerRegistry([credentialsId: 'docker-registry-creds', url: "https://${config.registry}"]) {
sh "docker push ${config.registry}/${config.imageName}:${BUILD_NUMBER}"
}
}
}
Helper Classes in src/
// src/org/dodatech/PipelineUtils.groovy
package org.dodatech
class PipelineUtils implements Serializable {
static String gitCommitHash() {
return "git rev-parse --short HEAD".execute().text.trim()
}
static String versionFromFile(String filePath) {
return readFile(filePath).trim()
}
static boolean isMainBranch() {
return env.BRANCH_NAME == 'main'
}
static String buildTag(String appName) {
return "${appName}-${BUILD_NUMBER}-${gitCommitHash()}"
}
}
// Usage in Jenkinsfile
@Library('dodatech-lib@v2.5') _
import org.dodatech.PipelineUtils
pipeline {
agent any
stages {
stage('Build') {
steps {
script {
def tag = PipelineUtils.buildTag('auth-service')
echo "Tag: ${tag}"
}
}
}
}
}
Versioning and Branching
// Jenkinsfile using a specific version
@Library('dodatech-lib@v2.5') _
// Jenkinsfile using a feature branch
@Library('dodatech-lib@feature/new-deploy-strategy') _
// Jenkinsfile using default version (configured in Jenkins)
@Library('dodatech-lib') _
Testing Shared Libraries
// test/vars/DockerPublishSpec.groovy
class DockerPublishSpec extends PipelineSpockBase {
def 'dockerPublish builds and pushes image'() {
given:
def config = [registry: 'registry.dodatech.com', imageName: 'test-app']
when:
dockerPublish(config)
then:
1 * sh("docker build -t registry.dodatech.com/test-app:${BUILD_NUMBER} .")
1 * sh("docker push registry.dodatech.com/test-app:${BUILD_NUMBER}")
}
}
Common Configuration Mistakes
Not pinning library versions: Using
@Library('dodatech-lib')_without a version pulls the default branch. Pinning to@Library('dodatech-lib@v2.5')_ensures consistency across pipelines.Overwriting library-vended environment variables: Libraries often set
envvariables that can conflict with Jenkinsfile variables. Use unique prefixes likeDODATECH_*to avoid collisions.Missing
Serializablein helper classes: Pipeline steps must be serializable for Jenkins to pause and resume. Addimplements Serializableto all helper classes.Hardcoding credentials in library code: Libraries should accept credential IDs as parameters and use
withCredentialsinternally, not hardcode IDs.Ignoring library test coverage: Untested library changes break all consuming pipelines. Use JenkinsPipelineUnit or Spock to test library functions.
Practice Questions
What is the purpose of the
vars/directory in a shared library? Answer: Thevars/directory defines global DSL variables that appear as top-level pipeline steps. Each.groovyfile invars/becomes a callable function.How do you version shared libraries? Answer: Library versions correspond to Git tags or branches in the library Repository. Use
@Library('name@version')_to pin a specific version.What is the difference between
vars/andsrc/in a shared library? Answer:vars/defines global DSL functions callable from any Jenkinsfile.src/contains standard Groovy classes for complex logic, loaded viaimportstatements.How are shared libraries loaded at pipeline runtime? Answer: The
@Libraryannotation loads the library at pipeline start, checking out the specified version from the configured SCM Repository.
Challenge
Create a Jenkins Shared Library Repository with global variables for building Java (Maven/Gradle), Node.js, and Python applications. Include helper classes for version management, artifact upload to Nexus, and multi-environment Kubernetes deployment. Write unit tests for each function. Create a sample Jenkinsfile that uses all library features.
Built by the developers of Doda Browser, DodaZIP, and Durga Antivirus Pro.
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro