fix bug
All checks were successful
Build and Deploy Log Center / build-and-deploy (push) Successful in 2m7s
All checks were successful
Build and Deploy Log Center / build-and-deploy (push) Successful in 2m7s
This commit is contained in:
parent
20a3b0b374
commit
61cbcfc4c4
@ -10,6 +10,11 @@ GIT_USER_NAME=repair-agent
|
|||||||
GIT_USER_EMAIL=agent@airlabs.art
|
GIT_USER_EMAIL=agent@airlabs.art
|
||||||
GITEA_TOKEN=your_token_here
|
GITEA_TOKEN=your_token_here
|
||||||
|
|
||||||
|
# GitHub 仓库地址(为空则不执行 git 操作)
|
||||||
|
GITHUB_REPO_RTC_BACKEND=
|
||||||
|
GITHUB_REPO_RTC_WEB=
|
||||||
|
GITHUB_REPO_AIRHUB_APP=
|
||||||
|
|
||||||
# 项目路径映射 (project_id -> 本地路径)
|
# 项目路径映射 (project_id -> 本地路径)
|
||||||
PATH_rtc_backend=/Users/maidong/Desktop/zyc/qy_gitlab/rtc_backend
|
PATH_rtc_backend=/Users/maidong/Desktop/zyc/qy_gitlab/rtc_backend
|
||||||
PATH_rtc_web=/Users/maidong/Desktop/zyc/qy_gitlab/rtc_web
|
PATH_rtc_web=/Users/maidong/Desktop/zyc/qy_gitlab/rtc_web
|
||||||
|
|||||||
@ -1,12 +1,15 @@
|
|||||||
"""
|
"""
|
||||||
Repair Agent CLI - 命令行入口
|
Repair Agent CLI - 命令行入口
|
||||||
"""
|
"""
|
||||||
|
from typing import List, Optional
|
||||||
|
|
||||||
import typer
|
import typer
|
||||||
from loguru import logger
|
from loguru import logger
|
||||||
from rich.console import Console
|
from rich.console import Console
|
||||||
from rich.table import Table
|
from rich.table import Table
|
||||||
|
|
||||||
from .agent import RepairEngine, TaskManager
|
from .agent import RepairEngine, TaskManager
|
||||||
|
from .agent.scheduler import RepairScheduler
|
||||||
from .config import settings
|
from .config import settings
|
||||||
|
|
||||||
app = typer.Typer(
|
app = typer.Typer(
|
||||||
@ -118,10 +121,14 @@ def status():
|
|||||||
console.print(f"最大修改文件数: [cyan]{settings.max_modified_files}[/cyan]")
|
console.print(f"最大修改文件数: [cyan]{settings.max_modified_files}[/cyan]")
|
||||||
console.print(f"核心文件关键词: [cyan]{settings.critical_files}[/cyan]")
|
console.print(f"核心文件关键词: [cyan]{settings.critical_files}[/cyan]")
|
||||||
|
|
||||||
console.print("\n[bold]项目路径映射:[/bold]")
|
console.print("\n[bold]项目配置:[/bold]")
|
||||||
console.print(f" rtc_backend: [blue]{settings.path_rtc_backend}[/blue]")
|
for pid in ["rtc_backend", "rtc_web", "airhub_app"]:
|
||||||
console.print(f" rtc_web: [blue]{settings.path_rtc_web}[/blue]")
|
path = settings.get_project_path(pid)
|
||||||
console.print(f" airhub_app: [blue]{settings.path_airhub_app}[/blue]")
|
repo = settings.get_github_repo(pid)
|
||||||
|
git_status = f"[green]{repo}[/green]" if repo else "[yellow]未配置(Git 已禁用)[/yellow]"
|
||||||
|
console.print(f" {pid}:")
|
||||||
|
console.print(f" 路径: [blue]{path}[/blue]")
|
||||||
|
console.print(f" GitHub: {git_status}")
|
||||||
|
|
||||||
|
|
||||||
@app.command()
|
@app.command()
|
||||||
@ -155,6 +162,47 @@ def analyze(
|
|||||||
engine.close()
|
engine.close()
|
||||||
|
|
||||||
|
|
||||||
|
@app.command()
|
||||||
|
def watch(
|
||||||
|
interval: int = typer.Option(
|
||||||
|
None, "--interval", "-i",
|
||||||
|
help="扫描间隔(秒),默认读取 WATCH_INTERVAL 环境变量或 3600",
|
||||||
|
),
|
||||||
|
project: Optional[List[str]] = typer.Option(
|
||||||
|
None, "--project", "-p",
|
||||||
|
help="监控的项目ID,可多次指定,默认监控所有已配置项目",
|
||||||
|
),
|
||||||
|
test: bool = typer.Option(True, "--test/--no-test", help="修复后是否运行测试"),
|
||||||
|
commit: bool = typer.Option(False, "--commit", "-c", help="是否自动提交代码"),
|
||||||
|
):
|
||||||
|
"""
|
||||||
|
启动定时扫描守护进程,每隔固定时间扫描新 Bug 并自动修复。
|
||||||
|
|
||||||
|
示例:
|
||||||
|
python -m repair_agent watch # 默认每小时扫描所有项目
|
||||||
|
python -m repair_agent watch -i 1800 # 每 30 分钟扫描
|
||||||
|
python -m repair_agent watch -p rtc_backend # 只监控 rtc_backend
|
||||||
|
python -m repair_agent watch -p rtc_backend -p rtc_web -c # 监控两个项目并自动提交
|
||||||
|
"""
|
||||||
|
scan_interval = interval or settings.watch_interval
|
||||||
|
projects = list(project) if project else None
|
||||||
|
|
||||||
|
console.print("[bold blue]启动 Repair Scheduler[/bold blue]")
|
||||||
|
console.print(f" 扫描间隔: [cyan]{scan_interval}s[/cyan]")
|
||||||
|
console.print(f" 监控项目: [cyan]{', '.join(projects) if projects else '全部已配置项目'}[/cyan]")
|
||||||
|
console.print(f" 运行测试: [cyan]{test}[/cyan]")
|
||||||
|
console.print(f" 自动提交: [cyan]{commit}[/cyan]")
|
||||||
|
console.print("\n[dim]按 Ctrl+C 停止[/dim]\n")
|
||||||
|
|
||||||
|
scheduler = RepairScheduler(
|
||||||
|
interval=scan_interval,
|
||||||
|
run_tests=test,
|
||||||
|
auto_commit=commit,
|
||||||
|
projects=projects,
|
||||||
|
)
|
||||||
|
scheduler.start()
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
"""入口函数"""
|
"""入口函数"""
|
||||||
app()
|
app()
|
||||||
|
|||||||
@ -3,6 +3,7 @@ from .task_manager import TaskManager
|
|||||||
from .git_manager import GitManager
|
from .git_manager import GitManager
|
||||||
from .claude_service import ClaudeService
|
from .claude_service import ClaudeService
|
||||||
from .test_runner import TestRunner
|
from .test_runner import TestRunner
|
||||||
|
from .scheduler import RepairScheduler
|
||||||
|
|
||||||
__all__ = [
|
__all__ = [
|
||||||
"RepairEngine",
|
"RepairEngine",
|
||||||
@ -10,4 +11,5 @@ __all__ = [
|
|||||||
"GitManager",
|
"GitManager",
|
||||||
"ClaudeService",
|
"ClaudeService",
|
||||||
"TestRunner",
|
"TestRunner",
|
||||||
|
"RepairScheduler",
|
||||||
]
|
]
|
||||||
|
|||||||
@ -64,17 +64,21 @@ class RepairEngine:
|
|||||||
)
|
)
|
||||||
|
|
||||||
logger.info(f"获取到 {len(bugs)} 个待修复 Bug")
|
logger.info(f"获取到 {len(bugs)} 个待修复 Bug")
|
||||||
|
|
||||||
# 初始化 Git 管理器
|
# 检查是否启用 Git
|
||||||
git_manager = GitManager(project_path)
|
git_enabled = settings.is_git_enabled(project_id)
|
||||||
|
git_manager = None
|
||||||
# 拉取最新代码
|
|
||||||
git_manager.pull()
|
if git_enabled:
|
||||||
|
github_repo = settings.get_github_repo(project_id)
|
||||||
# 创建修复分支
|
git_manager = GitManager(project_path, github_repo=github_repo)
|
||||||
branch_name = f"fix/auto-{datetime.now().strftime('%Y%m%d-%H%M%S')}"
|
git_manager.pull()
|
||||||
if auto_commit:
|
|
||||||
git_manager.create_branch(branch_name)
|
branch_name = f"fix/auto-{datetime.now().strftime('%Y%m%d-%H%M%S')}"
|
||||||
|
if auto_commit:
|
||||||
|
git_manager.create_branch(branch_name)
|
||||||
|
else:
|
||||||
|
logger.info(f"项目 {project_id} 未配置 GitHub 仓库,跳过 Git 操作")
|
||||||
|
|
||||||
# 更新所有 Bug 状态为 FIXING
|
# 更新所有 Bug 状态为 FIXING
|
||||||
for bug in bugs:
|
for bug in bugs:
|
||||||
@ -86,19 +90,22 @@ class RepairEngine:
|
|||||||
results = []
|
results = []
|
||||||
if success:
|
if success:
|
||||||
# 获取修改的文件
|
# 获取修改的文件
|
||||||
modified_files = git_manager.get_modified_files()
|
modified_files = []
|
||||||
diff = git_manager.get_diff()
|
diff = ""
|
||||||
|
if git_manager:
|
||||||
|
modified_files = git_manager.get_modified_files()
|
||||||
|
diff = git_manager.get_diff()
|
||||||
|
|
||||||
logger.info(f"修复完成,修改了 {len(modified_files)} 个文件")
|
logger.info(f"修复完成,修改了 {len(modified_files)} 个文件")
|
||||||
|
|
||||||
# 安全检查
|
# 安全检查(仅在 Git 启用时)
|
||||||
if not self._safety_check(modified_files, diff):
|
if git_manager and not self._safety_check(modified_files, diff):
|
||||||
logger.warning("安全检查未通过,回滚更改")
|
logger.warning("安全检查未通过,回滚更改")
|
||||||
git_manager.reset_hard()
|
git_manager.reset_hard()
|
||||||
|
|
||||||
for bug in bugs:
|
for bug in bugs:
|
||||||
self.task_manager.update_status(
|
self.task_manager.update_status(
|
||||||
bug.id,
|
bug.id,
|
||||||
BugStatus.FIX_FAILED,
|
BugStatus.FIX_FAILED,
|
||||||
"安全检查未通过"
|
"安全检查未通过"
|
||||||
)
|
)
|
||||||
@ -107,7 +114,7 @@ class RepairEngine:
|
|||||||
success=False,
|
success=False,
|
||||||
message="安全检查未通过",
|
message="安全检查未通过",
|
||||||
))
|
))
|
||||||
|
|
||||||
return BatchFixResult(
|
return BatchFixResult(
|
||||||
project_id=project_id,
|
project_id=project_id,
|
||||||
total=len(bugs),
|
total=len(bugs),
|
||||||
@ -115,17 +122,18 @@ class RepairEngine:
|
|||||||
failed_count=len(bugs),
|
failed_count=len(bugs),
|
||||||
results=results,
|
results=results,
|
||||||
)
|
)
|
||||||
|
|
||||||
# 运行测试
|
# 运行测试
|
||||||
test_result = None
|
test_result = None
|
||||||
if run_tests:
|
if run_tests:
|
||||||
test_runner = TestRunner(project_path, project_id)
|
test_runner = TestRunner(project_path, project_id)
|
||||||
test_result = test_runner.run_full_suite()
|
test_result = test_runner.run_full_suite()
|
||||||
|
|
||||||
if not test_result.success:
|
if not test_result.success:
|
||||||
logger.error("测试未通过,回滚更改")
|
logger.error("测试未通过,回滚更改")
|
||||||
git_manager.reset_hard()
|
if git_manager:
|
||||||
|
git_manager.reset_hard()
|
||||||
|
|
||||||
for bug in bugs:
|
for bug in bugs:
|
||||||
self.task_manager.update_status(
|
self.task_manager.update_status(
|
||||||
bug.id,
|
bug.id,
|
||||||
@ -137,7 +145,7 @@ class RepairEngine:
|
|||||||
success=False,
|
success=False,
|
||||||
message="测试未通过",
|
message="测试未通过",
|
||||||
))
|
))
|
||||||
|
|
||||||
return BatchFixResult(
|
return BatchFixResult(
|
||||||
project_id=project_id,
|
project_id=project_id,
|
||||||
total=len(bugs),
|
total=len(bugs),
|
||||||
@ -145,11 +153,11 @@ class RepairEngine:
|
|||||||
failed_count=len(bugs),
|
failed_count=len(bugs),
|
||||||
results=results,
|
results=results,
|
||||||
)
|
)
|
||||||
|
|
||||||
# 标记成功并上传报告
|
# 标记成功并上传报告
|
||||||
for bug in bugs:
|
for bug in bugs:
|
||||||
self.task_manager.update_status(bug.id, BugStatus.FIXED)
|
self.task_manager.update_status(bug.id, BugStatus.FIXED)
|
||||||
|
|
||||||
# 上传修复报告
|
# 上传修复报告
|
||||||
try:
|
try:
|
||||||
report = RepairReport(
|
report = RepairReport(
|
||||||
@ -174,12 +182,14 @@ class RepairEngine:
|
|||||||
modified_files=modified_files,
|
modified_files=modified_files,
|
||||||
diff=diff,
|
diff=diff,
|
||||||
))
|
))
|
||||||
|
|
||||||
# 自动提交
|
# 自动提交(仅在 Git 启用时)
|
||||||
if auto_commit and modified_files:
|
if git_enabled and auto_commit and modified_files and git_manager:
|
||||||
bug_ids = ", ".join([f"#{b.id}" for b in bugs])
|
bug_ids = ", ".join([f"#{b.id}" for b in bugs])
|
||||||
git_manager.commit(f"fix: auto repair bugs {bug_ids}")
|
git_manager.commit(f"fix: auto repair bugs {bug_ids}")
|
||||||
logger.info("代码已提交")
|
logger.info("代码已提交")
|
||||||
|
elif not git_enabled and auto_commit:
|
||||||
|
logger.info("未配置 GitHub 仓库,跳过自动提交")
|
||||||
else:
|
else:
|
||||||
logger.error(f"修复失败: {output}")
|
logger.error(f"修复失败: {output}")
|
||||||
for bug in bugs:
|
for bug in bugs:
|
||||||
|
|||||||
@ -11,12 +11,13 @@ from ..config import settings
|
|||||||
|
|
||||||
class GitManager:
|
class GitManager:
|
||||||
"""负责 Git 操作"""
|
"""负责 Git 操作"""
|
||||||
|
|
||||||
def __init__(self, project_path: str):
|
def __init__(self, project_path: str, github_repo: str = ""):
|
||||||
self.project_path = project_path
|
self.project_path = project_path
|
||||||
|
self.github_repo = github_repo.strip()
|
||||||
self.repo: Optional[Repo] = None
|
self.repo: Optional[Repo] = None
|
||||||
self._init_repo()
|
self._init_repo()
|
||||||
|
|
||||||
def _init_repo(self):
|
def _init_repo(self):
|
||||||
"""初始化 Git 仓库"""
|
"""初始化 Git 仓库"""
|
||||||
try:
|
try:
|
||||||
@ -25,10 +26,31 @@ class GitManager:
|
|||||||
with self.repo.config_writer() as config:
|
with self.repo.config_writer() as config:
|
||||||
config.set_value("user", "name", settings.git_user_name)
|
config.set_value("user", "name", settings.git_user_name)
|
||||||
config.set_value("user", "email", settings.git_user_email)
|
config.set_value("user", "email", settings.git_user_email)
|
||||||
|
|
||||||
|
# 如果指定了 GitHub 仓库地址,确保 origin 指向正确的仓库
|
||||||
|
if self.github_repo:
|
||||||
|
self._ensure_remote(self.github_repo)
|
||||||
|
|
||||||
logger.info(f"Git 仓库初始化成功: {self.project_path}")
|
logger.info(f"Git 仓库初始化成功: {self.project_path}")
|
||||||
except InvalidGitRepositoryError:
|
except InvalidGitRepositoryError:
|
||||||
logger.error(f"无效的 Git 仓库: {self.project_path}")
|
logger.error(f"无效的 Git 仓库: {self.project_path}")
|
||||||
self.repo = None
|
self.repo = None
|
||||||
|
|
||||||
|
def _ensure_remote(self, repo_url: str):
|
||||||
|
"""确保 origin remote 指向指定的仓库地址"""
|
||||||
|
if not self.repo:
|
||||||
|
return
|
||||||
|
try:
|
||||||
|
if "origin" in [r.name for r in self.repo.remotes]:
|
||||||
|
current_url = self.repo.remotes.origin.url
|
||||||
|
if current_url != repo_url:
|
||||||
|
self.repo.remotes.origin.set_url(repo_url)
|
||||||
|
logger.info(f"更新 origin 地址: {repo_url}")
|
||||||
|
else:
|
||||||
|
self.repo.create_remote("origin", repo_url)
|
||||||
|
logger.info(f"添加 origin 地址: {repo_url}")
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"配置 remote 失败: {e}")
|
||||||
|
|
||||||
def pull(self) -> bool:
|
def pull(self) -> bool:
|
||||||
"""拉取最新代码"""
|
"""拉取最新代码"""
|
||||||
|
|||||||
201
repair_agent/agent/scheduler.py
Normal file
201
repair_agent/agent/scheduler.py
Normal file
@ -0,0 +1,201 @@
|
|||||||
|
"""
|
||||||
|
Scheduler - 定时扫描并自动修复 Bug
|
||||||
|
"""
|
||||||
|
import signal
|
||||||
|
import threading
|
||||||
|
import time
|
||||||
|
from datetime import datetime
|
||||||
|
from typing import Optional
|
||||||
|
|
||||||
|
from loguru import logger
|
||||||
|
|
||||||
|
from ..config import settings
|
||||||
|
from .core import RepairEngine
|
||||||
|
from .task_manager import TaskManager
|
||||||
|
|
||||||
|
|
||||||
|
class RepairScheduler:
|
||||||
|
"""定时扫描 Bug 并自动触发修复"""
|
||||||
|
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
interval: int = 3600,
|
||||||
|
run_tests: bool = True,
|
||||||
|
auto_commit: bool = False,
|
||||||
|
projects: Optional[list[str]] = None,
|
||||||
|
):
|
||||||
|
"""
|
||||||
|
Args:
|
||||||
|
interval: 扫描间隔(秒),默认 3600(1 小时)
|
||||||
|
run_tests: 修复后是否运行测试
|
||||||
|
auto_commit: 是否自动提交代码
|
||||||
|
projects: 要监控的项目列表,None 表示所有已配置项目
|
||||||
|
"""
|
||||||
|
self.interval = interval
|
||||||
|
self.run_tests = run_tests
|
||||||
|
self.auto_commit = auto_commit
|
||||||
|
self.projects = projects or self._get_configured_projects()
|
||||||
|
self._stop_event = threading.Event()
|
||||||
|
self._repairing = False
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _get_configured_projects() -> list[str]:
|
||||||
|
"""获取所有有路径配置的项目"""
|
||||||
|
candidates = ["rtc_backend", "rtc_web", "airhub_app"]
|
||||||
|
return [p for p in candidates if settings.get_project_path(p)]
|
||||||
|
|
||||||
|
def start(self):
|
||||||
|
"""启动定时任务(阻塞式,Ctrl+C 退出)"""
|
||||||
|
signal.signal(signal.SIGINT, self._handle_signal)
|
||||||
|
signal.signal(signal.SIGTERM, self._handle_signal)
|
||||||
|
|
||||||
|
logger.info("=" * 60)
|
||||||
|
logger.info("Repair Scheduler 启动")
|
||||||
|
logger.info(f" 扫描间隔: {self.interval}s ({self.interval // 60} min)")
|
||||||
|
logger.info(f" 监控项目: {', '.join(self.projects)}")
|
||||||
|
logger.info(f" 运行测试: {self.run_tests}")
|
||||||
|
logger.info(f" 自动提交: {self.auto_commit}")
|
||||||
|
logger.info("=" * 60)
|
||||||
|
|
||||||
|
# 启动后立即执行一次
|
||||||
|
self._tick()
|
||||||
|
|
||||||
|
while not self._stop_event.is_set():
|
||||||
|
self._stop_event.wait(self.interval)
|
||||||
|
if not self._stop_event.is_set():
|
||||||
|
self._tick()
|
||||||
|
|
||||||
|
logger.info("Repair Scheduler 已停止")
|
||||||
|
|
||||||
|
def stop(self):
|
||||||
|
"""停止定时任务"""
|
||||||
|
logger.info("正在停止 Scheduler...")
|
||||||
|
self._stop_event.set()
|
||||||
|
|
||||||
|
def _handle_signal(self, signum, frame):
|
||||||
|
logger.info(f"收到信号 {signum},准备停止")
|
||||||
|
self.stop()
|
||||||
|
|
||||||
|
def _tick(self):
|
||||||
|
"""执行一次扫描和修复"""
|
||||||
|
ts = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
||||||
|
logger.info(f"[{ts}] 开始扫描...")
|
||||||
|
|
||||||
|
task_manager = TaskManager()
|
||||||
|
total_found = 0
|
||||||
|
total_fixed = 0
|
||||||
|
|
||||||
|
try:
|
||||||
|
for project_id in self.projects:
|
||||||
|
bugs = task_manager.fetch_pending_bugs(project_id)
|
||||||
|
# 也拉取 UI 触发的 PENDING_FIX 状态
|
||||||
|
pending_bugs = self._fetch_pending_fix_bugs(task_manager, project_id)
|
||||||
|
all_bugs = bugs + pending_bugs
|
||||||
|
|
||||||
|
if not all_bugs:
|
||||||
|
logger.info(f" [{project_id}] 无新 Bug")
|
||||||
|
continue
|
||||||
|
|
||||||
|
count = len(all_bugs)
|
||||||
|
total_found += count
|
||||||
|
logger.info(f" [{project_id}] 发现 {count} 个待修复 Bug")
|
||||||
|
|
||||||
|
# 检查项目路径是否存在
|
||||||
|
project_path = settings.get_project_path(project_id)
|
||||||
|
if not project_path:
|
||||||
|
logger.warning(f" [{project_id}] 未配置项目路径,跳过")
|
||||||
|
continue
|
||||||
|
|
||||||
|
# 执行修复
|
||||||
|
fixed = self._run_repair(project_id)
|
||||||
|
total_fixed += fixed
|
||||||
|
|
||||||
|
logger.info(
|
||||||
|
f"[{ts}] 扫描完成: "
|
||||||
|
f"发现 {total_found} 个 Bug, 成功修复 {total_fixed} 个"
|
||||||
|
)
|
||||||
|
logger.info(f"下次扫描: {self.interval}s 后")
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"扫描过程出错: {e}")
|
||||||
|
finally:
|
||||||
|
task_manager.close()
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _fetch_pending_fix_bugs(
|
||||||
|
task_manager: TaskManager, project_id: str
|
||||||
|
) -> list:
|
||||||
|
"""获取 PENDING_FIX 状态的 Bug(由 UI 触发)"""
|
||||||
|
try:
|
||||||
|
import httpx
|
||||||
|
|
||||||
|
params = {"status": "PENDING_FIX", "project_id": project_id}
|
||||||
|
response = task_manager.client.get(
|
||||||
|
f"{task_manager.base_url}/api/v1/bugs", params=params
|
||||||
|
)
|
||||||
|
response.raise_for_status()
|
||||||
|
data = response.json()
|
||||||
|
|
||||||
|
from ..models import Bug, BugStatus
|
||||||
|
|
||||||
|
bugs = []
|
||||||
|
for item in data.get("items", []):
|
||||||
|
stack_trace = item.get("stack_trace")
|
||||||
|
if isinstance(stack_trace, str):
|
||||||
|
stack_trace = stack_trace.split("\n")
|
||||||
|
|
||||||
|
bug = Bug(
|
||||||
|
id=item["id"],
|
||||||
|
project_id=item["project_id"],
|
||||||
|
environment=item.get("environment", "production"),
|
||||||
|
level=item.get("level", "ERROR"),
|
||||||
|
error={
|
||||||
|
"type": item.get("error_type", "Unknown"),
|
||||||
|
"message": item.get("error_message", ""),
|
||||||
|
"file_path": item.get("file_path"),
|
||||||
|
"line_number": item.get("line_number"),
|
||||||
|
"stack_trace": stack_trace,
|
||||||
|
},
|
||||||
|
context=item.get("context"),
|
||||||
|
status=BugStatus(item.get("status", "PENDING_FIX")),
|
||||||
|
retry_count=item.get("retry_count", 0),
|
||||||
|
)
|
||||||
|
bugs.append(bug)
|
||||||
|
return bugs
|
||||||
|
except Exception as e:
|
||||||
|
logger.debug(f"获取 PENDING_FIX Bug 失败: {e}")
|
||||||
|
return []
|
||||||
|
|
||||||
|
def _run_repair(self, project_id: str) -> int:
|
||||||
|
"""对指定项目执行修复,返回成功修复的数量"""
|
||||||
|
if self._repairing:
|
||||||
|
logger.warning(f" [{project_id}] 上一次修复尚未完成,跳过")
|
||||||
|
return 0
|
||||||
|
|
||||||
|
self._repairing = True
|
||||||
|
try:
|
||||||
|
engine = RepairEngine()
|
||||||
|
result = engine.fix_project(
|
||||||
|
project_id=project_id,
|
||||||
|
run_tests=self.run_tests,
|
||||||
|
auto_commit=self.auto_commit,
|
||||||
|
)
|
||||||
|
engine.close()
|
||||||
|
|
||||||
|
if result.total == 0:
|
||||||
|
return 0
|
||||||
|
|
||||||
|
logger.info(
|
||||||
|
f" [{project_id}] 修复结果: "
|
||||||
|
f"{result.success_count}/{result.total} 成功"
|
||||||
|
)
|
||||||
|
for r in result.results:
|
||||||
|
icon = "✓" if r.success else "✗"
|
||||||
|
logger.info(f" {icon} Bug #{r.bug_id}: {r.message}")
|
||||||
|
|
||||||
|
return result.success_count
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f" [{project_id}] 修复过程出错: {e}")
|
||||||
|
return 0
|
||||||
|
finally:
|
||||||
|
self._repairing = False
|
||||||
@ -24,7 +24,15 @@ class Settings(BaseSettings):
|
|||||||
git_user_name: str = Field(default="repair-agent", description="Git 用户名")
|
git_user_name: str = Field(default="repair-agent", description="Git 用户名")
|
||||||
git_user_email: str = Field(default="agent@airlabs.art", description="Git 邮箱")
|
git_user_email: str = Field(default="agent@airlabs.art", description="Git 邮箱")
|
||||||
gitea_token: Optional[str] = Field(default=None, description="Gitea Token")
|
gitea_token: Optional[str] = Field(default=None, description="Gitea Token")
|
||||||
|
|
||||||
|
# GitHub 仓库地址(为空则不执行 git 操作)
|
||||||
|
github_repo_rtc_backend: str = Field(default="", description="rtc_backend GitHub 仓库地址")
|
||||||
|
github_repo_rtc_web: str = Field(default="", description="rtc_web GitHub 仓库地址")
|
||||||
|
github_repo_airhub_app: str = Field(default="", description="airhub_app GitHub 仓库地址")
|
||||||
|
|
||||||
|
# 定时任务
|
||||||
|
watch_interval: int = Field(default=3600, description="定时扫描间隔(秒)")
|
||||||
|
|
||||||
# 安全配置
|
# 安全配置
|
||||||
max_retry_count: int = Field(default=3, description="最大重试次数")
|
max_retry_count: int = Field(default=3, description="最大重试次数")
|
||||||
max_modified_lines: int = Field(default=500, description="最大修改行数")
|
max_modified_lines: int = Field(default=500, description="最大修改行数")
|
||||||
@ -62,6 +70,19 @@ class Settings(BaseSettings):
|
|||||||
}
|
}
|
||||||
return path_map.get(project_id)
|
return path_map.get(project_id)
|
||||||
|
|
||||||
|
def get_github_repo(self, project_id: str) -> str:
|
||||||
|
"""获取项目的 GitHub 仓库地址,为空表示未配置"""
|
||||||
|
repo_map = {
|
||||||
|
"rtc_backend": self.github_repo_rtc_backend,
|
||||||
|
"rtc_web": self.github_repo_rtc_web,
|
||||||
|
"airhub_app": self.github_repo_airhub_app,
|
||||||
|
}
|
||||||
|
return repo_map.get(project_id, "").strip()
|
||||||
|
|
||||||
|
def is_git_enabled(self, project_id: str) -> bool:
|
||||||
|
"""检查项目是否启用了 Git 操作(GitHub 仓库地址不为空)"""
|
||||||
|
return bool(self.get_github_repo(project_id))
|
||||||
|
|
||||||
def get_critical_files(self) -> list[str]:
|
def get_critical_files(self) -> list[str]:
|
||||||
"""获取核心文件关键词列表"""
|
"""获取核心文件关键词列表"""
|
||||||
return [f.strip() for f in self.critical_files.split(",") if f.strip()]
|
return [f.strip() for f in self.critical_files.split(",") if f.strip()]
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user