From f74ae30d414a6cc3a8a53bdd2d70bb877b80b786 Mon Sep 17 00:00:00 2001 From: cfdaily Date: Fri, 19 Jun 2026 13:18:11 +0800 Subject: [PATCH] =?UTF-8?q?[moz]=20impl(=C2=A717):=20CI/=E9=83=A8=E7=BD=B2?= =?UTF-8?q?=E5=A4=B1=E8=B4=A5=20steps=20=E5=88=86=E6=94=AF=E6=8C=87?= =?UTF-8?q?=E5=BC=95=20+=20=E5=9F=BA=E7=A1=80=E8=AE=BE=E6=96=BD=20Issue=20?= =?UTF-8?q?=E8=BD=AC=E4=BA=A4=E6=B5=81=E7=A8=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 改动 1: ci_failure steps 增加分支指引(代码问题自己修/基础设施问题提Issue给姜维) 改动 2: deploy_failure steps 同上分支指引(2处定义都改) 改动 3: issue_assigned handler 按 type/infrastructure label 分流 - infrastructure label → infrastructure_failure event_type(运维排查 steps) - 其他 → 原有编码 steps 改动 4: ToolchainApiSection 新增「需要创建 Issue 时」API 指引段落 改动 5: Red Flags 新增「不是我代码的问题」条目 测试: 8 个新测试,463 passed --- src/api/toolchain_routes.py | 89 ++++++++++++++++--------- src/daemon/toolchain_handler.py | 14 ++++ tests/unit/test_toolchain_handler_v2.py | 70 +++++++++++++++++++ 3 files changed, 141 insertions(+), 32 deletions(-) diff --git a/src/api/toolchain_routes.py b/src/api/toolchain_routes.py index 39ed51d..93896ff 100644 --- a/src/api/toolchain_routes.py +++ b/src/api/toolchain_routes.py @@ -778,9 +778,8 @@ def _send_deploy_failure_task(repo: str, pr_number: int, pr_title: str, reason: action_type="deploy_failure", steps=[ "检查 deploy 日志", - "排查失败原因", - "修复并重新部署", - "提交 action report(POST http://localhost:8083/api/projects/_toolchain/tasks//comments,comment_type=action_report)", + "根据 deploy 日志判断失败原因类型:\n a. 代码/配置问题(rsync 路径错、依赖缺失、启动失败)→ 修复 → 重新部署\n b. 基础设施问题(Gitea 不可用、网络不通、磁盘满、SSH 故障)→ 在该仓库创建 Issue 指派 jiangwei-infra(见下方「需要创建 Issue 时」),label 必须包含 type/infrastructure", + "提交 action report(POST http://localhost:8083/api/projects/_toolchain/tasks//comments,comment_type=action_report)— 报告中说明判断的原因类型和执行的操作", ], context_data={ "repo": repo, @@ -997,30 +996,58 @@ async def _handle_issues(payload: Dict[str, Any]) -> None: "brief": brief, }) - title = f"Issue 指派: {issue_title} ({repo}#{issue_number})" - _send_toolchain_task( - to_agent=assignee, - title=title, - description=text, - event_type="issue_assigned", - action_type="issue_assigned", - steps=[ - f"创建分支 fix/{issue_number}-{brief}", - "编码 + 写 UT", - "push → 等 CI", - f"CI 通过后创建 PR(Gitea API: POST /repos/{repo}/pulls)", - "等 Review", + # 检查是否是基础设施 Issue(按 label 分流) + is_infrastructure = any("infrastructure" in lbl.lower() for lbl in labels_list) + + if is_infrastructure: + infra_steps = [ + "根据 Issue body 中的错误来源和日志片段排查问题", + "修复基础设施问题(如修复 CI runner 环境、恢复网络、重启服务等)", + "修复后在 Issue 上 comment 说明修复方式和结果", "提交 action report(POST http://localhost:8083/api/projects/_toolchain/tasks//comments,comment_type=action_report)", - ], - context_data={ - "issue_number": issue_number, - "repo": repo, - "issue_title": issue_title, - "labels": labels, - "issue_body": issue_body or "(无描述)", - "brief": brief, - }, - ) + ] + title = f"基础设施 Issue: {issue_title} ({repo}#{issue_number})" + _send_toolchain_task( + to_agent=assignee, + title=title, + description=text, + event_type="infrastructure_failure", + action_type="infrastructure_failure", + steps=infra_steps, + context_data={ + "issue_number": issue_number, + "repo": repo, + "issue_title": issue_title, + "labels": labels, + "issue_body": issue_body or "(无描述)", + "brief": brief, + }, + ) + else: + title = f"Issue 指派: {issue_title} ({repo}#{issue_number})" + _send_toolchain_task( + to_agent=assignee, + title=title, + description=text, + event_type="issue_assigned", + action_type="issue_assigned", + steps=[ + f"创建分支 fix/{issue_number}-{brief}", + "编码 + 写 UT", + "push → 等 CI", + f"CI 通过后创建 PR(Gitea API: POST /repos/{repo}/pulls)", + "等 Review", + "提交 action report(POST http://localhost:8083/api/projects/_toolchain/tasks//comments,comment_type=action_report)", + ], + context_data={ + "issue_number": issue_number, + "repo": repo, + "issue_title": issue_title, + "labels": labels, + "issue_body": issue_body or "(无描述)", + "brief": brief, + }, + ) elif action == "opened": if "部署失败" in issue_title: @@ -1043,9 +1070,8 @@ async def _handle_issues(payload: Dict[str, Any]) -> None: action_type="deploy_failure", steps=[ "检查 deploy 日志", - "排查失败原因", - "修复并重新部署", - "提交 action report(POST http://localhost:8083/api/projects/_toolchain/tasks//comments,comment_type=action_report)", + "根据 deploy 日志判断失败原因类型:\n a. 代码/配置问题(rsync 路径错、依赖缺失、启动失败)→ 修复 → 重新部署\n b. 基础设施问题(Gitea 不可用、网络不通、磁盘满、SSH 故障)→ 在该仓库创建 Issue 指派 jiangwei-infra(见下方「需要创建 Issue 时」),label 必须包含 type/infrastructure", + "提交 action report(POST http://localhost:8083/api/projects/_toolchain/tasks//comments,comment_type=action_report)— 报告中说明判断的原因类型和执行的操作", ], context_data={ "repo": repo, @@ -1126,9 +1152,8 @@ async def _handle_issue_comment(payload: Dict[str, Any]) -> None: action_type="ci_failure", steps=[ "查看完整 CI 日志(PR 页面或 Gitea Actions 页面)", - "修复失败的测试", - "push → CI 自动重跑", - "提交 action report(POST http://localhost:8083/api/projects/_toolchain/tasks//comments,comment_type=action_report)", + "根据 CI 日志判断失败原因类型:\n a. 代码问题(lint/test 失败)→ 修复失败的测试 → push 到原分支 → CI 自动重跑\n b. 基础设施问题(runner 环境/Python/venv/Gitea/网络故障)→ 在该仓库创建 Issue 指派 jiangwei-infra(见下方「需要创建 Issue 时」),label 必须包含 type/infrastructure", + "提交 action report(POST http://localhost:8083/api/projects/_toolchain/tasks//comments,comment_type=action_report)— 报告中说明判断的原因类型和执行的操作", ], context_data={ "pr_number": issue_number, diff --git a/src/daemon/toolchain_handler.py b/src/daemon/toolchain_handler.py index d317b28..2326b93 100644 --- a/src/daemon/toolchain_handler.py +++ b/src/daemon/toolchain_handler.py @@ -166,6 +166,19 @@ class ToolchainApiSection: "", "⚠️ 不要使用 Mail API(飞鸽传书)。所有协作通过 Gitea 留痕。", "", + "### 需要创建 Issue 时", + "", + "如果步骤中要求创建 Issue 指派他人(如 jiangwei-infra):", + "```bash", + f'curl -s -X POST "{_GITEA_BASE}/repos/{{repo}}/issues" \\', + ' -H "Authorization: token " \\', + ' -H "Content-Type: application/json" \\', + ' -d \'{"title": "[moz] infra: 简述问题", "body": "## 问题描述\\n\\n<简要描述问题现象>\\n\\n## 错误来源\\n\\n- 仓库: \\n- PR/Commit: <链接>\\n- CI/Deploy run: \\n\\n## 日志关键片段\\n\\n```<错误日志摘要>```\\n\\n## 判断依据\\n\\n<为什么判断为基础设施问题>", "assignees": ["jiangwei-infra"], "labels": []}\'', + "```", + "", + "⚠️ Issue body 必须包含错误来源链接(PR/Commit + CI run),让排查者能直接看到全貌。", + "⚠️ label 数字 ID 先 GET /repos/{repo}/labels 查询 type/infrastructure 对应的 ID。", + "", ] return "\n".join(lines) @@ -218,6 +231,7 @@ class ToolchainConstraintsSection: '| “我已经知道了” | ❌ 知道不等于执行。执行步骤 + 提交 action report 才算完成 |', '| “步骤太多了,选几个做就行” | ❌ 错!必须逐条执行,不可跳过 |', '| “这个步骤不适用于当前情况” | ❌ 如果确实不适用,在 action report 中说明原因,但其他步骤必须执行 |', + '| “CI/部署失败不是我代码的问题,我什么也不用做” | ❌ 错!即使是基础设施问题,你也必须创建 Issue 指派 jiangwei-infra(body 含错误来源链接 + 日志 + 判断依据),并在 action report 中说明。不能只报告“不是我的问题”就完事 |', "", ] return "\n".join(lines) diff --git a/tests/unit/test_toolchain_handler_v2.py b/tests/unit/test_toolchain_handler_v2.py index 620495b..1a9fa17 100644 --- a/tests/unit/test_toolchain_handler_v2.py +++ b/tests/unit/test_toolchain_handler_v2.py @@ -523,3 +523,73 @@ class TestFullPromptBuild: # Must have constraints with Red Flags assert "Red Flags" in prompt assert "强制要求" in prompt + + +# --------------------------------------------------------------------------- +# §17 v2: CI/deploy failure branching + issue label routing + Issue API guidance +# --------------------------------------------------------------------------- + +class TestCiFailureBranching: + """ci_failure steps should include a/b branching guidance.""" + + def test_ci_failure_steps_contain_branching(self): + source_file = PROJECT_ROOT / "src" / "api" / "toolchain_routes.py" + source = source_file.read_text() + assert '基础设施问题' in source + assert 'type/infrastructure' in source + assert 'jiangwei-infra' in source + + +class TestDeployFailureBranching: + """deploy_failure steps should include a/b branching guidance.""" + + def test_deploy_failure_steps_contain_branching(self): + source_file = PROJECT_ROOT / "src" / "api" / "toolchain_routes.py" + source = source_file.read_text() + count = source.count('基础设施问题(Gitea 不可用') + assert count >= 2, f'Expected >=2 deploy_failure branching, found {count}' + + +class TestIssueAssignedLabelRouting: + """issue_assigned handler should route by type/infrastructure label.""" + + def test_label_check_in_source(self): + source_file = PROJECT_ROOT / "src" / "api" / "toolchain_routes.py" + source = source_file.read_text() + assert 'is_infrastructure' in source + assert 'infrastructure_failure' in source + assert '基础设施 Issue' in source + + def test_normal_issue_keeps_coding_steps(self): + source_file = PROJECT_ROOT / "src" / "api" / "toolchain_routes.py" + source = source_file.read_text() + assert '创建分支 fix/' in source + assert 'issue_assigned' in source + + +class TestToolchainApiIssueGuidance: + """ToolchainApiSection should include Issue creation guidance.""" + + def test_has_issue_creation_section(self): + source_file = PROJECT_ROOT / "src" / "daemon" / "toolchain_handler.py" + source = source_file.read_text() + assert "需要创建 Issue 时" in source + assert "/issues" in source + assert "jiangwei-infra" in source + assert "type/infrastructure" in source + + def test_issue_body_template_mentions_required_fields(self): + source_file = PROJECT_ROOT / "src" / "daemon" / "toolchain_handler.py" + source = source_file.read_text() + assert "错误来源" in source + assert "判断依据" in source + + +class TestRedFlagsInfrastructure: + """Red Flags should include the 'not my code' entry.""" + + def test_has_infrastructure_red_flag(self): + source_file = PROJECT_ROOT / "src" / "daemon" / "toolchain_handler.py" + source = source_file.read_text() + assert "不是我代码的问题" in source + assert "基础设施问题" in source