Jenkins Share Library教程 —— 企业级 Jenkins Shared Library 实战示例

写在前面

好久不见~最近状态稍缓,更新也慢了些,这篇文章同样让大家等了挺久,先跟大家说声抱歉。

如果你认真读了前面几篇,还跟着实践了,那到这里,咱们就要正式开启真正的 "进阶阶段" 啦!

确实,大多数公司内部的 Jenkins Shared Library 不只是简单的"封装几个 stage"而已,它们往往包含:

  • workspace 清理机制(避免磁盘爆满)
  • 缓存/依赖重用逻辑
  • 多分支/多环境配置
  • 动态参数和条件逻辑
  • 流水线钩子(pre/post hooks)
  • 高级通知系统(Slack、邮件、飞书、Teams)

下面,我们可以一起写一个更"企业级"的案例,会让你看懂为什么公司那套 Shared Library 那么复杂了。

目标:构建一个带 workspace 清理、缓存管理、错误恢复、通知系统的可复用 CI Pipeline。

一、项目结构

复制代码
jenkins-shared-lib-enterprise/
├── vars/
│   ├── enterprisePipeline.groovy       # 主入口 Pipeline
│   ├── workspaceManager.groovy         # 清理与准备工作区
│   ├── notifyManager.groovy            # 通知模块
│   ├── gitHelper.groovy                # Git 拉取与分支操作
├── src/org/company/
│   ├── Utils.groovy                    # 工具类(时间戳、日志、重试等)
│   └── ConfigLoader.groovy             # YAML/JSON 配置读取器
└── resources/templates/
    └── notifyTemplate.txt

二、清理函数

📄 vars/cleanWorkspace.groovy

复制代码
def call(Map config = [:]) {
    def cleanBeforeBuild = config.get('clean', true)
    def keepPatterns = config.get('keep', ['.git', '.gradle'])

    if (!cleanBeforeBuild) {
        echo "⏭️ Skipping workspace cleanup."
        echo "✅ Workspace ready at: ${pwd()}"
        return
    }

    echo "🧹 Cleaning workspace, preserving: ${keepPatterns}"

    def os = getOS()

    if (os == 'Windows') {
        cleanWindows(keepPatterns)
    } else {
        cleanUnix(keepPatterns)
    }

    echo "✅ Workspace ready at: ${pwd()}"
}

// 判断操作系统
private String getOS() {
    def osName = System.getProperty('os.name').toLowerCase()
    return osName.contains('windows') ? 'Windows' : 'Unix'
}

// Unix/Linux/macOS 清理方式
private void cleanUnix(List keepPatterns) {
    def patternStr = keepPatterns.join('|')
    sh """
        echo "Running on Unix/Linux..."
        ls -A1 | grep -v -E '^(${patternStr})\$' | xargs rm -rf || true
    """
}

// Windows 清理方式(使用 cmd 或 PowerShell)
private void cleanWindows(List keepPatterns) {
    // 转成小写用于比较
    def keepSet = keepPatterns.collect { it.toLowerCase() } as Set

    // 获取当前目录下所有文件/目录(包括隐藏)
    def files = new File(pwd()).listFiles()
    if (!files) return

    files.each { file ->
        def name = file.name.toLowerCase()
        if (!keepSet.contains(name)) {
            echo "🗑️ Deleting: ${file.name}"
            deleteFileOrDir(file)
        }
    }
}

// 安全删除文件或目录(递归)
private void deleteFileOrDir(File file) {
    try {
        if (file.directory) {
            file.deleteDir() // 递归删除目录
        } else {
            file.delete()
        }
    } catch (Exception e) {
        echo "⚠️ Failed to delete ${file.name}: ${e.message}"
    }
}

功能说明:

  • 默认在每次构建前清理工作区(但保留 .git.gradle 等缓存目录)

  • 公司里这样做是为了避免:

    • Jenkins 节点磁盘爆满
    • 前次构建残留文件导致构建异常

Jenkinsfile 调用方式

groovy 复制代码
@Library('my-shared-lib') _

pipeline {
    agent any 

    stages {
        stage('Cleanup') {
            steps {
                script {
                    cleanWorkspace(
                        clean: true,
                        keep: ['.git', '.gradle', 'settings.gradle']
                    )
                }
            }
        }
        // ... 其他阶段
    }
}

运行后,你会在控制台输出看到:

三、通知模块

📄 vars/notifyManager.groovy

复制代码
def call(Map args = [:]) {
    def status = args.status ?: 'SUCCESS'
    def message = args.message ?: "Build completed."
    def color = status == 'SUCCESS' ? 'good' : (status == 'FAILURE' ? 'danger' : 'warning')
    def project = env.JOB_NAME
    def buildUrl = env.BUILD_URL

    def fullMessage = """
    [${status}] ${project}
    ${message}
    👉 ${buildUrl}
    """
    echo "🔔 Notify: ${fullMessage.trim()}"
}

