diff --git a/.gitea/workflows/auto-approve.yml b/.gitea/workflows/auto-approve.yml new file mode 100644 index 000000000..ee39194c0 --- /dev/null +++ b/.gitea/workflows/auto-approve.yml @@ -0,0 +1,164 @@ +name: Auto Approve CI PRs + +on: + pull_request: + types: [synchronize, opened, ready_for_review] + +jobs: + auto-approve: + name: Auto Approve on CI Green + runs-on: ci-l1 + if: github.event_name == 'pull_request' && !github.event.pull_request.draft + timeout-minutes: 20 + steps: + - name: Checkout code + uses: actions/checkout@v3 + + - name: Auto approve when CI passes + shell: bash + env: + GITHUB_TOKEN: ${{ github.token }} + REVIEW_TOKEN: ${{ secrets.REVIEW_GITEA_TOKEN }} + PR_NUMBER: ${{ github.event.pull_request.number }} + run: | + 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 + # 根据目标分支决定检查哪些门禁 + TARGET_BRANCH="${GITHUB_BASE_REF}" + echo "目标分支: ${TARGET_BRANCH}" + + if [ "$SKIP_BACKEND" = "true" ]; then + CONTEXTS=("CI/CD Pipeline / Frontend Lint (pull_request)") + elif [ "$TARGET_BRANCH" = "main" ]; then + # main分支只检查required statuses: Validate + Frontend Lint + # 不检查Tests/test(不是required门禁) + CONTEXTS=( + "CI/CD Pipeline / Validate Code Quality And Tests (pull_request)" + "CI/CD Pipeline / Frontend Lint (pull_request)" + ) + else + CONTEXTS=( + "CI/CD Pipeline / Validate Code Quality And Tests (pull_request)" + "CI/CD Pipeline / Unit Tests (pull_request)" + "CI/CD Pipeline / Frontend Lint (pull_request)" + ) + fi + + echo "需要通过的CI检查: ${#CONTEXTS[@]} 项" + for ctx in "${CONTEXTS[@]}"; do + echo " - $ctx" + done + echo + + # 轮询等待,最多20分钟(120次x10秒) + for attempt in $(seq 1 120); do + ALL_SUCCESS=true + ANY_FAILED=false + + echo "--- 第${attempt}次检查 ($(date '+%H:%M:%S')) ---" + + # 调用辅助脚本检查每个context状态 + for ctx in "${CONTEXTS[@]}"; do + STATE=$(python3 scripts/check_ci_status.py "$GITHUB_TOKEN" "$GITHUB_REPOSITORY" "$GITHUB_SHA" "$ctx") + echo " $ctx: $STATE" + + if [ "$STATE" != "success" ]; then + ALL_SUCCESS=false + fi + if [ "$STATE" = "failure" ] || [ "$STATE" = "error" ]; then + ANY_FAILED=true + fi + done + + if [ "$ALL_SUCCESS" = "true" ]; then + echo + echo "✅ 所有CI检查通过,自动审批 PR #${PR_NUMBER}" + + # 检查是否已有审批(任何用户的APPROVED都算,避免重复审批) + 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(Gitea API需要先创建再提交) + 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 + + # 如果已经是APPROVED就不用再submit了(兼容不同Gitea版本) + 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 + + if [ "$ANY_FAILED" = "true" ]; then + echo + echo "❌ CI检查有失败项,不自动审批" + exit 0 + fi + + sleep 10 + done + + echo + echo "⏰ 等待超时(20分钟),CI尚未全部完成" + exit 0