Skip to content

Commit 63edd0f

Browse files
committed
bugfix: fix task type issue
1 parent 249c99e commit 63edd0f

6 files changed

Lines changed: 374 additions & 9 deletions

File tree

docs/ISSUE_TYPE_GUIDE.md

Lines changed: 179 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,179 @@
1+
# Issue Type 识别指南
2+
3+
## 问题说明
4+
5+
GitHub Issues 本身没有原生的 "Type" 字段。Type 信息通常通过以下方式存储:
6+
7+
1. **Labels(标签)** - 最常见的方式
8+
2. **GitHub Projects V2 字段** - 需要将 issue 添加到 Project
9+
3. **Issue Templates** - 通过模板设置,但不会存储在 issue 对象中
10+
11+
## 当前实现
12+
13+
Bot 的 `get_issue_type()` 函数会尝试从以下位置读取 Type:
14+
15+
### 1. 直接字段(优先级最高)
16+
```python
17+
issue.get("type") # 检查是否有直接的 type 字段
18+
```
19+
20+
### 2. Labels 标签(主要方式)
21+
支持以下标签格式:
22+
- 直接标签:`Task`, `Bug`, `Feature`
23+
- 前缀格式:`Type: Task`, `Type: Bug`, `Type: Feature`
24+
- 斜杠格式:`type/task`, `type/bug`, `type/feature`
25+
26+
### 3. Projects V2 字段(未来支持)
27+
需要 GraphQL API 支持。
28+
29+
## 调试方法
30+
31+
### 运行调试脚本
32+
33+
```bash
34+
export GH_TOKEN=your_github_token
35+
python debug/inspect_issue_structure.py
36+
```
37+
38+
这个脚本会:
39+
1. 搜索你的组织的 issues
40+
2. 显示 issue 对象的完整结构
41+
3. 列出所有使用的标签
42+
4. 识别可能的 Type 标签
43+
44+
### 分析输出
45+
46+
检查输出中的 "Labels" 部分,看看你的仓库使用的是哪种标签格式:
47+
48+
```
49+
Labels:
50+
- Task
51+
- P1
52+
- good first issue
53+
```
54+
55+
56+
57+
```
58+
Labels:
59+
- Type: Task
60+
- Priority: P1
61+
- good first issue
62+
```
63+
64+
## 配置建议
65+
66+
### 方案 A: 使用直接标签(推荐)
67+
68+
创建以下标签:
69+
- `Task`
70+
- `Bug`
71+
- `Feature`
72+
73+
配置文件:
74+
```yaml
75+
task_type: Task
76+
```
77+
78+
### 方案 B: 使用前缀标签
79+
80+
创建以下标签:
81+
- `Type: Task`
82+
- `Type: Bug`
83+
- `Type: Feature`
84+
85+
`get_issue_type()` 会自动识别并提取 "Task" 部分。
86+
87+
### 方案 C: 使用 label 过滤(临时方案)
88+
89+
如果你的仓库已经有一个用于标识任务的标签(比如 "task"、"需求" 等),可以临时使用 label 过滤:
90+
91+
```yaml
92+
task_type: "" # 不使用 Type 过滤
93+
task_label: "task" # 使用此 label 过滤
94+
```
95+
96+
## 当前问题排查
97+
98+
如果你看到:
99+
```
100+
- 问题总数 (所有打开的): 50
101+
- Task 类型问题: 0
102+
- 检查的问题 (有优先级): 0
103+
```
104+
105+
这表示:
106+
1. 找到了 50 个打开的 issues
107+
2. 但没有一个 issue 的 Type 是 "Task"
108+
109+
**可能的原因:**
110+
- Issues 没有 `Task` 标签
111+
- 使用了其他标签格式(如 `type/task`, `Type: Task`)
112+
- Type 信息存储在 Projects 字段中(当前不支持)
113+
114+
**解决方案:**
115+
1. 运行调试脚本查看实际的标签格式
116+
2. 根据实际情况调整 `get_issue_type()` 函数
117+
3. 或者为 issues 添加正确的标签
118+
119+
## 扩展 get_issue_type() 函数
120+
121+
如果你的仓库使用其他标签格式,可以修改 `src/libs/github_client.py` 中的 `get_issue_type()` 函数:
122+
123+
```python
124+
def get_issue_type(issue):
125+
# ... 现有代码 ...
126+
127+
# 添加自定义标签格式
128+
for label in issue.get("labels", []):
129+
label_name = label.get("name", "")
130+
131+
# 示例:识别中文标签
132+
if label_name == "任务":
133+
return "Task"
134+
if label_name == "缺陷":
135+
return "Bug"
136+
if label_name == "功能":
137+
return "Feature"
138+
139+
# 示例:识别表情符号标签
140+
if label_name == "✨ feature":
141+
return "Feature"
142+
if label_name == "🐛 bug":
143+
return "Bug"
144+
if label_name == "📋 task":
145+
return "Task"
146+
147+
return ""
148+
```
149+
150+
## 最佳实践
151+
152+
1. **统一标签格式**: 在组织内统一使用同一种标签格式
153+
2. **使用标签模板**: 在 `.github/labels.yml` 中定义标准标签
154+
3. **Issue 模板**: 在 issue 模板中自动添加 Type 标签
155+
4. **文档化**: 在仓库的 CONTRIBUTING.md 中说明标签规范
156+
157+
## 示例:创建标准标签
158+
159+
在仓库中创建 `.github/labels.yml`:
160+
161+
```yaml
162+
- name: "Task"
163+
color: "0E8A16"
164+
description: "工作任务"
165+
166+
- name: "Bug"
167+
color: "D73A4A"
168+
description: "错误或缺陷"
169+
170+
- name: "Feature"
171+
color: "A2EEEF"
172+
description: "新功能请求"
173+
```
174+
175+
然后使用 GitHub CLI 应用:
176+
177+
```bash
178+
gh label sync --force
179+
```
Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
#!/usr/bin/env python3
2+
"""
3+
调试脚本:检查 GitHub Issue 对象的结构
4+
5+
用于查看 GitHub API 返回的 Issue 对象包含哪些字段,
6+
特别是查找 Type 字段的位置。
7+
8+
使用方法:
9+
export GH_TOKEN=your_token
10+
python debug/inspect_issue_structure.py
11+
"""
12+
13+
import os
14+
import sys
15+
import json
16+
from pathlib import Path
17+
18+
# 添加 src 到路径
19+
sys.path.insert(0, str(Path(__file__).parent.parent / "src"))
20+
21+
from libs.github_client import search_issues
22+
23+
24+
def inspect_issues():
25+
"""检查 issue 的结构"""
26+
token = os.environ.get("GH_TOKEN", "").strip()
27+
if not token:
28+
print("错误: 请设置环境变量 GH_TOKEN")
29+
return
30+
31+
# 搜索一些 issues
32+
org = "mcpp-community"
33+
query = f"org:{org} is:issue is:open"
34+
35+
print(f"搜索: {query}")
36+
print("=" * 60)
37+
38+
try:
39+
issues = search_issues(token, query, per_page=5) # 只取5个样本
40+
print(f"找到 {len(issues)} 个 issues\n")
41+
42+
for i, issue in enumerate(issues[:2], 1): # 只详细查看前2个
43+
print(f"\n{'=' * 60}")
44+
print(f"Issue #{i}: #{issue['number']} - {issue['title']}")
45+
print(f"仓库: {issue.get('repository_url', 'N/A').split('/')[-1]}")
46+
print("=" * 60)
47+
48+
# 打印所有顶级字段
49+
print("\n顶级字段:")
50+
for key in sorted(issue.keys()):
51+
value = issue[key]
52+
if isinstance(value, (str, int, bool, type(None))):
53+
print(f" {key}: {value}")
54+
elif isinstance(value, list):
55+
print(f" {key}: [{len(value)} items]")
56+
elif isinstance(value, dict):
57+
print(f" {key}: {{dict with {len(value)} keys}}")
58+
59+
# 详细查看 labels
60+
print("\nLabels:")
61+
labels = issue.get("labels", [])
62+
if labels:
63+
for label in labels:
64+
print(f" - {label.get('name')}")
65+
else:
66+
print(" (无标签)")
67+
68+
# 查找可能的 Type 字段
69+
print("\n查找 Type 相关字段:")
70+
potential_type_fields = [
71+
"type", "issue_type", "state_reason",
72+
"active_lock_reason", "performed_via_github_app"
73+
]
74+
for field in potential_type_fields:
75+
if field in issue:
76+
print(f" {field}: {issue[field]}")
77+
78+
# 完整的 JSON 输出(便于分析)
79+
print("\n完整 JSON (部分):")
80+
# 只显示部分字段,避免过多输出
81+
filtered_issue = {
82+
k: v for k, v in issue.items()
83+
if k in ["number", "title", "state", "labels", "type",
84+
"issue_type", "state_reason", "active_lock_reason"]
85+
}
86+
print(json.dumps(filtered_issue, indent=2, ensure_ascii=False))
87+
88+
# 分析所有 issues 的 labels
89+
print("\n" + "=" * 60)
90+
print("所有 issues 的标签分析:")
91+
print("=" * 60)
92+
93+
all_labels = set()
94+
for issue in issues:
95+
for label in issue.get("labels", []):
96+
all_labels.add(label.get("name"))
97+
98+
if all_labels:
99+
print(f"\n找到 {len(all_labels)} 个不同的标签:")
100+
for label in sorted(all_labels):
101+
print(f" - {label}")
102+
103+
# 查找可能的 Type 标签
104+
type_labels = [l for l in all_labels if "type" in l.lower() or
105+
l in ["Task", "Bug", "Feature", "Enhancement"]]
106+
if type_labels:
107+
print(f"\n可能的 Type 标签:")
108+
for label in type_labels:
109+
print(f" - {label}")
110+
else:
111+
print("(没有找到标签)")
112+
113+
except Exception as e:
114+
print(f"错误: {e}")
115+
import traceback
116+
traceback.print_exc()
117+
118+
119+
if __name__ == "__main__":
120+
inspect_issues()