Jenkinsfile 调用方式

groovy 复制代码
@Library('my-shared-lib') _

pipeline {
    agent any  // 可以是 linux、windows、docker 等

    stages {
        stage('Cleanup') {
            steps {
                script {
                    notifyManager(
                        status: 'SUCCESS',
                        message: 'Build completed.'
                    )
                }
            }
        }
        // ... 其他阶段
    }
}

运行后,你会在控制台输出看到:


四、Git 操作封装

📄 vars/gitHelper.groovy

复制代码
// vars/gitCheckout.groovy
def call(Map config = [:]) {
    def repo = config.repo
    def branch = config.get('branch', 'main')
    def credentialsId = config.get('credentialsId', 'git-credentials')

    if (!repo) {
        error "❌ gitCheckout: 'repo' parameter is required!"
    }

    echo "🔁 Checking out ${repo} (branch: ${branch})"

    checkout([
        $class: 'GitSCM',
        branches: [[name: branch]],
        userRemoteConfigs: [[
            url: repo,
            credentialsId: credentialsId
        ]]
    ])
}

Jenkinsfile 调用方式

groovy 复制代码
@Library('my-shared-lib') _

pipeline {
    agent any

    options {
        skipDefaultCheckout()  // 👈 关键!跳过默认的 SCM 步骤
    }

    parameters {
        string(name: 'GIT_REPO', defaultValue: 'your giturl', description: 'Git repository URL')
        string(name: 'GIT_BRANCH', defaultValue: 'master', description: 'Branch to build')
    }

    stages {
        stage('Checkout Code') {
            steps {
                script {
                    def repo = params.GIT_REPO
                    def branch = params.GIT_BRANCH

                    echo "🔁 开始拉取代码:${repo} (分支:${branch})"

                    // 调用 vars/gitHelper.groovy
                    gitHelper(
                        repo: repo,
                        branch: branch,
                        credentialsId: 'gitee-credentials'  // 替换为你的实际凭据 ID
                    )
                }
            }
        }
        stage('Debug') {
            steps {
                echo "📦 当前路径:${pwd()}"
                bat 'git branch -a'
                bat 'git log --oneline -5'
            }
        }
    }

    post {
        failure {
            echo "❌ 构建失败!"
        }
    }
}

运行后,你会在控制台输出看到:


五、核心 Pipeline

📄 vars/enterprisePipeline.groovy

复制代码
import com.company.Utils

def call(Map config = [:], Closure customStages = null) {
    echo "repo: ${config.repo}, branch: ${config.branch}"
    // 初始化
    Utils.printHeader(this, "Initializing Pipeline")
    cleanWorkspace([clean: true, keep: ['.git']])
    gitHelper([repo: config.repo, branch: config.branch])

    // 构建
    Utils.printHeader(this, "Build Stage")
    try {
        bat config.buildCommand ?: 'mvn clean package -DskipTests'
    } catch (err) {
        notifyManager([status: 'FAILURE', message: "Build failed: ${err.message}"])
        error("Build failed.")
    }

    // 测试
    Utils.printHeader(this, "Test Stage")
    try {
        bat config.testCommand ?: 'mvn compile test'
    } catch (err) {
        notifyManager([status: 'FAILURE', message: "Tests failed: ${err.message}"])
        error("Tests failed.")
    }

    // 自定义阶段
    if (customStages != null) {
        Utils.printHeader(this, "Custom Stages")
        customStages()
    }

    // 部署
    if (config.deploy == true) {
        Utils.printHeader(this, "Deploy Stage")
        // bat config.deployCommand ?: './deploy.sh'
        notifyManager([status: 'SUCCESS', message: "Deployed successfully!"])
    }

    // 收尾
    def result = currentBuild.result ?: 'SUCCESS'
    notifyManager([status: result, message: "Pipeline finished with status: ${result}"])
    Utils.printHeader(this, "Cleaning workspace after build")
    cleanWorkspace([clean: true])
}

Jenkinsfile 调用方式

groovy 复制代码
@Library('my-shared-lib') _

pipeline {
    agent any
    tools {
        maven 'maven'
    }
    parameters {
        string(name: 'REPO_URL', defaultValue: 'your giturl', description: 'Git仓库地址')
        string(name: 'BRANCH', defaultValue: 'master', description: '分支')
    }
    stages {
        stage('企业流水线') {
            steps {
                script {
                    enterprisePipeline([
                        repo: params.REPO_URL,
                        branch: params.BRANCH
                    ])
                }
            }
        }
    }
}

运行后,你会在控制台输出看到:

写在最后

本文以 Maven 项目为示例,核心思路可迁移至其他语言项目,后续仍有扩展空间,感兴趣者可自行探索。

感谢你的认真阅读!若文章对你有价值,欢迎文末留言交流,也请帮忙点赞转发,谢谢支持!