From 1babf11c4f55196ebbe1df84f22b1f7dcf4f7573 Mon Sep 17 00:00:00 2001 From: xiaoxia Date: Mon, 13 Jul 2026 14:54:47 +0800 Subject: [PATCH] fix(auto-merge): add CI status validation before merging PRs --- scripts/auto_merge_prs.sh | 87 ++++++++++++++++++++++++++++++++++----- 1 file changed, 76 insertions(+), 11 deletions(-) diff --git a/scripts/auto_merge_prs.sh b/scripts/auto_merge_prs.sh index 720b865a7..91253a200 100755 --- a/scripts/auto_merge_prs.sh +++ b/scripts/auto_merge_prs.sh @@ -1,43 +1,108 @@ #!/bin/bash # 自动合并通过 CI 检查的 PR # 用法: ./scripts/auto_merge_prs.sh [target_branch] +# +# 合并前必须验证的 CI 检查项: +# - CI/CD Pipeline / Validate Code Quality And Tests (push) +# - CI/CD Pipeline / Frontend Lint (push) +# 只有两个检查项均为 success 状态才允许合并 -GITEA_API="https://git.xiaoxiajianji.com/api/v1" +GITEA_API="${GITEA_API_URL:-https://git.xiaoxiajianji.com/api/v1}" TOKEN="${GITEA_API_TOKEN:?Please set GITEA_API_TOKEN environment variable}" REPO="xiaoxia/xiaoxia-saas" TARGET_BRANCH="${1:-develop}" +# 必需的 CI 检查项(context 名称前缀匹配,避免 pipeline 名称变化导致匹配失败) +REQUIRED_CHECKS=( + "Validate Code Quality And Tests" + "Frontend Lint" +) + echo "=== Checking open PRs targeting $TARGET_BRANCH ===" # 获取所有 open PR PRS=$(curl -s -H "Authorization: token $TOKEN" \ - "$GITEA_API/repos/$REPO/pulls?state=open&labels=0" | python3 -c " + "$GITEA_API/repos/$REPO/pulls?state=open&sort=updated&direction=desc" | python3 -c " import json, sys data = json.load(sys.stdin) for pr in data: if pr.get('base', {}).get('ref') == '$TARGET_BRANCH': - if pr.get('mergeable', False): - print(f\"{pr['number']}|{pr['title']}|{pr.get('mergeable', 'unknown')}\") + head_sha = pr.get('head', {}).get('sha', '') + print(f\"{pr['number']}|{pr['title']}|{head_sha}\") ") if [ -z "$PRS" ]; then - echo "No mergeable PRs found for $TARGET_BRANCH" + echo "No open PRs found for $TARGET_BRANCH" exit 0 fi -echo "$PRS" | while IFS='|' read -r number title mergeable; do - echo "Merging PR #$number: $title" +merge_count=0 +skip_count=0 + +echo "$PRS" | while IFS='|' read -r number title head_sha; do + echo "" + echo "--- PR #$number: $title ---" + echo " Head SHA: $head_sha" + + # 获取该 commit 的 combined CI 状态 + STATUS_JSON=$(curl -s -H "Authorization: token $TOKEN" \ + "$GITEA_API/repos/$REPO/commits/$head_sha/status") + + # 检查每个必需的 CI 项是否通过 + all_passed=true + failed_checks="" + + for check_pattern in "${REQUIRED_CHECKS[@]}"; do + state=$(echo "$STATUS_JSON" | python3 -c " +import json, sys +d = json.load(sys.stdin) +pattern = '$check_pattern' +# 在 statuses 中找到匹配的最新状态 +target = None +for s in d.get('statuses', []): + if pattern in s.get('context', ''): + target = s + break # status 接口返回的是每个 context 的最新状态,取第一个匹配即可 +if target: + print(target.get('state', 'unknown')) +else: + print('not_found') +") + + if [ "$state" = "success" ]; then + echo " ✅ $check_pattern: $state" + else + echo " ❌ $check_pattern: $state" + all_passed=false + failed_checks="$failed_checks $check_pattern($state)" + fi + done + + if [ "$all_passed" != "true" ]; then + echo " ⏭️ Skipping - CI not passed:$failed_checks" + skip_count=$((skip_count + 1)) + continue + fi + + # CI 全部通过,执行合并 + echo " 🚀 All CI checks passed, merging..." RESULT=$(curl -s -X POST \ -H "Authorization: token $TOKEN" \ -H "Content-Type: application/json" \ "$GITEA_API/repos/$REPO/pulls/$number/merge" \ - -d '{\"merge_method\": \"merge\"}') - - if echo "$RESULT" | python3 -c "import json,sys; d=json.load(sys.stdin); sys.exit(0 if 'id' in d else 1)"; then + -d '{"Do": "merge"}') + + if echo "$RESULT" | python3 -c "import json,sys; d=json.load(sys.stdin); sys.exit(0 if d.get('merged', False) or 'id' in d else 1)" 2>/dev/null; then echo " ✅ PR #$number merged successfully" + merge_count=$((merge_count + 1)) else - echo " ❌ PR #$number failed: $RESULT" + echo " ❌ PR #$number merge failed" + # 提取错误信息 + err_msg=$(echo "$RESULT" | python3 -c "import json,sys; d=json.load(sys.stdin); print(d.get('message', str(d)[:200]))" 2>/dev/null) + echo " Error: $err_msg" fi done +echo "" echo "=== Done ===" +echo "Merged: $merge_count | Skipped: $skip_count" -- 2.54.0