src/components/task_checker.py

Lines changed: 27 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
get_label_value,
1313
get_priority_from_project,
1414
get_issue_assignees,
15+
get_issue_type,
1516
list_org_repos,
1617
)
1718
from libs.utils import load_simple_yaml
@@ -161,16 +162,20 @@ def scan_repo_tasks(token, repo, config, verbose=False):
161162
Returns:
162163
Dictionary with summary statistics
163164
"""
164-
# Build search query for open issues with Task label
165-
task_label = config.get("task_label", "Task")
166-
165+
# Build search query for open issues
166+
# Note: We can't filter by Type in search query since it's not a standard field
167+
# We'll search for all open issues and filter by Type later
167168
query_parts = [
168169
f"repo:{repo}",
169170
"is:issue",
170171
"is:open",
171-
f"label:{task_label}"
172172
]
173173

174+
# Optionally add label filter if configured
175+
task_label = config.get("task_label", "")
176+
if task_label:
177+
query_parts.append(f"label:{task_label}")
178+
174179
query = " ".join(query_parts)
175180

176181
# Search issues
@@ -180,6 +185,7 @@ def scan_repo_tasks(token, repo, config, verbose=False):
180185
print(f"Error searching {repo}: {e}")
181186
return {
182187
"total_issues": 0,
188+
"task_issues": 0,
183189
"checked_issues": 0,
184190
"reminders_sent": 0,
185191
"skipped_no_priority": 0,
@@ -190,14 +196,27 @@ def scan_repo_tasks(token, repo, config, verbose=False):
190196
}
191197

192198
if verbose:
193-
print(f"找到 {len(issues)} 个 Task 标签的 Issue\n")
199+
print(f"找到 {len(issues)} 个打开的 Issue\n")
200+
201+
# Filter by Type first
202+
task_type = config.get("task_type", "Task")
203+
filtered_issues = []
204+
205+
for issue in issues:
206+
issue_type = get_issue_type(issue)
207+
if issue_type == task_type:
208+
filtered_issues.append(issue)
209+
210+
if verbose:
211+
print(f"其中 {len(filtered_issues)} 个 Type={task_type} 的任务\n")
194212

195213
# Filter issues by priority and check for timeouts
196214
priority_filter = config.get("priorities_to_check", ["P0", "P1", "P2"])
197215
priority_pattern = config.get("priority_pattern", r"^P([012])$")
198216

199217
summary = {
200218
"total_issues": len(issues),
219+
"task_issues": len(filtered_issues),
201220
"checked_issues": 0,
202221
"reminders_sent": 0,
203222
"skipped_no_priority": 0, # 未设置优先级
@@ -206,7 +225,7 @@ def scan_repo_tasks(token, repo, config, verbose=False):
206225
"skipped_not_timeout": 0, # 未超时
207226
"reminded_issues": [], # List of issues that got reminders
208227
}
209-
for issue in issues:
228+
for issue in filtered_issues:
210229
if verbose:
211230
print(f"检查 Issue #{issue['number']}: {issue['title']}")
212231

@@ -284,6 +303,7 @@ def scan_org_tasks(token, org, config, verbose=False):
284303
total_summary = {
285304
"total_repos": 0,
286305
"total_issues": 0,
306+
"task_issues": 0,
287307
"checked_issues": 0,
288308
"reminders_sent": 0,
289309
"skipped_no_priority": 0,
@@ -309,6 +329,7 @@ def scan_org_tasks(token, org, config, verbose=False):
309329
repo_summary = scan_repo_tasks(token, repo_full_name, config, verbose=verbose)
310330
total_summary["total_repos"] += 1
311331
total_summary["total_issues"] += repo_summary.get("total_issues", 0)
332+
total_summary["task_issues"] += repo_summary.get("task_issues", 0)
312333
total_summary["checked_issues"] += repo_summary.get("checked_issues", 0)
313334
total_summary["reminders_sent"] += repo_summary.get("reminders_sent", 0)
314335
total_summary["skipped_no_priority"] += repo_summary.get("skipped_no_priority", 0)

0 commit comments

Comments
 (0)