|
|
pipeline {
|
|
|
agent any
|
|
|
|
|
|
environment {
|
|
|
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 'maven396'
|
|
|
jdk 'jdk21'
|
|
|
}
|
|
|
|
|
|
stages {
|
|
|
stage('0. 初始化工具链') {
|
|
|
steps {
|
|
|
script {
|
|
|
env.JAVA_HOME = tool 'jdk21'
|
|
|
env.MAVEN_HOME = tool 'maven396'
|
|
|
env.PATH = "${env.JAVA_HOME}\\bin;${env.MAVEN_HOME}\\bin;${env.PATH}"
|
|
|
}
|
|
|
bat '''
|
|
|
echo 当前 JAVA_HOME=%JAVA_HOME%
|
|
|
"%JAVA_HOME%\\bin\\java" -version
|
|
|
mvn -version
|
|
|
'''
|
|
|
}
|
|
|
}
|
|
|
|
|
|
stage('1. 拉取代码') {
|
|
|
steps {
|
|
|
echo '========== 从 Gitea 拉取代码 =========='
|
|
|
checkout scm
|
|
|
echo '✓ 代码拉取成功'
|
|
|
}
|
|
|
}
|
|
|
|
|
|
stage('2. 编译项目') {
|
|
|
steps {
|
|
|
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 {
|
|
|
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 '⚠ 没有测试报告'
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
stage('4. SonarQube 质检') {
|
|
|
when {
|
|
|
expression { currentBuild.result == null || currentBuild.result == 'SUCCESS' }
|
|
|
}
|
|
|
steps {
|
|
|
echo '========== 执行 SonarQube 代码质量检测 =========='
|
|
|
withSonarQubeEnv('SonarQube') {
|
|
|
bat '''
|
|
|
set JAVA_HOME=%JAVA_HOME%
|
|
|
mvn sonar:sonar -Dsonar.qualitygate.wait=true -Dsonar.qualitygate.timeout=300
|
|
|
'''
|
|
|
}
|
|
|
echo '✓ SonarQube 分析完成'
|
|
|
}
|
|
|
}
|
|
|
|
|
|
stage('5. 质量阈检查') {
|
|
|
steps {
|
|
|
echo '========== 等待 CSTATM-MTE-Quality-Gate 质量门禁结果 =========='
|
|
|
timeout(time: 10, unit: 'MINUTES') {
|
|
|
script {
|
|
|
withSonarQubeEnv('SonarQube') {
|
|
|
def qg = waitForQualityGate()
|
|
|
if (qg.status != 'OK') {
|
|
|
error "CSTATM-MTE-Quality-Gate 质量门禁未通过: ${qg.status}"
|
|
|
} else {
|
|
|
echo "✓ CSTATM-MTE-Quality-Gate 质量门禁检查通过: ${qg.status}"
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
stage('6. 打包项目') {
|
|
|
when {
|
|
|
expression { currentBuild.result == null || currentBuild.result == 'SUCCESS' }
|
|
|
}
|
|
|
steps {
|
|
|
echo '========== 清理 launch4j 缓存(首次运行) =========='
|
|
|
script {
|
|
|
try {
|
|
|
bat '''
|
|
|
if exist "%USERPROFILE%\\.m2\\repository\\net\\sf\\launch4j" (
|
|
|
echo 清理损坏的 launch4j 缓存...
|
|
|
rmdir /s /q "%USERPROFILE%\\.m2\\repository\\net\\sf\\launch4j"
|
|
|
)
|
|
|
'''
|
|
|
} catch (Exception e) {
|
|
|
echo '⚠️ 无法清理 launch4j 缓存,继续构建'
|
|
|
}
|
|
|
}
|
|
|
|
|
|
echo '========== 打包 cstatm-mte 项目(JAR + 跳过 EXE) =========='
|
|
|
bat '''
|
|
|
set JAVA_HOME=%JAVA_HOME%
|
|
|
set PATH=%JAVA_HOME%\bin;%PATH%
|
|
|
mvn -B clean package -DskipTests -Dlaunch4j.skip=true
|
|
|
if errorlevel 1 exit /b %ERRORLEVEL%
|
|
|
'''
|
|
|
echo '✓ 项目打包成功'
|
|
|
echo '✓ JAR 文件: target/cstatm-mte-uber.jar'
|
|
|
echo '⚠ 已跳过 EXE 打包(launch4j.skip=true)'
|
|
|
|
|
|
echo '========== 使用 WiX 生成 MSI =========='
|
|
|
bat """
|
|
|
if not exist installer mkdir installer
|
|
|
del /q installer\\cstatm-mte.wixobj 2>nul
|
|
|
del /q installer\\cstatm-mte.wixpdb 2>nul
|
|
|
del /q installer\\cstatm-mte.msi 2>nul
|
|
|
\"C:\\Program Files (x86)\\WiX Toolset v3.11\\bin\\candle.exe\" \"installer\\cstatm-mte.wxs\" -out \"installer\\cstatm-mte.wixobj\"
|
|
|
if errorlevel 1 exit /b %ERRORLEVEL%
|
|
|
\"C:\\Program Files (x86)\\WiX Toolset v3.11\\bin\\light.exe\" \"installer\\cstatm-mte.wixobj\" -out \"installer\\cstatm-mte.msi\"
|
|
|
if errorlevel 1 exit /b %ERRORLEVEL%
|
|
|
"""
|
|
|
echo '✓ MSI 文件: installer/cstatm-mte.msi'
|
|
|
}
|
|
|
}
|
|
|
|
|
|
stage('7. 归档制品') {
|
|
|
when {
|
|
|
expression { currentBuild.result == null || currentBuild.result == 'SUCCESS' }
|
|
|
}
|
|
|
steps {
|
|
|
echo '========== 归档构建制品 =========='
|
|
|
script {
|
|
|
// 获取构建信息
|
|
|
def buildNumber = env.BUILD_NUMBER
|
|
|
def gitCommit = bat(script: '@git rev-parse --short HEAD', returnStdout: true).trim()
|
|
|
|
|
|
def timestamp = new Date().format('yyyyMMdd-HHmmss')
|
|
|
|
|
|
// 创建版本信息文件
|
|
|
bat """
|
|
|
echo Build Number: %BUILD_NUMBER% > target/build-info.txt
|
|
|
echo Git Commit: ${gitCommit} >> target/build-info.txt
|
|
|
echo Build Time: ${timestamp} >> target/build-info.txt
|
|
|
echo Java Version: %JAVA_HOME% >> target/build-info.txt
|
|
|
"""
|
|
|
|
|
|
// 归档所有制品
|
|
|
archiveArtifacts artifacts: '''
|
|
|
target/*.jar,
|
|
|
target/*.war,
|
|
|
target/*.exe,
|
|
|
target/build-info.txt,
|
|
|
installer/*.msi,
|
|
|
installer/*.cab
|
|
|
''',
|
|
|
fingerprint: true,
|
|
|
allowEmptyArchive: true
|
|
|
|
|
|
// 发布制品信息
|
|
|
echo "✓ 制品归档成功 - 构建号: ${buildNumber}, 提交: ${gitCommit}"
|
|
|
|
|
|
echo '========== 准备 release 分支制品 =========='
|
|
|
bat """
|
|
|
set RELEASE_ROOT=release
|
|
|
if not exist %RELEASE_ROOT% mkdir %RELEASE_ROOT%
|
|
|
set REL_DIR=%RELEASE_ROOT%\\Build-%BUILD_NUMBER%
|
|
|
if exist %REL_DIR% rmdir /s /q %REL_DIR%
|
|
|
mkdir %REL_DIR%
|
|
|
if exist target\\cstatm-mte-uber.jar copy /y target\\cstatm-mte-uber.jar %REL_DIR%\\ >nul
|
|
|
if exist installer\\cstatm-mte.msi copy /y installer\\cstatm-mte.msi %REL_DIR%\\ >nul
|
|
|
if exist target\\build-info.txt copy /y target\\build-info.txt %REL_DIR%\\ >nul
|
|
|
if exist start-atm.bat copy /y start-atm.bat %REL_DIR%\\ >nul
|
|
|
echo Release artifacts prepared at %REL_DIR%
|
|
|
"""
|
|
|
echo '✓ release 分支制品已准备,稍后推送到仓库'
|
|
|
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
stage('8. 推送代码到头歌') {
|
|
|
when {
|
|
|
expression { currentBuild.result == null || currentBuild.result == 'SUCCESS' }
|
|
|
}
|
|
|
steps {
|
|
|
echo '========== 推送源代码到头歌仓库 =========='
|
|
|
script {
|
|
|
try {
|
|
|
withCredentials([usernamePassword(
|
|
|
credentialsId: 'educoder-credentials',
|
|
|
usernameVariable: 'TOUGO_USER',
|
|
|
passwordVariable: 'TOUGO_PASS'
|
|
|
)]) {
|
|
|
bat '''
|
|
|
@echo off
|
|
|
setlocal EnableExtensions EnableDelayedExpansion
|
|
|
for /f %%i in ('powershell -NoLogo -NoProfile -Command "[Console]::Out.Write([uri]::EscapeDataString($env:TOUGO_USER))"') do set EDU_USER_ENC=%%i
|
|
|
for /f %%i in ('powershell -NoLogo -NoProfile -Command "[Console]::Out.Write([uri]::EscapeDataString($env:TOUGO_PASS))"') do set EDU_PASS_ENC=%%i
|
|
|
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
|
|
|
|
|
|
for /f %%i in ('git rev-parse HEAD') do set BASE_COMMIT=%%i
|
|
|
|
|
|
if exist release (
|
|
|
git add release
|
|
|
git diff --quiet HEAD -- release
|
|
|
set RELEASE_DIFF=!ERRORLEVEL!
|
|
|
if "!RELEASE_DIFF!"=="0" (
|
|
|
echo release 目录无变化,跳过 release 分支制品发布
|
|
|
git reset HEAD release >nul 2>&1
|
|
|
) else (
|
|
|
git commit -m "chore: release artifacts Build %BUILD_NUMBER%"
|
|
|
if errorlevel 1 exit /b %ERRORLEVEL%
|
|
|
git push https://%EDU_USER_ENC%:%EDU_PASS_ENC%@bdgit.educoder.net/pu6zrsfoy/cstatm-mte.git HEAD:release --force
|
|
|
if errorlevel 1 exit /b %ERRORLEVEL%
|
|
|
git reset --hard %BASE_COMMIT%
|
|
|
)
|
|
|
) else (
|
|
|
echo release 目录不存在,跳过 release 分支制品发布
|
|
|
)
|
|
|
|
|
|
if exist release rmdir /s /q release
|
|
|
|
|
|
git push https://%EDU_USER_ENC%:%EDU_PASS_ENC%@bdgit.educoder.net/pu6zrsfoy/cstatm-mte.git HEAD:main --force
|
|
|
'''
|
|
|
}
|
|
|
echo '✓ release 制品与 main 源码已同步到头歌'
|
|
|
} catch (Exception e) {
|
|
|
echo '⚠ 推送代码到头歌失败'
|
|
|
echo "错误信息: ${e.message}"
|
|
|
currentBuild.result = 'UNSTABLE'
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
stage('9. 归档制品') {
|
|
|
when {
|
|
|
expression { currentBuild.result == null || currentBuild.result == 'SUCCESS' }
|
|
|
}
|
|
|
steps {
|
|
|
echo '========== 归档构建制品 =========='
|
|
|
script {
|
|
|
// 获取构建信息
|
|
|
def buildNumber = env.BUILD_NUMBER
|
|
|
def gitCommit = bat(script: '@git rev-parse --short HEAD', returnStdout: true).trim()
|
|
|
|
|
|
def timestamp = new Date().format('yyyyMMdd-HHmmss')
|
|
|
|
|
|
// 创建版本信息文件
|
|
|
bat """
|
|
|
echo Build Number: %BUILD_NUMBER% > target/build-info.txt
|
|
|
echo Git Commit: ${gitCommit} >> target/build-info.txt
|
|
|
echo Build Time: ${timestamp} >> target/build-info.txt
|
|
|
echo Java Version: %JAVA_HOME% >> target/build-info.txt
|
|
|
"""
|
|
|
|
|
|
// 归档所有制品
|
|
|
archiveArtifacts artifacts: '''
|
|
|
target/*.jar,
|
|
|
target/*.war,
|
|
|
target/*.exe,
|
|
|
target/build-info.txt,
|
|
|
installer/*.msi,
|
|
|
installer/*.cab
|
|
|
''',
|
|
|
fingerprint: true,
|
|
|
allowEmptyArchive: true
|
|
|
|
|
|
// 发布制品信息
|
|
|
echo "✓ 制品归档成功 - 构建号: ${buildNumber}, 提交: ${gitCommit}"
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
post {
|
|
|
success {
|
|
|
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: '602924803@qq.com'
|
|
|
)
|
|
|
}
|
|
|
|
|
|
failure {
|
|
|
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}console">点击这里</a></p>
|
|
|
""",
|
|
|
to: '602924803@qq.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: '602924803@qq.com'
|
|
|
)
|
|
|
}
|
|
|
|
|
|
always {
|
|
|
echo '========== 清理工作空间 =========='
|
|
|
cleanWs()
|
|
|
}
|
|
|
}
|
|
|
} |