fix(ci): 修复auto-merge checkout缓存损坏 + 清理老合并脚本 #484
@@ -12,7 +12,10 @@ jobs:
|
||||
timeout-minutes: 20
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v3
|
||||
shell: sh
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ github.token }}
|
||||
run: "set -eu\npython3 - <<'PY'\nimport io, os, tarfile, time, urllib.request, urllib.error\nurl = f\"{os.environ['GITHUB_API_URL']}/repos/{os.environ['GITHUB_REPOSITORY']}/archive/{os.environ['GITHUB_SHA']}.tar.gz\"\nrequest = urllib.request.Request(url, headers={\"Authorization\": f\"token {os.environ['GITHUB_TOKEN']}\"})\nlast_err = None\nfor attempt in range(5):\n try:\n with urllib.request.urlopen(request, timeout=120) as response:\n archive = response.read()\n break\n except urllib.error.HTTPError as e:\n last_err = e\n if e.code >= 500 and attempt < 4:\n wait = 2 ** attempt\n print(f\"Checkout HTTP {e.code}, retrying in {wait}s (attempt {attempt+1}/5)...\")\n time.sleep(wait)\n continue\n raise\n except Exception as e:\n last_err = e\n if attempt < 4:\n wait = 2 ** attempt\n print(f\"Checkout error: {e}, retrying in {wait}s (attempt {attempt+1}/5)...\")\n time.sleep(wait)\n continue\n raise\nelse:\n raise last_err\nwith tarfile.open(fileobj=io.BytesIO(archive), mode='r:gz') as tar:\n root_prefix = tar.getmembers()[0].name.split('/', 1)[0] + '/'\n for member in tar.getmembers():\n name = member.name\n if name == root_prefix[:-1]:\n continue\n if name.startswith(root_prefix):\n member.name = name[len(root_prefix):]\n if member.name:\n tar.extract(member, '.')\nPY\n"
|
||||
|
||||
- name: Auto approve when CI passes
|
||||
shell: bash
|
||||
|
||||
@@ -12,7 +12,10 @@ jobs:
|
||||
timeout-minutes: 30
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v3
|
||||
shell: sh
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ github.token }}
|
||||
run: "set -eu\npython3 - <<'PY'\nimport io, os, tarfile, time, urllib.request, urllib.error\nurl = f\"{os.environ['GITHUB_API_URL']}/repos/{os.environ['GITHUB_REPOSITORY']}/archive/{os.environ['GITHUB_SHA']}.tar.gz\"\nrequest = urllib.request.Request(url, headers={\"Authorization\": f\"token {os.environ['GITHUB_TOKEN']}\"})\nlast_err = None\nfor attempt in range(5):\n try:\n with urllib.request.urlopen(request, timeout=120) as response:\n archive = response.read()\n break\n except urllib.error.HTTPError as e:\n last_err = e\n if e.code >= 500 and attempt < 4:\n wait = 2 ** attempt\n print(f\"Checkout HTTP {e.code}, retrying in {wait}s (attempt {attempt+1}/5)...\")\n time.sleep(wait)\n continue\n raise\n except Exception as e:\n last_err = e\n if attempt < 4:\n wait = 2 ** attempt\n print(f\"Checkout error: {e}, retrying in {wait}s (attempt {attempt+1}/5)...\")\n time.sleep(wait)\n continue\n raise\nelse:\n raise last_err\nwith tarfile.open(fileobj=io.BytesIO(archive), mode='r:gz') as tar:\n root_prefix = tar.getmembers()[0].name.split('/', 1)[0] + '/'\n for member in tar.getmembers():\n name = member.name\n if name == root_prefix[:-1]:\n continue\n if name.startswith(root_prefix):\n member.name = name[len(root_prefix):]\n if member.name:\n tar.extract(member, '.')\nPY\n"
|
||||
|
||||
- name: Auto merge when CI passes and approved
|
||||
shell: bash
|
||||
|
||||
@@ -1,108 +0,0 @@
|
||||
#!/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="${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&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':
|
||||
head_sha = pr.get('head', {}).get('sha', '')
|
||||
print(f\"{pr['number']}|{pr['title']}|{head_sha}\")
|
||||
")
|
||||
|
||||
if [ -z "$PRS" ]; then
|
||||
echo "No open PRs found for $TARGET_BRANCH"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
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 '{"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 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"
|
||||
Reference in New Issue
Block a user