Problem Statement
Explain Jenkins Shared Libraries in detail including directory structure, global variables, classes, versioning, and best practices for building reusable pipeline code.
Explanation
Shared Library structure follows convention: vars/ contains global variables (pipeline functions), src/ contains Groovy classes (complex logic), resources/ contains non-Groovy files (templates, configs). Directory structure:
```
my-pipeline-library/
├── vars/
│ ├── buildJava.groovy
│ ├── deployApp.groovy
│ └── sendNotification.groovy
├── src/
│ └── com/
│ └── example/
│ └── Utils.groovy
└── resources/
└── templates/
└── Dockerfile.template
```
Global variables in vars/ define callable functions. Simple function (vars/buildJava.groovy):
```groovy
def call(Map config) {
pipeline {
agent any
stages {
stage('Build') {
steps {
sh "mvn ${config.goals ?: 'clean package'}"
}
}
stage('Test') {
when {
expression { config.runTests != false }
}
steps {
sh 'mvn test'
}
}
}
}
}
```
Usage in Jenkinsfile:
```groovy
@Library('my-pipeline-library') _
buildJava(goals: 'clean install', runTests: true)
```
Custom steps (vars/deployApp.groovy):
```groovy
def call(String environment) {
echo "Deploying to ${environment}"
sh "./deploy.sh ${environment}"
}
def rollback(String environment) {
echo "Rolling back ${environment}"
sh "./rollback.sh ${environment}"
}
```
Usage:
```groovy
@Library('my-pipeline-library') _
pipeline {
stages {
stage('Deploy') {
steps {
deployApp('production')
}
}
}
}
```
Groovy classes in src/ for complex logic (src/com/example/Utils.groovy):
```groovy
package com.example
class Utils implements Serializable {
def script
Utils(script) {
this.script = script
}
def parseVersion(String tag) {
def matcher = tag =~ /v(\d+)\.(\d+)\.(\d+)/
if (matcher) {
return [major: matcher[0][1], minor: matcher[0][2], patch: matcher[0][3]]
}
return null
}
}
```
Usage:
```groovy
@Library('my-pipeline-library') _
import com.example.Utils
node {
def utils = new Utils(this)
def version = utils.parseVersion(env.TAG_NAME)
echo "Version: ${version.major}.${version.minor}.${version.patch}"
}
```
Library configuration: configure in Jenkins (Manage Jenkins > Configure System > Global Pipeline Libraries) with name, Git repository URL, and default version (branch/tag). Multiple libraries can be configured.
Versioning: specify library version when loading:
```groovy
@Library('my-pipeline-library@v1.2.3') _ // Specific tag
@Library('my-pipeline-library@main') _ // Branch
@Library('my-pipeline-library@commit') _ // Commit SHA
@Library('my-pipeline-library') _ // Default version from config
```
Dynamic loading:
```groovy
library identifier: 'my-lib@master', retriever: modernSCM([
$class: 'GitSCMSource',
remote: 'https://github.com/org/pipeline-library.git'
])
```
Resource files (resources/templates/Dockerfile.template):
```groovy
def dockerfileTemplate = libraryResource 'templates/Dockerfile.template'
writeFile file: 'Dockerfile', text: dockerfileTemplate
```
Best practices: version libraries (use semantic versioning), test library changes thoroughly before merging, document functions with comments and examples, keep libraries focused (separate libraries for different concerns), use classes for complex logic (easier to test), make functions configurable with sensible defaults, implement proper error handling, avoid side effects in library code, publish library documentation. Example testing library:
```groovy
// test/vars/buildJavaTest.groovy
import org.junit.Test
import static org.junit.Assert.*
class BuildJavaTest {
@Test
void testBuildJava() {
// Mock script context and test function
}
}
```
Understanding Shared Libraries enables building reusable, maintainable pipeline code shared across organization, reducing duplication and standardizing CI/CD processes.