Jenkins Shared Library Tutorial
Jenkins Shared Library Tutorial
Learn to build reusable, testable pipeline code that follows the DRY principle
A comprehensive, production-ready guide to Jenkins Shared Libraries with working examples, tests, and Job DSL integration.
๐ Documentation
- Getting Started โ Start here
- Advanced Guide - Testing, Job DSL, advanced patterns
- Reference - Complete examples, API, troubleshooting
- Demo Repository - Working, runnable code
Getting Started
What is Jenkins Shared Library?
When we say "CI/CD as code," it should embrace modularity and reusability, following the DRY principle (Don't Repeat Yourself). Jenkins Shared Library makes this possible.
The Problem
Imagine 10 Java microservices, each with its own pipeline. The Maven build step is duplicated across all 10. When you add a service, you copy-paste the pipeline. Need to change Maven parameters? Update all 10+ pipelines manually. This is:
- โ Time-consuming
- โ Error-prone
- โ Not scalable
The Solution:
Write the Maven build once, reference it everywhere:
// Before: Duplicated in every pipeline
stage('Build') {
sh 'mvn clean package -DskipTests'
}
// After: Shared library (one place)
stage('Build') {
mavenBuild(goals: 'clean package', skipTests: true)
}
Update the library once โ all pipelines get the change automatically.
Key Benefits
- Code Reusability: Write once, use across teams and projects
- Consistency: Uniform workflows and standards
- Maintainability: Update once, apply everywhere
- Testability: Unit tests for pipeline code
- Versioning: Tag and version your CI/CD code
Quick Start
Prerequisites
- Jenkins with Pipeline and Job DSL plugins
- Basic Groovy knowledge
- Git repository
1. Create Repository Structure
my-shared-library/
โโโ vars/ # Global pipeline steps
โ โโโ buildApp.groovy
โ โโโ buildApp.txt # Documentation
โโโ src/ # Reusable classes
โ โโโ com/company/jenkins/
โ โโโ utils/
โ โโโ Helper.groovy
โโโ resources/ # Static files
โ โโโ scripts/
โ โโโ deploy.sh
โโโ test/ # Unit tests
โ โโโ unit/groovy/
โโโ build.gradle # Build configuration
2. Write Your First Global Variable
File: vars/buildApp.groovy
/**
* Build application using specified build tool.
*/
def call(Map config) {
String buildTool = config.buildTool ?: 'gradle'
String command = ""
switch(buildTool) {
case 'gradle':
command = './gradlew clean build'
break
case 'maven':
command = 'mvn clean package'
break
case 'npm':
command = 'npm install && npm run build'
break
default:
error("Unsupported build tool: ${buildTool}")
}
echo "Building with ${buildTool}..."
sh command
}
File: vars/buildApp.txt
NAME
buildApp - Build application using various build tools
SYNOPSIS
buildApp(buildTool: 'gradle')
PARAMETERS
buildTool (String, optional, default: 'gradle')
Build tool to use: 'gradle', 'maven', or 'npm'
EXAMPLES
buildApp(buildTool: 'maven')
buildApp(buildTool: 'npm')
3. Configure in Jenkins
- Go to
Manage JenkinsโSystem - Find
Global Trusted Pipeline Libraries - Click
Addand configure:
- Name:
my-shared-lib - Default version:
main - Retrieval method: Modern SCM โ Git
- Repository URL:
https://github.com/your-org/my-shared-library.git - Credentials: Select your Git credentials
- Name:
4. Use in Pipeline
File: Jenkinsfile (in your application repo)
@Library('my-shared-lib@main') _
pipeline {
agent any
stages {
stage('Build') {
steps {
buildApp(buildTool: 'gradle')
}
}
stage('Test') {
steps {
sh './gradlew test'
}
}
}
}
That's it! You've created and used your first shared library function.
Repository Structure Deep Dive
vars/ Directory
Global pipeline steps - Each .groovy file becomes a callable function.
Key points:
- Filename = function name (e.g.,
buildApp.groovyโbuildApp()) - Must have a
call()method - Optional
.txtfile for documentation rendered in Jenkins UI
Example structure:
vars/
โโโ buildApp.groovy # Implementation
โโโ buildApp.txt # Help documentation in Jenkins UI
โโโ deployToK8s.groovy
โโโ dockerBuild.groovy
โโโ log.groovy
src/ Directory
Reusable Groovy classes - Complex, object-oriented code.
When to use:
- Complex business logic
- Utility classes
- When vars/ DSLs aren't flexible enough
Example structure:
src/
โโโ com/company/jenkins/
โโโ builders/
โ โโโ DockerBuilder.groovy
โโโ deployers/
โ โโโ KubernetesDeployer.groovy
โโโ utils/
โโโ StringHelper.groovy
โโโ DateHelper.groovy
Usage:
import com.company.jenkins.builders.DockerBuilder
def builder = new DockerBuilder()
builder.build(imageName: 'myapp')
resources/ Directory
Static files - Scripts, templates, config files.
Common use cases:
- Shell scripts for deployment
- HTML email templates
- JSON/XML configuration files
Example structure:
resources/
โโโ scripts/
โ โโโ deploy.sh
โ โโโ healthcheck.sh
โโโ templates/
โโโ notification.html
Usage:
// Load and use resource
def script = libraryResource 'scripts/deploy.sh'
writeFile file: 'deploy.sh', text: script
sh 'chmod +x deploy.sh && ./deploy.sh'
test/ Directory
Unit and integration tests - Keep pipelines reliable.
test/
โโโ unit/groovy/
โโโ com/company/jenkins/
โโโ builders/
โ โโโ DockerBuilderSpec.groovy
โโโ utils/
โโโ StringHelperSpec.groovy
Writing Reusable Classes
Example: String Helper Utility
File: src/com/company/jenkins/utils/StringHelper.groovy
package com.company.jenkins.utils
class StringHelper {
static String toCamelCase(String text) {
return text.replaceAll(/[^a-zA-Z0-9]+([a-zA-Z0-9])/) {
full, firstLetter -> firstLetter.toUpperCase()
}
}
static String toSnakeCase(String text) {
return text.replaceAll(/([a-z])([A-Z])/, '$1_$2').toLowerCase()
}
static String slugify(String text) {
return text.toLowerCase()
.replaceAll(/[^a-z0-9]+/, '-')
.replaceAll(/^-+|-+$/, '')
}
}
Example: Bash Wrapper
File: src/com/company/jenkins/shell/Bash.groovy
package com.company.jenkins.shell
class Bash implements Serializable {
private Script steps
private boolean silent
private boolean debug
Bash(Script steps, boolean silent = false, boolean debug = false) {
this.steps = steps
this.silent = silent
this.debug = debug
}
def call(String description, String script) {
steps.echo("Executing: ${description}")
String formattedScript = formatScript(script)
int exitCode = steps.sh(
script: formattedScript,
returnStatus: true
)
if (exitCode != 0) {
steps.error("Script failed with exit code: ${exitCode}")
}
}
String formatScript(String script) {
List<String> lines = []
if (debug) {
lines.add('set -x') // Enable debug mode
}
lines.add('set -e') // Exit on error
lines.add(script)
return lines.join('\n')
}
}
Usage in vars:
// vars/bash.groovy
import com.company.jenkins.shell.Bash
def call(String script, Map options = [:]) {
Bash bash = new Bash(
this,
options.silent ?: false,
options.debug ?: false
)
bash.call('Bash execution', script)
}
Simple Working Examples
Example 1: Logging with Color
File: vars/log.groovy
def call(String message, Map options = [:]) {
Integer color = options.color ?: 37 // White default
ansiColor('xterm') {
echo "\033[${color}m${message}\033[0m"
}
}
def info(String message) {
call(message, [color: 36]) // Cyan
}
def warn(String message) {
call(message, [color: 33]) // Yellow
}
def error(String message) {
call(message, [color: 31]) // Red
}
def success(String message) {
call(message, [color: 32]) // Green
}
Usage:
log.info('Starting build')
log.warn('Deprecated feature')
log.error('Build failed')
log.success('Deployment complete')
Example 2: Docker Build
File: vars/buildDockerImage.groovy
def call(Map config) {
String imageName = config.imageName
List tags = config.tags ?: ['latest']
Map buildArgs = config.buildArgs ?: [:]
// Build image
String buildArgsString = buildArgs.collect { k, v -> "--build-arg ${k}=${v}" }.join(' ')
sh "docker build ${buildArgsString} -t ${imageName}:${tags[0]} ."
// Tag additional versions
tags.drop(1).each { tag ->
sh "docker tag ${imageName}:${tags[0]} ${imageName}:${tag}"
}
echo "Built image: ${imageName} with tags: ${tags.join(', ')}"
}
Usage:
buildDockerImage(
imageName: 'myapp',
tags: ['v1.0.0', 'latest'],
buildArgs: [VERSION: '1.0.0', BUILD_DATE: '2025-01-15']
)
Example 3: Kubernetes Deployment
File: vars/deployToK8s.groovy
def call(Map config) {
String namespace = config.namespace ?: 'default'
String deployment = config.deployment
String image = config.image
withCredentials([file(credentialsId: 'kubeconfig', variable: 'KUBECONFIG')]) {
sh """
kubectl config use-context ${config.cluster}
kubectl set image deployment/${deployment} ${deployment}=${image} -n ${namespace}
kubectl rollout status deployment/${deployment} -n ${namespace}
"""
}
echo "Deployed ${image} to ${namespace}/${deployment}"
}
Usage:
deployToK8s(
cluster: 'production',
namespace: 'apps',
deployment: 'user-service',
image: 'registry.company.com/user-service:v1.0.0'
)
Testing Your Library
Setup Test Dependencies
File: build.gradle
plugins {
id 'groovy'
id 'codenarc'
id 'jacoco'
}
repositories {
mavenCentral()
}
dependencies {
implementation 'org.codehaus.groovy:groovy-all:3.0.9'
testImplementation 'org.spockframework:spock-core:2.1-groovy-3.0'
testImplementation 'com.lesfurets:jenkins-pipeline-unit:1.17'
testImplementation 'junit:junit:4.13.2'
}
test {
useJUnit()
}
Write a Simple Test
File: test/unit/groovy/com/company/jenkins/utils/StringHelperSpec.groovy
package com.company.jenkins.utils
import spock.lang.Specification
import spock.lang.Unroll
class StringHelperSpec extends Specification {
@Unroll
def 'toCamelCase converts "#input" to "#expected"'() {
expect:
StringHelper.toCamelCase(input) == expected
where:
input || expected
'hello-world' || 'helloWorld'
'foo_bar_baz' || 'fooBarBaz'
'test' || 'test'
}
@Unroll
def 'slugify converts "#input" to "#expected"'() {
expect:
StringHelper.slugify(input) == expected
where:
input || expected
'Hello World' || 'hello-world'
'Foo Bar!' || 'foo-bar'
'test' || 'test'
}
}
Run Tests
# Run all tests
./gradlew test
# Run specific test
./gradlew test --tests "StringHelperSpec"
# Generate coverage report
./gradlew jacocoTestReport
open build/reports/jacoco/test/html/index.html
Best Practices
1. Always Version Your Library
// โ Bad - unpredictable
@Library('my-shared-lib') _
// โ Good - specific version
@Library('my-shared-lib@v1.2.3') _
// โ Good for development
@Library('my-shared-lib@develop') _
2. Validate Input Parameters
def call(Map config) {
// Validate required parameters
List required = ['param1', 'param2']
List missing = required.findAll { !config.containsKey(it) }
if (missing) {
error("Missing required parameters: ${missing.join(', ')}")
}
// Your logic here
}
3. Handle Errors Gracefully
def call(Map config) {
try {
// Your logic
performAction(config)
} catch (Exception e) {
log.error("Failed: ${e.message}")
currentBuild.result = 'FAILURE'
throw e
} finally {
// Cleanup
cleanup()
}
}
4. Document Everything
Create .txt files for all global variables:
NAME
myFunction - Brief description
SYNOPSIS
myFunction(config)
PARAMETERS
config (Map):
- param1 (String, required): Description
- param2 (Boolean, optional, default: false): Description
EXAMPLES
myFunction(param1: 'value')
5. Keep Functions Small
// โ Bad - doing too much
def deployAll(config) {
build()
test()
dockerBuild()
deploy()
}
// โ Good - focused
def buildAndTest(config) {
build(config)
test(config)
}
def packageAndDeploy(config) {
dockerBuild(config)
deploy(config)
}
Next Steps
For Learning
- โ Complete this getting started guide
- ๐ Read Advanced Guide - Testing, Job DSL, patterns
- ๐ Review Reference - Complete examples and API
- ๐งช Explore Demo Repository - Working code
For Implementation
- Clone/fork this repository
- Customize package names and structure
- Add your organization-specific functions
- Configure in your Jenkins
- Start converting existing pipelines
Resources
Project Structure
jenkins-shared-library-tutorial/
โโโ README.md โ You are here
โโโ docs/
โ โโโ advanced-guide.md โ Testing, Job DSL, team-based patterns
โ โโโ reference.md โ API reference, troubleshooting
โโโ demo-repository/
โโโ vars/ โ Working global variables
โโโ src/ โ Working classes
โโโ resources/ โ Example resources
โโโ test/ โ Complete test suite
โโโ jobs/ โ Team-based Job DSL structure
โ โโโ jobSeed.groovy โ Seed job for team-based CI/CD
โ โโโ jenkins-controllers/
โ โโโ common/ โ Cross-team shared jobs
โ โโโ data-science/ โ ML/AI workflows (MLflow, model serving)
โ โโโ engineering/ โ Software development (CI/CD, testing)
โ โโโ devops/ โ Infrastructure (Terraform, Kubernetes)
โโโ build.gradle โ Team-based build configuration
Ready to get started? Run the demo:
cd demo-repository
./gradlew clean test
Team-Based Architecture
This repository demonstrates a modern team-based approach to Jenkins CI/CD, moving beyond traditional environment-based structures (development/production) to domain-specific team organization.
๐๏ธ Team Structure
Team | Focus | Technologies | Pipeline Examples |
|---|---|---|---|
Common | Shared utilities, monitoring | Jenkins, Slack, backups | Health checks, security scans |
Data Science | ML/AI workflows | MLflow, SageMaker, Kubernetes | Model training, A/B testing, inference |
Engineering | Software development | Docker, Kubernetes, testing | CI/CD, deployment strategies, E2E testing |
DevOps | Infrastructure & platform | Terraform, Kubernetes, monitoring | Infrastructure as Code, compliance |
๐ Key Benefits
- Team Autonomy: Each team manages their own CI/CD workflows
- Domain Expertise: Pipelines tailored to specific technology stacks
- Scalability: Teams can evolve independently
- Modern Tooling: Integration with MLflow, Terraform, advanced testing
๐ Team Directory Structure
jobs/jenkins-controllers/
โโโ common/
โ โโโ src/dsl/groovy/commonJobs.groovy
โ โโโ src/main/groovy/monitoring/
โโโ data-science/
โ โโโ src/dsl/groovy/dataScienceJobs.groovy
โ โโโ src/main/groovy/
โ โโโ ml-experiments/ # Model training, experimentation
โ โโโ model-deployment/ # Multi-platform model serving
โ โโโ data-pipeline/ # Feature engineering, validation
โโโ engineering/
โ โโโ src/dsl/groovy/engineeringJobs.groovy
โ โโโ src/main/groovy/
โ โโโ applications/ # CI/CD, deployment strategies
โ โโโ testing/ # E2E, performance, integration
โ โโโ libraries/ # Shared library development
โโโ devops/
โโโ src/dsl/groovy/devopsJobs.groovy
โโโ src/main/groovy/
โโโ infrastructure/ # Terraform, cloud resources
โโโ platform-engineering/ # Kubernetes, service mesh
โโโ compliance/ # Security, auditing
๐ง Team-Specific Features
Data Science Team:
- MLflow experiment tracking and model registry
- Multi-platform model deployment (SageMaker, Kubernetes, Lambda)
- Automated model validation and drift detection
- A/B testing infrastructure
Engineering Team:
- Advanced deployment strategies (rolling, blue-green, canary)
- Comprehensive testing automation (unit, integration, E2E)
- Container security scanning and vulnerability management
- Multi-environment application deployment
DevOps Team:
- Infrastructure as Code with Terraform
- Kubernetes cluster management and compliance
- Security scanning and cost optimization
- Backup, disaster recovery, and monitoring automation
Questions? Check the troubleshooting guide or advanced patterns.
Updated on: 02/11/2025
Thank you!