diff --git a/.gitea/workflows/auto-approve.yml b/.gitea/workflows/auto-approve.yml new file mode 100644 index 000000000..3f7ccdaff --- /dev/null +++ b/.gitea/workflows/auto-approve.yml @@ -0,0 +1,152 @@ +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: Check if frontend-only change + id: frontend-only + shell: bash + env: + GITHUB_TOKEN: ${{ github.token }} + PR_NUMBER: ${{ github.event.pull_request.number }} + run: | + set -eu + 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 + echo "skip_backend=true" >> $GITHUB_OUTPUT + echo "✅ 纯前端改动,跳过后端CI检查" + else + echo "skip_backend=false" >> $GITHUB_OUTPUT + echo "🔧 包含后端/公共变更,检查全部CI" + fi + + - name: Wait for CI and auto approve + shell: bash + env: + GITHUB_TOKEN: ${{ github.token }} + REVIEW_TOKEN: ${{ secrets.REVIEW_GITEA_TOKEN }} + PR_NUMBER: ${{ github.event.pull_request.number }} + SKIP_BACKEND: ${{ steps.frontend-only.outputs.skip_backend }} + run: | + set -eu + + # 定义需要检查的status context + if [ "$SKIP_BACKEND" = "true" ]; then + CONTEXTS=("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')) ---" + + # 获取当前commit的所有status + STATUSES=$(curl -s -H "Authorization: token ${GITHUB_TOKEN}" \ + "${GITHUB_API_URL}/repos/${GITHUB_REPOSITORY}/commits/${GITHUB_SHA}/statuses?per_page=100") + + for ctx in "${CONTEXTS[@]}"; do + STATE=$(echo "$STATUSES" | python3 -c " +import sys, json +statuses = json.load(sys.stdin) +target = '$ctx' +# 找最新的对应context +latest = None +for s in statuses: + if s.get('context') == target: + latest = s + break # API返回的是倒序,第一个就是最新的 +print(latest.get('state', 'pending') if latest else 'pending') +") + 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}" + + # 先检查是否已有审批,避免重复审批 + 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) +for r in reviews: + if r.get('state') == 'APPROVED' and r.get('user',{}).get('login') == 'xiaoxia': + print('yes') + break +else: + print('no') +") + + if [ "$EXISTING" = "yes" ]; then + echo "ℹ️ PR #${PR_NUMBER} 已有审批,跳过" + exit 0 + fi + + # 提交审批 + HTTP_CODE=$(curl -s -o /tmp/approve_resp.json -w "%{http_code}" \ + -X POST \ + -H "Authorization: token ${REVIEW_TOKEN}" \ + -H "Content-Type: application/json" \ + -d '{"event": "APPROVE", "body": "CI全绿,自动审批通过。"}' \ + "${GITHUB_API_URL}/repos/${GITHUB_REPOSITORY}/pulls/${PR_NUMBER}/reviews") + + echo "审批API HTTP状态: $HTTP_CODE" + cat /tmp/approve_resp.json 2>/dev/null || true + echo + + if [ "$HTTP_CODE" = "200" ] || [ "$HTTP_CODE" = "201" ]; then + echo "✅ 自动审批成功" + exit 0 + else + echo "❌ 自动审批失败" + exit 1 + fi + fi + + if [ "$ANY_FAILED" = "true" ]; then + echo + echo "❌ CI检查有失败项,不自动审批" + exit 0 # 正常退出,不算workflow失败 + fi + + sleep 10 + done + + echo + echo "⏰ 等待超时(20分钟),CI尚未全部完成" + exit 0 # 超时也正常退出,不产生告警