Jenkins运维之路(共享库集成流水线发布) - 详解

news/2025/10/10 22:52:32/文章来源:https://www.cnblogs.com/wzzkaifa/p/19134018

1. 集成共享库后Pipeline

总体思路,将一些常用且用法比较固定的流水线抽离出来做为方法,在其他流水线中可以进行服用减少代码量

#!groovy
//引入共享库
@Library('ops-share-librarya@master') _
//引入方法
def nexus = new org.devops.nexus()
def sonarq = new org.devops.sonarq()
def dingtalk = new org.devops.dingtalk()
//这里因为有些插件不能使用environment定义的变量所以直接放在这里
String robotId = "BuildBoy"
String GIT_REPO_URL_String = 'git@gitee.com:xxx/spring-boot-3-hello-world-jdk21.git'
pipeline {
agent { node { label "node47"}}
tools {
//...
}
environment {
GIT_REPO_URL = 'git@gitee.com:xxxx/spring-boot-3-hello-world-jdk21.git'
REGISTRY_URL = 'harbor.xxxx.top'
HARBOR_URL = 'https://harbor.xxxx.top'
PROJECT_GROUP = 'devops'
PROJECT_NAME = 'hello-world-jdk21'
CONTAINER_NAME = 'hello-world-jdk21'
OPS_SHARE_LIBRARY = 'git@gitee.com:xxxx/ops-share-librarya.git'
REPOSITORY = 'DevopsArtifact'
}
options {
timeout(time: 10, unit: 'MINUTES')
disableConcurrentBuilds()
timestamps()
}
parameters {
gitParameter(
name: 'BRANCH_TAG',
type: 'PT_BRANCH_TAG',
defaultValue: 'master',
description: '请选择你要部署的分支或Tag',
useRepository: GIT_REPO_URL_String, // 这里要使用完整的Git仓库地址
quickFilterEnabled: true // 启用快速筛选
)
booleanParam(defaultValue: false, description: '是否进行项目回滚?', name: 'ROLLBACK_TAG')
}
stages {
stage('Example') {
steps {
script {
// 测试了下打印方法
PrintMes("Generated version: green", 'green')
//def version = createVersion()
//PrintMes("Generated version: ${version}", 'red')
}
}
}
stage('Check requirement') {
steps {
script {
// 判断条件
//...省略
}
}
}
stage('CleanWorkDir') {
steps {
//...省略
}
}
stage('Checkout') {
steps {
script {
echo "Using repository: ${env.GIT_REPO_URL}"
checkout([$class: 'GitSCM',
branches: [[name: params.BRANCH_TAG]],
userRemoteConfigs: [[url: "${GIT_REPO_URL}", credentialsId: "GiteeKey"]]]
)
// 增加了一些构建信息变量
def PULL_TIME = sh(script: "echo `date +'%Y-%m-%d %H:%M:%S'`", returnStdout: true).trim() // 构建开始时间
def COMMIT_ID = sh(script: 'git log -1 --pretty=format:%h',  returnStdout: true).trim() // 代码COMMIT_ID
def TRACE_ID = sh(script: "echo `head -c 32 /dev/random | base64`",  returnStdout: true).trim() // 随机生成TRACE_ID
def COMMIT_USER = sh(script: 'git log -1 --pretty=format:%an', returnStdout: true).trim() // 代码最后提交者
def COMMIT_TIME = sh(script: 'git log -1 --pretty=format:%ai', returnStdout: true).trim() // 提交最后时间
def COMMIT_INFO = sh(script: 'git log -1 --pretty=format:%s',  returnStdout: true).trim() // 提交最后信息
PrintMes("Commit User: ${COMMIT_USER}", 'purple')
PrintMes("Commit Time: ${COMMIT_TIME}", 'purple')
PrintMes("Commit Info: ${COMMIT_INFO}", 'purple')
if (params.ROLLBACK_TAG) {
wrap([$class: 'BuildUser']) {
echo "Built by: ${env.BUILD_USER_ID}"
currentBuild.description =
"流水线执行者: ${env.BUILD_USER_ID}\n" +
"项目回滚ID: ${env.BRANCH_TAG}\n" +
"tag: '项目回滚'"
}
} else {
env._tag = createVersion()
wrap([$class: 'BuildUser']) {
echo "Built by: ${env.BUILD_USER_ID}"
currentBuild.description =
"流水线执行者: ${env.BUILD_USER_ID}\n" +
"分支: ${env.BRANCH_TAG}\n" +
"tag: ${_tag}\n" +
"代码提交者: ${COMMIT_USER}\n" +
"构建开始时间: ${PULL_TIME}\n" +
"提交时间: ${COMMIT_TIME}\n" +
"提交信息: ${COMMIT_INFO}"
}
}
}
}
}
stage('Build') {
when {
expression {
return !params.BRANCH_TAG.startsWith('rel-')
}
}
steps {
PrintMes("项目开始构建", 'green')
sh 'mvn clean package -Dmaven.test.skip=true'
sh 'tar zcf ${PROJECT_NAME}.tar.gz target/spring-boot-3-hello-world-1.0.0-SNAPSHOT.jar'
}
}
stage('Upload to Nexus') {
when {
expression {
return !params.BRANCH_TAG.startsWith('rel-')
}
}
steps {
script {
PrintMes("Upload to Nexus", 'green')
echo "Uploading ${PROJECT_NAME} to ${REPOSITORY} with tag ${_tag}"
//使用方法进行上传,传入参数的时候不要带有${},直接使用变量名即可,否则报错
nexus.NexusUploadTargz(PROJECT_NAME,REPOSITORY,_tag)
}
}
}
stage("SonarQube Analysis") {
when {
expression {
// 检查 BRANCH_TAG 是否不以 'rel-' 开头
return !params.BRANCH_TAG.startsWith('rel-')
}
}
steps {
script {
PrintMes("SonarQube Analysis", 'green')
//使用方法进行 代码检查
sonarq.SonarQubeAnalysis("${JOB_NAME}", 'SonarqubeServ13', 'Jenkins-SonarqubeServ')
}
}
}
stage("Quality Gate") {
when {
expression {
// 检查 BRANCH_TAG 是否不以 'rel-' 开头
return !params.BRANCH_TAG.startsWith('rel-')
}
}
steps {
script {
//使用方法进行 代码检查状态查询
PrintMes("Checking Quality Gate", 'green')
sonarq.checkQualityGate(5)
}
}
}
stage('Wait for SonarQube Analysis') {
when {
expression {
// 检查 BRANCH_TAG 是否不以 'rel-' 开头
return !params.BRANCH_TAG.startsWith('rel-')
}
}
steps {
script {
PrintMes("Wait for SonarQube Analysis'", 'green')
//使用方法进行 代码检查状态查询
sonarq.waitForSonarQubeAnalysis('SonarqubeServ13', 'Jenkins-SonarqubeServ', "${JOB_NAME}", 10)
}
}
}
//部署过程这里就省略了,可以根据自己实际情况或者我前面的ansible脚本进行修改
}
post {
always{
script {
println("流水线结束后,经常做的事情")
}
}
success {
script {
//使用dingtalk方法进行告警
println("流水线结束后,经常做的事情")
dingtalk.DingdingReq(robotId, "构建成功 ✅")
}
}
failure{
script {
println("流水线结束后,经常做的事情")
dingtalk.DingdingReq(robotId, "构建失败 ❌")
}
}
aborted{
script {
println("流水线取消后,做的事情 ")
dingtalk.DingdingReq(robotId, "构建取消 ⚠️")
}
}
}
}

