Merge PR #93: [moz] impl(§17): CI/部署失败 steps 分支指引 + 基础设施 Issue 转交流程
Deploy / ci (push) Successful in 10s
Deploy / deploy (push) Successful in 13s
Deploy / notify-deploy-failure (push) Successful in 0s
Deploy / notify-deploy-success (push) Successful in 1s

This commit was merged in pull request #93.
This commit is contained in:
2026-06-19 05:20:36 +00:00
3 changed files with 141 additions and 32 deletions
+34 -9
View File
@@ -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 reportPOST http://localhost:8083/api/projects/_toolchain/tasks/<task_id>/commentscomment_type=action_report",
"根据 deploy 日志判断失败原因类型:\n a. 代码/配置问题(rsync 路径错、依赖缺失、启动失败)→ 修复 → 重新部署\n b. 基础设施问题(Gitea 不可用、网络不通、磁盘满、SSH 故障)→ 在该仓库创建 Issue 指派 jiangwei-infra(见下方「需要创建 Issue 时」),label 必须包含 type/infrastructure",
"提交 action reportPOST http://localhost:8083/api/projects/_toolchain/tasks/<task_id>/commentscomment_type=action_report)— 报告中说明判断的原因类型和执行的操作",
],
context_data={
"repo": repo,
@@ -997,6 +996,34 @@ async def _handle_issues(payload: Dict[str, Any]) -> None:
"brief": brief,
})
# 检查是否是基础设施 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 reportPOST http://localhost:8083/api/projects/_toolchain/tasks/<task_id>/commentscomment_type=action_report",
]
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,
@@ -1043,9 +1070,8 @@ async def _handle_issues(payload: Dict[str, Any]) -> None:
action_type="deploy_failure",
steps=[
"检查 deploy 日志",
"排查失败原因",
"修复并重新部署",
"提交 action reportPOST http://localhost:8083/api/projects/_toolchain/tasks/<task_id>/commentscomment_type=action_report",
"根据 deploy 日志判断失败原因类型:\n a. 代码/配置问题(rsync 路径错、依赖缺失、启动失败)→ 修复 → 重新部署\n b. 基础设施问题(Gitea 不可用、网络不通、磁盘满、SSH 故障)→ 在该仓库创建 Issue 指派 jiangwei-infra(见下方「需要创建 Issue 时」),label 必须包含 type/infrastructure",
"提交 action reportPOST http://localhost:8083/api/projects/_toolchain/tasks/<task_id>/commentscomment_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 reportPOST http://localhost:8083/api/projects/_toolchain/tasks/<task_id>/commentscomment_type=action_report",
"根据 CI 日志判断失败原因类型:\n a. 代码问题(lint/test 失败)→ 修复失败的测试 → push 到原分支 → CI 自动重跑\n b. 基础设施问题(runner 环境/Python/venv/Gitea/网络故障)→ 在该仓库创建 Issue 指派 jiangwei-infra(见下方「需要创建 Issue 时」),label 必须包含 type/infrastructure",
"提交 action reportPOST http://localhost:8083/api/projects/_toolchain/tasks/<task_id>/commentscomment_type=action_report)— 报告中说明判断的原因类型和执行的操作",
],
context_data={
"pr_number": issue_number,
+14
View File
@@ -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 <your-token>" \\',
' -H "Content-Type: application/json" \\',
' -d \'{"title": "[moz] infra: 简述问题", "body": "## 问题描述\\n\\n<简要描述问题现象>\\n\\n## 错误来源\\n\\n- 仓库: <repo>\\n- PR/Commit: <链接>\\n- CI/Deploy run: <Gitea Actions 页面链接>\\n\\n## 日志关键片段\\n\\n```<错误日志摘要>```\\n\\n## 判断依据\\n\\n<为什么判断为基础设施问题>", "assignees": ["jiangwei-infra"], "labels": [<label_id>]}\'',
"```",
"",
"⚠️ 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-infrabody 含错误来源链接 + 日志 + 判断依据),并在 action report 中说明。不能只报告“不是我的问题”就完事 |',
"",
]
return "\n".join(lines)
+70
View File
@@ -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