diff --git a/Jenkinsfile b/Jenkinsfile index 76640fd..0424d3b 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -375,109 +375,154 @@ pipeline { } stage('9. 推送到头歌平台') { - steps { - echo '========== 推送制品到头歌 release 分支 ==========' - script { - try { - withCredentials([usernamePassword( - credentialsId: 'educoder-credentials', - usernameVariable: 'EDUCODER_USER', - passwordVariable: 'EDUCODER_PASS' - )]) { - bat ''' - @echo off - setlocal EnableExtensions EnableDelayedExpansion - - set "VERSION_TAG=v1.0.0.%BUILD_NUMBER%" - set "ARTIFACTS_DIR=artifacts\\!VERSION_TAG!" - - echo ============================================ - echo 准备制品目录: !VERSION_TAG! - echo ============================================ - - REM 创建制品目录 - if not exist artifacts mkdir artifacts - if exist "!ARTIFACTS_DIR!" rmdir /S /Q "!ARTIFACTS_DIR!" - mkdir "!ARTIFACTS_DIR!" - - REM 复制制品并重命名 (使用 fatJar) - echo 复制制品... - for %%f in (cli\\build\\libs\\mcslms-cli-*-all.jar) do ( - copy /Y "%%f" "!ARTIFACTS_DIR!\\mcslms-cli-!VERSION_TAG!.jar" >nul - echo ✓ CLI JAR (fatJar) - ) - for %%f in (gui\\build\\libs\\mcslms-gui-*-all.jar) do ( - copy /Y "%%f" "!ARTIFACTS_DIR!\\mcslms-gui-!VERSION_TAG!.jar" >nul - echo ✓ GUI JAR (fatJar) - ) - for %%f in (backend\\build\\libs\\mcslms-backend-*.jar) do ( - copy /Y "%%f" "!ARTIFACTS_DIR!\\mcslms-backend-!VERSION_TAG!.jar" >nul - echo ✓ Backend JAR - ) - for %%f in (backend\\build\\libs\\mcslms-web-*.war) do ( - copy /Y "%%f" "!ARTIFACTS_DIR!\\mcslms-web-!VERSION_TAG!.war" >nul - echo ✓ Web WAR - ) - - REM 查找 APK - for %%f in (android\\build\\outputs\\apk\\debug\\*.apk) do ( - copy /Y "%%f" "!ARTIFACTS_DIR!\\mcslms-app-!VERSION_TAG!.apk" >nul - echo ✓ Android APK - ) - - REM 复制 GUI EXE/ZIP - if exist artifacts\\mcslms-gui-!VERSION_TAG!.exe ( - copy /Y artifacts\\mcslms-gui-!VERSION_TAG!.exe "!ARTIFACTS_DIR!\\\\" >nul - echo ✓ GUI EXE - ) - if exist artifacts\\mcslms-gui.zip ( - copy /Y artifacts\\mcslms-gui.zip "!ARTIFACTS_DIR!\\mcslms-gui-!VERSION_TAG!.zip" >nul - echo ✓ GUI ZIP - ) - - REM 生成清单文件 - echo MCSLMS Release Manifest - !VERSION_TAG! > "!ARTIFACTS_DIR!\\manifest.txt" - echo Build ID: %BUILD_NUMBER% >> "!ARTIFACTS_DIR!\\manifest.txt" - echo 发布时间: %date% %time% >> "!ARTIFACTS_DIR!\\manifest.txt" - echo. >> "!ARTIFACTS_DIR!\\manifest.txt" - echo 制品列表: >> "!ARTIFACTS_DIR!\\manifest.txt" - for %%F in (!ARTIFACTS_DIR!\\*) do echo %%~nxF >> "!ARTIFACTS_DIR!\\manifest.txt" - - echo. - echo 制品目录内容: - dir /B "!ARTIFACTS_DIR!" - - REM URL编码凭据 - for /f %%i in ('powershell -NoLogo -NoProfile -Command "[Console]::Out.Write([uri]::EscapeDataString($env:EDUCODER_USER))"') do set USER_ENC=%%i - for /f %%i in ('powershell -NoLogo -NoProfile -Command "[Console]::Out.Write([uri]::EscapeDataString($env:EDUCODER_PASS))"') do set PASS_ENC=%%i - - echo. - echo ============================================ - echo 推送到头歌 release 分支 - echo ============================================ - - git config user.name "Jenkins CI" - git config user.email "jenkins@mcslms.local" - - REM 创建 orphan 分支只包含制品 - git checkout --orphan release-%BUILD_NUMBER% - git rm -rf . --ignore-unmatch >nul 2>&1 - git add artifacts/ - git commit -m "release: 发布 !VERSION_TAG! (Build #%BUILD_NUMBER%)" - - echo 推送到头歌平台... - git push https://%USER_ENC%:%PASS_ENC%@bdgit.educoder.net/pu6zrsfoy/mcslms.git HEAD:refs/heads/release --force - - REM 恢复原分支 - git checkout -f develop 2>nul || git checkout -f main 2>nul - - echo ✓ 制品已推送到头歌 release 分支: !VERSION_TAG! - ''' + parallel { + stage('9.1 推送源码到 main') { + steps { + echo '========== 推送源代码到头歌 main 分支 ==========' + script { + try { + withCredentials([usernamePassword( + credentialsId: 'educoder-credentials', + usernameVariable: 'EDUCODER_USER', + passwordVariable: 'EDUCODER_PASS' + )]) { + bat ''' + @echo off + setlocal EnableExtensions EnableDelayedExpansion + + REM URL编码用户名和密码 + for /f %%i in ('powershell -NoLogo -NoProfile -Command "[Console]::Out.Write([uri]::EscapeDataString($env:EDUCODER_USER))"') do set USER_ENC=%%i + for /f %%i in ('powershell -NoLogo -NoProfile -Command "[Console]::Out.Write([uri]::EscapeDataString($env:EDUCODER_PASS))"') do set PASS_ENC=%%i + + git config user.name "Jenkins CI" + git config user.email "jenkins@mcslms.local" + git remote remove educoder 2>nul || echo Remote educoder does not exist + git remote add educoder https://bdgit.educoder.net/pu6zrsfoy/mcslms.git + git fetch --unshallow 2>nul || echo Already a complete repository + + REM 获取当前提交的 SHA + for /f %%i in ('git rev-parse HEAD') do set CURRENT_SHA=%%i + echo 当前提交 SHA: !CURRENT_SHA! + + REM 使用 SHA 推送到 main 分支 + git push https://%USER_ENC%:%PASS_ENC%@bdgit.educoder.net/pu6zrsfoy/mcslms.git !CURRENT_SHA!:refs/heads/main --force + ''' + } + echo '✓ 源代码推送到 main 成功' + } catch (Exception e) { + echo "⚠ 推送代码到 main 失败: ${e.message}" + currentBuild.result = 'UNSTABLE' + } + } + } + } + + stage('9.2 推送制品到 release') { + steps { + echo '========== 推送制品到头歌 release 分支 ==========' + script { + try { + withCredentials([usernamePassword( + credentialsId: 'educoder-credentials', + usernameVariable: 'EDUCODER_USER', + passwordVariable: 'EDUCODER_PASS' + )]) { + bat ''' + @echo off + setlocal EnableExtensions EnableDelayedExpansion + + set "VERSION_TAG=v1.0.0.%BUILD_NUMBER%" + set "ARTIFACTS_DIR=artifacts\\!VERSION_TAG!" + + echo ============================================ + echo 准备制品目录: !VERSION_TAG! + echo ============================================ + + REM 创建制品目录 + if not exist artifacts mkdir artifacts + if exist "!ARTIFACTS_DIR!" rmdir /S /Q "!ARTIFACTS_DIR!" + mkdir "!ARTIFACTS_DIR!" + + REM 复制制品并重命名 (使用 fatJar) + echo 复制制品... + for %%f in (cli\\build\\libs\\mcslms-cli-*-all.jar) do ( + copy /Y "%%f" "!ARTIFACTS_DIR!\\mcslms-cli-!VERSION_TAG!.jar" >nul + echo ✓ CLI JAR (fatJar) + ) + for %%f in (gui\\build\\libs\\mcslms-gui-*-all.jar) do ( + copy /Y "%%f" "!ARTIFACTS_DIR!\\mcslms-gui-!VERSION_TAG!.jar" >nul + echo ✓ GUI JAR (fatJar) + ) + for %%f in (backend\\build\\libs\\mcslms-backend-*.jar) do ( + copy /Y "%%f" "!ARTIFACTS_DIR!\\mcslms-backend-!VERSION_TAG!.jar" >nul + echo ✓ Backend JAR + ) + for %%f in (backend\\build\\libs\\mcslms-web-*.war) do ( + copy /Y "%%f" "!ARTIFACTS_DIR!\\mcslms-web-!VERSION_TAG!.war" >nul + echo ✓ Web WAR + ) + + REM 查找 APK + for %%f in (android\\build\\outputs\\apk\\debug\\*.apk) do ( + copy /Y "%%f" "!ARTIFACTS_DIR!\\mcslms-app-!VERSION_TAG!.apk" >nul + echo ✓ Android APK + ) + + REM 复制 GUI EXE/ZIP + if exist artifacts\\mcslms-gui-!VERSION_TAG!.exe ( + copy /Y artifacts\\mcslms-gui-!VERSION_TAG!.exe "!ARTIFACTS_DIR!\\\\" >nul + echo ✓ GUI EXE + ) + if exist artifacts\\mcslms-gui.zip ( + copy /Y artifacts\\mcslms-gui.zip "!ARTIFACTS_DIR!\\mcslms-gui-!VERSION_TAG!.zip" >nul + echo ✓ GUI ZIP + ) + + REM 生成清单文件 + echo MCSLMS Release Manifest - !VERSION_TAG! > "!ARTIFACTS_DIR!\\manifest.txt" + echo Build ID: %BUILD_NUMBER% >> "!ARTIFACTS_DIR!\\manifest.txt" + echo 发布时间: %date% %time% >> "!ARTIFACTS_DIR!\\manifest.txt" + echo. >> "!ARTIFACTS_DIR!\\manifest.txt" + echo 制品列表: >> "!ARTIFACTS_DIR!\\manifest.txt" + for %%F in (!ARTIFACTS_DIR!\\*) do echo %%~nxF >> "!ARTIFACTS_DIR!\\manifest.txt" + + echo. + echo 制品目录内容: + dir /B "!ARTIFACTS_DIR!" + + REM URL编码凭据 + for /f %%i in ('powershell -NoLogo -NoProfile -Command "[Console]::Out.Write([uri]::EscapeDataString($env:EDUCODER_USER))"') do set USER_ENC=%%i + for /f %%i in ('powershell -NoLogo -NoProfile -Command "[Console]::Out.Write([uri]::EscapeDataString($env:EDUCODER_PASS))"') do set PASS_ENC=%%i + + echo. + echo ============================================ + echo 推送到头歌 release 分支 + echo ============================================ + + git config user.name "Jenkins CI" + git config user.email "jenkins@mcslms.local" + + REM 创建 orphan 分支只包含制品 + git checkout --orphan release-%BUILD_NUMBER% + git rm -rf . --ignore-unmatch >nul 2>&1 + git add artifacts/ + git commit -m "release: 发布 !VERSION_TAG! (Build #%BUILD_NUMBER%)" + + echo 推送到头歌平台... + git push https://%USER_ENC%:%PASS_ENC%@bdgit.educoder.net/pu6zrsfoy/mcslms.git HEAD:refs/heads/release --force + + REM 恢复原分支 + git checkout -f develop 2>nul || git checkout -f main 2>nul + + echo ✓ 制品已推送到头歌 release 分支: !VERSION_TAG! + ''' + } + echo '✓ 头歌平台发布成功' + } catch (Exception e) { + echo "⚠ 推送到头歌失败: ${e.message}" + currentBuild.result = 'UNSTABLE' + } } - echo '✓ 头歌平台发布成功' - } catch (Exception e) { - echo "⚠ 推送到头歌失败: ${e.message}" - currentBuild.result = 'UNSTABLE' } } } @@ -497,6 +542,7 @@ pipeline { echo ' - Tomcat: http://localhost:8080/mcslms/' echo ' - 测试报告: Jenkins -> Core 测试报告' echo ' - SonarQube: http://localhost:9000/dashboard?id=mcslms' + echo ' - 头歌源码: https://bdgit.educoder.net/pu6zrsfoy/mcslms/tree/main' echo ' - 头歌制品: https://bdgit.educoder.net/pu6zrsfoy/mcslms/tree/release' echo '==========================================' } @@ -504,10 +550,68 @@ pipeline { echo '❌ 构建失败,请检查日志' } always { - echo "=== 构建完成: ${env.RELEASE_VERSION} ===" - // 清理临时目录 - bat 'if exist jpackage-input rmdir /S /Q jpackage-input 2>nul' - bat 'if exist jpackage-output rmdir /S /Q jpackage-output 2>nul' + script { + echo "=== 构建完成: ${env.RELEASE_VERSION} ===" + + // 清理临时目录 + bat 'if exist jpackage-input rmdir /S /Q jpackage-input 2>nul' + bat 'if exist jpackage-output rmdir /S /Q jpackage-output 2>nul' + + // 发送邮件通知 + echo '========== 发送邮件通知 ==========' + def finalResult = currentBuild.result ?: 'SUCCESS' + def emailSubject = "" + def emailStatus = "" + + if (finalResult == 'SUCCESS') { + emailSubject = "✅ MCSLMS 构建成功 - ${env.RELEASE_VERSION} (Build #${BUILD_NUMBER})" + emailStatus = "成功" + } else if (finalResult == 'FAILURE') { + emailSubject = "❌ MCSLMS 构建失败 - ${env.RELEASE_VERSION} (Build #${BUILD_NUMBER})" + emailStatus = "失败" + } else if (finalResult == 'UNSTABLE') { + emailSubject = "⚠️ MCSLMS 构建不稳定 - ${env.RELEASE_VERSION} (Build #${BUILD_NUMBER})" + emailStatus = "不稳定" + } else { + emailSubject = "ℹ️ MCSLMS 构建完成 - ${env.RELEASE_VERSION} (Build #${BUILD_NUMBER})" + emailStatus = "完成" + } + + echo "准备发送邮件: ${emailSubject}" + echo "收件人: 602924803@qq.com" + + try { + mail to: '602924803@qq.com', + subject: emailSubject, + body: """MCSLMS 项目构建${emailStatus} + +构建编号: #${BUILD_NUMBER} +Release 版本: ${env.RELEASE_VERSION} +构建状态: ${emailStatus} +构建时间: ${new Date(currentBuild.startTimeInMillis)} +Git 提交: ${env.GIT_COMMIT ?: 'N/A'} + +制品列表: +- mcslms-cli-${env.RELEASE_VERSION}.jar (命令行版本) +- mcslms-gui-${env.RELEASE_VERSION}.jar (图形界面版本) +- mcslms-gui-${env.RELEASE_VERSION}.exe (Windows安装包) +- mcslms-web-${env.RELEASE_VERSION}.war (Web应用版本) +- mcslms-backend-${env.RELEASE_VERSION}.jar (后端服务) +- mcslms-app-${env.RELEASE_VERSION}.apk (Android版本) + +访问地址: +- Tomcat: http://localhost:8080/mcslms/ +- SonarQube: http://localhost:9000/dashboard?id=mcslms +- 头歌源码: https://bdgit.educoder.net/pu6zrsfoy/mcslms/tree/main +- 头歌制品: https://bdgit.educoder.net/pu6zrsfoy/mcslms/tree/release + +查看构建详情: ${BUILD_URL} +""" + echo '✓ 邮件发送成功' + } catch (Exception e) { + echo "⚠️ 邮件发送失败: ${e.message}" + } + } } } } diff --git a/sonar-project.properties b/sonar-project.properties index ffc39c6..bed3784 100644 --- a/sonar-project.properties +++ b/sonar-project.properties @@ -105,16 +105,16 @@ sonar.test.exclusions=\ # ======================================== # Gitea 本地仓库 -sonar.links.homepage=http://localhost:3000/mcslms/SLMS +sonar.links.homepage=http://localhost:3000/mcslms/ # Jenkins CI sonar.links.ci=http://localhost:8084/job/mcslms # 头歌远程仓库 -sonar.links.scm=https://bdgit.educoder.net/pu6zrsfoy/CHZU_CS231_SEB_lab.git +sonar.links.scm=https://bdgit.educoder.net/pu6zrsfoy/mcslms.git # 问题跟踪 -sonar.links.issue=http://localhost:3000/mcslms/SLMS/issues +sonar.links.issue=http://localhost:3000/mcslms/issues # ======================================== # 其他配置