Files
jenkins-pipeline/SCM/构建镜像/v5/build_image_flymoon_admin.groovy
2026-02-11 14:55:11 +08:00

336 lines
16 KiB
Groovy
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

pipeline {
agent any
tools{
maven 'mvn3.8.8'
jdk 'jdk21'
}
parameters {
gitParameter(
branchFilter: 'origin/(.*)', // 过滤分支:只显示 origin/* 的远程分支
defaultValue: 'master', // 默认值:改为完整的远程分支名
name: 'Code_branch', // 参数名
type: 'PT_BRANCH_TAG', // 类型:分支 + Tags
selectedValue: 'DEFAULT', // 选中策略:使用默认值
sortMode: 'DESCENDING_SMART', // 排序:智能降序(最新版本在前)
description: '选择代码分支或 Tag', // 说明文字
quickFilterEnabled: true, // 启用快速搜索
tagFilter: '*', // Tag 过滤:显示所有 Tags
listSize: "1" // 列表显示行数10 行更直观
)
choice(
name: 'NAME_SPACES',
choices: ['sit', 'test', 'prod'],
description: '选择存放镜像的仓库命名空间:'
)
string(
name: 'CUSTOM_TAG',
defaultValue: '',
description: '可选:自定义镜像 Tag (字母、数字、点、下划线、短横线), 留空则自动生成 “ v+构建次数_分支名_短哈希_构建时间 ”'
)
booleanParam(
name: 'DEPLOY_TO_S1',
defaultValue: false,
description: '可选:构建成功后部署到 S1 环境 (触发 job: DM_s1_flymoon_admin)'
)
}
environment {
LARK_ROBOT = "4b8d66d0-c0f0-4587-b0e5-cff772cb3046" // 飞书机器人ID用于发送构建通知
REGISTRY = "uswccr.ccs.tencentyun.com" // 镜像仓库地址
NAMESPACE = "lessie${params.NAME_SPACES}" // 命名空间根据choices的选择拼接
IMAGE_NAME = "flymoon-admin" // 镜像名(固定前缀)
CREDENTIALS_ID = "dxin_img_hub_auth" // 容器仓库凭证ID
}
stages {
stage('拉取代码') {
steps {
script {
checkout([
$class: 'GitSCM', // 1. 指定使用 Git SCM 插件
branches: [[name: "${params.Code_branch}"]], // 2. 要检出的分支/Tag/提交
doGenerateSubmoduleConfigurations: false, // 3. 子模块配置
extensions: [ // 4. Git 扩展功能
[$class: 'CloneOption', depth: 0, noTags: false, shallow: false]
],
userRemoteConfigs: [[ // 5. 远程仓库配置
credentialsId: 'fly_gitlab_auth',
url: 'http://172.24.16.20/root/fly_moon_admin.git',
refspec: '+refs/heads/*:refs/remotes/origin/* +refs/tags/*:refs/tags/*'
]]
])
echo "✅ 成功拉取代码: ${params.Code_branch}"
}
}
}
stage('获取信息') {
steps {
script {
// 获取构建者
def cause = currentBuild.getBuildCauses('hudson.model.Cause$UserIdCause')
env.ACTUAL_USER = cause ? cause.userName[0] : "系统/自动触发"
// 获取Git信息
env.GIT_COMMIT_SHORT = sh(script: 'git rev-parse --short HEAD', returnStdout: true).trim()
env.GIT_COMMIT_MSG = sh(script: 'git log -1 --pretty=format:%s', returnStdout: true).trim()
// 生成镜像Tag
def buildNumber = env.BUILD_NUMBER
// 格式化分支名, 使用参数传入的分支名,去掉 origin/ 前缀
def branchName = params.Code_branch.replaceFirst(/^origin\//, '')
def formattedBranch = branchName.replace('/', '-').replace('_', '-')
def buildTime = sh(script: 'date +%Y%m%d%H%M', returnStdout: true).trim()
env.currentTime = sh(script: 'date "+%Y-%m-%d %H:%M:%S"', returnStdout: true).trim()
def defaultTag = "v${buildNumber}_${formattedBranch}_${GIT_COMMIT_SHORT}_${buildTime}"
def customTag = params.CUSTOM_TAG?.trim()
def tagPattern = ~/^[a-zA-Z0-9._-]+$/
// 最终Tag
if (customTag && customTag ==~ tagPattern) {
echo "✅ 使用自定义镜像 Tag: ${customTag}"
env.IMAGE_TAG = customTag
} else if (customTag) {
echo "⚠️ 自定义 Tag '${customTag}' 不符合规范,将使用默认生成的 Tag: ${defaultTag}"
def confirmed = true
timeout(time: 1, unit: 'MINUTES') {
try {
input(
message: """⚠️ Tag 命名不规范:
${customTag}
将使用自动生成的 Tag
${defaultTag}
是否继续构建?""",
ok: ''
)
} catch (err) {
// 用户点击“取消”或中断
echo "🚫 用户取消构建"
confirmed = false
}
}
if (confirmed) {
echo "✅ 用户确认使用自动生成的 Tag${defaultTag}"
env.IMAGE_TAG = defaultTag
} else {
error("流水线已终止。")
}
} else {
env.IMAGE_TAG = defaultTag
echo "未输入自定义 Tag, 使用自动生成规则: ${env.IMAGE_TAG}"
}
}
}
}
stage('') {
steps {
withCredentials([usernamePassword(
credentialsId: env.CREDENTIALS_ID,
usernameVariable: 'REGISTRY_USER',
passwordVariable: 'REGISTRY_PWD'
)]) {
sh '''
echo "$REGISTRY_PWD" | docker login ${REGISTRY} -u ${REGISTRY_USER} --password-stdin
'''
}
}
}
stage('') {
steps {
script {
// 构建镜像,添加标签信息
sh """
docker build -t ${REGISTRY}/${NAMESPACE}/${IMAGE_NAME}:${IMAGE_TAG} \
--label "git-branch='${Code_branch}'" \
--label "git-commit='${GIT_COMMIT_SHORT}'" \
--label "git-message='${GIT_COMMIT_MSG}'" \
--label "build-time='${currentTime}'" \
.
"""
}
}
}
stage('') {
steps {
script {
sh "docker push ${REGISTRY}/${NAMESPACE}/${IMAGE_NAME}:${IMAGE_TAG}"
echo "推送镜像成功:${REGISTRY}/${NAMESPACE}/${IMAGE_NAME}:${IMAGE_TAG}"
}
}
}
}
post {
always {
script {
def keepCount = 2
echo "开始清理本地旧镜像,仅保留最近 ${keepCount} 个构建版本"
def imagePrefix = "${REGISTRY}/${NAMESPACE}/${IMAGE_NAME}"
// 获取所有镜像(按创建时间排序,越新的越前)
// 格式Repository:Tag ImageID CreatedAt
def allImagesRaw = sh(script: "docker images ${imagePrefix} --format '{{.Repository}}:{{.Tag}} {{.ID}} {{.CreatedAt}}' | sort -rk3", returnStdout: true).trim()
if (!allImagesRaw) {
echo "未找到任何镜像,无需清理"
return
}
def allImages = allImagesRaw.split('\n')
if (allImages.size() <= keepCount) {
echo "当前镜像数未超过 ${keepCount} 个,无需清理"
return
}
def oldImages = allImages.drop(keepCount)
echo "发现 ${oldImages.size()} 个旧镜像需要清理"
oldImages.each { line ->
echo " ${line}"
}
oldImages.each { line ->
def parts = line.split(' ')
def imageTag = parts[0]
def imageId = parts.size() > 1 ? parts[1] : ""
// 对于标签为<none>的无效镜像使用镜像ID删除
if (imageTag.contains("<none>") && imageId) {
echo "删除无效镜像: ${imageId}"
sh(returnStatus: true, script: "docker rmi -f ${imageId} || true")
} else if (imageId) {
// 对于有标签的有效镜像优先使用镜像ID删除
echo "删除旧镜像: ${imageTag} (${imageId})"
sh(returnStatus: true, script: "docker rmi -f ${imageId} || true")
} else {
// 兜底方案,使用标签删除
echo "删除旧镜像: ${imageTag}"
sh(returnStatus: true, script: "docker rmi -f ${imageTag} || true")
}
}
echo "清理完成,当前机器上还有的镜像:"
sh """
docker images ${imagePrefix} --format 'table {{.Repository}}\\t{{.Tag}}\\t{{.CreatedAt}}\\t{{.Size}}'
"""
}
}
success {
script {
// 发送飞书通知,失败不影响构建结果
try {
lark (
robot: "${LARK_ROBOT}",
type: "CARD",
title: "${IMAGE_NAME}镜像构建成功",
text: [
"📅 **构建时间**: ${currentTime}",
"📋 **任务名称**: [${JOB_NAME}](${JOB_URL})",
"🔢 **任务编号**: [${BUILD_DISPLAY_NAME}](${BUILD_URL})",
"🔀 **代码分支**: ${params.Code_branch}",
"🌟 **构建状态**: <font color='green'>构建成功</font>",
"⏱️ **构建用时**: ${currentBuild.duration.intdiv(1000)} 秒",
"📦 **镜像名称**: ${REGISTRY}/${NAMESPACE}/${IMAGE_NAME}:${IMAGE_TAG}",
"📝 **提交摘要**: ${env.GIT_COMMIT_MSG}",
"👤 **执行账号**: ${env.ACTUAL_USER}",
]
)
} catch (Exception e) {
echo "⚠️ 飞书通知发送失败(可能是限频),但不影响构建结果: ${e.message}"
}
// 输出构建结果
echo """
✅ 镜像构建成功
镜像地址:${REGISTRY}/${NAMESPACE}/${IMAGE_NAME}:${IMAGE_TAG}
代码分支:${params.Code_branch}
提交摘要:${env.GIT_COMMIT_MSG}
""".stripIndent()
}
script {
// 构建成功后,根据勾选情况触发部署任务
// 定义部署映射:参数名 → 部署 Job 名
def deployMap = [
'DEPLOY_TO_S1' : 'DM_s1_flymoon_admin'
]
// 获取完整镜像名(从已有 env 变量拼接)
def fullImageName = "${env.REGISTRY}/${env.NAMESPACE}/${env.IMAGE_NAME}:${env.IMAGE_TAG}"
deployMap.each { paramKey, jobName ->
if (params."${paramKey}" == true) {
echo "触发部署:${jobName} ← ${fullImageName}"
// 异步触发
build job: jobName,
parameters: [
string(name: 'CUSTOM_IMAGE', value: fullImageName),
string(name: 'ACTUAL_USER', value: ACTUAL_USER),
],
wait: false // ← 异步(此 Job 结束,不等待被调用的 Job 结束)
} else {
echo "未勾选 ${paramKey}, 跳过触发部署:${jobName}"
}
}
}
}
failure {
script {
try {
lark (
robot: "${LARK_ROBOT}",
type: "CARD",
title: "${IMAGE_NAME}镜像构建失败",
text: [
"📅 **构建时间**: ${currentTime}",
"📋 **任务名称**: [${JOB_NAME}](${JOB_URL})",
"🔢 **任务编号**: [${BUILD_DISPLAY_NAME}](${BUILD_URL})",
"🔀 **代码分支**: ${params.Code_branch}",
"🌟 **构建状态**: <font color='red'>构建失败</font>",
"⏱️ **构建用时**: ${currentBuild.duration.intdiv(1000)} 秒",
"📦 **镜像名称**: ${REGISTRY}/${NAMESPACE}/${IMAGE_NAME}:${IMAGE_TAG}",
"📝 **提交摘要**: ${env.GIT_COMMIT_MSG}",
"👤 **执行账号**: ${env.ACTUAL_USER}",
]
)
} catch (Exception e) {
echo "⚠️ 飞书通知发送失败: ${e.message}"
}
echo "❌ 构建失败"
}
}
aborted {
script {
try {
lark (
robot: "${LARK_ROBOT}",
type: "CARD",
title: "${IMAGE_NAME}镜像构建取消",
text: [
"📅 **构建时间**: ${currentTime}",
"📋 **任务名称**: [${JOB_NAME}](${JOB_URL})",
"🔢 **任务编号**: [${BUILD_DISPLAY_NAME}](${BUILD_URL})",
"🔀 **代码分支**: ${params.Code_branch}",
"🌟 **构建状态**: <font color='orange'></font>",
"⏱️ **构建用时**: ${currentBuild.duration.intdiv(1000)} 秒",
"📦 **镜像名称**: ${REGISTRY}/${NAMESPACE}/${IMAGE_NAME}:${IMAGE_TAG}",
"📝 ****: ${env.GIT_COMMIT_MSG}",
"👤 ****: ${env.ACTUAL_USER}",
]
)
} catch (Exception e) {
echo " : ${e.message}"
}
echo " "
}
}
}
}