2.方法代码

src/org/devops/dingtalk.groovy(钉钉告警方法),这里采集了下构建信息然后定义了2个参数在使用时进行传参。

package org.devops
def GetChangeString() {
MAX_MSG_LEN = 100
def changeString = ""
def changeLogSets = currentBuild.changeSets
for (int i = 0; i  - {truncated_msg} [{entry.author} {commitTime}]\n"
}
}
if (!changeString) {
changeString = "> - No new changes"
}
return changeString
}
def DingdingReq(RobotID, Status) {
// 确保使用正确的上下文
wrap([$class: 'BuildUser']) {
def changeString = GetChangeString()
dingtalk (
robot: RobotID,
type: 'MARKDOWN',
title: '你有新的消息,请注意查收',
text: [
"### 构建信息",
"> - 应用名称:**${env.JOB_NAME}**",
"> - 构建结果:**${Status}**",
"> - 构建分支Or回滚Tag:**${env.BRANCH_TAG}**",
"> - 构建发起:**${env.BUILD_USER}**",
"> - 持续时间:**${currentBuild.durationString}**",
"> - 构建日志:[点击查看详情](${env.BUILD_URL}console)",
"### 更新记录:",
"${changeString}"
],
at: [
'xxx'
]
)
}
}

src/org/devops/nexus.groovy Nexus上传制品防范,定义了2个方法,一个上传tar.gz方法,一个上传jar,上传jar的方法如果你要使用根据自己要求,要调试下

