You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

372 lines
16 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

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()
}
}
}