From 3944aa1841e1bc404769421fe2284728ee1be63c Mon Sep 17 00:00:00 2001 From: xiaoxia Date: Fri, 17 Jul 2026 10:06:57 +0800 Subject: [PATCH 1/2] =?UTF-8?q?fix(ci):=20=E4=BF=AE=E5=A4=8Dauto-approve.y?= =?UTF-8?q?ml=20YAML=E8=AF=AD=E6=B3=95=20-=20=E5=86=85=E8=81=94Python?= =?UTF-8?q?=E5=A4=9A=E8=A1=8C=E7=BC=A9=E8=BF=9B=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitea/workflows/auto-approve.yml | 93 +++++++++++-------------------- 1 file changed, 34 insertions(+), 59 deletions(-) diff --git a/.gitea/workflows/auto-approve.yml b/.gitea/workflows/auto-approve.yml index 3f7ccdaff..a00eb98c9 100644 --- a/.gitea/workflows/auto-approve.yml +++ b/.gitea/workflows/auto-approve.yml @@ -11,39 +11,37 @@ jobs: if: github.event_name == 'pull_request' && !github.event.pull_request.draft timeout-minutes: 20 steps: - - name: Check if frontend-only change - id: frontend-only + - 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 - echo "skip_backend=true" >> $GITHUB_OUTPUT - echo "✅ 纯前端改动,跳过后端CI检查" + SKIP_BACKEND=true + echo "✅ 纯前端改动,只检查Frontend Lint" else - echo "skip_backend=false" >> $GITHUB_OUTPUT + SKIP_BACKEND=false 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 + # 定义需要检查的context if [ "$SKIP_BACKEND" = "true" ]; then CONTEXTS=("CI/CD Pipeline / Frontend Lint (pull_request)") else @@ -53,39 +51,25 @@ jobs: "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") - + + # 调用辅助脚本检查每个context状态 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') -") + 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 @@ -93,30 +77,21 @@ print(latest.get('state', 'pending') if latest else 'pending') 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') -") - + | python3 -c "import sys,json; reviews=json.load(sys.stdin); print('yes' if any(r.get('state')=='APPROVED' and r.get('user',{}).get('login')=='xiaoxia' for r in reviews) else '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 \ @@ -124,11 +99,11 @@ else: -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 @@ -137,16 +112,16 @@ else: exit 1 fi fi - + if [ "$ANY_FAILED" = "true" ]; then echo echo "❌ CI检查有失败项,不自动审批" - exit 0 # 正常退出,不算workflow失败 + exit 0 fi - + sleep 10 done - + echo echo "⏰ 等待超时(20分钟),CI尚未全部完成" - exit 0 # 超时也正常退出,不产生告警 + exit 0 -- 2.54.0 From f2e44d3fb45667f0be2de2a266ee5ef5090b0f03 Mon Sep 17 00:00:00 2001 From: xiaoxia Date: Fri, 17 Jul 2026 10:07:06 +0800 Subject: [PATCH 2/2] =?UTF-8?q?feat(ci):=20=E6=96=B0=E5=A2=9Echeck=5Fci=5F?= =?UTF-8?q?status.py=E8=BE=85=E5=8A=A9=E8=84=9A=E6=9C=AC=20-=20CI=E7=8A=B6?= =?UTF-8?q?=E6=80=81=E6=A3=80=E6=9F=A5=E5=B7=A5=E5=85=B7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- scripts/check_ci_status.py | 44 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) create mode 100644 scripts/check_ci_status.py diff --git a/scripts/check_ci_status.py b/scripts/check_ci_status.py new file mode 100644 index 000000000..cc159f9eb --- /dev/null +++ b/scripts/check_ci_status.py @@ -0,0 +1,44 @@ +#!/usr/bin/env python3 +"""检查指定commit的CI status状态。 + +用法: python3 check_ci_status.py +返回: 打印状态 (success/failure/pending/error) +""" + +import json +import sys +import urllib.error +import urllib.request + + +def main(): + if len(sys.argv) != 5: + print("pending") + return + + token = sys.argv[1] + repo = sys.argv[2] + sha = sys.argv[3] + target_context = sys.argv[4] + + api_url = f"https://git.xiaoxiajianji.com/api/v1/repos/{repo}/commits/{sha}/statuses?per_page=100" + req = urllib.request.Request(api_url, headers={"Authorization": f"token {token}"}) + + try: + with urllib.request.urlopen(req, timeout=30) as resp: + statuses = json.loads(resp.read().decode()) + except Exception: + print("pending") + return + + # API返回按时间倒序,第一个就是最新的 + for s in statuses: + if s.get("context") == target_context: + print(s.get("status", "pending")) + return + + print("pending") + + +if __name__ == "__main__": + main() -- 2.54.0