303 lines
7.6 KiB
Markdown
303 lines
7.6 KiB
Markdown
# gitea-pr-review 设计文档
|
||
|
||
- 日期: 2026-04-08
|
||
- 项目: `gitea-pr-review`
|
||
- 目标: 读取 Gitea PR 信息,输出 LLM 友好的 `Markdown` 与 `JSON`
|
||
|
||
## 1. 背景与目标
|
||
|
||
当前需求是将单个 PR 的评审上下文整理成可直接喂给在同仓库工作的 LLM 的输入文档。重点是:
|
||
|
||
1. 评论与回复内容完整保留(最高优先级)。
|
||
2. 保留基础 PR 元信息,尤其是分支名与 commit 列表。
|
||
3. 提供基础 diff stat(不含完整 diff 内容)。
|
||
4. 输出两种格式:
|
||
- `markdown`: 直接阅读与投喂 LLM。
|
||
- `json`: 结构化保真输出,便于脚本和后续处理。
|
||
|
||
## 2. 范围
|
||
|
||
### 2.1 In Scope
|
||
|
||
1. 输入 `pr_index`,通过 Gitea API 拉取:
|
||
- PR 基本信息(标题、作者、状态、源/目标分支、时间)
|
||
- review 列表
|
||
- review comments(含回复)
|
||
- commit 列表
|
||
- diff stat(文件级增删统计或可等价聚合的数据)
|
||
2. 生成 Markdown 输出,结构遵循 `README.md` 约定。
|
||
3. 生成 JSON 输出,字段可追溯到源 API 实体。
|
||
4. 处理评论正文为 Markdown 的情况,避免破坏外层文档结构。
|
||
|
||
### 2.2 Out of Scope
|
||
|
||
1. 不输出完整 diff patch。
|
||
2. 不做评论摘要、情感分析、优先级排序等“解释型加工”。
|
||
3. 不做跨 PR 聚合。
|
||
|
||
## 3. 方案对比与结论
|
||
|
||
### 方案 A: 拉取后直接拼 Markdown
|
||
|
||
- 优点: 最快。
|
||
- 缺点: 结构易耦合;JSON 输出扩展困难;线程关系处理易混乱。
|
||
|
||
### 方案 B: 标准化模型 + 多格式渲染(推荐)
|
||
|
||
- 流程: `fetch -> normalize -> render(markdown/json)`
|
||
- 优点:
|
||
1. 采集与展示解耦。
|
||
2. 评论线程完整性规则可集中实现。
|
||
3. 便于新增输出格式或字段。
|
||
- 缺点: 初期代码比 A 多一层。
|
||
|
||
### 方案 C: 模板引擎渲染
|
||
|
||
- 优点: 视觉格式更可配置。
|
||
- 缺点: 对当前固定结构需求偏重,收益不高。
|
||
|
||
### 结论
|
||
|
||
采用方案 B,优先保证数据完整性与可追溯性,再做格式输出。
|
||
|
||
## 4. 架构设计
|
||
|
||
### 4.1 模块划分
|
||
|
||
1. `cli`
|
||
- 解析参数与环境变量。
|
||
- 决定输出格式与输出路径。
|
||
2. `gitea_client`
|
||
- 仅负责 API 调用与分页。
|
||
- 返回原始 DTO(与 API 字段接近)。
|
||
3. `normalize`
|
||
- 将原始 DTO 归一化为内部模型 `PrReviewDocument`。
|
||
- 负责线程聚合、排序、缺失值兜底。
|
||
4. `render_markdown`
|
||
- 将 `PrReviewDocument` 渲染为 Markdown。
|
||
5. `render_json`
|
||
- 将 `PrReviewDocument` 序列化为稳定 JSON。
|
||
|
||
### 4.2 核心数据模型(内部)
|
||
|
||
```text
|
||
PrReviewDocument
|
||
meta:
|
||
repo, pr_index, pr_title, pr_state, author
|
||
base_branch, head_branch
|
||
created_at, updated_at, merged_at?
|
||
commits: CommitItem[]
|
||
diff_stat:
|
||
files_changed, additions, deletions
|
||
files?: FileStat[]
|
||
reviews: ReviewItem[]
|
||
threads: CommentThread[]
|
||
```
|
||
|
||
```text
|
||
CommentThread
|
||
thread_id
|
||
file_path?
|
||
line?
|
||
root_comment: CommentItem
|
||
replies: CommentItem[]
|
||
```
|
||
|
||
`CommentItem.body` 保存原始文本,不做语义改写。
|
||
|
||
## 5. 数据流与排序规则
|
||
|
||
1. 读取输入:
|
||
- `pr_index` 来自命令行。
|
||
- `repo/url/token` 来自环境变量或参数。
|
||
2. 拉取数据:
|
||
- PR 基本信息
|
||
- reviews
|
||
- comments(含回复关系字段)
|
||
- commits
|
||
- diff stat
|
||
3. 归一化:
|
||
- 基于 `in_reply_to` / `original_id` 等字段聚合线程。
|
||
- 无父评论但标记为回复的异常数据,单独作为孤立线程并记录 warning。
|
||
- 统一按时间排序:
|
||
- 线程按根评论时间升序。
|
||
- 回复按时间升序。
|
||
4. 渲染:
|
||
- Markdown: 面向阅读。
|
||
- JSON: 面向程序消费。
|
||
|
||
## 6. Markdown 输出规范
|
||
|
||
目标: 对齐 `README.md` 给出的基础结构,保证评论内容完整。
|
||
|
||
### 6.1 顶层结构
|
||
|
||
````md
|
||
# <repo> `#<pr-index>` <pr-title>
|
||
|
||
<基本信息区>
|
||
|
||
## Commits
|
||
- <sha-short> <title> (<author>, <date>)
|
||
...
|
||
|
||
## Diff Stat
|
||
- files changed: <n>
|
||
- additions: <n>
|
||
- deletions: <n>
|
||
|
||
## Review 1 (<review-state>)
|
||
> <reviewer>
|
||
|
||
### Comment 1.1
|
||
<file>:<line>
|
||
<user>:
|
||
```md
|
||
<原始评论正文>
|
||
```
|
||
|
||
### Reply 1.1.1
|
||
<user>:
|
||
```md
|
||
<原始回复正文>
|
||
```
|
||
````
|
||
|
||
### 6.2 Markdown 正文保真策略
|
||
|
||
评论正文可能本身含 Markdown(标题、列表、代码块、引用等),为避免破坏外层结构,统一策略:
|
||
|
||
1. 评论正文使用 fenced code block 包裹,info string 为 `md`。
|
||
2. 若正文内已含三反引号,动态选择更长 fence(例如四反引号)。
|
||
3. 不转义正文中的 Markdown 语法,不改写文本。
|
||
|
||
## 7. JSON 输出规范
|
||
|
||
输出为单对象,建议结构如下:
|
||
|
||
```json
|
||
{
|
||
"meta": {
|
||
"repo": "Origami404/aaa",
|
||
"pr_index": 123,
|
||
"title": "feat: ...",
|
||
"state": "open",
|
||
"author": "alice",
|
||
"base_branch": "main",
|
||
"head_branch": "feature/x",
|
||
"created_at": "2026-04-07T10:00:00Z",
|
||
"updated_at": "2026-04-08T10:00:00Z",
|
||
"merged_at": null
|
||
},
|
||
"commits": [
|
||
{
|
||
"sha": "abcdef...",
|
||
"short_sha": "abcdef1",
|
||
"title": "fix: ...",
|
||
"author": "bob",
|
||
"date": "2026-04-07T12:00:00Z"
|
||
}
|
||
],
|
||
"diff_stat": {
|
||
"files_changed": 3,
|
||
"additions": 120,
|
||
"deletions": 30,
|
||
"files": [
|
||
{"path": "src/a.rs", "additions": 10, "deletions": 2}
|
||
]
|
||
},
|
||
"reviews": [
|
||
{
|
||
"id": 1,
|
||
"state": "COMMENT",
|
||
"reviewer": "carol",
|
||
"submitted_at": "2026-04-08T08:00:00Z"
|
||
}
|
||
],
|
||
"threads": [
|
||
{
|
||
"thread_id": "t-1",
|
||
"file_path": "src/main.rs",
|
||
"line": 42,
|
||
"root_comment": {
|
||
"id": 11,
|
||
"user": "carol",
|
||
"created_at": "2026-04-08T08:01:00Z",
|
||
"body": "原始 markdown 文本"
|
||
},
|
||
"replies": [
|
||
{
|
||
"id": 12,
|
||
"user": "alice",
|
||
"created_at": "2026-04-08T08:02:00Z",
|
||
"body": "原始 markdown 文本"
|
||
}
|
||
]
|
||
}
|
||
]
|
||
}
|
||
```
|
||
|
||
约束:
|
||
|
||
1. `body` 保留原始字符串。
|
||
2. 数组顺序稳定(按时间/出现顺序规则)。
|
||
3. 字段缺失时使用 `null` 或空数组,不省略关键键。
|
||
|
||
## 8. CLI 与配置
|
||
|
||
### 8.1 输入
|
||
|
||
1. 必填:
|
||
- `pr_index`(位置参数)
|
||
2. 环境变量(可被参数覆盖):
|
||
- `GITEA_PR_CLI_API_TOKEN`
|
||
- `GITEA_PR_CLI_URL`
|
||
- `GITEA_PR_CLI_REPO`
|
||
|
||
### 8.2 输出参数(新增)
|
||
|
||
1. `--format markdown|json|both`(默认 `markdown`)
|
||
2. `--out <path>`(单输出文件)
|
||
3. `--out-dir <dir>`(`both` 时用于分文件输出)
|
||
|
||
## 9. 错误处理策略
|
||
|
||
1. 配置错误: 缺 token/repo/url,直接报错并退出非 0。
|
||
2. 网络/API 错误: 明确打印 HTTP 状态码与接口路径。
|
||
3. 数据不完整:
|
||
- 评论缺父节点: 降级为孤立线程并 warning。
|
||
- 分支或 commit 字段缺失: 输出 `null` 并 warning。
|
||
4. 渲染错误: 指出是 Markdown 还是 JSON 渲染失败。
|
||
|
||
## 10. 测试策略
|
||
|
||
1. 单元测试
|
||
- 线程聚合(正常链路、孤立回复、乱序输入)。
|
||
- Markdown fence 选择逻辑(三反引号冲突场景)。
|
||
- JSON 序列化稳定性(字段与顺序)。
|
||
2. 快照测试
|
||
- 固定输入 DTO -> Markdown 输出快照。
|
||
3. 集成测试
|
||
- 使用 mock server 覆盖分页、鉴权失败、空评论 PR。
|
||
|
||
## 11. 里程碑计划(高层)
|
||
|
||
1. M1: 完成 `gitea_client` + DTO。
|
||
2. M2: 完成 `normalize` 与线程规则。
|
||
3. M3: 完成 Markdown 渲染并对齐 README 结构。
|
||
4. M4: 完成 JSON 输出。
|
||
5. M5: 补齐测试与错误处理。
|
||
|
||
## 12. 验收标准
|
||
|
||
1. 能对任意有效 PR index 输出 Markdown,且包含:
|
||
- 基础信息
|
||
- 分支名
|
||
- commit 列表
|
||
- diff stat
|
||
- review/comment/reply 完整正文
|
||
2. 能输出 JSON 且字段完整、顺序稳定。
|
||
3. 评论正文含 Markdown/代码块时,Markdown 总文档结构不被破坏。
|
||
4. 关键异常路径有清晰错误信息并返回非 0。
|