diff --git a/scripts/ci/auto_fix_formatting.py b/scripts/ci/auto_fix_formatting.py index b6470c642..8575e918c 100755 --- a/scripts/ci/auto_fix_formatting.py +++ b/scripts/ci/auto_fix_formatting.py @@ -25,51 +25,47 @@ def run(cmd, check=True, capture=True, cwd=None): def ensure_git_repo(api_url, repo, token, pr_number): - """确保当前目录是git仓库,并切换到PR源分支。 + """确保当前目录是git仓库,保持在 merge commit 状态。 checkout脚本用tarball方式下载代码(PR merge后的commit),没有.git目录。 - 这里自动初始化git仓库,fetch PR源分支并强制checkout, - 使工作区变为PR源分支的代码,确保后续格式化修复基于源分支。 + 这里自动初始化git仓库,但不切换分支,保持在merge commit状态, + 使格式化检查与 Frontend Lint 的范围完全一致。 """ if os.path.exists(".git"): return print("检测到tarball checkout(无.git目录),自动初始化git仓库...") + print("保持在 merge commit 状态(与 Frontend Lint 一致)") # 构造带认证的远端URL server_url = api_url.rsplit("/api/v1", 1)[0] remote_url = f"{server_url.replace('https://', f'https://x-access-token:{token}@')}/{repo}.git" - # 获取PR的源分支 + # 获取PR的源分支(仅用于后续推送,不checkout) pr_api_url = f"{api_url}/repos/{repo}/pulls/{pr_number}" req_obj = urllib.request.Request(pr_api_url, headers={"Authorization": f"token {token}"}) with urllib.request.urlopen(req_obj) as resp: pr = json.loads(resp.read()) head_branch = pr["head"]["ref"] - print(f"PR源分支: {head_branch}") + print(f"PR源分支: {head_branch}(仅用于推送,不切换)") - # 初始化git + # 初始化git(不fetch/checkout,保持tarball内容即merge commit状态) run("git init -q") run(f"git remote add origin {remote_url}") run('git config user.name "CI Bot"') run('git config user.email "ci-bot@xiaoxiajianji.com"') - # fetch源分支(浅克隆,只要最新commit) - print("fetch源分支...") - run(f"git fetch --depth=1 origin {head_branch}") - - # 强制checkout到源分支(覆盖tarball内容) - # tarball是merge后的commit,源分支才是我们要修改并推送的目标 - print("切换到源分支...") - run(f"git checkout -f -B {head_branch} FETCH_HEAD") + # 将当前目录(tarball解压的merge commit)作为初始commit + run("git add -A") + run('git commit -q -m "CI merge commit snapshot"') result = run("git status --porcelain") if result.stdout.strip(): n = len(result.stdout.strip().splitlines()) - print(f"⚠️ 工作区有 {n} 个未追踪文件") + print(f"⚠️ 工作区有 {n} 个未追踪文件") else: - print("✅ git仓库就绪,工作区clean") + print("✅ git仓库就绪(merge commit 状态)") return head_branch @@ -236,24 +232,39 @@ def main(): for line in result.stdout.strip().split("\n"): print(f" {line}") - # 提交修复 + # 提交修复(在 merge commit 状态) run("git add -A") - run('git commit -m "style: auto-format with black + isort + prettier [ci skip]"') + run('git commit -m "style: auto-format with black + isort + prettier"') - # 推送(head_branch已从ensure_git_repo获取) + # 记录格式修复的 commit hash + format_commit = run("git rev-parse HEAD").stdout.strip() + + # 推送:将格式化变更推回 PR 源分支 print(f"\nPR来源分支: {head_branch}") + + # 切换到源分支,cherry-pick 格式化修复 + print(f"fetch 源分支 {head_branch}...") + run(f"git fetch --depth=1 origin {head_branch}") + run(f"git checkout -f -B {head_branch} FETCH_HEAD") + + print(f"cherry-pick 格式化修复到源分支...") + cherry_result = run(f"git cherry-pick {format_commit}", check=False) + if cherry_result.returncode != 0: + print(f"cherry-pick 冲突(源分支可能已有不同格式),中止cherry-pick") + run("git cherry-pick --abort", check=False) + print("没有需要推送的格式化改动") + return + + # 推送(保留原有的重试逻辑) print("推送格式修复到远端...") - # 推送前先 rebase 拉取远端最新,避免快进冲突 - # 最多重试 3 次:rebase → push,失败则重新拉取再试 max_retries = 3 push_success = False last_error = "" for attempt in range(1, max_retries + 1): - print(f" 尝试 {attempt}/{max_retries}: 拉取最新代码并推送...") + print(f" 尝试 {attempt}/{max_retries}: 推送...") - # 先拉取远端最新 commit 并 rebase fetch_result = run(f"git fetch origin {head_branch}", check=False) if fetch_result.returncode != 0: last_error = f"git fetch 失败: {fetch_result.stderr.strip()}" @@ -266,10 +277,8 @@ def main(): last_error = f"git rebase 失败,中止并重置: {rebase_result.stderr.strip()[:200]}" print(f" {last_error}") run("git rebase --abort", check=False) - # rebase 失败通常是冲突,重试没用,直接跳出 break - # 推送 push_result = run(f'git push origin "HEAD:{head_branch}"', check=False) if push_result.returncode == 0: push_success = True