Articles on: Jenkins Service

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

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

Filevars/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
}

Filevars/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

  1. Go to Manage Jenkins โ†’ System
  2. Find Global Trusted Pipeline Libraries
  3. Click Add and configure:

4. Use in Pipeline

FileJenkinsfile (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 .txt file 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

Filesrc/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

Filesrc/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

Filevars/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

Filevars/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

Filevars/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

Filebuild.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

Filetest/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

  1. โœ… Complete this getting started guide
  2. ๐Ÿ“– Read Advanced Guide - Testing, Job DSL, patterns
  3. ๐Ÿ“š Review Reference - Complete examples and API
  4. ๐Ÿงช Explore Demo Repository - Working code


For Implementation

  1. Clone/fork this repository
  2. Customize package names and structure
  3. Add your organization-specific functions
  4. Configure in your Jenkins
  5. 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

Was this article helpful?

Share your feedback

Cancel

Thank you!