|
|
pipeline {
|
|
|
agent any
|
|
|
|
|
|
environment {
|
|
|
JAVA_HOME = 'E:\\2025-2026\\GitAIOps\\jdk'
|
|
|
ANDROID_HOME = 'D:\\development\\Android'
|
|
|
WIX_HOME = 'C:\\Program Files (x86)\\WiX Toolset v3.11'
|
|
|
SONAR_HOST_URL = 'http://localhost:9000'
|
|
|
SONAR_PROJECT_KEY = 'slms'
|
|
|
SONAR_PROJECT_NAME = 'slms'
|
|
|
SONARQUBE_SCANNER_PARAMS = '-Dsonar.qualitygate.wait=true -Dsonar.qualitygate.timeout=300'
|
|
|
SONARQUBE_QUALITY_GATE = 'SLMS-Quality-Gate'
|
|
|
}
|
|
|
|
|
|
tools {
|
|
|
maven 'maven396'
|
|
|
jdk 'jdk21'
|
|
|
}
|
|
|
|
|
|
stages {
|
|
|
stage('1. 拉取Gitea代码') {
|
|
|
steps {
|
|
|
echo '========== 从 Gitea 拉取代码 =========='
|
|
|
|
|
|
// 清理可能被占用的文件
|
|
|
script {
|
|
|
try {
|
|
|
bat '''
|
|
|
@echo off
|
|
|
echo 清理工作空间中可能被占用的文件...
|
|
|
|
|
|
REM 只结束与当前工作空间相关的 Java 进程
|
|
|
for /f "tokens=2" %%i in ('tasklist /FI "IMAGENAME eq java.exe" /FO LIST ^| findstr /C:"PID:"') do (
|
|
|
wmic process where "ProcessId=%%i and CommandLine like '%%slms%%'" delete 2>nul
|
|
|
)
|
|
|
|
|
|
REM 等待进程结束
|
|
|
timeout /t 1 /nobreak >nul
|
|
|
|
|
|
echo ✓ 清理完成
|
|
|
'''
|
|
|
} catch (Exception e) {
|
|
|
echo "清理过程中出现警告: ${e.message}"
|
|
|
}
|
|
|
}
|
|
|
|
|
|
// 使用优化的 Git 拉取配置
|
|
|
checkout([
|
|
|
$class: 'GitSCM',
|
|
|
branches: [[name: '*/main']],
|
|
|
extensions: [
|
|
|
[$class: 'CloneOption',
|
|
|
depth: 1,
|
|
|
noTags: true,
|
|
|
shallow: true],
|
|
|
[$class: 'CleanBeforeCheckout'],
|
|
|
[$class: 'CleanCheckout']
|
|
|
],
|
|
|
userRemoteConfigs: [[
|
|
|
url: 'http://localhost:3000/gitea/slms.git',
|
|
|
credentialsId: 'gitea-credentials'
|
|
|
]]
|
|
|
])
|
|
|
echo '✓ 代码拉取成功'
|
|
|
}
|
|
|
}
|
|
|
|
|
|
stage('2. Maven编译') {
|
|
|
steps {
|
|
|
echo '========== Maven 编译 SLMS 项目 =========='
|
|
|
bat '''
|
|
|
set JAVA_HOME=%JAVA_HOME%
|
|
|
|
|
|
REM 强制删除 target 目录(如果存在)
|
|
|
if exist target (
|
|
|
echo 清理旧的 target 目录...
|
|
|
rmdir /S /Q target 2>nul
|
|
|
if exist target (
|
|
|
echo 警告: 无法删除 target 目录,尝试使用 Maven clean...
|
|
|
) else (
|
|
|
echo ✓ target 目录已清理
|
|
|
)
|
|
|
)
|
|
|
|
|
|
REM Maven 编译
|
|
|
mvn clean compile test-compile -DskipTests
|
|
|
'''
|
|
|
echo '✓ 项目编译成功'
|
|
|
}
|
|
|
}
|
|
|
|
|
|
stage('3. 运行Mock测试') {
|
|
|
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 {
|
|
|
junit allowEmptyResults: true, testResults: '**/target/surefire-reports/*.xml'
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
stage('4. Sonar质检') {
|
|
|
when {
|
|
|
expression { currentBuild.result == null || currentBuild.result == 'SUCCESS' || currentBuild.result == 'UNSTABLE' }
|
|
|
}
|
|
|
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 '========== 等待 SLMS-Quality-Gate 质量门禁结果 =========='
|
|
|
timeout(time: 10, unit: 'MINUTES') {
|
|
|
script {
|
|
|
withSonarQubeEnv('SonarQube') {
|
|
|
def qg = waitForQualityGate()
|
|
|
if (qg.status != 'OK') {
|
|
|
error "SLMS-Quality-Gate 质量门禁未通过: ${qg.status}"
|
|
|
} else {
|
|
|
echo "✓ SLMS-Quality-Gate 质量门禁检查通过: ${qg.status}"
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
stage('6. 准备打包') {
|
|
|
when {
|
|
|
expression { currentBuild.result == null || currentBuild.result == 'SUCCESS' || currentBuild.result == 'UNSTABLE' }
|
|
|
}
|
|
|
steps {
|
|
|
echo '========== 复制 target 目录用于并行打包 =========='
|
|
|
bat '''
|
|
|
REM 为 CLI、GUI、Web、Android 创建独立的 target 副本
|
|
|
echo 复制 target 到 target-cli...
|
|
|
xcopy /E /I /Y /Q target target-cli >nul
|
|
|
|
|
|
echo 复制 target 到 target-gui...
|
|
|
xcopy /E /I /Y /Q target target-gui >nul
|
|
|
|
|
|
echo 复制 target 到 target-web...
|
|
|
xcopy /E /I /Y /Q target target-web >nul
|
|
|
|
|
|
echo 复制 target 到 target-android...
|
|
|
xcopy /E /I /Y /Q target target-android >nul
|
|
|
|
|
|
echo ✓ target 目录复制完成(4份),准备并行打包
|
|
|
'''
|
|
|
}
|
|
|
}
|
|
|
|
|
|
stage('7. 四端并行打包') {
|
|
|
when {
|
|
|
expression { currentBuild.result == null || currentBuild.result == 'SUCCESS' || currentBuild.result == 'UNSTABLE' }
|
|
|
}
|
|
|
parallel {
|
|
|
stage('7.1 CLI 打包 (JAR)') {
|
|
|
steps {
|
|
|
echo '========== 打包 CLI 应用 (JAR) =========='
|
|
|
bat '''
|
|
|
set JAVA_HOME=%JAVA_HOME%
|
|
|
echo 使用 target-cli 打包 CLI 应用...
|
|
|
|
|
|
REM 临时设置 Maven 输出目录为 target-cli
|
|
|
mvn package -Pcli -DskipTests -Dmaven.compiler.skip=true -Dmaven.build.directory=target-cli
|
|
|
|
|
|
REM 复制生成的 shaded JAR 到 target
|
|
|
if exist target-cli\\smart-library-management-system-1.0-SNAPSHOT-cli-shaded.jar (
|
|
|
copy /Y target-cli\\smart-library-management-system-1.0-SNAPSHOT-cli-shaded.jar target\\slms-cli.jar
|
|
|
echo ✓ CLI JAR 打包完成: slms-cli.jar
|
|
|
) else (
|
|
|
echo ⚠️ 警告: 找不到 CLI shaded JAR
|
|
|
dir target-cli
|
|
|
)
|
|
|
|
|
|
REM 复制 library.db 到 target 目录
|
|
|
if exist library.db (
|
|
|
copy /Y library.db target\\
|
|
|
echo ✓ 已复制 library.db 到 target 目录
|
|
|
)
|
|
|
'''
|
|
|
echo '✓ CLI JAR 打包成功'
|
|
|
}
|
|
|
}
|
|
|
|
|
|
stage('7.2 GUI 打包 (Swing JAR)') {
|
|
|
steps {
|
|
|
echo '========== 打包 GUI Swing 应用 (独立 JAR) =========='
|
|
|
bat '''
|
|
|
set JAVA_HOME=%JAVA_HOME%
|
|
|
echo 使用 gui-swing profile 打包 GUI 应用...
|
|
|
|
|
|
REM 使用 gui-swing profile 生成独立 JAR
|
|
|
mvn package -Pgui-swing -DskipTests -Dmaven.compiler.skip=true
|
|
|
|
|
|
REM 检查并复制生成的 Swing JAR
|
|
|
if exist target\\smart-library-management-system-1.0-SNAPSHOT-gui-swing.jar (
|
|
|
copy /Y target\\smart-library-management-system-1.0-SNAPSHOT-gui-swing.jar target\\slms-gui.jar
|
|
|
echo ✓ GUI Swing JAR 打包完成: slms-gui.jar (%.2f MB)
|
|
|
|
|
|
REM 复制 library.db
|
|
|
if exist library.db (
|
|
|
copy /Y library.db target\\library.db
|
|
|
echo ✓ 已复制 library.db 到 target
|
|
|
)
|
|
|
|
|
|
REM 创建启动脚本
|
|
|
echo @echo off > target\\run-gui.bat
|
|
|
echo chcp 65001 ^>nul >> target\\run-gui.bat
|
|
|
echo echo ============================================ >> target\\run-gui.bat
|
|
|
echo echo SLMS GUI Application (Swing Version) >> target\\run-gui.bat
|
|
|
echo echo ============================================ >> target\\run-gui.bat
|
|
|
echo echo. >> target\\run-gui.bat
|
|
|
echo echo Starting GUI application... >> target\\run-gui.bat
|
|
|
echo java -jar slms-gui.jar >> target\\run-gui.bat
|
|
|
echo if errorlevel 1 ( >> target\\run-gui.bat
|
|
|
echo echo. >> target\\run-gui.bat
|
|
|
echo echo Error: Failed to start GUI application >> target\\run-gui.bat
|
|
|
echo echo Please ensure Java 11+ is installed >> target\\run-gui.bat
|
|
|
echo pause >> target\\run-gui.bat
|
|
|
echo ^) >> target\\run-gui.bat
|
|
|
echo ✓ 已创建 GUI 启动脚本: run-gui.bat
|
|
|
|
|
|
REM 创建 README 文件
|
|
|
echo SLMS GUI Application (Swing Version) > target\\README-GUI.txt
|
|
|
echo. >> target\\README-GUI.txt
|
|
|
echo ========================================== >> target\\README-GUI.txt
|
|
|
echo. >> target\\README-GUI.txt
|
|
|
echo 运行方式: >> target\\README-GUI.txt
|
|
|
echo 1. 双击 run-gui.bat (推荐) >> target\\README-GUI.txt
|
|
|
echo 2. 命令行: java -jar slms-gui.jar >> target\\README-GUI.txt
|
|
|
echo. >> target\\README-GUI.txt
|
|
|
echo 特点: >> target\\README-GUI.txt
|
|
|
echo - 单一 JAR 文件,无需额外依赖 >> target\\README-GUI.txt
|
|
|
echo - 使用 Swing 界面,兼容性好 >> target\\README-GUI.txt
|
|
|
echo - 包含完整功能:图书管理、借阅管理 >> target\\README-GUI.txt
|
|
|
echo - 文件大小约 36 MB >> target\\README-GUI.txt
|
|
|
echo. >> target\\README-GUI.txt
|
|
|
echo 系统要求: >> target\\README-GUI.txt
|
|
|
echo - Java 11 或更高版本 >> target\\README-GUI.txt
|
|
|
echo - Windows / Linux / macOS >> target\\README-GUI.txt
|
|
|
echo. >> target\\README-GUI.txt
|
|
|
echo 注意: >> target\\README-GUI.txt
|
|
|
echo - library.db 必须与应用文件在同一目录 >> target\\README-GUI.txt
|
|
|
echo - 首次运行会自动初始化数据库 >> target\\README-GUI.txt
|
|
|
echo. >> target\\README-GUI.txt
|
|
|
echo ========================================== >> target\\README-GUI.txt
|
|
|
echo ✓ 已创建 README-GUI.txt
|
|
|
|
|
|
echo.
|
|
|
echo ✓ GUI Swing 打包完成
|
|
|
echo - JAR 文件: slms-gui.jar
|
|
|
echo - 启动脚本: run-gui.bat
|
|
|
echo - 说明文档: README-GUI.txt
|
|
|
) else (
|
|
|
echo ⚠️ 错误: GUI Swing JAR 未找到
|
|
|
echo 检查 target 目录内容:
|
|
|
dir target\\*.jar
|
|
|
exit /b 1
|
|
|
)
|
|
|
'''
|
|
|
echo '✓ GUI Swing JAR 打包成功'
|
|
|
}
|
|
|
}
|
|
|
|
|
|
stage('7.3 Web 打包 (WAR)') {
|
|
|
steps {
|
|
|
echo '========== 打包 Web 应用 (WAR) =========='
|
|
|
bat '''
|
|
|
set JAVA_HOME=%JAVA_HOME%
|
|
|
echo 使用 target-web 打包 Web 应用...
|
|
|
|
|
|
REM 临时设置 Maven 输出目录为 target-web
|
|
|
mvn package -Pweb -DskipTests -Dmaven.compiler.skip=true -Dmaven.build.directory=target-web
|
|
|
|
|
|
REM 复制生成的 WAR 到 target
|
|
|
if exist target-web\\smart-library-management-system-1.0-SNAPSHOT.war (
|
|
|
copy /Y target-web\\smart-library-management-system-1.0-SNAPSHOT.war target\\slms-web.war
|
|
|
echo ✓ Web WAR 打包完成: slms-web.war
|
|
|
) else if exist target-web\\smart-library-management-system-1.0-SNAPSHOT.jar (
|
|
|
copy /Y target-web\\smart-library-management-system-1.0-SNAPSHOT.jar target\\slms-web.jar
|
|
|
echo ✓ Web JAR 打包完成: slms-web.jar (备用)
|
|
|
) else (
|
|
|
echo ⚠️ 警告: 找不到 Web WAR/JAR
|
|
|
dir target-web
|
|
|
)
|
|
|
'''
|
|
|
echo '✓ Web WAR 打包成功'
|
|
|
}
|
|
|
}
|
|
|
|
|
|
stage('7.4 Android Gradle 打包 (APK)') {
|
|
|
steps {
|
|
|
echo '========== 打包 Android 应用 (APK) =========='
|
|
|
bat '''
|
|
|
@echo off
|
|
|
setlocal EnableDelayedExpansion
|
|
|
set JAVA_HOME=%JAVA_HOME%
|
|
|
set ANDROID_HOME=%ANDROID_HOME%
|
|
|
|
|
|
echo 打包 Android 应用...
|
|
|
|
|
|
REM 使用 Gradle 打包 Android APK(不操作 target 目录)
|
|
|
call gradlew.bat :android:assembleDebug
|
|
|
|
|
|
REM 先列出 APK 目录看文件名
|
|
|
echo 列出 APK 目录:
|
|
|
dir android\\build\\outputs\\apk\\debug\\*.apk
|
|
|
|
|
|
REM 检查并复制 APK - 如果文件名相同就跳过
|
|
|
if exist android\\build\\outputs\\apk\\debug\\SLMS-debug.apk (
|
|
|
echo ✓ 找到 SLMS-debug.apk
|
|
|
if /I "SLMS-debug.apk" NEQ "slms-debug.apk" (
|
|
|
copy /Y android\\build\\outputs\\apk\\debug\\SLMS-debug.apk android\\build\\outputs\\apk\\debug\\slms-debug.apk >nul 2>&1
|
|
|
)
|
|
|
echo ✓ Android APK 打包完成: slms-debug.apk
|
|
|
exit /b 0
|
|
|
)
|
|
|
|
|
|
if exist android\\build\\outputs\\apk\\debug\\android-debug.apk (
|
|
|
echo ✓ 找到 android-debug.apk
|
|
|
copy /Y android\\build\\outputs\\apk\\debug\\android-debug.apk android\\build\\outputs\\apk\\debug\\slms-debug.apk >nul 2>&1
|
|
|
echo ✓ Android APK 打包完成: slms-debug.apk
|
|
|
exit /b 0
|
|
|
)
|
|
|
|
|
|
REM 尝试复制第一个找到的 APK
|
|
|
echo ⚠️ 警告: 找不到预期的 APK 文件,尝试复制第一个找到的 APK
|
|
|
set APK_FOUND=0
|
|
|
for %%f in (android\\build\\outputs\\apk\\debug\\*.apk) do (
|
|
|
if !APK_FOUND! EQU 0 (
|
|
|
echo 找到 APK: %%f
|
|
|
copy /Y "%%f" android\\build\\outputs\\apk\\debug\\slms-debug.apk >nul 2>&1
|
|
|
echo ✓ 已复制 %%f 为 slms-debug.apk
|
|
|
set APK_FOUND=1
|
|
|
)
|
|
|
)
|
|
|
|
|
|
if !APK_FOUND! EQU 0 (
|
|
|
echo ✗ 错误: 完全找不到 APK 文件
|
|
|
exit /b 1
|
|
|
)
|
|
|
|
|
|
exit /b 0
|
|
|
'''
|
|
|
echo '✓ Android APK 打包成功'
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
stage('8. 归档制品') {
|
|
|
when {
|
|
|
expression { currentBuild.result == null || currentBuild.result == 'SUCCESS' || currentBuild.result == 'UNSTABLE' }
|
|
|
}
|
|
|
steps {
|
|
|
echo '========== 归档构建制品 =========='
|
|
|
script {
|
|
|
// 显示当前工作目录
|
|
|
bat '''
|
|
|
@echo off
|
|
|
echo 当前工作目录:
|
|
|
cd
|
|
|
echo.
|
|
|
echo 列出 target 目录:
|
|
|
dir target\\*.jar 2>nul || echo 没有找到 JAR 文件
|
|
|
echo.
|
|
|
echo 列出 android/build/outputs/apk/debug 目录:
|
|
|
dir android\\build\\outputs\\apk\\debug\\*.apk 2>nul || echo 没有找到 APK 文件
|
|
|
echo.
|
|
|
'''
|
|
|
|
|
|
// 检查文件是否存在
|
|
|
bat '''
|
|
|
@echo off
|
|
|
echo 检查制品文件...
|
|
|
echo.
|
|
|
echo [CLI 应用]
|
|
|
if exist target\\slms-cli.jar (
|
|
|
echo ✓ 找到 slms-cli.jar
|
|
|
for %%F in (target\\slms-cli.jar) do echo 大小: %%~zF bytes
|
|
|
) else (
|
|
|
echo ✗ 未找到 slms-cli.jar
|
|
|
)
|
|
|
echo.
|
|
|
echo [GUI 应用 - Swing 版本]
|
|
|
if exist target\\slms-gui.jar (
|
|
|
echo ✓ 找到 slms-gui.jar
|
|
|
for %%F in (target\\slms-gui.jar) do echo 大小: %%~zF bytes
|
|
|
) else (
|
|
|
echo ✗ 未找到 slms-gui.jar
|
|
|
)
|
|
|
if exist target\\run-gui.bat (
|
|
|
echo ✓ 找到 run-gui.bat
|
|
|
) else (
|
|
|
echo ⚠️ 未找到 run-gui.bat
|
|
|
)
|
|
|
if exist target\\README-GUI.txt (
|
|
|
echo ✓ 找到 README-GUI.txt
|
|
|
) else (
|
|
|
echo ⚠️ 未找到 README-GUI.txt
|
|
|
)
|
|
|
echo.
|
|
|
echo [Web 应用]
|
|
|
if exist target\\slms-web.war (
|
|
|
echo ✓ 找到 slms-web.war
|
|
|
for %%F in (target\\slms-web.war) do echo 大小: %%~zF bytes
|
|
|
) else if exist target\\slms-web.jar (
|
|
|
echo ✓ 找到 slms-web.jar
|
|
|
for %%F in (target\\slms-web.jar) do echo 大小: %%~zF bytes
|
|
|
) else (
|
|
|
echo ✗ 未找到 slms-web.war/jar
|
|
|
)
|
|
|
echo.
|
|
|
echo [Android 应用]
|
|
|
if exist android\\build\\outputs\\apk\\debug\\slms-debug.apk (
|
|
|
echo ✓ 找到 slms-debug.apk
|
|
|
for %%F in (android\\build\\outputs\\apk\\debug\\slms-debug.apk) do echo 大小: %%~zF bytes
|
|
|
) else if exist android\\build\\outputs\\apk\\debug\\SLMS-debug.apk (
|
|
|
echo ✓ 找到 SLMS-debug.apk
|
|
|
for %%F in (android\\build\\outputs\\apk\\debug\\SLMS-debug.apk) do echo 大小: %%~zF bytes
|
|
|
) else (
|
|
|
echo ✗ 未找到 APK
|
|
|
)
|
|
|
echo.
|
|
|
echo [数据库文件]
|
|
|
if exist target\\library.db (
|
|
|
echo ✓ 找到 library.db
|
|
|
) else (
|
|
|
echo ⚠️ 未找到 library.db
|
|
|
)
|
|
|
exit /b 0
|
|
|
'''
|
|
|
|
|
|
// 归档制品 - 使用 try-catch 确保即使部分文件缺失也能继续
|
|
|
try {
|
|
|
echo '开始归档制品...'
|
|
|
// 注意: 已移除 EXE 和 MSI,现在只归档 JAR/WAR 文件
|
|
|
archiveArtifacts artifacts: 'target/slms-*.jar,target/slms-*.war,target/*.bat,target/README-*.txt,target/library.db,android/build/outputs/apk/debug/*.apk',
|
|
|
fingerprint: true,
|
|
|
allowEmptyArchive: true,
|
|
|
onlyIfSuccessful: false
|
|
|
echo '✓ 制品归档完成'
|
|
|
} catch (Exception e) {
|
|
|
echo "⚠️ 制品归档时出现问题: ${e.message}"
|
|
|
echo "继续执行流水线..."
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
stage('9. 推送头歌') {
|
|
|
when {
|
|
|
expression { currentBuild.result == null || currentBuild.result == 'SUCCESS' || currentBuild.result == 'UNSTABLE' }
|
|
|
}
|
|
|
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 "ldl@chzu.edu.cn"
|
|
|
git remote add educoder https://bdgit.educoder.net/pu6zrsfoy/CHZU_CS231_SEB_lab.git || git remote set-url educoder https://bdgit.educoder.net/pu6zrsfoy/CHZU_CS231_SEB_lab.git
|
|
|
git push https://%USER_ENC%:%PASS_ENC%@bdgit.educoder.net/pu6zrsfoy/CHZU_CS231_SEB_lab.git HEAD:refs/heads/main --force
|
|
|
'''
|
|
|
}
|
|
|
echo '✓ 源代码推送到 main 成功'
|
|
|
} catch (Exception e) {
|
|
|
echo '⚠ 推送代码到 main 失败'
|
|
|
echo "错误信息: ${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
|
|
|
|
|
|
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
|
|
|
|
|
|
REM 显示当前工作目录
|
|
|
echo 当前工作目录:
|
|
|
cd
|
|
|
|
|
|
REM 创建 artifacts 目录
|
|
|
if not exist artifacts mkdir artifacts
|
|
|
|
|
|
REM 复制制品
|
|
|
echo 复制制品到 artifacts 目录...
|
|
|
if exist target\\slms-cli.jar (
|
|
|
copy /Y target\\slms-cli.jar artifacts\\
|
|
|
echo ✓ 已复制 CLI JAR
|
|
|
) else (
|
|
|
echo ⚠️ 警告: 找不到 CLI JAR
|
|
|
)
|
|
|
if exist target\\slms-gui.jar (
|
|
|
copy /Y target\\slms-gui.jar artifacts\\
|
|
|
echo ✓ 已复制 GUI JAR
|
|
|
) else (
|
|
|
echo ⚠️ 警告: 找不到 GUI JAR
|
|
|
)
|
|
|
if exist target\\slms-web.war (
|
|
|
copy /Y target\\slms-web.war artifacts\\
|
|
|
echo ✓ 已复制 Web WAR
|
|
|
) else if exist target\\slms-web.jar (
|
|
|
copy /Y target\\slms-web.jar artifacts\\
|
|
|
echo ✓ 已复制 Web JAR (备用)
|
|
|
) else (
|
|
|
echo ⚠️ 警告: 找不到 Web WAR/JAR
|
|
|
)
|
|
|
if exist android\\build\\outputs\\apk\\debug\\slms-debug.apk (
|
|
|
copy /Y android\\build\\outputs\\apk\\debug\\slms-debug.apk artifacts\\
|
|
|
echo ✓ 已复制 Android APK
|
|
|
) else if exist android\\build\\outputs\\apk\\debug\\SLMS-debug.apk (
|
|
|
copy /Y android\\build\\outputs\\apk\\debug\\SLMS-debug.apk artifacts\\slms-debug.apk
|
|
|
echo ✓ 已复制 Android APK (SLMS-debug.apk)
|
|
|
) else (
|
|
|
echo ⚠️ 警告: 找不到 APK 文件
|
|
|
)
|
|
|
if exist target\\library.db (
|
|
|
copy /Y target\\library.db artifacts\\
|
|
|
echo ✓ 已复制 library.db
|
|
|
) else if exist library.db (
|
|
|
copy /Y library.db artifacts\\
|
|
|
echo ✓ 已复制 library.db (从根目录)
|
|
|
) else (
|
|
|
echo ⚠️ 警告: 找不到 library.db
|
|
|
)
|
|
|
if exist target\\slms-gui.exe (
|
|
|
copy /Y target\\slms-gui.exe artifacts\\
|
|
|
echo ✓ 已复制 slms-gui.exe
|
|
|
) else (
|
|
|
echo ⚠️ 警告: 找不到 slms-gui.exe
|
|
|
)
|
|
|
if exist target\\slms-gui-installer.msi (
|
|
|
copy /Y target\\slms-gui-installer.msi artifacts\\
|
|
|
echo ✓ 已复制 slms-gui-installer.msi
|
|
|
) else (
|
|
|
echo ⚠️ 警告: 找不到 slms-gui-installer.msi
|
|
|
)
|
|
|
if exist target\\run-gui.bat (
|
|
|
copy /Y target\\run-gui.bat artifacts\\
|
|
|
echo ✓ 已复制 run-gui.bat
|
|
|
)
|
|
|
if exist target\\README-GUI.txt (
|
|
|
copy /Y target\\README-GUI.txt artifacts\\
|
|
|
echo ✓ 已复制 README-GUI.txt
|
|
|
)
|
|
|
REM 不复制 libs 目录,减少仓库大小
|
|
|
REM if exist target\\libs (
|
|
|
REM xcopy /E /I /Y /Q target\\libs artifacts\\libs >nul 2>&1
|
|
|
REM echo ✓ 已复制 libs 目录
|
|
|
REM )
|
|
|
|
|
|
REM 显示复制的文件
|
|
|
echo.
|
|
|
echo 制品列表:
|
|
|
dir artifacts
|
|
|
|
|
|
REM 创建临时目录用于 release 分支
|
|
|
set RELEASE_DIR=%TEMP%\\slms-release-%BUILD_NUMBER%
|
|
|
if exist "%RELEASE_DIR%" rmdir /S /Q "%RELEASE_DIR%"
|
|
|
mkdir "%RELEASE_DIR%"
|
|
|
|
|
|
REM 复制制品到临时目录
|
|
|
echo 准备 release 分支内容...
|
|
|
xcopy /E /I /Y /Q artifacts "%RELEASE_DIR%\\artifacts" >nul
|
|
|
|
|
|
REM 创建 README 文件
|
|
|
echo SLMS 项目构建制品 > "%RELEASE_DIR%\\README.md"
|
|
|
echo. >> "%RELEASE_DIR%\\README.md"
|
|
|
echo 构建编号: #%BUILD_NUMBER% >> "%RELEASE_DIR%\\README.md"
|
|
|
echo 构建时间: %DATE% %TIME% >> "%RELEASE_DIR%\\README.md"
|
|
|
echo. >> "%RELEASE_DIR%\\README.md"
|
|
|
echo ## 制品列表 >> "%RELEASE_DIR%\\README.md"
|
|
|
echo. >> "%RELEASE_DIR%\\README.md"
|
|
|
echo - slms-cli.jar - CLI 命令行应用 >> "%RELEASE_DIR%\\README.md"
|
|
|
echo - slms-gui.jar - GUI 桌面应用 (JAR) >> "%RELEASE_DIR%\\README.md"
|
|
|
echo - slms-gui.exe - GUI 桌面应用 (EXE) >> "%RELEASE_DIR%\\README.md"
|
|
|
echo - slms-gui-installer.msi - GUI 安装包 (MSI) >> "%RELEASE_DIR%\\README.md"
|
|
|
echo - slms-web.war - Web 应用 >> "%RELEASE_DIR%\\README.md"
|
|
|
echo - slms-debug.apk - Android 应用 >> "%RELEASE_DIR%\\README.md"
|
|
|
echo - library.db - 数据库文件 >> "%RELEASE_DIR%\\README.md"
|
|
|
echo. >> "%RELEASE_DIR%\\README.md"
|
|
|
echo ## 使用说明 >> "%RELEASE_DIR%\\README.md"
|
|
|
echo. >> "%RELEASE_DIR%\\README.md"
|
|
|
echo 详细使用说明请参考各应用的 README 文件。 >> "%RELEASE_DIR%\\README.md"
|
|
|
|
|
|
REM 进入临时目录
|
|
|
cd "%RELEASE_DIR%"
|
|
|
|
|
|
REM 初始化 Git 仓库
|
|
|
git init
|
|
|
git config user.name "Jenkins CI"
|
|
|
git config user.email "ldl@chzu.edu.cn"
|
|
|
|
|
|
REM 添加所有文件
|
|
|
git add .
|
|
|
git commit -m "release: 构建制品 Build #%BUILD_NUMBER%"
|
|
|
|
|
|
REM 推送到 release 分支(强制覆盖,因为只包含制品)
|
|
|
echo 推送制品到头歌 release 分支(仅制品,无源码)...
|
|
|
git push https://%USER_ENC%:%PASS_ENC%@bdgit.educoder.net/pu6zrsfoy/CHZU_CS231_SEB_lab.git HEAD:refs/heads/release --force
|
|
|
|
|
|
if errorlevel 1 (
|
|
|
echo ✗ 推送失败!
|
|
|
exit /b 1
|
|
|
)
|
|
|
|
|
|
echo ✓ 推送成功!
|
|
|
|
|
|
REM 清理临时目录
|
|
|
cd %WORKSPACE%
|
|
|
rmdir /S /Q "%RELEASE_DIR%" 2>nul
|
|
|
echo ✓ 已清理临时目录
|
|
|
'''
|
|
|
}
|
|
|
echo '✓ 制品推送到 release 分支成功'
|
|
|
} catch (Exception e) {
|
|
|
echo '⚠ 推送制品到 release 失败'
|
|
|
echo "错误信息: ${e.message}"
|
|
|
currentBuild.result = 'UNSTABLE'
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
post {
|
|
|
always {
|
|
|
script {
|
|
|
echo '========== 流水线执行完成 =========='
|
|
|
echo "当前构建结果: ${currentBuild.result}"
|
|
|
echo "当前构建状态: ${currentBuild.currentResult}"
|
|
|
|
|
|
// 清理残留的 Java 进程
|
|
|
echo '========== 清理残留进程 =========='
|
|
|
try {
|
|
|
bat '''
|
|
|
@echo off
|
|
|
echo 清理与 slms 相关的残留进程...
|
|
|
for /f "tokens=2" %%i in ('tasklist /FI "IMAGENAME eq java.exe" /FO LIST ^| findstr /C:"PID:"') do (
|
|
|
wmic process where "ProcessId=%%i and CommandLine like '%%slms%%'" delete 2>nul
|
|
|
)
|
|
|
echo ✓ 进程清理完成
|
|
|
'''
|
|
|
} catch (Exception e) {
|
|
|
echo "⚠️ 清理进程时出错: ${e.message}"
|
|
|
}
|
|
|
|
|
|
// 检查制品是否存在
|
|
|
echo '========== 检查制品文件 =========='
|
|
|
try {
|
|
|
bat '''
|
|
|
@echo off
|
|
|
if exist target\\slms-cli.jar (echo ✓ slms-cli.jar 存在) else (echo ✗ slms-cli.jar 不存在)
|
|
|
if exist target\\slms-gui.jar (echo ✓ slms-gui.jar 存在) else (echo ✗ slms-gui.jar 不存在)
|
|
|
if exist target\\slms-web.war (echo ✓ slms-web.war 存在) else if exist target\\slms-web.jar (echo ✓ slms-web.jar 存在) else (echo ✗ slms-web.war/jar 不存在)
|
|
|
if exist android\\build\\outputs\\apk\\debug\\slms-debug.apk (echo ✓ slms-debug.apk 存在) else (echo ✗ slms-debug.apk 不存在)
|
|
|
exit /b 0
|
|
|
'''
|
|
|
} catch (Exception e) {
|
|
|
echo "⚠️ 检查制品文件时出错: ${e.message}"
|
|
|
}
|
|
|
|
|
|
// 发送邮件 - 使用最简单的方式
|
|
|
echo '========== 发送邮件通知 =========='
|
|
|
def finalResult = currentBuild.result ?: 'SUCCESS'
|
|
|
def emailSubject = ""
|
|
|
def emailStatus = ""
|
|
|
|
|
|
if (finalResult == 'SUCCESS') {
|
|
|
emailSubject = "✅ SLMS 构建成功 - Build #${BUILD_NUMBER}"
|
|
|
emailStatus = "成功"
|
|
|
} else if (finalResult == 'FAILURE') {
|
|
|
emailSubject = "❌ SLMS 构建失败 - Build #${BUILD_NUMBER}"
|
|
|
emailStatus = "失败"
|
|
|
} else if (finalResult == 'UNSTABLE') {
|
|
|
emailSubject = "⚠️ SLMS 构建不稳定 - Build #${BUILD_NUMBER}"
|
|
|
emailStatus = "不稳定"
|
|
|
} else {
|
|
|
emailSubject = "ℹ️ SLMS 构建完成 - Build #${BUILD_NUMBER}"
|
|
|
emailStatus = "完成"
|
|
|
}
|
|
|
|
|
|
echo "准备发送邮件: ${emailSubject}"
|
|
|
echo "收件人: 602924803@qq.com"
|
|
|
|
|
|
try {
|
|
|
emailext (
|
|
|
subject: emailSubject,
|
|
|
to: '602924803@qq.com',
|
|
|
from: 'ldl@chzu.edu.cn',
|
|
|
replyTo: 'ldl@chzu.edu.cn',
|
|
|
body: """
|
|
|
<h2>SLMS 项目构建${emailStatus}</h2>
|
|
|
<p><strong>构建编号:</strong> #${BUILD_NUMBER}</p>
|
|
|
<p><strong>构建状态:</strong> ${emailStatus}</p>
|
|
|
<p><strong>构建时间:</strong> ${new Date(currentBuild.startTimeInMillis)}</p>
|
|
|
<p><strong>Git 提交:</strong> ${env.GIT_COMMIT}</p>
|
|
|
<hr>
|
|
|
<h3><3E> 相关链接</h3>
|
|
|
<ul>
|
|
|
<li><a href="${BUILD_URL}">📊 查看构建详情</a></li>
|
|
|
<li><a href="${BUILD_URL}console">📋 查看构建日志</a></li>
|
|
|
<li><a href="${BUILD_URL}artifact/">📦 Jenkins 制品列表</a></li>
|
|
|
<li><a href="http://localhost:9000/dashboard?id=slms:slms">📈 SonarQube 报告</a></li>
|
|
|
<li><a href="https://bdgit.educoder.net/pu6zrsfoy/CHZU_CS231_SEB_lab/tree/release/SLMS/artifacts">🎁 头歌 Release 分支</a></li>
|
|
|
</ul>
|
|
|
<hr>
|
|
|
<p style="color: #666; font-size: 12px;">
|
|
|
<em>此邮件由 Jenkins CI/CD 自动发送 | Build #${BUILD_NUMBER}</em>
|
|
|
</p>
|
|
|
""",
|
|
|
mimeType: 'text/html'
|
|
|
)
|
|
|
echo "✓ 邮件已发送到: 602924803@qq.com"
|
|
|
echo "✓ 邮件主题: ${emailSubject}"
|
|
|
} catch (Exception e) {
|
|
|
echo "❌ 邮件发送失败!"
|
|
|
echo "错误信息: ${e.message}"
|
|
|
echo "错误类型: ${e.class.name}"
|
|
|
echo "错误详情:"
|
|
|
e.printStackTrace()
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
}
|