Files
AI-Check-Test/.gitea/checker/git_utils.py
dongzi 556f5b8ab6
Some checks failed
API Parameter Change Check / api-param-check (push) Failing after 3s
test
2026-06-03 15:02:21 +08:00

141 lines
4.2 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""
Git 操作工具模块。
负责在 CI 环境中检出上一版本代码、获取变更文件列表及提交元信息。
"""
import os
import subprocess
from dataclasses import dataclass
from pathlib import Path
from typing import List, Optional
@dataclass
class CommitInfo:
"""单次 Git 提交的元信息。"""
sha: str
author: str
commit_time: str
message: str
def run_git(args: List[str], cwd: Optional[Path] = None) -> str:
"""
执行 git 命令并返回标准输出。
:param args: git 子命令及参数,如 ["log", "-1", "--format=%H"]
:param cwd: 工作目录,默认为当前目录
:return: 命令 stdout 文本(已 strip
:raises RuntimeError: git 命令执行失败时抛出
"""
cmd = ["git"] + args
result = subprocess.run(
cmd,
cwd=cwd,
capture_output=True,
text=True,
encoding="utf-8",
errors="replace",
)
if result.returncode != 0:
raise RuntimeError(f"Git 命令失败: {' '.join(cmd)}\n{result.stderr}")
return result.stdout.strip()
def get_current_commit() -> CommitInfo:
"""
获取当前 HEAD 提交的元信息(推送人、时间等,用于通知模板)。
:return: CommitInfo 对象
"""
sha = run_git(["rev-parse", "HEAD"])
author = run_git(["log", "-1", "--format=%an"])
commit_time = run_git(["log", "-1", "--format=%ci"])
message = run_git(["log", "-1", "--format=%s"])
return CommitInfo(sha=sha, author=author, commit_time=commit_time, message=message)
def get_previous_commit_sha() -> Optional[str]:
"""
获取上一次提交的 SHAHEAD~1
若是首次提交则返回 None。
:return: 上一 commit SHA或 None
"""
try:
return run_git(["rev-parse", "HEAD~1"])
except RuntimeError:
return None
def checkout_commit(sha: str, worktree_dir: Path) -> None:
"""
将指定 commit 的代码检出到独立工作目录(不影响当前工作区)。
:param sha: 目标 commit SHA
:param worktree_dir: git worktree 目录
"""
worktree_dir.parent.mkdir(parents=True, exist_ok=True)
if worktree_dir.exists():
# 已存在则先移除旧 worktree
run_git(["worktree", "remove", "--force", str(worktree_dir)])
run_git(["worktree", "add", str(worktree_dir), sha])
def get_changed_java_controller_files(base_sha: str, head_sha: str) -> List[str]:
"""
获取两次提交之间变更的 Controller 相关 Java 文件路径。
:param base_sha: 基准 commit旧版本
:param head_sha: 目标 commit新版本
:return: 相对路径列表,如 ["src/main/java/.../UserController.java"]
"""
diff_output = run_git(["diff", "--name-only", base_sha, head_sha])
if not diff_output:
return []
changed = []
for line in diff_output.splitlines():
line = line.strip()
if line.endswith(".java") and "Controller" in line:
changed.append(line.replace("\\", "/"))
return changed
def get_controller_files_diff(base_sha: str, head_sha: str, changed_files: List[str]) -> str:
"""
获取变更 Controller 文件的 Git diff供 LLM 审核接口参数变更时参考。
:param base_sha: 旧版本 commit SHA
:param head_sha: 新版本 commit SHA
:param changed_files: 变更文件相对路径列表
:return: diff 文本
"""
if not changed_files:
return ""
try:
return run_git(["diff", base_sha, head_sha, "--"] + changed_files)
except RuntimeError as exc:
print(f"[警告] 获取 Git diff 失败: {exc}")
return ""
def prepare_worktrees(repo_root: Path) -> tuple:
"""
准备新旧两个版本的代码工作目录,供 AST 解析器分别扫描。
:param repo_root: 仓库根目录
:return: (新版本目录, 旧版本目录, 旧版本SHA);首次提交时旧版本目录为 None
"""
prev_sha = get_previous_commit_sha()
current_dir = repo_root
if prev_sha is None:
return current_dir, None, None
prev_dir = repo_root / ".gitea" / ".cache" / "prev-worktree"
checkout_commit(prev_sha, prev_dir)
return current_dir, prev_dir, prev_sha