package org.devops
def NexusUploadTargz(String PROJECT_NAME, String REPOSITORY, String VERSION){
// 使用之前生成的 version 变量
nexusArtifactUploader artifacts: [
[
artifactId: "${PROJECT_NAME}", // 替换为您的 artifactId
classifier: '',
file: "${PROJECT_NAME}.tar.gz", // 替换为您的文件路径
type: 'tar.gz' // 根据您的文件类型进行修改
]
],
//文件file类型有jar,pom,war,zip,tar.gz
credentialsId: 'Nexus3-DevOps',
groupId: 'top.xxx',
nexusUrl: 'registryv.xxx.top',
nexusVersion: 'nexus3',
protocol: 'https',
repository: "${REPOSITORY}",
version: "${VERSION}"
}
def NexusUploadJar(){
// 使用之前生成的 version 变量
nexusArtifactUploader artifacts: [
[
artifactId: 'basejar', // 替换为您的 artifactId
classifier: '',
file: 'target/spring-boot-3-hello-world-1.0.0-SNAPSHOT.jar', // 替换为您的文件路径
type: 'jar' // 根据您的文件类型进行修改
],
[
artifactId: 'spring-boot-3-hello-world', // 同样的 artifactId,或根据需要修改
classifier: 'pom',
file: 'pom.xml', // 指向您的 pom.xml 文件
type: 'pom' // 文件类型为 pom
]
],
//文件file类型有jar,pom,war,zip,tar.gz
credentialsId: 'Nexus3-DevOps',
groupId: 'top.xxxx',
nexusUrl: 'registryv.xxxx.top',
nexusVersion: 'nexus3',
protocol: 'https',
repository: 'DevopsArtifact',
version: "v8"
}

src/org/devops/sonarq.groovy 代码审查方法,这里定义了3个方法,扫描的,探测状态的,虽然这玩意没人用,但是还是先做出来以后万一用呢

