All checks were successful
Build and Deploy Log Center / build-and-deploy (push) Successful in 1m27s
154 lines
4.9 KiB
Python
154 lines
4.9 KiB
Python
"""
|
||
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)
|
||
|
||
if not bugs:
|
||
logger.info(f" [{project_id}] 无待修复 Bug")
|
||
continue
|
||
|
||
count = len(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()
|
||
|
||
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
|