docs: add gitea-pr-review design spec

This commit is contained in:
2026-04-08 22:32:01 +08:00
parent a74d7c6ca0
commit 03a7d98669
@@ -0,0 +1,302 @@
# 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。