From f2122997d736d42e070154a056b1aec0c0e11f99 Mon Sep 17 00:00:00 2001 From: wangzhibiao Date: Tue, 15 Sep 2026 11:22:38 +0800 Subject: [PATCH] =?UTF-8?q?=E6=8C=81=E7=BB=AD=E9=9B=86=E6=88=90=E6=B5=81?= =?UTF-8?q?=E6=B0=B4=E7=BA=BF=EF=BC=9A=E5=BC=BA=E5=8C=96=E6=9C=80=E7=BB=88?= =?UTF-8?q?=E8=87=AA=E5=8A=A8=E9=83=A8=E7=BD=B2=E6=B5=81=E6=B0=B4=E7=BA=BF?= =?UTF-8?q?=E3=80=81=E6=B5=8B=E8=AF=95=E9=97=A8=E7=A6=81=E4=B8=8E=E7=94=9F?= =?UTF-8?q?=E4=BA=A7=E5=86=92=E7=83=9F=E8=87=AA=E6=A3=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitlab-ci.yml | 42 ++++++++++++++++++++++--- deploy/restart-all.sh | 71 ++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 103 insertions(+), 10 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index b3646ee74..266fbed72 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -16,6 +16,7 @@ build_backend: - railway-deploy rules: - if: '$CI_COMMIT_BRANCH' + - if: '$CI_PIPELINE_SOURCE == "merge_request_event"' script: - echo "=== 开始编译后端 RuoYi-Vue ===" - export JAVA_HOME=/data2/wzb/tools/jdk8 @@ -24,7 +25,9 @@ build_backend: - bash scripts/bootstrap/check-kingbase-jdbc.sh - cd RuoYi-Vue - mvn clean package - - echo "=== 后端编译成功 ===" + - | + python3 -c 'import glob, re; runs = [int(m.group(1)) for f in glob.glob("ruoyi-*/target/surefire-reports/*.txt") for m in re.finditer(r"Tests run:\s*(\d+)", open(f, errors="ignore").read())]; print("=== 后端测试执行统计: Tests run: %d ===" % sum(runs))' + - echo "=== 后端测试与构建成功 ===" artifacts: expire_in: 2 days paths: @@ -37,6 +40,7 @@ build_frontend: - railway-deploy rules: - if: '$CI_COMMIT_BRANCH' + - if: '$CI_PIPELINE_SOURCE == "merge_request_event"' script: - echo "=== 开始编译前端 build_ui ===" - export NODE_OPTIONS="--openssl-legacy-provider --dns-result-order=ipv4first" @@ -46,7 +50,11 @@ build_frontend: ln -sfn /data2/wzb/tools/frontend-cache/node_modules ./node_modules; fi - npm run build:prod - - echo "=== 前端编译成功 ===" + - if [ ! -d "dist" ] || [ ! -f "dist/index.html" ]; then + echo "错误:前端构建产物 build_ui/dist/index.html 不存在,构建失败"; + exit 1; + fi + - echo "=== 前端编译成功,build_ui/dist/index.html 校验通过 ===" artifacts: expire_in: 2 days paths: @@ -59,13 +67,37 @@ deploy_production: - railway-deploy rules: - if: '$CI_COMMIT_BRANCH == "feat/cicd-pipeline"' + - when: never script: - - echo "=== 开始部署应用 ===" - # 部署前端静态资源到发布目录 + - echo "=== 0. 验证部署运行时环境配置 ===" + - if [ -z "${TRAIN_STUDENT_IDENTITY_ENCRYPTION_KEY:-}" ] || [ -z "${TRAIN_STUDENT_IDENTITY_HASH_KEY:-}" ] || [ -z "${SPRING_REDIS_PASSWORD:-}" ]; then + echo "错误:缺少必需的运行时配置"; + exit 1; + fi + - echo "required runtime configuration present." + - echo "=== 1. 执行部署备份 (Rollback 兜底) ===" + - if [ -d "/data2/wzb/deploy/frontend" ] && [ "$(ls -A /data2/wzb/deploy/frontend 2>/dev/null)" ]; then + echo "备份现有前端资源到 /data2/wzb/deploy/frontend.bak ..."; + mkdir -p /data2/wzb/deploy/frontend.bak; + cp -a /data2/wzb/deploy/frontend/. /data2/wzb/deploy/frontend.bak/; + fi + - if [ -f "/data2/wzb/deploy/backend/ruoyi-admin.jar" ]; then + echo "备份现有后端 Jar 到 /data2/wzb/deploy/backend/ruoyi-admin.jar.bak ..."; + cp -p /data2/wzb/deploy/backend/ruoyi-admin.jar /data2/wzb/deploy/backend/ruoyi-admin.jar.bak; + fi + - echo "=== 2. 部署前端静态资源与文档中心 ===" + - mkdir -p /data2/wzb/deploy/frontend - rm -rf /data2/wzb/deploy/frontend/* - cp -r build_ui/dist/* /data2/wzb/deploy/frontend/ - # 部署后端 Jar 包并执行平滑重启 + - mkdir -p /data2/wzb/deploy/frontend/docs + - cp -r docs/* /data2/wzb/deploy/frontend/docs/ + - echo "=== 3. 部署后端 Jar 包 ===" + - mkdir -p /data2/wzb/deploy/backend - cp RuoYi-Vue/ruoyi-admin/target/ruoyi-admin.jar /data2/wzb/deploy/backend/ + - echo "=== 4. 执行平滑重启与生产自检 ===" - chmod +x deploy/restart-all.sh - bash deploy/restart-all.sh + - echo "=== 5. 输出访问地址 ===" + - echo "APPLICATION_URL=http://192.168.139.230:8082/" + - echo "DOCUMENTATION_URL=http://192.168.139.230:8082/docs/visual/index.html" - echo "=== 部署与自检成功完成! ===" diff --git a/deploy/restart-all.sh b/deploy/restart-all.sh index 658a6e381..712fa8271 100644 --- a/deploy/restart-all.sh +++ b/deploy/restart-all.sh @@ -21,22 +21,28 @@ mkdir -p "$BACKEND_DIR" "$FRONTEND_DIR" "$LOG_DIR" echo "=== 0. 检查运行时安全配置 ===" +MISSING_CONFIG=0 if [ -z "${TRAIN_STUDENT_IDENTITY_ENCRYPTION_KEY:-}" ]; then echo "错误:缺少 TRAIN_STUDENT_IDENTITY_ENCRYPTION_KEY" - exit 1 + MISSING_CONFIG=1 fi if [ -z "${TRAIN_STUDENT_IDENTITY_HASH_KEY:-}" ]; then echo "错误:缺少 TRAIN_STUDENT_IDENTITY_HASH_KEY" - exit 1 + MISSING_CONFIG=1 fi if [ -z "${SPRING_REDIS_PASSWORD:-}" ]; then echo "错误:缺少 SPRING_REDIS_PASSWORD" + MISSING_CONFIG=1 +fi + +if [ "$MISSING_CONFIG" -ne 0 ]; then + echo "运行时安全配置检查失败" exit 1 fi -echo "运行时安全配置存在性检查通过" +echo "required runtime configuration present." echo "=== 1. 检查并停止当前用户的旧后端进程 ===" TARGET_PID="" @@ -100,8 +106,8 @@ for i in {1..35}; do sleep 2 done -if [ "$HEALTH_OK" -ne 1 ]; then - echo "错误:后端健康检查超时,请检查 $LOG_PATH" +if [ "$HEALTH_OK" != "1" ]; then + echo "Backend health check failed: 后端健康检查失败或超时,请检查 $LOG_PATH" if kill -0 "$NEW_PID" 2>/dev/null; then kill -15 "$NEW_PID" 2>/dev/null || true @@ -167,6 +173,12 @@ http { NGINX_EOF fi +echo "验证 Nginx 配置文件 (Nginx Gate)..." +if ! /usr/sbin/nginx -t -c "$NGINX_CONF"; then + echo "错误:Nginx 配置文件测试失败,禁止启动或重载" + exit 1 +fi + if pgrep -u "$(whoami)" -f "nginx: master" >/dev/null; then echo "平滑重载前端 Nginx 服务..." @@ -183,4 +195,53 @@ else fi fi +echo "=== 4. 执行生产环境自动化冒烟测试 (Production Smoke Gate) ===" +SMOKE_PASS=1 + +# A. 验证前端应用首页 +HTTP_STATUS_A=$(curl -o /dev/null -s -w "%{http_code}" "http://127.0.0.1:8082/") +if [ "$HTTP_STATUS_A" = "200" ]; then + echo "[Smoke PASS] A: http://127.0.0.1:8082/ -> HTTP $HTTP_STATUS_A" +else + echo "[Smoke FAIL] A: http://127.0.0.1:8082/ -> HTTP $HTTP_STATUS_A (预期 200)" + SMOKE_PASS=0 +fi + +# B. 验证后端 API 反向代理及业务状态 +RES_B=$(curl -s "http://127.0.0.1:8082/prod-api/captchaImage" 2>/dev/null) +HTTP_STATUS_B=$(curl -o /dev/null -s -w "%{http_code}" "http://127.0.0.1:8082/prod-api/captchaImage") +if [ "$HTTP_STATUS_B" = "200" ] && echo "$RES_B" | grep -q '"code":200'; then + echo "[Smoke PASS] B: http://127.0.0.1:8082/prod-api/captchaImage -> HTTP $HTTP_STATUS_B 且 code:200" +else + echo "[Smoke FAIL] B: http://127.0.0.1:8082/prod-api/captchaImage -> HTTP $HTTP_STATUS_B 业务响应异常" + SMOKE_PASS=0 +fi + +# C. 验证 Human Documentation Portal 首页 +HTTP_STATUS_C=$(curl -o /dev/null -s -w "%{http_code}" "http://127.0.0.1:8082/docs/visual/index.html") +if [ "$HTTP_STATUS_C" = "200" ]; then + echo "[Smoke PASS] C: http://127.0.0.1:8082/docs/visual/index.html -> HTTP $HTTP_STATUS_C" +else + echo "[Smoke FAIL] C: http://127.0.0.1:8082/docs/visual/index.html -> HTTP $HTTP_STATUS_C (预期 200)" + SMOKE_PASS=0 +fi + +# D. 验证文档静态资源 docs.css 与 docs.js +HTTP_STATUS_CSS=$(curl -o /dev/null -s -w "%{http_code}" "http://127.0.0.1:8082/docs/visual/assets/docs.css") +HTTP_STATUS_JS=$(curl -o /dev/null -s -w "%{http_code}" "http://127.0.0.1:8082/docs/visual/assets/docs.js") +if [ "$HTTP_STATUS_CSS" = "200" ] && [ "$HTTP_STATUS_JS" = "200" ]; then + echo "[Smoke PASS] D: docs.css (HTTP $HTTP_STATUS_CSS) & docs.js (HTTP $HTTP_JS) -> HTTP 200" +else + echo "[Smoke FAIL] D: docs.css (HTTP $HTTP_STATUS_CSS), docs.js (HTTP $HTTP_JS) (预期 200)" + SMOKE_PASS=0 +fi + +if [ "$SMOKE_PASS" != "1" ]; then + echo "错误:生产环境冒烟测试未全部通过,部署失败!" + exit 1 +fi + +echo "=== 生产环境冒烟测试全部通过 (Production Smoke Gate PASSED) ===" +echo "APPLICATION_URL=http://192.168.139.230:8082/" +echo "DOCUMENTATION_URL=http://192.168.139.230:8082/docs/visual/index.html" echo "=== 部署全部就绪!前端访问地址: http://192.168.139.230:8082 ==="