|
|
|
|
@ -1,219 +1,228 @@
|
|
|
|
|
pipeline {
|
|
|
|
|
agent any
|
|
|
|
|
|
|
|
|
|
// 全局环境变量配置
|
|
|
|
|
environment {
|
|
|
|
|
// Java环境
|
|
|
|
|
JAVA_HOME = "${tool 'JDK-21'}"
|
|
|
|
|
MAVEN_HOME = "${tool 'Maven-3.9'}"
|
|
|
|
|
MAVEN_OPTS = '-Xmx1024m'
|
|
|
|
|
|
|
|
|
|
// 应用信息
|
|
|
|
|
APP_NAME = 'cstatm-mte'
|
|
|
|
|
APP_VERSION = '1.0.0'
|
|
|
|
|
|
|
|
|
|
// SonarQube配置
|
|
|
|
|
SONAR_SCANNER_HOME = tool 'SonarQubeScanner'
|
|
|
|
|
SONARQUBE_SCANNER_PARAMS = '-Dsonar.host.url=http://localhost:9000 -Dsonar.login=$SONAR_TOKEN'
|
|
|
|
|
|
|
|
|
|
// 头歌仓库凭据
|
|
|
|
|
EDU_CRED = credentials('educoder-cred')
|
|
|
|
|
JAVA_HOME = 'E:\\2025-2026\\GitAIOps\\jdk'
|
|
|
|
|
SONAR_HOST_URL = 'http://localhost:9000'
|
|
|
|
|
SONAR_PROJECT_KEY = 'sqp_9d031755bd0b2619fa06f5002e7aa9d6aef349d6'
|
|
|
|
|
SONARQUBE_SCANNER_PARAMS = '-Dsonar.qualitygate.wait=true -Dsonar.qualitygate.timeout=300'
|
|
|
|
|
SONARQUBE_QUALITY_GATE = 'CSTATM-MTE-Quality-Gate'
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 工具配置
|
|
|
|
|
tools {
|
|
|
|
|
maven 'Maven-3.9'
|
|
|
|
|
jdk 'JDK-21'
|
|
|
|
|
sonarQubeScanner 'SonarQubeScanner'
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 流水线选项
|
|
|
|
|
options {
|
|
|
|
|
skipDefaultCheckout(true)
|
|
|
|
|
timeout(time: 60, unit: 'MINUTES')
|
|
|
|
|
buildDiscarder(logRotator(numToKeepStr: '10'))
|
|
|
|
|
timestamps()
|
|
|
|
|
maven 'maven396'
|
|
|
|
|
jdk 'jdk21'
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
stages {
|
|
|
|
|
// 阶段1:拉取代码
|
|
|
|
|
stage('拉取代码') {
|
|
|
|
|
stage('1. 拉取代码') {
|
|
|
|
|
steps {
|
|
|
|
|
// 清理工作空间
|
|
|
|
|
cleanWs()
|
|
|
|
|
|
|
|
|
|
// 从Gitea仓库拉取代码
|
|
|
|
|
git branch: 'main',
|
|
|
|
|
url: 'http://localhost:3000/gitea/cstatm-mte.git',
|
|
|
|
|
credentialsId: 'git-credentials'
|
|
|
|
|
|
|
|
|
|
// 显示提交信息
|
|
|
|
|
script {
|
|
|
|
|
def commit = sh(returnStdout: true, script: 'git rev-parse HEAD')
|
|
|
|
|
def message = sh(returnStdout: true, script: 'git log -1 --pretty=%B')
|
|
|
|
|
echo "当前提交: ${commit}"
|
|
|
|
|
echo "提交信息: ${message}"
|
|
|
|
|
}
|
|
|
|
|
echo '========== 从 Gitea 拉取代码 =========='
|
|
|
|
|
checkout scm
|
|
|
|
|
echo '✓ 代码拉取成功'
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 阶段2:代码质量检测
|
|
|
|
|
stage('代码质量检测') {
|
|
|
|
|
stage('2. 编译项目') {
|
|
|
|
|
steps {
|
|
|
|
|
// 执行SonarQube扫描
|
|
|
|
|
withSonarQubeEnv('SonarQube') {
|
|
|
|
|
sh "mvn sonar:sonar ${SONARQUBE_SCANNER_PARAMS}"
|
|
|
|
|
echo '========== 编译 cstatm-mte 项目 =========='
|
|
|
|
|
bat '''
|
|
|
|
|
set JAVA_HOME=%JAVA_HOME%
|
|
|
|
|
mvn clean compile -DskipTests
|
|
|
|
|
'''
|
|
|
|
|
echo '✓ 项目编译成功'
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
stage('3. 运行测试') {
|
|
|
|
|
steps {
|
|
|
|
|
echo '========== 运行单元测试 =========='
|
|
|
|
|
script {
|
|
|
|
|
try {
|
|
|
|
|
bat '''
|
|
|
|
|
set JAVA_HOME=%JAVA_HOME%
|
|
|
|
|
mvn test
|
|
|
|
|
'''
|
|
|
|
|
echo '✓ 测试执行完成'
|
|
|
|
|
} catch (Exception e) {
|
|
|
|
|
echo '⚠️ 测试失败,但继续流水线(测试代码需要修复)'
|
|
|
|
|
echo "错误信息: ${e.message}"
|
|
|
|
|
currentBuild.result = 'UNSTABLE'
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
post {
|
|
|
|
|
always {
|
|
|
|
|
// 等待质量门禁结果
|
|
|
|
|
script {
|
|
|
|
|
def qg = waitForQualityGate()
|
|
|
|
|
if (qg.status != 'OK') {
|
|
|
|
|
error "代码质量检测未通过: ${qg.status}"
|
|
|
|
|
} else {
|
|
|
|
|
echo "代码质量检测通过: ${qg.status}"
|
|
|
|
|
try {
|
|
|
|
|
junit 'target/surefire-reports/*.xml'
|
|
|
|
|
// 发布代码覆盖率报告
|
|
|
|
|
publishHTML([
|
|
|
|
|
allowMissing: false,
|
|
|
|
|
alwaysLinkToLastBuild: true,
|
|
|
|
|
keepAll: true,
|
|
|
|
|
reportDir: 'target/site/jacoco',
|
|
|
|
|
reportFiles: 'index.html',
|
|
|
|
|
reportName: 'JaCoCo Coverage Report'
|
|
|
|
|
])
|
|
|
|
|
} catch (Exception e) {
|
|
|
|
|
echo '⚠️ 没有测试报告'
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 阶段3:编译构建
|
|
|
|
|
stage('编译构建') {
|
|
|
|
|
stage('4. SonarQube 质检') {
|
|
|
|
|
steps {
|
|
|
|
|
// 执行Maven编译
|
|
|
|
|
sh "mvn clean compile"
|
|
|
|
|
|
|
|
|
|
// 检查编译结果
|
|
|
|
|
script {
|
|
|
|
|
def targetDir = 'target/classes'
|
|
|
|
|
if (fileExists(targetDir)) {
|
|
|
|
|
echo "编译成功,生成class文件"
|
|
|
|
|
sh "find ${targetDir} -name '*.class' | wc -l"
|
|
|
|
|
} else {
|
|
|
|
|
error "编译失败,未找到class文件"
|
|
|
|
|
}
|
|
|
|
|
echo '========== 执行 SonarQube 代码质量检测 =========='
|
|
|
|
|
withSonarQubeEnv('SonarQube') {
|
|
|
|
|
bat '''
|
|
|
|
|
set JAVA_HOME=%JAVA_HOME%
|
|
|
|
|
mvn sonar:sonar %SONARQUBE_SCANNER_PARAMS%
|
|
|
|
|
'''
|
|
|
|
|
}
|
|
|
|
|
echo '✓ SonarQube 分析完成'
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 阶段4:运行测试
|
|
|
|
|
stage('运行测试') {
|
|
|
|
|
stage('5. 质量阈检查') {
|
|
|
|
|
steps {
|
|
|
|
|
// 执行单元测试
|
|
|
|
|
sh "mvn test"
|
|
|
|
|
|
|
|
|
|
// 发布测试报告
|
|
|
|
|
junit 'target/surefire-reports/*.xml'
|
|
|
|
|
|
|
|
|
|
// 发布代码覆盖率报告
|
|
|
|
|
publishHTML([
|
|
|
|
|
allowMissing: false,
|
|
|
|
|
alwaysLinkToLastBuild: true,
|
|
|
|
|
keepAll: true,
|
|
|
|
|
reportDir: 'target/site/jacoco',
|
|
|
|
|
reportFiles: 'index.html',
|
|
|
|
|
reportName: 'JaCoCo Coverage Report'
|
|
|
|
|
])
|
|
|
|
|
echo '========== 等待 CSTATM-MTE-Quality-Gate 质量门禁结果 =========='
|
|
|
|
|
timeout(time: 10, unit: 'MINUTES') {
|
|
|
|
|
script {
|
|
|
|
|
def qg = waitForQualityGate()
|
|
|
|
|
if (qg.status != 'OK') {
|
|
|
|
|
error "CSTATM-MTE-Quality-Gate 质量门禁未通过: ${qg.status}"
|
|
|
|
|
} else {
|
|
|
|
|
echo "✓ CSTATM-MTE-Quality-Gate 质量门禁检查通过: ${qg.status}"
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 阶段5:打包应用
|
|
|
|
|
stage('打包应用') {
|
|
|
|
|
stage('6. 打包项目') {
|
|
|
|
|
when {
|
|
|
|
|
expression { currentBuild.result == null || currentBuild.result == 'SUCCESS' }
|
|
|
|
|
}
|
|
|
|
|
steps {
|
|
|
|
|
// 执行Maven打包
|
|
|
|
|
sh "mvn package -DskipTests"
|
|
|
|
|
|
|
|
|
|
// 归档构建产物
|
|
|
|
|
archiveArtifacts artifacts: 'target/*.jar', fingerprint: true
|
|
|
|
|
echo '========== 打包 cstatm-mte 项目 =========='
|
|
|
|
|
bat '''
|
|
|
|
|
set JAVA_HOME=%JAVA_HOME%
|
|
|
|
|
mvn clean package -DskipTests
|
|
|
|
|
'''
|
|
|
|
|
echo '✓ 项目打包成功'
|
|
|
|
|
|
|
|
|
|
// 显示打包结果
|
|
|
|
|
script {
|
|
|
|
|
def jarFiles = sh(returnStdout: true, script: 'ls -la target/*.jar || true')
|
|
|
|
|
echo "打包结果: ${jarFiles}"
|
|
|
|
|
}
|
|
|
|
|
echo '========== 使用 jpackage 创建可执行文件 =========='
|
|
|
|
|
bat '''
|
|
|
|
|
set JAVA_HOME=%JAVA_HOME%
|
|
|
|
|
jpackage --name cstatm-mte --input target --main-jar *.jar --main-class com.atm.AtmApplication --type exe --dest target/dist
|
|
|
|
|
'''
|
|
|
|
|
echo '✓ 可执行文件创建成功'
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 阶段6:推送源码至头歌release分支
|
|
|
|
|
stage('推送源码至头歌release分支') {
|
|
|
|
|
stage('7. 归档制品') {
|
|
|
|
|
when {
|
|
|
|
|
expression { currentBuild.result == null || currentBuild.result == 'SUCCESS' }
|
|
|
|
|
}
|
|
|
|
|
steps {
|
|
|
|
|
echo '========== 归档构建制品 =========='
|
|
|
|
|
script {
|
|
|
|
|
def eduRepo = "https://bdgit.educoder.net/pu6zrsfoy/cstatm-mte.git"
|
|
|
|
|
def eduBranch = "release"
|
|
|
|
|
|
|
|
|
|
// 配置Git用户
|
|
|
|
|
sh 'git config --global user.name "Jenkins"'
|
|
|
|
|
sh 'git config --global user.email "602924803@qq.com"'
|
|
|
|
|
|
|
|
|
|
// 关联头歌仓库并推送源码
|
|
|
|
|
sh "git remote add educoder ${eduRepo} || true"
|
|
|
|
|
sh "git pull educoder ${eduBranch} --rebase" // 拉取最新代码避免冲突
|
|
|
|
|
sh "git push https://${EDU_CRED_USR}:${EDU_CRED_PSW}@bdgit.educoder.net/pu6zrsfoy/cstatm-mte.git main:${eduBranch}"
|
|
|
|
|
// 归档所有制品
|
|
|
|
|
archiveArtifacts artifacts: '''
|
|
|
|
|
target/*.jar,
|
|
|
|
|
target/*.war,
|
|
|
|
|
target/dist/*.exe
|
|
|
|
|
''',
|
|
|
|
|
fingerprint: true,
|
|
|
|
|
allowEmptyArchive: true
|
|
|
|
|
}
|
|
|
|
|
echo '✓ 制品归档成功'
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 阶段7:发布制品至头歌
|
|
|
|
|
stage('发布制品至头歌') {
|
|
|
|
|
stage('8. 推送代码到头歌') {
|
|
|
|
|
when {
|
|
|
|
|
expression { currentBuild.result == null || currentBuild.result == 'SUCCESS' }
|
|
|
|
|
}
|
|
|
|
|
steps {
|
|
|
|
|
echo '========== 推送源代码到头歌仓库 =========='
|
|
|
|
|
script {
|
|
|
|
|
// 复制JAR包并提交
|
|
|
|
|
sh 'cp target/*.jar ./'
|
|
|
|
|
sh 'git add *.jar'
|
|
|
|
|
sh 'git commit -m "发布登录功能制品:$(date +%Y%m%d)"'
|
|
|
|
|
sh "git push https://${EDU_CRED_USR}:${EDU_CRED_PSW}@bdgit.educoder.net/pu6zrsfoy/cstatm-mte.git HEAD:release"
|
|
|
|
|
try {
|
|
|
|
|
withCredentials([usernamePassword(
|
|
|
|
|
credentialsId: 'tougo-credentials',
|
|
|
|
|
usernameVariable: 'TOUGO_USER',
|
|
|
|
|
passwordVariable: 'TOUGO_PASS'
|
|
|
|
|
)]) {
|
|
|
|
|
bat """
|
|
|
|
|
git config user.name "Jenkins CI"
|
|
|
|
|
git config user.email "jenkins@cstatm-mte.local"
|
|
|
|
|
git remote add tougo https://bdgit.educoder.net/pu6zrsfoy/cstatm-mte.git || git remote set-url tougo https://bdgit.educoder.net/pu6zrsfoy/cstatm-mte.git
|
|
|
|
|
git push https://%TOUGO_USER%:%TOUGO_PASS%@bdgit.educoder.net/pu6zrsfoy/cstatm-mte.git HEAD:main --force
|
|
|
|
|
"""
|
|
|
|
|
}
|
|
|
|
|
echo '✓ 源代码推送到头歌成功'
|
|
|
|
|
} catch (Exception e) {
|
|
|
|
|
echo '⚠ 推送代码到头歌失败'
|
|
|
|
|
echo "错误信息: ${e.message}"
|
|
|
|
|
currentBuild.result = 'UNSTABLE'
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 后置操作
|
|
|
|
|
post {
|
|
|
|
|
// 成功时执行
|
|
|
|
|
success {
|
|
|
|
|
echo "构建成功"
|
|
|
|
|
|
|
|
|
|
// 归档构建产物
|
|
|
|
|
archiveArtifacts artifacts: 'target/*', fingerprint: true
|
|
|
|
|
|
|
|
|
|
// 发布HTML报告
|
|
|
|
|
publishHTML([
|
|
|
|
|
allowMissing: false,
|
|
|
|
|
alwaysLinkToLastBuild: true,
|
|
|
|
|
keepAll: true,
|
|
|
|
|
reportDir: 'target/site',
|
|
|
|
|
reportFiles: 'index.html',
|
|
|
|
|
reportName: 'Maven Site Report'
|
|
|
|
|
])
|
|
|
|
|
echo '========== 流水线执行成功 =========='
|
|
|
|
|
emailext (
|
|
|
|
|
subject: "✅ cstatm-mte 构建成功 - Build #${BUILD_NUMBER}",
|
|
|
|
|
body: """
|
|
|
|
|
<h2>cstatm-mte 项目构建成功</h2>
|
|
|
|
|
<p><strong>构建编号:</strong> ${BUILD_NUMBER}</p>
|
|
|
|
|
<p><strong>构建时间:</strong> ${new Date()}</p>
|
|
|
|
|
<p><strong>提交信息:</strong> ${env.CHANGE_ID ?: 'N/A'}</p>
|
|
|
|
|
<p><strong>查看详情:</strong> <a href="${BUILD_URL}">点击这里</a></p>
|
|
|
|
|
""",
|
|
|
|
|
to: 'dev-team@example.com'
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 失败时执行
|
|
|
|
|
failure {
|
|
|
|
|
echo "构建失败"
|
|
|
|
|
|
|
|
|
|
// 发送失败通知
|
|
|
|
|
echo '========== 流水线执行失败 =========='
|
|
|
|
|
emailext (
|
|
|
|
|
subject: "构建失败: ${env.JOB_NAME} - ${env.BUILD_NUMBER}",
|
|
|
|
|
body: "构建失败,请查看日志: ${env.BUILD_URL}console",
|
|
|
|
|
subject: "❌ cstatm-mte 构建失败 - Build #${BUILD_NUMBER}",
|
|
|
|
|
body: """
|
|
|
|
|
<h2>cstatm-mte 项目构建失败</h2>
|
|
|
|
|
<p><strong>构建编号:</strong> ${BUILD_NUMBER}</p>
|
|
|
|
|
<p><strong>构建时间:</strong> ${new Date()}</p>
|
|
|
|
|
<p><strong>失败阶段:</strong> ${currentBuild.currentResult}</p>
|
|
|
|
|
<p><strong>查看详情:</strong> <a href="${BUILD_URL}console">点击这里</a></p>
|
|
|
|
|
""",
|
|
|
|
|
to: 'dev-team@example.com'
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
unstable {
|
|
|
|
|
echo '========== 流水线执行不稳定 =========='
|
|
|
|
|
emailext (
|
|
|
|
|
subject: "⚠️ cstatm-mte 构建不稳定 - Build #${BUILD_NUMBER}",
|
|
|
|
|
body: """
|
|
|
|
|
<h2>cstatm-mte 项目构建不稳定</h2>
|
|
|
|
|
<p><strong>构建编号:</strong> ${BUILD_NUMBER}</p>
|
|
|
|
|
<p><strong>构建时间:</strong> ${new Date()}</p>
|
|
|
|
|
<p><strong>状态:</strong> ${currentBuild.currentResult}</p>
|
|
|
|
|
<p><strong>查看详情:</strong> <a href="${BUILD_URL}">点击这里</a></p>
|
|
|
|
|
""",
|
|
|
|
|
to: 'dev-team@example.com'
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 总是执行
|
|
|
|
|
always {
|
|
|
|
|
// 清理工作空间
|
|
|
|
|
echo '========== 清理工作空间 =========='
|
|
|
|
|
cleanWs()
|
|
|
|
|
|
|
|
|
|
// 显示构建结果
|
|
|
|
|
script {
|
|
|
|
|
def result = currentBuild.result ?: 'SUCCESS'
|
|
|
|
|
def duration = currentBuild.durationString.replaceAll(' and counting', '')
|
|
|
|
|
echo "构建结果: ${result}"
|
|
|
|
|
echo "构建时长: ${duration}"
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|