动态

详情 返回 返回

Jenkins Share Library教程 —— 企業級 Jenkins Shared Library 實戰示例 - 动态 详情

image.png

寫在前面

好久不見~最近狀態稍緩,更新也慢了些,這篇文章同樣讓大家等了挺久,先跟大家説聲抱歉。

如果你認真讀了前面幾篇,還跟着實踐了,那到這裏,咱們就要正式開啓真正的 “進階階段” 啦!

確實,大多數公司內部的 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 調用方式

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

pipeline {
    agent any 

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

運行後,你會在控制枱輸出看到:

image.png

三、通知模塊

📄 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 調用方式

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

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

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

運行後,你會在控制枱輸出看到:

image.png


四、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 調用方式

@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 "❌ 構建失敗!"
        }
    }
}

運行後,你會在控制枱輸出看到:

image.png


五、核心 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 調用方式

@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
                    ])
                }
            }
        }
    }
}

運行後,你會在控制枱輸出看到:

image.png

寫在最後

本文以 Maven 項目為示例,核心思路可遷移至其他語言項目,後續仍有擴展空間,感興趣者可自行探索。

感謝你的認真閲讀!若文章對你有價值,歡迎文末留言交流,也請幫忙點贊轉發,謝謝支持!

user avatar u_17037082 头像 u_13137233 头像 u_17353607 头像 u_16281588 头像 kohler21 头像 shumile_5f6954c414184 头像 eolink 头像 xingchendahai_68d7dff410962 头像 buildyuan 头像 tssc 头像 chiqingdezhentou 头像 wnhyang 头像
点赞 15 用户, 点赞了这篇动态!
点赞

Add a new 评论

Some HTML is okay.