Skip to content

Shared Libraries

Reuse pipeline code across multiple projects with Jenkins Shared Libraries.


Why Shared Libraries?

When 3+ projects have similar Jenkinsfiles, you end up copying the same code. Shared Libraries solve this by putting common logic in a separate Git repository.

Benefits:

  • Write once, use in many pipelines
  • Update logic in one place — all pipelines get the update
  • Keep Jenkinsfiles short and clean
  • Test library code separately

Library Structure

jenkins-shared-library/
├── vars/                   ← Global pipeline steps
│   ├── buildDocker.groovy
│   ├── runTests.groovy
│   └── notifySlack.groovy
├── src/                    ← Groovy classes (optional)
│   └── com/
│       └── example/
│           └── Config.groovy
├── resources/              ← Non-Groovy files (templates, configs)
│   └── deploy-template.yaml
└── README.md
Folder Purpose
vars/ Custom pipeline steps — one file per step
src/ Groovy classes for complex logic
resources/ Static files (templates, configs)

Creating a Custom Step

vars/runTests.groovy

def call(Map config = [:]) {
    def testDir = config.get('testDir', 'tests/')
    def marker = config.get('marker', '')
    def markerArg = marker ? "-m ${marker}" : ''

    stage('Run Tests') {
        sh """
            pip install -r requirements.txt
            pytest ${testDir} ${markerArg} --junitxml=report.xml -v
        """
        junit 'report.xml'
    }
}

vars/buildDocker.groovy

def call(Map config) {
    def image = config.image
    def tag = config.get('tag', env.BUILD_NUMBER)
    def dockerfile = config.get('dockerfile', 'Dockerfile')

    stage('Build Docker Image') {
        sh "docker build -t ${image}:${tag} -f ${dockerfile} ."
    }
}

vars/notifySlack.groovy

def call(String status) {
    def color = status == 'SUCCESS' ? 'good' : 'danger'
    def message = "${env.JOB_NAME} #${env.BUILD_NUMBER}: ${status}"
    slackSend(channel: '#ci', color: color, message: message)
}

Using the Library

Go to Manage Jenkins → System → Global Pipeline Libraries and add your library repository. Then use it in Jenkinsfile:

@Library('my-shared-library') _

pipeline {
    agent { docker { image 'python:3.12' } }

    stages {
        stage('Build') {
            steps {
                buildDocker(image: 'my-app')
            }
        }
        stage('Test') {
            steps {
                runTests(testDir: 'tests/', marker: 'unit')
            }
        }
    }

    post {
        always {
            notifySlack(currentBuild.result ?: 'SUCCESS')
        }
    }
}

Method 2: Load from Git directly

@Library('my-library@main') _

Pin library version

Use @Library('my-library@v1.0') with tags to avoid breaking pipelines when the library changes.


Using Resources

Load files from the resources/ folder:

// vars/deployHelm.groovy
def call(Map config) {
    def template = libraryResource('deploy-template.yaml')
    writeFile file: 'deploy.yaml', text: template
    sh "helm upgrade --install ${config.app} -f deploy.yaml"
}

Pipeline Templates

Create a full pipeline as a library step:

vars/standardPipeline.groovy

def call(Map config) {
    pipeline {
        agent { docker { image config.get('image', 'python:3.12') } }

        options {
            timeout(time: 30, unit: 'MINUTES')
            disableConcurrentBuilds()
        }

        stages {
            stage('Test') {
                steps { sh 'pytest tests/ --junitxml=report.xml' }
                post { always { junit 'report.xml' } }
            }
            stage('Build') {
                steps { sh "docker build -t ${config.image}:${env.BUILD_NUMBER} ." }
            }
            stage('Deploy') {
                when { branch 'main' }
                steps { sh "deploy.sh ${config.get('env', 'staging')}" }
            }
        }
    }
}

Then every project just has a short Jenkinsfile:

@Library('my-shared-library') _

standardPipeline(image: 'my-service')