package org.devops
def SonarQubeAnalysis(String projectName, String sonarServer, String credentialsId) {
def sonarqubeScanner = tool name: 'SonarScanner501'
// 使用 SonarQube 环境
withSonarQubeEnv(sonarServer) {
// 使用 Jenkins 凭据中的 SonarQube 令牌
withCredentials([string(credentialsId: credentialsId, variable: 'SONAR_TOKEN')]) {
// 执行 SonarQube Scanner CLI 分析
sh """
${sonarqubeScanner}/bin/sonar-scanner \
-Dsonar.projectKey=${projectName} \
-Dsonar.projectName=${projectName} \
-Dsonar.sources=src \
-Dsonar.java.binaries=target/classes \
-Dsonar.host.url=${env.SONAR_HOST_URL} \
-Dsonar.login=${env.SONAR_AUTH_TOKEN}
"""
}
}
}
def checkQualityGate(int timeoutMinutes) {
timeout(time: timeoutMinutes, unit: 'MINUTES') {
script {
def qg = waitForQualityGate() // 等待 SonarQube 的质量门结果
if (qg.status != 'OK') { // 检查质量门的状态
error "Pipeline aborted due to quality gate failure: ${qg.status}" // 中止流水线
}
}
}
}
def waitForSonarQubeAnalysis(String sonarServer, String credentialsId, String projectName, int timeoutMinutes) {
//echo "Project Name: ${projectName}"
withSonarQubeEnv(sonarServer) {
withCredentials([string(credentialsId: credentialsId, variable: 'SONAR_AUTH_TOKEN')]) {
timeout(time: timeoutMinutes, unit: 'MINUTES') {
waitUntil {
def curlCommand = "curl -s -u \$SONAR_AUTH_TOKEN: \$SONAR_HOST_URL/api/project_analyses/search?project=${projectName}"
def response = sh(script: curlCommand, returnStdout: true).trim()
def exitCode = sh(script: curlCommand, returnStatus: true)
echo "Response from SonarQube: ${response}"
if (exitCode != 0) {
error("Curl command failed with exit code: ${exitCode}. Response: ${response}")
}
if (response) {
try {
def jsonResponse = readJSON(text: response)
if (jsonResponse.analyses && jsonResponse.analyses.size() > 0) {
def latestAnalysis = jsonResponse.analyses[0]
echo "Latest Analysis Key: ${latestAnalysis.key}, Date: ${latestAnalysis.date}"
if (latestAnalysis.events.size() > 0) {
echo "New events found in analysis."
} else {
echo "No new events found in analysis."
}
return true
} else {
error("No analyses found in the response.")
}
} catch (Exception e) {
error("Failed to parse JSON response: ${e.message}. Response: ${response}")
}
} else {
error("Received empty response from SonarQube.")
}
}
}
}
}
}

3.全局方法

src/org/vars/createVersion.groovy 创建tag的全局方法

def call(){
return new Date().format('yyyyMMddHHmmss') + "_${env.BUILD_ID}"
}

src/org/vars/PrintMes.groovy 打印彩色字体方法

def call(String value, String color) {
def colors = [
'red'    : "\033[40;31m >>>>>>>>>>>${value}>>>>>>>>>>${value}>>>>>>>>>>${value}>>>>>>>>>>${value}<<<<<<<<<<< \033[0m"
]
ansiColor('xterm') {
echo colors[color]
}
}

4.共享流水线代码库目录结构

image-20250924162741866

image-20250924162741866

5.引用共享库前后对比

image-20250924163127359

image-20250924163127359

image-20250924163311175

image-20250924163311175

可以看出来,如果将一些共性的东西抽离出来可以极大的简化你的流水线,同时减少你重复写流水线过程,还是很nice的,好了大概Jenkins就这么完结撒花了,等以后在有什么新的发现在更新(这周必须要完工虽有流水线更新,压力大,可能晚点更新内容了撒! 如果你觉得文章还算有用可以帮我点个赞,转个发 嘿嘿!)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/934460.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

使用命令行删除 Windows 网络映射驱动器

前言 不得不吐槽一下,Windows 的体验真是依托答辩 我从大学时就在喷 Windows 垃圾,没想到现在还这么烂🤣最近升级了 NAS 之前这三个网络映射盘 (W:, X:, Y:) 是通过 SMB 映射到 TKL FileServer 现在迁移到 TrueNAS…

实验报告4(使用顺序表和单链表,进行有序表的合并)

一、实验目的: 熟练使用顺序表和单链表,进行有序表的合并。 二、实验仪器或设备: 操作系统:Windows11 编程环境:Dev-cpp 5.11 三、算法总体设计 (一)使用单链表进行有序表的合并 1. 打印链表 2. 合并两个有序链表 …

20232314 2025-2026-1 《网络与系统攻防技术》实验一实验报告

1.实验内容通过修改可执行文件,改变程序执行流程,跳转到getShell函数。 利用foo函数的Bof漏洞,构造一个攻击输入字符串,覆盖返回地址,触发getShell函数。 注入一个自己制作的shellcode并运行这段shellcode。2.实验…

