Skip to content

Commit 045a7fd

Browse files
authored
Merge branch 'trpc-group:main' into feature/session-replay-test
2 parents 5b900a7 + f2a34ff commit 045a7fd

192 files changed

Lines changed: 23026 additions & 442 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
你现在的任务不是重新做代码审查,而是将已经生成的 `review.md` 提取为结构化结果,供后续脚本用于行内评论、阻断判断和其他自动化处理。
2+
3+
输入文件:
4+
1. 仓库根目录下的 `review.md`:这是主依据,包含第一阶段已经确认的问题。
5+
2. 仓库根目录下的 `pr.diff`:仅用于辅助定位文件路径和行号,必要时可用于补全位置;不要基于 `pr.diff` 新增问题。
6+
7+
任务要求:
8+
1. 只提取 `review.md` 中已经明确列出的问题,不要新增、扩展或重写任何新问题。
9+
2. 不要重新判断代码是否有问题;第一阶段 review 的结论已经成立,你的任务只是标准化提取。
10+
3. 如果 `review.md` 中某个问题缺少明确的文件路径或行号,不要编造;但不要跳过该问题,仍应保留该 finding,并将 `inline_candidate` 设为 `false`。在这种情况下,`path``start_line``end_line` 可留空字符串、0 或与定位失败一致的保守值,但不能让该问题从结果中消失。
11+
4. 允许参考 `pr.diff` 来确认问题对应的目标文件路径、起始行和结束行,但不要因此新增新的发现。
12+
5. 输出必须是严格合法的 JSON,且只能输出 JSON,不要输出 Markdown、解释文字、代码块、前后缀或额外说明。
13+
6. 输出必须直接以 `{` 开始、以 `}` 结束;不要使用 ```json、``` 或任何其他 Markdown 代码块围栏包裹 JSON。
14+
15+
严重级别映射:
16+
- `Critical` -> `critical`
17+
- `Warning` -> `warning`
18+
- `Suggestion` -> `suggestion`
19+
20+
输出 JSON 格式:
21+
```json
22+
{
23+
"findings": [
24+
{
25+
"severity": "critical",
26+
"path": "relative/path/to/file.py",
27+
"start_line": 12,
28+
"end_line": 12,
29+
"title": "问题标题",
30+
"body": "1-3 句简洁说明,适合直接用于 GitHub 行内评论。",
31+
"inline_candidate": true
32+
}
33+
]
34+
}
35+
```
36+
37+
字段要求:
38+
1. `severity`:必须是 `critical``warning``suggestion` 之一。
39+
2. `path`:仓库相对路径,例如 `tests/ai_test.py`;如果确实无法稳定定位,可为空字符串,但该 finding 仍需保留。
40+
3. `start_line`:问题起始行号,必须优先使用变更后文件的目标行号;如果确实无法稳定定位,可为 `0`,但该 finding 仍需保留。
41+
4. `end_line`
42+
- 单行问题时,填写与 `start_line` 相同的值;
43+
- 范围问题时,填写结束行号。
44+
5. `title`:简短问题标题,适合作为行内评论标题,不要过长。
45+
6. `body`:1-3 句简洁说明,说明风险和建议修复方向,适合直接显示在代码旁边。
46+
7. `inline_candidate`
47+
- `true`:适合做行内评论,问题能明确归因到一个文件的一行或一小段范围,且适合直接挂在代码旁解释。
48+
- `false`:更适合放在总评论中,例如跨文件、跨逻辑链路、整体性结论、位置不稳定或无法明确锚定到 diff 行的问题。
49+
50+
提取规则:
51+
1. 如果 `review.md` 中同一个问题覆盖多个独立位置,只有在这些位置都适合单独挂行内评论时,才拆成多条 findings;否则保持为一条并将 `inline_candidate` 设为 `false`
52+
2. 如果一个问题已经在第一阶段被合并表述为一个综合问题(例如“命令注入 + 路径穿越”),不要在这里再次拆分成多个新问题,除非 `review.md` 本身已经明确拆开。
53+
3. `body` 不要照抄整段 review 原文;应提炼成适合行内评论展示的短说明。
54+
4. 不允许因为定位失败、信息缺失或不适合行内评论而省略第一阶段已经明确列出的问题;这类问题必须保留在 `findings` 中,并通过 `inline_candidate=false` 与保守字段值表达。
55+
5. 如果 `review.md` 中没有任何问题,输出:
56+
```json
57+
{"findings":[]}
58+
```
59+
60+
校验要求:
61+
1. 输出必须能被标准 JSON 解析器直接解析。
62+
2. 所有字符串必须使用双引号。
63+
3. 不要在 JSON 外再输出任何额外字符。
64+
4. 不要输出 ```json 或 ```,也不要输出任何注释。
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
你是一个资深代码审查助手。请审查当前 PR 的代码变更,并给出稳定、可复用、可执行的 review 结论。
2+
3+
审查范围:
4+
1. 以仓库根目录下的 `pr.diff` 作为审查主范围,只反馈落在 diff 变更中的问题。
5+
2. 允许结合仓库中的相关上下文辅助判断 diff 是否存在问题,例如被调用方、配置、类型定义、测试、公共函数和上下游调用关系;但不要脱离 diff 单独审查未修改代码。
6+
3. 不要修改任何文件,不要提交代码。
7+
4. 只反馈可以从 diff 及其相关仓库上下文中定位和验证的问题;不要基于猜测扩展到未修改代码。
8+
5. 每个问题必须标注文件路径和行号。行号优先使用 diff 中新增代码对应的目标文件行号;如果只能定位到代码块,请说明“附近行”。
9+
10+
审查重点:
11+
1. 正确性:逻辑错误、状态流转错误、条件判断错误、返回值或异常处理错误。
12+
2. 安全性:凭证泄露、命令注入、路径穿越、不可信输入未校验、权限绕过、敏感信息输出。
13+
3. 稳定性:边界条件缺失、空值处理、并发或异步时序问题、资源未释放、超时和重试缺失。
14+
4. 兼容性:公开 API、配置项、持久化数据、CI/workflow 行为的破坏性变更。
15+
5. 测试有效性:高风险逻辑缺少必要测试,或测试没有覆盖实际风险路径。
16+
6. 可维护性:仅在影响理解、扩展或长期维护时提出,不输出泛泛的代码风格建议。
17+
18+
质量等级定义:
19+
- 🚨 Critical:必须修复的问题。包括安全漏洞、明确的逻辑错误、会导致核心功能失败、数据错误、权限问题、CI 阻塞或线上风险的问题。
20+
- ⚠️ Warning:建议修复的问题。包括性能隐患、边界条件缺失、异常路径不完整、兼容性风险、测试覆盖不足等可能导致问题但不一定立即失败的情况。
21+
- 💡 Suggestion:可选优化。包括代码可读性、结构简化、维护性提升、轻微重复等不影响当前正确性的改进建议。
22+
23+
输出要求:
24+
1. 使用 Markdown 输出,保持简洁,避免长篇解释。
25+
2. 先列问题,再给总结。
26+
3. 按 Critical、Warning、Suggestion 的顺序输出;同级别内按风险从高到低排序。
27+
4. 合并同类问题:同一根因、同一文件相邻代码、同一修复方式的问题应合并为一条,不要重复展开,也不要用不同表述重复描述同一问题。
28+
5. 优先输出高价值问题。Critical 和 Warning 不限数量但要合并;Suggestion 最多 2 条,且只有在确实有维护价值时输出。
29+
6. 每条问题控制在 2-3 句话内:说明问题、影响和修复方向即可,不要分别展开大段“问题/影响/建议”。
30+
7. 问题位置必须使用反引号包裹的 `文件路径:行号``文件路径:起始行-结束行` 格式,便于评论自动生成 GitHub 跳转链接。
31+
8. 如果没有发现明确问题,请直接说明“未发现明显阻塞问题”,不要为了完整性补充低价值建议。
32+
9. 如果同一段代码同时涉及多个相关风险(如命令注入、路径穿越),优先合并成一条,除非修复方式或影响范围明显不同。
33+
10. 对测试代码、示例代码、演示代码也按真实执行风险评估;如果这些代码会进入 CI、测试运行路径或被其他开发者复用,不要因为“仅用于示例/测试”而降低问题等级。
34+
11. 仅当问题离开具体代码片段不易理解时,才附带 3-8 行最小必要代码片段;前后可用 `...` 省略,不要默认给每个问题都附代码块,也不要贴大段代码。
35+
12. 结论要尽量确定:只有在确实无法从 diff 验证时,才使用“可能”“疑似”等措辞。
36+
37+
输出格式:
38+
39+
## 发现的问题
40+
41+
如果存在问题,请按下面格式逐条输出。每条尽量一行位置 + 一段简短说明:
42+
43+
### 🚨 Critical
44+
45+
- `文件路径:行号`:问题标题
46+
- 简要说明具体风险和建议修复方式。
47+
- 如有必要,可附最小必要代码片段:
48+
```python
49+
...
50+
问题代码
51+
...
52+
```
53+
54+
### ⚠️ Warning
55+
56+
- `文件路径:行号`:问题标题
57+
- 简要说明具体风险和建议修复方式。
58+
- 如有必要,可附最小必要代码片段:
59+
```python
60+
...
61+
问题代码
62+
...
63+
```
64+
65+
### 💡 Suggestion
66+
67+
- `文件路径:行号`:问题标题
68+
- 简要说明维护性影响和可选优化方式。
69+
70+
如果某个等级没有问题,可以省略该等级。
71+
72+
## 总结
73+
74+
1-2 句话说明整体风险,以及是否存在必须修复的问题。
75+
76+
## 测试建议
77+
78+
如果需要补充测试,用 1-2 条说明建议覆盖的场景;如果不需要,请说明“暂无额外测试建议”。
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
#!/usr/bin/env python3
2+
3+
import json
4+
import os
5+
import sys
6+
7+
8+
def main():
9+
findings_path = os.environ.get("FINDINGS_PATH", "findings.json")
10+
with open(findings_path, "r", encoding="utf-8") as f:
11+
data = json.load(f)
12+
13+
findings = data.get("findings", [])
14+
for finding in findings:
15+
if finding.get("severity") == "critical":
16+
print("Critical issues found in AI Code Review, blocking pipeline")
17+
return 1
18+
19+
print("No critical findings, pipeline can continue")
20+
return 0
21+
22+
23+
if __name__ == "__main__":
24+
sys.exit(main())
Lines changed: 170 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,170 @@
1+
#!/usr/bin/env python3
2+
3+
import json
4+
import os
5+
import re
6+
import sys
7+
import urllib.error
8+
import urllib.request
9+
10+
11+
def read_findings(path):
12+
with open(path, "r", encoding="utf-8") as f:
13+
data = json.load(f)
14+
return data.get("findings", [])
15+
16+
17+
def build_position_map(diff_path):
18+
with open(diff_path, "r", encoding="utf-8") as f:
19+
lines = f.readlines()
20+
21+
position_map = {}
22+
current_path = None
23+
new_line_no = None
24+
position = 0
25+
in_hunk = False
26+
27+
for raw in lines:
28+
line = raw.rstrip("\n")
29+
30+
if line.startswith("diff --git "):
31+
current_path = None
32+
new_line_no = None
33+
position = 0
34+
in_hunk = False
35+
continue
36+
37+
if line.startswith("+++ b/"):
38+
current_path = line[6:]
39+
if current_path not in position_map:
40+
position_map[current_path] = {}
41+
continue
42+
43+
if line.startswith("@@ "):
44+
match = re.match(r"@@ -\d+(?:,\d+)? \+(\d+)(?:,\d+)? @@", line)
45+
if match:
46+
new_line_no = int(match.group(1))
47+
in_hunk = True
48+
continue
49+
50+
if not in_hunk or current_path is None:
51+
continue
52+
53+
if not line:
54+
continue
55+
56+
prefix = line[0]
57+
if prefix not in (" ", "+", "-"):
58+
continue
59+
60+
position += 1
61+
62+
if prefix == " ":
63+
position_map[current_path][new_line_no] = position
64+
new_line_no += 1
65+
elif prefix == "+":
66+
position_map[current_path][new_line_no] = position
67+
new_line_no += 1
68+
elif prefix == "-":
69+
continue
70+
71+
return position_map
72+
73+
74+
def build_payload(commit_id, path, position, title, body):
75+
comment_body = "**%s**" % title
76+
if body:
77+
comment_body += "\n\n%s" % body
78+
return {
79+
"body": comment_body,
80+
"commit_id": commit_id,
81+
"path": path,
82+
"position": position,
83+
}
84+
85+
86+
def post_inline_comment(api_url, token, payload):
87+
data = json.dumps(payload).encode("utf-8")
88+
req = urllib.request.Request(
89+
api_url,
90+
data=data,
91+
method="POST",
92+
headers={
93+
"Authorization": "Bearer %s" % token,
94+
"Accept": "application/vnd.github+json",
95+
"Content-Type": "application/json",
96+
},
97+
)
98+
try:
99+
with urllib.request.urlopen(req) as resp:
100+
return True, "status=%s" % resp.status
101+
except urllib.error.HTTPError as e:
102+
try:
103+
err = e.read().decode("utf-8", errors="ignore")
104+
except Exception:
105+
err = ""
106+
return False, "status=%s body=%s" % (e.code, err)
107+
108+
109+
def main():
110+
findings_path = os.environ.get("FINDINGS_PATH", "findings.json")
111+
diff_path = os.environ.get("PR_DIFF_PATH", "pr.diff")
112+
api_url = os.environ["INLINE_COMMENT_API_URL"]
113+
token = os.environ["GITHUB_TOKEN"]
114+
commit_id = os.environ["GITHUB_HEAD_SHA"]
115+
116+
findings = read_findings(findings_path)
117+
if not findings:
118+
print("no findings found, skip inline comments")
119+
return 0
120+
121+
position_map = build_position_map(diff_path)
122+
created = 0
123+
124+
for finding in findings:
125+
if finding.get("severity") != "critical":
126+
continue
127+
if not finding.get("inline_candidate"):
128+
continue
129+
130+
path = finding.get("path")
131+
start_line = finding.get("start_line")
132+
end_line = finding.get("end_line")
133+
title = finding.get("title", "").strip()
134+
body = finding.get("body", "").strip()
135+
136+
if not path or not start_line or not title:
137+
print("skip finding without required fields: %s" % json.dumps(finding, ensure_ascii=False))
138+
continue
139+
140+
start_line = int(start_line)
141+
end_line = int(end_line) if end_line else None
142+
target_line = start_line
143+
144+
file_positions = position_map.get(path, {})
145+
position = file_positions.get(target_line)
146+
if position is None and end_line and end_line > start_line:
147+
position = file_positions.get(end_line)
148+
if position is not None:
149+
target_line = end_line
150+
151+
target = "%s:%s-%s" % (path, start_line, end_line) if end_line and end_line > start_line else "%s:%s" % (path, start_line)
152+
153+
if position is None:
154+
print("skip finding without diff position: %s" % target)
155+
continue
156+
157+
payload = build_payload(commit_id, path, position, title, body)
158+
ok, msg = post_inline_comment(api_url, token, payload)
159+
if ok:
160+
created += 1
161+
print("inline comment created: %s position=%s %s" % (target, position, msg))
162+
else:
163+
print("inline comment failed: %s position=%s %s" % (target, position, msg))
164+
165+
print("inline comments created: %s" % created)
166+
return 0
167+
168+
169+
if __name__ == "__main__":
170+
sys.exit(main())

0 commit comments

Comments
 (0)