feat: add hierarchical review numbering and pr description header

This commit is contained in:
2026-04-08 23:48:24 +08:00
parent 56f436592d
commit ed67986320
8 changed files with 36 additions and 6 deletions
+5 -1
View File
@@ -59,6 +59,10 @@ gitea-pr-review render-md --in pr.json --out pr.md
````md ````md
# <repo> `#<pr-index>` <pr-title> # <repo> `#<pr-index>` <pr-title>
> Numbering: Review `<pr>.<review>`; Comment `<pr>.<review>.<comment>`; Reply `<pr>.<review>.<comment>.<reply>`
<pr description>
## Metadata ## Metadata
### Commits ### Commits
@@ -68,7 +72,7 @@ gitea-pr-review render-md --in pr.json --out pr.md
total: <files_changed> files, +<additions>, -<deletions> total: <files_changed> files, +<additions>, -<deletions>
- <file-path>: +<additions>, -<deletions> - <file-path>: +<additions>, -<deletions>
## Review 1 (<review-state>) ## Review <pr-index>.<review-seq> (<review-state>)
> <reviewer> > <reviewer>
### Comment <pr-index>.<review-seq>.<comment-seq> ### Comment <pr-index>.<review-seq>.<comment-seq>
+5 -1
View File
@@ -59,6 +59,10 @@ gitea-pr-review render-md --in pr.json --out pr.md
````md ````md
# <repo> `#<pr-index>` <pr-title> # <repo> `#<pr-index>` <pr-title>
> 编号规则:Review `<pr>.<review>`Comment `<pr>.<review>.<comment>`Reply `<pr>.<review>.<comment>.<reply>`
<pr 描述>
## Metadata ## Metadata
### Commits ### Commits
@@ -68,7 +72,7 @@ gitea-pr-review render-md --in pr.json --out pr.md
total: <files_changed> files, +<additions>, -<deletions> total: <files_changed> files, +<additions>, -<deletions>
- <file-path>: +<additions>, -<deletions> - <file-path>: +<additions>, -<deletions>
## Review 1 (<review-state>) ## Review <pr-index>.<review-seq> (<review-state>)
> <reviewer> > <reviewer>
### Comment <pr-index>.<review-seq>.<comment-seq> ### Comment <pr-index>.<review-seq>.<comment-seq>
+1
View File
@@ -14,6 +14,7 @@ pub struct PrMeta {
pub repo: String, pub repo: String,
pub pr_index: i64, pub pr_index: i64,
pub title: String, pub title: String,
pub description: Option<String>,
pub state: String, pub state: String,
pub author: String, pub author: String,
pub base_branch: String, pub base_branch: String,
+1
View File
@@ -32,6 +32,7 @@ pub fn normalize_bundle(repo: &str, bundle: PullBundleDto) -> PrReviewDocument {
repo: repo.to_string(), repo: repo.to_string(),
pr_index: pull.number, pr_index: pull.number,
title: pull.title, title: pull.title,
description: pull.body,
state: pull.state, state: pull.state,
author: pull.user.login, author: pull.user.login,
base_branch: pull.base.ref_name, base_branch: pull.base.ref_name,
+14 -1
View File
@@ -62,6 +62,18 @@ pub fn render_markdown(doc: &PrReviewDocument) -> String {
"# {} `#{}` {}\n\n", "# {} `#{}` {}\n\n",
doc.meta.repo, doc.meta.pr_index, doc.meta.title doc.meta.repo, doc.meta.pr_index, doc.meta.title
)); ));
out.push_str(
"> 编号规则:Review `<pr>.<review>`Comment `<pr>.<review>.<comment>`Reply `<pr>.<review>.<comment>.<reply>`\n\n",
);
let pr_description = doc
.meta
.description
.as_deref()
.map(str::trim)
.filter(|value| !value.is_empty())
.unwrap_or("_PR 描述为空_");
out.push_str(pr_description);
out.push_str("\n\n");
out.push_str("## Metadata\n\n"); out.push_str("## Metadata\n\n");
out.push_str(&format!("- state: {}\n", doc.meta.state)); out.push_str(&format!("- state: {}\n", doc.meta.state));
@@ -100,7 +112,8 @@ pub fn render_markdown(doc: &PrReviewDocument) -> String {
for (review_index, review) in doc.reviews.iter().enumerate() { for (review_index, review) in doc.reviews.iter().enumerate() {
out.push_str(&format!( out.push_str(&format!(
"## Review {} ({})\n\n", "## Review {}.{} ({})\n\n",
doc.meta.pr_index,
review_index + 1, review_index + 1,
review.state review.state
)); ));
+3 -1
View File
@@ -12,6 +12,7 @@ fn render_md_reads_json_and_outputs_markdown_to_stdout() {
"repo": "org/repo", "repo": "org/repo",
"pr_index": 1, "pr_index": 1,
"title": "t", "title": "t",
"description": "PR body",
"state": "open", "state": "open",
"author": "a", "author": "a",
"base_branch": "main", "base_branch": "main",
@@ -54,6 +55,7 @@ fn render_md_writes_to_out_file_when_requested() {
"repo": "org/repo", "repo": "org/repo",
"pr_index": 2, "pr_index": 2,
"title": "t2", "title": "t2",
"description": "PR body",
"state": "open", "state": "open",
"author": "a", "author": "a",
"base_branch": "main", "base_branch": "main",
@@ -220,7 +222,7 @@ fn fetch_writes_markdown_to_stdout_by_default() {
assert!(stdout.contains("# org/repo `#7` Fix parser")); assert!(stdout.contains("# org/repo `#7` Fix parser"));
assert!(stdout.contains("## Commits")); assert!(stdout.contains("## Commits"));
assert!(stdout.contains("## Diff Stat")); assert!(stdout.contains("## Diff Stat"));
assert!(stdout.contains("## Review 1 (COMMENT)")); assert!(stdout.contains("## Review 7.1 (COMMENT)"));
} }
#[test] #[test]
+1
View File
@@ -9,6 +9,7 @@ fn model_json_roundtrip() {
repo: "org/repo".into(), repo: "org/repo".into(),
pr_index: 9, pr_index: 9,
title: "feat: x".into(), title: "feat: x".into(),
description: Some("desc".into()),
state: "open".into(), state: "open".into(),
author: "alice".into(), author: "alice".into(),
base_branch: "main".into(), base_branch: "main".into(),
+6 -2
View File
@@ -11,6 +11,7 @@ fn render_markdown_includes_expected_sections_and_preserves_comment_markdown() {
repo: "org/repo".into(), repo: "org/repo".into(),
pr_index: 7, pr_index: 7,
title: "Fix parser".into(), title: "Fix parser".into(),
description: Some("PR body".into()),
state: "open".into(), state: "open".into(),
author: "alice".into(), author: "alice".into(),
base_branch: "main".into(), base_branch: "main".into(),
@@ -73,12 +74,14 @@ fn render_markdown_includes_expected_sections_and_preserves_comment_markdown() {
assert!(md.contains("## Metadata")); assert!(md.contains("## Metadata"));
assert!(md.contains("## Commits")); assert!(md.contains("## Commits"));
assert!(md.contains("## Diff Stat")); assert!(md.contains("## Diff Stat"));
assert!(md.contains("## Review 1 (COMMENT)")); assert!(md.contains("> 编号规则:Review `<pr>.<review>`Comment `<pr>.<review>.<comment>`Reply `<pr>.<review>.<comment>.<reply>`"));
assert!(md.contains("PR body"));
assert!(md.contains("## Review 7.1 (COMMENT)"));
assert!(md.contains("### Comment 7.1.1")); assert!(md.contains("### Comment 7.1.1"));
assert!(md.contains("src/main.rs:9")); assert!(md.contains("src/main.rs:9"));
assert!(md.contains("### Reply 7.1.1.1")); assert!(md.contains("### Reply 7.1.1.1"));
assert!(md.contains("src/main.rs:12")); assert!(md.contains("src/main.rs:12"));
let review_pos = md.find("## Review 1 (COMMENT)").unwrap(); let review_pos = md.find("## Review 7.1 (COMMENT)").unwrap();
let comment_pos = md.find("### Comment 7.1.1").unwrap(); let comment_pos = md.find("### Comment 7.1.1").unwrap();
assert!(comment_pos > review_pos); assert!(comment_pos > review_pos);
assert!(!md.contains("## Comments (No Review)")); assert!(!md.contains("## Comments (No Review)"));
@@ -95,6 +98,7 @@ fn render_markdown_uses_minimal_fence_for_plain_text() {
repo: "org/repo".into(), repo: "org/repo".into(),
pr_index: 1, pr_index: 1,
title: "T".into(), title: "T".into(),
description: None,
state: "open".into(), state: "open".into(),
author: "alice".into(), author: "alice".into(),
base_branch: "main".into(), base_branch: "main".into(),