> ## Knowledge Base Index
> Fetch the complete knowledge base index at: https://support.stacktrack.com/sitemap.xml
> Use this file to discover available pages before exploring further.
> Pure-Markdown content can be obtained by appending a '.md' suffix to the content URLs listed in the sitemap (without the trailing slash).

# 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**](/en/article/jenkins-shared-library-tutorial-1mk4i3k/#2-getting-started) ← Start here
* [**Advanced Guide**](https://support.stacktrack.com/en/article/jenkins-shared-library-advanced-guide-xpeg0c/) - Testing, Job DSL, advanced patterns
* [**Reference**](/en/article/jenkins-shared-libraries-tutorial-reference-11rdbe3/) - Complete examples, API, troubleshooting
* [**Demo Repository**](https://github.com/Servana/jenkins-shared-library-tutorial/tree/main/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
1. Go to `Manage Jenkins` → `System`
2. Find `Global Trusted Pipeline Libraries`
3. Click `Add` and 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
### 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 `.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
**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
1. ✅ Complete this getting started guide
2. 📖 Read [Advanced Guide](https://vscode-remote+wsl-002bubuntu.vscode-resource.vscode-cdn.net/home/tasssko/Clients/Servana/stacktrack-app-frontend/md/docs/advanced-guide.md) - Testing, Job DSL, patterns
3. 📚 Review [Reference](https://vscode-remote+wsl-002bubuntu.vscode-resource.vscode-cdn.net/home/tasssko/Clients/Servana/stacktrack-app-frontend/md/docs/reference.md) - Complete examples and API
4. 🧪 Explore [Demo Repository](https://vscode-remote+wsl-002bubuntu.vscode-resource.vscode-cdn.net/home/tasssko/Clients/Servana/stacktrack-app-frontend/md/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
* [Jenkins Shared Libraries Docs](https://www.jenkins.io/doc/book/pipeline/shared-libraries/)
* [Job DSL Plugin](https://github.com/jenkinsci/job-dsl-plugin/wiki)
* [Spock Framework](http://spockframework.org/)
* [JenkinsPipelineUnit](https://github.com/jenkinsci/JenkinsPipelineUnit)

## 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&#160;[troubleshooting guide](https://vscode-remote+wsl-002bubuntu.vscode-resource.vscode-cdn.net/home/tasssko/Clients/Servana/stacktrack-app-frontend/md/docs/reference.md#troubleshooting)&#160;or&#160;[advanced patterns](https://vscode-remote+wsl-002bubuntu.vscode-resource.vscode-cdn.net/home/tasssko/Clients/Servana/stacktrack-app-frontend/md/docs/advanced-guide.md).**