diff --git a/Jenkinsfile b/Jenkinsfile index 18028e2..c057431 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -19,6 +19,10 @@ pipeline { SONAR_HOST_URL = 'http://116.62.163.84:15010' SONAR_PROJECT_KEY = 'jenkins-demo' SONAR_TOKEN = 'squ_7e4217cabd0faae6f3b8ee359b3b8e2ac52eb69a' + + // 构建结果通知配置 + WEBHOOK_URL = 'http://116.62.163.84:15030/api/build-result' // 替换为你的接收服务器URL + WEBHOOK_SECRET = 'your-webhook-secret' // 可选的webhook密钥 } tools { @@ -36,8 +40,14 @@ pipeline { script: "git rev-parse --short HEAD", returnStdout: true ).trim() + // 获取仓库URL + env.REPO_URL = sh( + script: "git config --get remote.origin.url", + returnStdout: true + ).trim() } echo "📋 Git提交ID: ${env.GIT_COMMIT_SHORT}" + echo "📦 仓库URL: ${env.REPO_URL}" } } @@ -177,6 +187,56 @@ pipeline { } } + stage('镜像测试') { + steps { + echo '🧪 测试Docker镜像...' + script { + try { + // 启动测试容器 + sh """ + echo "启动测试容器..." + docker run -d --name test-${BUILD_NUMBER} -p 8081:8080 ${IMAGE_NAME}:${IMAGE_TAG} + + echo "等待应用启动..." + sleep 30 + + echo "检查容器状态..." + docker ps | grep test-${BUILD_NUMBER} || true + docker logs test-${BUILD_NUMBER} || true + + echo "测试健康检查端点..." + for i in \$(seq 1 5); do + echo "尝试连接 \$i/5..." + if curl -f http://localhost:8081/actuator/health; then + echo "✅ 健康检查通过" + break + else + echo "⚠️ 连接失败,等待5秒后重试..." + sleep 5 + fi + + if [ \$i -eq 5 ]; then + echo "❌ 镜像测试失败,但不中断构建" + docker logs test-${BUILD_NUMBER} + exit 1 + fi + done + """ + echo "✅ 镜像测试完成" + } catch (Exception e) { + echo "⚠️ 镜像测试失败: ${e.getMessage()}" + echo "📋 这可能是网络或启动时间问题,继续构建流程" + } finally { + // 清理测试容器 + sh """ + docker stop test-${BUILD_NUMBER} || true + docker rm test-${BUILD_NUMBER} || true + """ + } + } + } + } + stage('部署应用') { steps { echo '🚀 部署应用到服务器...' @@ -290,6 +350,10 @@ EOF always { script { echo '🧹 清理工作空间...' + + // 发送构建结果通知 + sendBuildResult() + try { // 清理Docker资源 sh ''' @@ -298,9 +362,13 @@ EOF # 清理构建缓存 docker builder prune -f || true + + # 清理测试容器 + docker stop test-${BUILD_NUMBER} || true + docker rm test-${BUILD_NUMBER} || true ''' } catch (Exception e) { - echo "⚠️ Docker清理失败: ${e.getMessage()}" + echo "⚠️ 清理失败: ${e.getMessage()}" } } } @@ -310,8 +378,10 @@ EOF echo '✅ 流水线执行成功!' def deployPort = '15020' + def environment = 'production' if (env.BRANCH_NAME == 'develop' || env.BRANCH_NAME?.startsWith('feature/')) { deployPort = '15021' + environment = 'test' } def message = """ @@ -324,9 +394,14 @@ EOF ⏱️ 持续时间: ${currentBuild.durationString} 🔗 构建链接: ${env.BUILD_URL} 🌐 应用地址: http://${DEPLOY_SERVER}:${deployPort} +📦 仓库链接: ${env.REPO_URL ?: 'unknown'} +🏷️ 环境: ${environment} """ echo message + + // 发送成功通知 + sendBuildResult('SUCCESS') } } @@ -343,9 +418,13 @@ EOF ⏱️ 持续时间: ${currentBuild.durationString} 🔗 构建链接: ${env.BUILD_URL} 📄 查看日志: ${env.BUILD_URL}console +📦 仓库链接: ${env.REPO_URL ?: 'unknown'} """ echo message + + // 发送失败通知 + sendBuildResult('FAILURE') } } @@ -361,4 +440,142 @@ EOF } } } +} + +// 发送构建结果的函数 +def sendBuildResult(String overrideResult = null) { + script { + try { + def buildResult = overrideResult ?: currentBuild.result ?: 'SUCCESS' + def deployPort = '15020' + def environment = 'production' + def applicationUrl = "http://${DEPLOY_SERVER}:${deployPort}" + + if (env.BRANCH_NAME == 'develop' || env.BRANCH_NAME?.startsWith('feature/')) { + deployPort = '15021' + environment = 'test' + applicationUrl = "http://${DEPLOY_SERVER}:${deployPort}" + } + + def payload = [ + // 基本信息 + jobName: env.JOB_NAME, + buildNumber: env.BUILD_NUMBER, + result: buildResult, + status: buildResult.toLowerCase(), + + // Git信息 + branch: env.BRANCH_NAME ?: 'unknown', + gitCommit: env.GIT_COMMIT_SHORT ?: 'unknown', + gitCommitFull: env.GIT_COMMIT ?: 'unknown', + repoUrl: env.REPO_URL ?: 'unknown', // 新增仓库链接字段 + + // 时间信息 + startTime: new Date(currentBuild.startTimeInMillis).format("yyyy-MM-dd HH:mm:ss"), + duration: currentBuild.durationString ?: 'unknown', + durationMs: currentBuild.duration ?: 0, + timestamp: new Date().format("yyyy-MM-dd HH:mm:ss"), + + // URL信息 + buildUrl: env.BUILD_URL, + consoleUrl: "${env.BUILD_URL}console", + applicationUrl: applicationUrl, + + // 部署信息 + environment: environment, + deployServer: DEPLOY_SERVER, + deployPort: deployPort, + imageName: IMAGE_NAME, + imageTag: IMAGE_TAG, + + // 扩展信息 + jenkinsUrl: env.JENKINS_URL, + workspace: env.WORKSPACE, + buildUser: env.BUILD_USER ?: 'system', + nodeLabel: env.NODE_LABEL ?: 'unknown', + + // Java项目特有信息 + projectType: 'java', + mavenVersion: sh(script: 'mvn -version | head -1', returnStdout: true).trim(), + javaVersion: sh(script: 'java -version 2>&1 | head -1', returnStdout: true).trim() + ] + + echo "📤 发送构建结果到: ${WEBHOOK_URL}" + echo "📋 构建结果: ${buildResult}" + + // 方式1: 使用HTTP Request Plugin(推荐) + try { + def headers = [ + [name: 'Content-Type', value: 'application/json'], + [name: 'User-Agent', value: 'Jenkins-Pipeline'], + [name: 'X-Jenkins-Build', value: env.BUILD_NUMBER] + ] + + // 如果设置了webhook密钥,添加签名头 + if (WEBHOOK_SECRET && WEBHOOK_SECRET != 'your-webhook-secret') { + headers.add([name: 'X-Webhook-Secret', value: WEBHOOK_SECRET]) + } + + def response = httpRequest( + httpMode: 'POST', + url: WEBHOOK_URL, + contentType: 'APPLICATION_JSON', + requestBody: groovy.json.JsonOutput.toJson(payload), + customHeaders: headers, + validResponseCodes: '200:299', + timeout: 30, + ignoreSslErrors: true + ) + + echo "✅ HTTP Request成功,响应状态: ${response.status}" + if (response.content) { + echo "📝 响应内容: ${response.content}" + } + + } catch (Exception httpException) { + echo "⚠️ HTTP Request Plugin失败: ${httpException.getMessage()}" + echo "🔄 尝试使用curl备用方案..." + + // 方式2: 使用curl作为备用方案 + try { + def jsonPayload = groovy.json.JsonOutput.toJson(payload) + def curlHeaders = "-H 'Content-Type: application/json' -H 'User-Agent: Jenkins-Pipeline' -H 'X-Jenkins-Build: ${env.BUILD_NUMBER}'" + + if (WEBHOOK_SECRET && WEBHOOK_SECRET != 'your-webhook-secret') { + curlHeaders += " -H 'X-Webhook-Secret: ${WEBHOOK_SECRET}'" + } + + def curlResponse = sh( + script: """ + curl -X POST \\ + ${curlHeaders} \\ + --data '${jsonPayload}' \\ + --connect-timeout 30 \\ + --max-time 60 \\ + --silent \\ + --show-error \\ + --write-out "HTTP_STATUS:%{http_code}" \\ + "${WEBHOOK_URL}" + """, + returnStdout: true + ).trim() + + if (curlResponse.contains('HTTP_STATUS:2')) { + echo "✅ curl请求成功" + echo "📝 响应: ${curlResponse}" + } else { + echo "⚠️ curl请求可能失败: ${curlResponse}" + } + + } catch (Exception curlException) { + echo "❌ curl备用方案也失败: ${curlException.getMessage()}" + echo "⚠️ 无法发送构建结果通知,但不影响构建流程" + } + } + + } catch (Exception e) { + echo "❌ 发送构建结果失败: ${e.getMessage()}" + echo "⚠️ 构建结果通知失败,但不影响构建流程" + } + } } \ No newline at end of file