fix bug
All checks were successful
Build and Deploy Log Center / build-and-deploy (push) Successful in 1m27s
All checks were successful
Build and Deploy Log Center / build-and-deploy (push) Successful in 1m27s
This commit is contained in:
parent
7ee724f8ac
commit
fe62f9ca81
@ -16,8 +16,9 @@ GITHUB_REPO_RTC_WEB=
|
|||||||
GITHUB_REPO_AIRHUB_APP=
|
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
|
||||||
|
PATH_AIRHUB_APP=/Users/maidong/Desktop/zyc/qiyuan_gitea/rtc_prd/airhub_app
|
||||||
|
|
||||||
# 安全配置
|
# 安全配置
|
||||||
MAX_RETRY_COUNT=3
|
MAX_RETRY_COUNT=3
|
||||||
|
|||||||
@ -20,16 +20,17 @@ class ClaudeService:
|
|||||||
self,
|
self,
|
||||||
prompt: str,
|
prompt: str,
|
||||||
cwd: str,
|
cwd: str,
|
||||||
tools: str = "Edit,Read,Write",
|
allowed_tools: Optional[str] = None,
|
||||||
) -> tuple[bool, str]:
|
) -> tuple[bool, str]:
|
||||||
"""
|
"""
|
||||||
执行 Claude CLI 命令
|
执行 Claude CLI 命令
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
prompt: 提示词
|
prompt: 提示词
|
||||||
cwd: 工作目录
|
cwd: 工作目录
|
||||||
tools: 可用工具
|
allowed_tools: 可选,限制可用工具(逗号分隔)。
|
||||||
|
为 None 时配合 --dangerously-skip-permissions 允许所有工具。
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
(成功与否, 输出内容)
|
(成功与否, 输出内容)
|
||||||
"""
|
"""
|
||||||
@ -37,12 +38,15 @@ class ClaudeService:
|
|||||||
cmd = [
|
cmd = [
|
||||||
self.cli_path,
|
self.cli_path,
|
||||||
"-p", prompt,
|
"-p", prompt,
|
||||||
"--tools", tools,
|
|
||||||
"--dangerously-skip-permissions",
|
"--dangerously-skip-permissions",
|
||||||
]
|
]
|
||||||
|
|
||||||
logger.debug(f"执行 Claude CLI: {' '.join(cmd[:3])}...")
|
if allowed_tools:
|
||||||
|
cmd.extend(["--allowedTools", allowed_tools])
|
||||||
|
|
||||||
|
logger.debug(f"执行 Claude CLI (cwd={cwd})")
|
||||||
|
logger.debug(f"Prompt 长度: {len(prompt)} 字符")
|
||||||
|
|
||||||
result = subprocess.run(
|
result = subprocess.run(
|
||||||
cmd,
|
cmd,
|
||||||
cwd=cwd,
|
cwd=cwd,
|
||||||
@ -50,16 +54,17 @@ class ClaudeService:
|
|||||||
text=True,
|
text=True,
|
||||||
timeout=self.timeout,
|
timeout=self.timeout,
|
||||||
)
|
)
|
||||||
|
|
||||||
output = result.stdout + result.stderr
|
output = result.stdout + result.stderr
|
||||||
|
|
||||||
if result.returncode == 0:
|
if result.returncode == 0:
|
||||||
logger.info("Claude CLI 执行成功")
|
logger.info("Claude CLI 执行成功")
|
||||||
|
logger.debug(f"输出前 500 字符: {output[:500]}")
|
||||||
return True, output.strip()
|
return True, output.strip()
|
||||||
else:
|
else:
|
||||||
logger.error(f"Claude CLI 执行失败: {output}")
|
logger.error(f"Claude CLI 执行失败 (code={result.returncode}): {output[:500]}")
|
||||||
return False, output.strip()
|
return False, output.strip()
|
||||||
|
|
||||||
except subprocess.TimeoutExpired:
|
except subprocess.TimeoutExpired:
|
||||||
logger.error(f"Claude CLI 超时 ({self.timeout}秒)")
|
logger.error(f"Claude CLI 超时 ({self.timeout}秒)")
|
||||||
return False, "执行超时"
|
return False, "执行超时"
|
||||||
@ -77,39 +82,44 @@ class ClaudeService:
|
|||||||
) -> tuple[bool, str]:
|
) -> tuple[bool, str]:
|
||||||
"""
|
"""
|
||||||
批量修复多个 Bug
|
批量修复多个 Bug
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
bugs: Bug 列表
|
bugs: Bug 列表
|
||||||
project_path: 项目路径
|
project_path: 项目路径
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
(成功与否, Claude 输出)
|
(成功与否, Claude 输出)
|
||||||
"""
|
"""
|
||||||
if not bugs:
|
if not bugs:
|
||||||
return False, "没有需要修复的 Bug"
|
return False, "没有需要修复的 Bug"
|
||||||
|
|
||||||
# 构造批量修复 Prompt
|
# 构造批量修复 Prompt
|
||||||
prompt_parts = [
|
prompt_parts = [
|
||||||
f"请修复以下 {len(bugs)} 个 Bug:",
|
f"你是一个自动化 Bug 修复代理。请直接修复以下 {len(bugs)} 个 Bug。",
|
||||||
|
"",
|
||||||
|
"重要:你必须使用工具(Read、Edit、Write 等)直接修改源代码文件来修复 Bug。",
|
||||||
|
"不要只是分析问题或给出建议,你需要实际定位文件并编辑代码。",
|
||||||
"",
|
"",
|
||||||
]
|
]
|
||||||
|
|
||||||
for bug in bugs:
|
for bug in bugs:
|
||||||
prompt_parts.append(bug.format_for_prompt())
|
prompt_parts.append(bug.format_for_prompt())
|
||||||
prompt_parts.append("")
|
prompt_parts.append("")
|
||||||
|
|
||||||
prompt_parts.extend([
|
prompt_parts.extend([
|
||||||
"## 修复要求",
|
"## 修复要求",
|
||||||
"1. 依次修复上述所有 Bug",
|
"1. 先用 Grep/Glob 定位相关源代码文件",
|
||||||
"2. 每个 Bug 只做最小必要的改动",
|
"2. 用 Read 读取文件内容,理解上下文",
|
||||||
"3. 确保不破坏现有功能",
|
"3. 用 Edit 或 Write 直接修改代码来修复 Bug",
|
||||||
"4. 修复完成后简要说明每个 Bug 的修复方式",
|
"4. 每个 Bug 只做最小必要的改动",
|
||||||
|
"5. 确保不破坏现有功能",
|
||||||
|
"6. 修复完成后简要说明每个 Bug 的修复方式",
|
||||||
"",
|
"",
|
||||||
"请开始修复。",
|
"请立即开始修复,直接编辑文件。",
|
||||||
])
|
])
|
||||||
|
|
||||||
prompt = "\n".join(prompt_parts)
|
prompt = "\n".join(prompt_parts)
|
||||||
|
|
||||||
logger.info(f"开始批量修复 {len(bugs)} 个 Bug...")
|
logger.info(f"开始批量修复 {len(bugs)} 个 Bug...")
|
||||||
return self.execute_prompt(prompt, project_path)
|
return self.execute_prompt(prompt, project_path)
|
||||||
|
|
||||||
@ -135,7 +145,7 @@ class ClaudeService:
|
|||||||
3. 可能影响的其他文件
|
3. 可能影响的其他文件
|
||||||
"""
|
"""
|
||||||
|
|
||||||
return self.execute_prompt(prompt, project_path, tools="Read")
|
return self.execute_prompt(prompt, project_path, allowed_tools="Read,Grep,Glob")
|
||||||
|
|
||||||
def review_changes(self, diff: str, project_path: str) -> tuple[bool, str]:
|
def review_changes(self, diff: str, project_path: str) -> tuple[bool, str]:
|
||||||
"""
|
"""
|
||||||
@ -164,4 +174,4 @@ class ClaudeService:
|
|||||||
如果审核通过,回复 "APPROVED"。否则说明问题。
|
如果审核通过,回复 "APPROVED"。否则说明问题。
|
||||||
"""
|
"""
|
||||||
|
|
||||||
return self.execute_prompt(prompt, project_path, tools="Read")
|
return self.execute_prompt(prompt, project_path, allowed_tools="Read,Grep,Glob")
|
||||||
|
|||||||
@ -88,15 +88,12 @@ class RepairScheduler:
|
|||||||
try:
|
try:
|
||||||
for project_id in self.projects:
|
for project_id in self.projects:
|
||||||
bugs = task_manager.fetch_pending_bugs(project_id)
|
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:
|
if not bugs:
|
||||||
logger.info(f" [{project_id}] 无新 Bug")
|
logger.info(f" [{project_id}] 无待修复 Bug")
|
||||||
continue
|
continue
|
||||||
|
|
||||||
count = len(all_bugs)
|
count = len(bugs)
|
||||||
total_found += count
|
total_found += count
|
||||||
logger.info(f" [{project_id}] 发现 {count} 个待修复 Bug")
|
logger.info(f" [{project_id}] 发现 {count} 个待修复 Bug")
|
||||||
|
|
||||||
@ -120,51 +117,6 @@ class RepairScheduler:
|
|||||||
finally:
|
finally:
|
||||||
task_manager.close()
|
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:
|
def _run_repair(self, project_id: str) -> int:
|
||||||
"""对指定项目执行修复,返回成功修复的数量"""
|
"""对指定项目执行修复,返回成功修复的数量"""
|
||||||
if self._repairing:
|
if self._repairing:
|
||||||
|
|||||||
@ -18,58 +18,60 @@ class TaskManager:
|
|||||||
|
|
||||||
def fetch_pending_bugs(self, project_id: Optional[str] = None) -> list[Bug]:
|
def fetch_pending_bugs(self, project_id: Optional[str] = None) -> list[Bug]:
|
||||||
"""
|
"""
|
||||||
获取待修复的 Bug 列表
|
获取待修复的 Bug 列表(包括 NEW 和 PENDING_FIX 状态)
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
project_id: 可选,筛选特定项目
|
project_id: 可选,筛选特定项目
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
Bug 列表
|
Bug 列表(已按 id 去重)
|
||||||
"""
|
"""
|
||||||
try:
|
all_bugs: dict[int, Bug] = {}
|
||||||
params = {"status": "NEW"}
|
|
||||||
if project_id:
|
for status in ("NEW", "PENDING_FIX"):
|
||||||
params["project_id"] = project_id
|
try:
|
||||||
|
params: dict[str, str] = {"status": status}
|
||||||
response = self.client.get(
|
if project_id:
|
||||||
f"{self.base_url}/api/v1/bugs",
|
params["project_id"] = project_id
|
||||||
params=params
|
|
||||||
)
|
response = self.client.get(
|
||||||
response.raise_for_status()
|
f"{self.base_url}/api/v1/bugs",
|
||||||
|
params=params,
|
||||||
data = response.json()
|
|
||||||
bugs = []
|
|
||||||
|
|
||||||
for item in data.get("items", []):
|
|
||||||
# stack_trace 可能是列表或字符串
|
|
||||||
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", "NEW")),
|
|
||||||
retry_count=item.get("retry_count", 0),
|
|
||||||
)
|
)
|
||||||
bugs.append(bug)
|
response.raise_for_status()
|
||||||
|
|
||||||
logger.info(f"获取到 {len(bugs)} 个待修复 Bug")
|
data = response.json()
|
||||||
return bugs
|
for item in data.get("items", []):
|
||||||
|
bug_id = item["id"]
|
||||||
except httpx.HTTPError as e:
|
if bug_id in all_bugs:
|
||||||
logger.error(f"获取 Bug 列表失败: {e}")
|
continue
|
||||||
return []
|
|
||||||
|
stack_trace = item.get("stack_trace")
|
||||||
|
if isinstance(stack_trace, str):
|
||||||
|
stack_trace = stack_trace.split("\n")
|
||||||
|
|
||||||
|
all_bugs[bug_id] = Bug(
|
||||||
|
id=bug_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", "NEW")),
|
||||||
|
retry_count=item.get("retry_count", 0),
|
||||||
|
)
|
||||||
|
except httpx.HTTPError as e:
|
||||||
|
logger.error(f"获取 {status} 状态 Bug 列表失败: {e}")
|
||||||
|
|
||||||
|
bugs = list(all_bugs.values())
|
||||||
|
logger.info(f"获取到 {len(bugs)} 个待修复 Bug(NEW + PENDING_FIX)")
|
||||||
|
return bugs
|
||||||
|
|
||||||
def update_status(self, bug_id: int, status: BugStatus, message: str = "") -> bool:
|
def update_status(self, bug_id: int, status: BugStatus, message: str = "") -> bool:
|
||||||
"""
|
"""
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user