详细介绍:【Windows10】MySQL9.4安装配置

详细介绍:【Windows10】MySQL9.4安装配置pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "…

ChatTime的一些理解

我先在 ChatTime 目录里全局搜索与“文本/时间序列/提示/特殊标记”相关的关键词,锁定模型如何区分两类输入与输出的代码位置,然后打开关键文件精读以回答你的问题。 搜索 time series|timeseries|time-series|时间序…

ChatTS的一些理解

Dingxingdi: 请你仔细阅读/workspace/ChatTS里面的文件,理解他们的含义逻辑和结构。现在我需要你告诉我,ChatTS在处理一段既包含文本又包含时间序列的输入的时候,是怎么精确地分别哪些是文本哪些是时序,从而对两者…

2025秋_9

今天依旧Java

10月10日

上午英语课学第三单元,下午写数据结构作业

[P2201 数列编辑器 // HDU-4699 Editor] 题解

lougu 看不到,遂写博客 题目描述 小 Z 是一个爱好数学的小学生。最近,他在研究一些关于整数数列的性质。为了方便他的研究,小 Z 希望实现一个叫做 "Open Continuous Lines Processor" 的数列编辑器。 一开…

centos网络打流测试 - 指南

centos网络打流测试 - 指南pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", …

一生一芯学习:基础设施(2)

一生一芯学习:基础设施(2)指令执行的踪迹 - itrace 首先写好一个环形缓冲区的代码,把反汇编的字符串存到环形缓冲区中,然后执行完代码在打印出来。 #include <stdio.h> #include <stdlib.h> #include &…

实验报告3(使用单链表简单实现图书管理系统)

一、实验目的: 使用单链表实现案例2.3的图书管理系统,要求实现查找、插入、删除和计数功能。要求包含主函数,用c语言或者c++实现。 二、实验仪器或设备: 操作系统:Windows11 编程环境:Dev-cpp 5.11 三、算法总体…

【黑马python】2.Python 字符串

参考链接黑马-2.Python 字符串 08-字符串的三种定义方式tbd

FineReport自定义登录系统技术 - 详解

pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New", …

实验报告2(简单实现图书馆管理系统)

一、实验目的:、 实现书上图书馆管理系统 (1) 主函数 (2) 修改:根据指定的ISBN,修改图书的价格 (3) 排序:将图书按照价格由低到高进行排序。 (4) 计数:统计文件中的图书数量 要求:用c语…

实验报告1(switch语句,二维数组)

一、实验目的: 熟练使用switch语句 熟练使用二维数组 二、实验仪器或设备: 操作系统:Windows11 编程环境:Dev-cpp 5.11 三、算法总体设计 (1)项目一:运输公司对用户计算运费 用到的算法的目的:计算并输出基于给…

【实现自己的 kafka!】kafka 的关键概念

kafka 的诞生 现在是在 2000 年代后期,你的名字叫做 Jay Kreps,你就职于 LinkedIn 公司。 LinkedIn 作为社交网络平台,用户规模和数据量现在快速增长,同时内部存在多种数据传递和处理需求,比如用户行为跟踪、日志…

12. 对话框

一、对话框对话框窗口是一个用来完成简单任务或者和用户进行临时交互的顶层窗口,通常用于输入信息、确认信息或者提示信息。Qt Quick 提供了一系列的标准对话框,如 FileDialog、ColorDialog、MessageDialog、FontDia…

2024ICPC区域赛香港站

define时间:#define int long long #define ind long double #define yes cout << "Yes" #define no cout << "No" #define pii pair<long long, long long> #define all(x) (…

AI产品经理要了解的算法有哪些?

中世纪拉丁语“algorismus”指的是用印度数字进行四个基本数学运算——加法,减法,乘法和除法的程序和捷径。后来,术语“算法”被人们用作表示任何逐步的逻辑过程,并成为计算逻辑的核心。 算法的历史可以分为三个阶…