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: """

cstatm-mte 项目构建成功

构建编号: ${BUILD_NUMBER}

构建时间: ${new Date()}

提交信息: ${env.CHANGE_ID ?: 'N/A'}

查看详情: 点击这里

""", to: '602924803@qq.com' ) } failure { echo '========== 流水线执行失败 ==========' emailext ( subject: "❌ cstatm-mte 构建失败 - Build #${BUILD_NUMBER}", body: """

cstatm-mte 项目构建失败

构建编号: ${BUILD_NUMBER}

构建时间: ${new Date()}

失败阶段: ${currentBuild.currentResult}

查看详情: 点击这里

""", to: '602924803@qq.com' ) } unstable { echo '========== 流水线执行不稳定 ==========' emailext ( subject: "⚠️ cstatm-mte 构建不稳定 - Build #${BUILD_NUMBER}", body: """

cstatm-mte 项目构建不稳定

构建编号: ${BUILD_NUMBER}

构建时间: ${new Date()}

状态: ${currentBuild.currentResult}

查看详情: 点击这里

""", to: '602924803@qq.com' ) } always { echo '========== 清理工作空间 ==========' cleanWs() } } }