#!/usr/bin/env bash # 自动审批:CI全绿后自动approve PR # 环境变量:GITHUB_TOKEN, REVIEW_TOKEN, PR_NUMBER, PR_HEAD_SHA, GITHUB_API_URL, GITHUB_REPOSITORY set -eu set -eu echo "PR #${PR_NUMBER} - 检查CI状态并自动审批" # 检查是否纯前端改动 API_URL="${GITHUB_API_URL}/repos/${GITHUB_REPOSITORY}/pulls/${PR_NUMBER}/files?limit=300" FILES=$(curl -s -H "Authorization: token ${GITHUB_TOKEN}" "$API_URL" | python3 -c "import sys,json; [print(f['filename']) for f in json.load(sys.stdin)]") FRONTEND_COUNT=$(echo "$FILES" | grep -c '^apps/web/' || true) BACKEND_COUNT=$(echo "$FILES" | grep -cv '^apps/web/' || true) TOTAL=$(echo "$FILES" | grep -cv '^$' || true) echo "变更文件: ${TOTAL} 个 (前端: ${FRONTEND_COUNT}, 后端/公共: ${BACKEND_COUNT})" if [ "$BACKEND_COUNT" = "0" ] && [ "$FRONTEND_COUNT" -gt "0" ]; then SKIP_BACKEND=true echo "✅ 纯前端改动,只检查Frontend Lint" else SKIP_BACKEND=false echo "🔧 包含后端/公共变更,检查全部CI" fi # 定义需要检查的context if [ "$SKIP_BACKEND" = "true" ]; then CONTEXTS=("CI/CD Pipeline / Frontend Lint (pull_request)") else CONTEXTS=( # CI 优化后 job 名称(2026-08):Code Quality 拆分为 Style+Security,Type Check+Migration 合并为 Python # 与 pr_auto_scan.py 的 REQUIRED_CONTEXTS_APPROVE 保持一致 "CI/CD Pipeline / Validate - Style (pull_request)" "CI/CD Pipeline / Validate - Security (pull_request)" "CI/CD Pipeline / Validate - Python (mypy + alembic) (pull_request)" "CI/CD Pipeline / Frontend Lint (pull_request)" ) fi echo "需要通过的CI检查: ${#CONTEXTS[@]} 项(与分支保护required门禁一致)" for ctx in "${CONTEXTS[@]}"; do echo " - $ctx" done echo # 初始等待30秒,给CI启动写status的时间 echo "等待30秒让CI启动..." sleep 30 # 轮询等待,最多20分钟(120次x10秒) for attempt in $(seq 1 12); do # 短作业模式:最多等2分钟(12次x10秒),不满足就退出等下次触发 ALL_SUCCESS=true ANY_FAILED=false ANY_PENDING=false echo "--- 第${attempt}次检查 ($(date '+%H:%M:%S')) ---" # 调用辅助脚本检查每个context状态 for ctx in "${CONTEXTS[@]}"; do STATE=$(python3 scripts/check_ci_status.py "$GITHUB_TOKEN" "$GITHUB_REPOSITORY" "$PR_HEAD_SHA" "$ctx") echo " $ctx: $STATE" if [ "$STATE" != "success" ]; then ALL_SUCCESS=false fi if [ "$STATE" = "failure" ] || [ "$STATE" = "error" ]; then ANY_FAILED=true fi if [ "$STATE" = "pending" ] || [ "$STATE" = "null" ]; then ANY_PENDING=true fi done if [ "$ALL_SUCCESS" = "true" ]; then echo echo "✅ 所有CI检查通过,自动审批 PR #${PR_NUMBER}" # 检查是否已有审批 EXISTING=$(curl -s -H "Authorization: token ${REVIEW_TOKEN}" \ "${GITHUB_API_URL}/repos/${GITHUB_REPOSITORY}/pulls/${PR_NUMBER}/reviews" \ | python3 -c "import sys,json; reviews=json.load(sys.stdin); print('yes' if any(r.get('state')=='APPROVED' for r in reviews) else 'no')") if [ "$EXISTING" = "yes" ]; then echo "ℹ️ PR #${PR_NUMBER} 已有审批,跳过" exit 0 fi # 第一步:创建PENDING review echo "创建review..." REVIEW_CREATE=$(curl -s -X POST \ -H "Authorization: token ${REVIEW_TOKEN}" \ -H "Content-Type: application/json" \ -d '{"event": "PENDING", "body": "CI全绿,自动审批通过。"}' \ "${GITHUB_API_URL}/repos/${GITHUB_REPOSITORY}/pulls/${PR_NUMBER}/reviews") REVIEW_ID=$(echo "$REVIEW_CREATE" | python3 -c "import sys,json; print(json.load(sys.stdin).get('id',''))") REVIEW_STATE=$(echo "$REVIEW_CREATE" | python3 -c "import sys,json; print(json.load(sys.stdin).get('state',''))") echo "创建结果: id=$REVIEW_ID state=$REVIEW_STATE" if [ -z "$REVIEW_ID" ]; then echo "❌ 创建review失败" echo "$REVIEW_CREATE" exit 1 fi if [ "$REVIEW_STATE" = "APPROVED" ]; then echo "✅ 自动审批成功(直接创建为APPROVED)" exit 0 fi # 第二步:submit review为APPROVED echo "提交review审批..." SUBMIT_CODE=$(curl -s -o /tmp/submit_resp.json -w "%{http_code}" \ -X POST \ -H "Authorization: token ${REVIEW_TOKEN}" \ -H "Content-Type: application/json" \ -d '{"event": "APPROVED", "body": "CI全绿,自动审批通过。"}' \ "${GITHUB_API_URL}/repos/${GITHUB_REPOSITORY}/pulls/${PR_NUMBER}/reviews/${REVIEW_ID}") echo "提交API HTTP状态: $SUBMIT_CODE" cat /tmp/submit_resp.json 2>/dev/null || true echo if [ "$SUBMIT_CODE" = "200" ] || [ "$SUBMIT_CODE" = "201" ]; then FINAL_STATE=$(python3 -c "import json; print(json.load(open('/tmp/submit_resp.json')).get('state',''))" 2>/dev/null || echo "?") echo "✅ 自动审批成功 (state: $FINAL_STATE)" exit 0 else echo "❌ 提交审批失败" exit 1 fi fi # 还有CI在跑 → 继续等 if [ "$ANY_PENDING" = "true" ]; then echo "⏳ CI仍在运行中(第${attempt}/12次),超时后将退出等待下次触发..." sleep 10 continue fi # 所有CI都跑完了但有失败 → 退出 if [ "$ANY_FAILED" = "true" ]; then echo echo "❌ CI检查有失败项,不自动审批" exit 0 fi sleep 10 done echo echo "⏰ 快速检查超时(2分钟),CI尚未完成,退出等待下次触发(workflow_run事件或5分钟定时扫描)" exit 0