name: Daily Health Check # 注意:使用 curl step_checkout.sh 方式以兼容 docker runner on: schedule: - cron: '0 19 * * *' # UTC 19:00 = 北京时间凌晨 3:00 workflow_dispatch: permissions: contents: read jobs: # ── 1. 生产环境冒烟测试 ───────────────────────────────────────────── production-smoke: name: Production Smoke Test runs-on: ci-l2 timeout-minutes: 8 outputs: report: ${{ steps.smoke.outputs.report }} steps: - name: Checkout code shell: sh env: GITHUB_TOKEN: ${{ github.token }} run: | curl -sH "Authorization: token $GITHUB_TOKEN" \ "${GITHUB_API_URL}/repos/${GITHUB_REPOSITORY}/raw/scripts/ci/step_checkout.sh?ref=${GITHUB_SHA}" \ | bash - name: Production health check & smoke test id: smoke shell: bash env: SMOKE_ENV: production EXISTING_TOKEN: ${{ secrets.PROD_E2E_TOKEN }} MODULES: health,assets,generation,subscription,nginx run: | set +e START_TIME=$(date +%s) chmod +x tests/e2e/api_smoke_test.sh BASE_URL="https://api.xiaoxiajianji.com" \ WEB_URL="https://saas.xiaoxiajianji.com" \ SMOKE_ENV="${SMOKE_ENV}" \ EXISTING_TOKEN="${EXISTING_TOKEN}" \ MODULES="${MODULES}" \ CLEANUP_ENABLED=0 \ PERF_CHECK_ENABLED=1 \ PERF_WARN_THRESHOLD_MS=500 \ PERF_FAIL_THRESHOLD_MS=5000 \ bash tests/e2e/api_smoke_test.sh 2>&1 | tee /tmp/prod-smoke.log SMOKE_EXIT=${PIPESTATUS[0]} END_TIME=$(date +%s) ELAPSED=$((END_TIME - START_TIME)) echo "" echo "========== 生产冒烟测试报告 ==========" echo "环境: https://api.xiaoxiajianji.com" echo "耗时: ${ELAPSED}s" # 提取通过/失败数 grep "测试完成:" /tmp/prod-smoke.log || true if [ "$SMOKE_EXIT" -eq 0 ]; then echo "结果: PASS" echo "report=PASS" >> "${GITHUB_OUTPUT}" else echo "结果: FAIL" grep "失败用例:" /tmp/prod-smoke.log || true echo "report=FAIL" >> "${GITHUB_OUTPUT}" fi echo "======================================" exit $SMOKE_EXIT - name: Report CI trace if: always() shell: sh env: AGENTLOOP_LICENSE_KEY: ${{ secrets.AGENTLOOP_LICENSE_KEY }} run: | STATUS="ok" [ ${{ job.status }} = "success" ] || STATUS="error" START_TIME="" [ -f /tmp/ci_job_start_time ] && START_TIME=$(cat /tmp/ci_job_start_time) python3 scripts/ci/ci_trace_report.py --service xiaoxia-saas-ci --status $STATUS --start-time "$START_TIME" || true # ── 2. Staging API 集成测试 ───────────────────────────────────────── staging-api-tests: name: Staging API Integration Tests runs-on: ci-l2 timeout-minutes: 10 outputs: report: ${{ steps.report.outputs.report }} steps: - name: Checkout code shell: sh env: GITHUB_TOKEN: ${{ github.token }} run: | curl -sH "Authorization: token $GITHUB_TOKEN" \ "${GITHUB_API_URL}/repos/${GITHUB_REPOSITORY}/raw/scripts/ci/step_checkout.sh?ref=${GITHUB_SHA}" \ | bash - name: Run API smoke test on staging id: smoke shell: bash env: STAGING_TEST_USER: ${{ secrets.STAGING_TEST_USER }} STAGING_TEST_PASSWORD: ${{ secrets.STAGING_TEST_PASSWORD }} run: | set +e START_TIME=$(date +%s) chmod +x tests/e2e/api_smoke_test.sh CONTAINER_NAME="ci-test-$$" docker create --name "$CONTAINER_NAME" \ -e BASE_URL=https://staging-api.xiaoxiajianji.com \ -e WEB_URL=https://staging.xiaoxiajianji.com \ -e TEST_USER="$STAGING_TEST_USER" \ -e TEST_PASSWORD="$STAGING_TEST_PASSWORD" \ -e CLEANUP_ENABLED=1 \ -e PERF_CHECK_ENABLED=1 \ -e PERF_WARN_THRESHOLD_MS=500 \ -e PERF_FAIL_THRESHOLD_MS=3000 \ -w /workspace \ git.xiaoxiajianji.com/xiaoxia/base/playwright:v1.45.0-jammy \ bash tests/e2e/api_smoke_test.sh 2>&1 docker cp . "$CONTAINER_NAME:/workspace" docker start -a "$CONTAINER_NAME" 2>&1 | tee /tmp/staging-api-smoke.log SMOKE_EXIT=${PIPESTATUS[0]} docker rm "$CONTAINER_NAME" > /dev/null 2>&1 || true END_TIME=$(date +%s) ELAPSED=$((END_TIME - START_TIME)) echo "" echo "========== Staging API 冒烟测试报告 ==========" echo "环境: https://staging-api.xiaoxiajianji.com" echo "耗时: ${ELAPSED}s" grep "测试完成:" /tmp/staging-api-smoke.log || true if [ "$SMOKE_EXIT" -eq 0 ]; then echo "结果: PASS" echo "api_report=PASS" >> "${GITHUB_OUTPUT}" else echo "结果: FAIL" grep "失败用例:" /tmp/staging-api-smoke.log || true echo "api_report=FAIL" >> "${GITHUB_OUTPUT}" fi echo "==============================================" exit $SMOKE_EXIT - name: Run Staging API Integration Tests (Playwright) id: e2e_api shell: bash run: | set +e START_TIME=$(date +%s) CONTAINER_NAME="ci-test-$$" docker create --name "$CONTAINER_NAME" \ -e E2E_BASE_URL=https://staging.xiaoxiajianji.com \ -e E2E_API_BASE=https://staging-api.xiaoxiajianji.com/api/v1 \ -w /workspace/apps/web \ git.xiaoxiajianji.com/xiaoxia/base/playwright:v1.45.0-jammy \ sh -lc "npm ci && npx playwright test --reporter=line e2e/test_auth.spec.ts e2e/test_asset.spec.ts e2e/test_project.spec.ts" 2>&1 docker cp . "$CONTAINER_NAME:/workspace" docker start -a "$CONTAINER_NAME" 2>&1 | tee /tmp/staging-api-e2e.log EXIT_CODE=${PIPESTATUS[0]} docker rm "$CONTAINER_NAME" > /dev/null 2>&1 || true END_TIME=$(date +%s) ELAPSED=$((END_TIME - START_TIME)) echo "" echo "========== Staging API 集成测试报告 ==========" echo "环境: https://staging-api.xiaoxiajianji.com" echo "耗时: ${ELAPSED}s" grep -E "passed|failed|timed out" /tmp/staging-api-e2e.log || true if [ "$EXIT_CODE" -eq 0 ]; then echo "结果: PASS" echo "int_report=PASS" >> "${GITHUB_OUTPUT}" else echo "结果: FAIL" echo "int_report=FAIL" >> "${GITHUB_OUTPUT}" fi echo "==============================================" exit $EXIT_CODE - name: Set report output id: report shell: sh run: | if [ "${{ steps.smoke.outputs.api_report }}" = "PASS" ] && [ "${{ steps.e2e_api.outputs.int_report }}" = "PASS" ]; then echo "report=PASS" >> "${GITHUB_OUTPUT}" else echo "report=FAIL" >> "${GITHUB_OUTPUT}" fi - name: Report CI trace if: always() shell: sh env: AGENTLOOP_LICENSE_KEY: ${{ secrets.AGENTLOOP_LICENSE_KEY }} run: | STATUS="ok" [ ${{ job.status }} = "success" ] || STATUS="error" START_TIME="" [ -f /tmp/ci_job_start_time ] && START_TIME=$(cat /tmp/ci_job_start_time) python3 scripts/ci/ci_trace_report.py --service xiaoxia-saas-ci --status $STATUS --start-time "$START_TIME" || true # ── 3. Staging 浏览器 E2E ────────────────────────────────────────── staging-e2e: name: Staging Browser E2E runs-on: ci-l2 timeout-minutes: 15 outputs: report: ${{ steps.e2e.outputs.report }} steps: - name: Checkout code shell: sh env: GITHUB_TOKEN: ${{ github.token }} run: | curl -sH "Authorization: token $GITHUB_TOKEN" \ "${GITHUB_API_URL}/repos/${GITHUB_REPOSITORY}/raw/scripts/ci/step_checkout.sh?ref=${GITHUB_SHA}" \ | bash - name: Run Playwright E2E on staging id: e2e shell: bash run: | set +e START_TIME=$(date +%s) CONTAINER_NAME="ci-test-$$" docker create --name "$CONTAINER_NAME" --ipc=host \ -e E2E_BASE_URL=https://staging.xiaoxiajianji.com \ -e E2E_API_BASE=https://staging-api.xiaoxiajianji.com/api/v1 \ -e E2E_BROWSER_CHANNEL=chromium \ -e PLAYWRIGHT_HEADLESS=1 \ -w /workspace/apps/web \ git.xiaoxiajianji.com/xiaoxia/base/playwright:v1.45.0-jammy \ sh -lc 'npm ci && npx playwright test --reporter=line --project=chromium e2e/auth.spec.ts e2e/auth-guard.spec.ts e2e/core-upload.spec.ts e2e/core-generation.spec.ts e2e/core-titles.spec.ts' 2>&1 docker cp . "$CONTAINER_NAME:/workspace" docker start -a "$CONTAINER_NAME" 2>&1 | tee /tmp/staging-e2e.log EXIT_CODE=${PIPESTATUS[0]} docker rm "$CONTAINER_NAME" > /dev/null 2>&1 || true END_TIME=$(date +%s) ELAPSED=$((END_TIME - START_TIME)) echo "" echo "========== Staging E2E 测试报告 ==========" echo "环境: https://staging.xiaoxiajianji.com" echo "耗时: ${ELAPSED}s" grep -E "passed|failed|timed out" /tmp/staging-e2e.log || true if [ "$EXIT_CODE" -eq 0 ]; then echo "结果: PASS" echo "report=PASS" >> "${GITHUB_OUTPUT}" else echo "结果: FAIL" echo "report=FAIL" >> "${GITHUB_OUTPUT}" fi echo "==========================================" exit $EXIT_CODE - name: Report CI trace if: always() shell: sh env: AGENTLOOP_LICENSE_KEY: ${{ secrets.AGENTLOOP_LICENSE_KEY }} run: | STATUS="ok" [ ${{ job.status }} = "success" ] || STATUS="error" START_TIME="" [ -f /tmp/ci_job_start_time ] && START_TIME=$(cat /tmp/ci_job_start_time) python3 scripts/ci/ci_trace_report.py --service xiaoxia-saas-ci --status $STATUS --start-time "$START_TIME" || true # ── 4. 性能基线巡检 ──────────────────────────────────────────────── performance-check: name: Performance Baseline Check runs-on: ci-l2 timeout-minutes: 8 outputs: report: ${{ steps.report.outputs.report }} steps: - name: Run performance baseline checks id: perf shell: sh env: STAGING_TEST_USER: ${{ secrets.STAGING_TEST_USER }} STAGING_TEST_PASSWORD: ${{ secrets.STAGING_TEST_PASSWORD }} run: | set +e START_TIME=$(date +%s) echo "==========================================" echo " 性能基线巡检 - Staging API" echo " 目标: https://staging-api.xiaoxiajianji.com" echo "==========================================" echo "" TOTAL=0 PASS=0 FAIL=0 WARN=0 WARN_LIST="" FAIL_LIST="" # 核心接口配置: 名称|路径|方法|阈值(ms)|失败阈值(ms) # 核心接口(core): 500ms # 普通接口(normal): 1000ms # 重操作接口(heavy): 3000ms ENDPOINTS=" 登录|/api/v1/auth/login|POST|500|3000 获取当前用户|/api/v1/auth/me|GET|500|3000 项目列表|/api/v1/projects|GET|500|3000 素材列表|/api/v1/assets|GET|500|3000 模板列表|/api/v1/templates|GET|500|3000 剪辑计划列表|/api/v1/edit-plans|GET|500|3000 生成任务列表|/api/v1/generation/tasks|GET|500|3000 订阅信息|/api/v1/subscription/current|GET|500|3000 音色列表|/api/v1/voices|GET|1000|5000 健康检查|/health|GET|200|1000 " # 先登录获取 token echo "--- 准备: 获取测试 Token ---" LOGIN_BODY="{\"email\":\"$STAGING_TEST_USER\",\"password\":\"$STAGING_TEST_PASSWORD\"}" AUTH_RESP=$(curl -s -w "\n%{http_code}" -X POST \ -H "Content-Type: application/json" \ -d "$LOGIN_BODY" \ "https://staging-api.xiaoxiajianji.com/api/v1/auth/login" \ --max-time 10 2>&1) AUTH_CODE=$(echo "$AUTH_RESP" | tail -1) AUTH_BODY=$(echo "$AUTH_RESP" | sed '$d') if [ "$AUTH_CODE" = "200" ]; then TOKEN=$(echo "$AUTH_BODY" | python3 -c "import sys,json; d=json.load(sys.stdin); print(d.get('access_token',''))" 2>/dev/null) if [ -n "$TOKEN" ]; then echo "Token 获取成功" else echo "Token 解析失败,部分接口可能无法测试" TOKEN="" fi else echo "登录失败 (HTTP $AUTH_CODE),部分接口将跳过鉴权测试" TOKEN="" fi echo "" echo "--- 开始性能测试 ---" echo "" echo "$ENDPOINTS" | while IFS='|' read -r name path method warn_ms fail_ms; do [ -z "$name" ] && continue TOTAL=$((TOTAL + 1)) # 构建 curl 命令 CURL_ARGS="-s -o /dev/null -w '%{http_code} %{time_total}' --max-time 30" if [ "$method" = "POST" ]; then CURL_ARGS="$CURL_ARGS -X POST -H 'Content-Type: application/json' -d \"$LOGIN_BODY\"" fi if [ -n "$TOKEN" ] && [ "$name" != "健康检查" ]; then CURL_ARGS="$CURL_ARGS -H 'Authorization: Bearer $TOKEN'" fi # 执行请求 RESP=$(eval curl $CURL_ARGS "https://staging-api.xiaoxiajianji.com${path}" 2>&1) HTTP_CODE=$(echo "$RESP" | awk '{print $1}') TIME_TOTAL=$(echo "$RESP" | awk '{print $2}') ELAPSED_MS=$(python3 -c "print(int(float('${TIME_TOTAL:-0}') * 1000))" 2>/dev/null || echo "0") if [ "$HTTP_CODE" -ge 500 ] 2>/dev/null; then FAIL=$((FAIL + 1)) FAIL_LIST="$FAIL_LIST\n ❌ $name - HTTP $HTTP_CODE (${ELAPSED_MS}ms)" echo "❌ $name - HTTP $HTTP_CODE - ${ELAPSED_MS}ms (FAIL)" elif [ "$ELAPSED_MS" -ge "$fail_ms" ] 2>/dev/null; then FAIL=$((FAIL + 1)) FAIL_LIST="$FAIL_LIST\n ❌ $name - ${ELAPSED_MS}ms > ${fail_ms}ms" echo "❌ $name - ${ELAPSED_MS}ms > ${fail_ms}ms (FAIL)" elif [ "$ELAPSED_MS" -ge "$warn_ms" ] 2>/dev/null; then WARN=$((WARN + 1)) WARN_LIST="$WARN_LIST\n ⚠️ $name - ${ELAPSED_MS}ms > ${warn_ms}ms" echo "⚠️ $name - ${ELAPSED_MS}ms (WARN, threshold: ${warn_ms}ms)" PASS=$((PASS + 1)) else PASS=$((PASS + 1)) echo "✅ $name - ${ELAPSED_MS}ms (OK, threshold: ${warn_ms}ms)" fi done # 由于 while 在子 shell 中执行,用文件传递结果 # 重新跑一次用文件计数方式 echo "" echo "--- 汇总性能数据 ---" END_TIME=$(date +%s) ELAPSED=$((END_TIME - START_TIME)) echo "" echo "========== 性能基线巡检报告 ==========" echo "环境: https://staging-api.xiaoxiajianji.com" echo "耗时: ${ELAPSED}s" echo "======================================" - name: Generate performance report id: report shell: sh env: STAGING_TEST_USER: ${{ secrets.STAGING_TEST_USER }} STAGING_TEST_PASSWORD: ${{ secrets.STAGING_TEST_PASSWORD }} run: | set +e echo "" echo "==========================================" echo " 性能基线巡检 - 详细报告" echo "==========================================" TOTAL=0 PASS=0 FAIL=0 WARN=0 RESULTS="" START_TIME=$(date +%s) LOGIN_BODY="{\"email\":\"$STAGING_TEST_USER\",\"password\":\"$STAGING_TEST_PASSWORD\"}" # 先登录获取 token AUTH_RESP=$(curl -s -w "\n%{http_code}" -X POST \ -H "Content-Type: application/json" \ -d "$LOGIN_BODY" \ "https://staging-api.xiaoxiajianji.com/api/v1/auth/login" \ --max-time 10 2>&1) AUTH_CODE=$(echo "$AUTH_RESP" | tail -1) AUTH_BODY=$(echo "$AUTH_RESP" | sed '$d') TOKEN="" if [ "$AUTH_CODE" = "200" ]; then TOKEN=$(echo "$AUTH_BODY" | python3 -c "import sys,json; d=json.load(sys.stdin); print(d.get('access_token',''))" 2>/dev/null || echo "") fi run_perf_test() { local name="$1" path="$2" method="$3" warn_ms="$4" fail_ms="$5" TOTAL=$((TOTAL + 1)) local CURL_ARGS="-s -o /dev/null -w '%{http_code} %{time_total}' --max-time 30" if [ "$method" = "POST" ]; then CURL_ARGS="$CURL_ARGS -X POST -H 'Content-Type: application/json' -d \"$LOGIN_BODY\"" fi if [ -n "$TOKEN" ] && [ "$name" != "健康检查" ]; then CURL_ARGS="$CURL_ARGS -H 'Authorization: Bearer $TOKEN'" fi local RESP=$(eval curl $CURL_ARGS "https://staging-api.xiaoxiajianji.com${path}" 2>&1) local HTTP_CODE=$(echo "$RESP" | awk '{print $1}') local TIME_TOTAL=$(echo "$RESP" | awk '{print $2}') local ELAPSED_MS=$(python3 -c "print(int(float('${TIME_TOTAL:-0}') * 1000))" 2>/dev/null || echo "0") if echo "$HTTP_CODE" | grep -q "^[5]"; then FAIL=$((FAIL + 1)) RESULTS="$RESULTS\n ❌ $name - HTTP $HTTP_CODE (${ELAPSED_MS}ms)" echo "❌ $name - HTTP $HTTP_CODE - ${ELAPSED_MS}ms [FAIL]" return 1 elif [ "$ELAPSED_MS" -ge "$fail_ms" ] 2>/dev/null; then FAIL=$((FAIL + 1)) RESULTS="$RESULTS\n ❌ $name - ${ELAPSED_MS}ms > ${fail_ms}ms [FAIL]" echo "❌ $name - ${ELAPSED_MS}ms > ${fail_ms}ms [FAIL]" return 1 elif [ "$ELAPSED_MS" -ge "$warn_ms" ] 2>/dev/null; then WARN=$((WARN + 1)) PASS=$((PASS + 1)) RESULTS="$RESULTS\n ⚠️ $name - ${ELAPSED_MS}ms (阈值: ${warn_ms}ms) [WARN]" echo "⚠️ $name - ${ELAPSED_MS}ms > 阈值 ${warn_ms}ms [WARN]" return 0 else PASS=$((PASS + 1)) RESULTS="$RESULTS\n ✅ $name - ${ELAPSED_MS}ms (阈值: ${warn_ms}ms) [OK]" echo "✅ $name - ${ELAPSED_MS}ms (阈值: ${warn_ms}ms) [OK]" return 0 fi } echo "" echo "=== 核心接口 (阈值: 500ms / 3000ms) ===" run_perf_test "登录" "/api/v1/auth/login" "POST" 500 3000 || true run_perf_test "获取当前用户" "/api/v1/auth/me" "GET" 500 3000 || true run_perf_test "项目列表" "/api/v1/projects" "GET" 500 3000 || true run_perf_test "素材列表" "/api/v1/assets" "GET" 500 3000 || true run_perf_test "模板列表" "/api/v1/templates" "GET" 500 3000 || true run_perf_test "剪辑计划列表" "/api/v1/edit-plans" "GET" 500 3000 || true run_perf_test "生成任务列表" "/api/v1/generation/tasks" "GET" 500 3000 || true run_perf_test "订阅信息" "/api/v1/subscription/current" "GET" 500 3000 || true echo "" echo "=== 普通接口 (阈值: 1000ms / 5000ms) ===" run_perf_test "音色列表" "/api/v1/voices" "GET" 1000 5000 || true echo "" echo "=== 基础接口 (阈值: 200ms / 1000ms) ===" run_perf_test "健康检查" "/health" "GET" 200 1000 || true END_TIME=$(date +%s) ELAPSED=$((END_TIME - START_TIME)) echo "" echo "========== 性能基线巡检报告 ==========" echo "环境: https://staging-api.xiaoxiajianji.com" echo "总接口: ${TOTAL}" echo "通过: ${PASS}" echo "失败: ${FAIL}" echo "警告: ${WARN}" echo "耗时: ${ELAPSED}s" echo "======================================" # 写入结果文件供 report job 使用 echo "${TOTAL}" > /tmp/perf_total echo "${PASS}" > /tmp/perf_pass echo "${FAIL}" > /tmp/perf_fail echo "${WARN}" > /tmp/perf_warn echo "${ELAPSED}" > /tmp/perf_elapsed if [ "$FAIL" -gt 0 ]; then echo "report=FAIL" >> "${GITHUB_OUTPUT}" echo "perf_detail=fail:${FAIL}:warn:${WARN}" >> "${GITHUB_OUTPUT}" exit 1 else echo "report=PASS" >> "${GITHUB_OUTPUT}" if [ "$WARN" -gt 0 ]; then echo "perf_detail=pass:warn:${WARN}" >> "${GITHUB_OUTPUT}" else echo "perf_detail=pass" >> "${GITHUB_OUTPUT}" fi exit 0 fi - name: Report CI trace if: always() shell: sh env: AGENTLOOP_LICENSE_KEY: ${{ secrets.AGENTLOOP_LICENSE_KEY }} run: | STATUS="ok" [ ${{ job.status }} = "success" ] || STATUS="error" START_TIME="" [ -f /tmp/ci_job_start_time ] && START_TIME=$(cat /tmp/ci_job_start_time) python3 scripts/ci/ci_trace_report.py --service xiaoxia-saas-ci --status $STATUS --start-time "$START_TIME" || true # ── 5. 每日巡检汇总报告 ──────────────────────────────────────────── daily-report: name: Daily Check Report runs-on: ci-l2 timeout-minutes: 2 if: always() needs: - production-smoke - staging-api-tests - staging-e2e - performance-check steps: - name: Print summary report shell: sh run: | echo "" echo "╔══════════════════════════════════════════════════════╗" echo "║ 每日巡检报告 ║" echo "╠══════════════════════════════════════════════════════╣" # 获取各 job 状态 PROD_STATUS="${{ needs.production-smoke.result }}" STAGING_API_STATUS="${{ needs.staging-api-tests.result }}" STAGING_E2E_STATUS="${{ needs.staging-e2e.result }}" PERF_STATUS="${{ needs.performance-check.result }}" format_result() { if [ "$1" = "success" ]; then echo "✅ PASS" elif [ "$1" = "failure" ]; then echo "❌ FAIL" elif [ "$1" = "skipped" ]; then echo "⏭️ SKIP" else echo "❓ UNKNOWN ($1)" fi } echo "║" echo "║ 生产冒烟测试: $(format_result "$PROD_STATUS")" echo "║ Staging API: $(format_result "$STAGING_API_STATUS")" echo "║ Staging E2E: $(format_result "$STAGING_E2E_STATUS")" echo "║ 性能基线巡检: $(format_result "$PERF_STATUS")" echo "║" echo "║ 巡检时间: $(date '+%Y-%m-%d %H:%M:%S UTC')" echo "║" # 判断整体状态 ALL_PASS=true FAILED_ITEMS="" for status_name in "$PROD_STATUS:生产冒烟" "$STAGING_API_STATUS:Staging API" "$STAGING_E2E_STATUS:Staging E2E" "$PERF_STATUS:性能基线"; do STATUS=$(echo "$status_name" | cut -d: -f1) NAME=$(echo "$status_name" | cut -d: -f2) if [ "$STATUS" != "success" ] && [ "$STATUS" != "skipped" ]; then ALL_PASS=false FAILED_ITEMS="$FAILED_ITEMS $NAME" fi done echo "╠══════════════════════════════════════════════════════╣" if [ "$ALL_PASS" = "true" ]; then echo "║ 整体状态: ✅ 全部通过 ║" else echo "║ 整体状态: ❌ 存在失败 ║" echo "║ 失败项: ${FAILED_ITEMS} ║" fi echo "╚══════════════════════════════════════════════════════╝" echo "" # 如果有失败项,以非零退出码结束(方便 Gitea 标记流水线失败) if [ "$ALL_PASS" = "false" ]; then echo "⚠️ 部分巡检项失败,请检查上方日志获取详细信息。" # 不 exit 1,因为我们用了 always(),保持 report job 成功, # 但其他失败的 job 已经让整体流水线标记为失败 fi - name: Report CI trace if: always() shell: sh env: AGENTLOOP_LICENSE_KEY: ${{ secrets.AGENTLOOP_LICENSE_KEY }} run: | STATUS="ok" [ ${{ job.status }} = "success" ] || STATUS="error" START_TIME="" [ -f /tmp/ci_job_start_time ] && START_TIME=$(cat /tmp/ci_job_start_time) python3 scripts/ci/ci_trace_report.py --service xiaoxia-saas-ci --status $STATUS --start-time "$START_TIME" || true