From 5fad0beb85c0129e63e32b0613ac917920b46de8 Mon Sep 17 00:00:00 2001 From: Luis Ball Date: Mon, 29 Dec 2025 00:47:00 -0800 Subject: [PATCH] feat: add use_badges parameter to build_table for private repo support - Add use_badges parameter to build_table function - Default (false) generates 3-column table without shields.io badges - When true, generates 4-column table with shields.io status badges - Update existing tests to use new default (no badges) - Add new tests for badges mode - Update main.rs call site to use new parameter --- src/main.rs | 9 +- src/markdown.rs | 241 ++++++++++++------ ...uild_table_all_closed_shows_checkmark.snap | 8 +- ...down__tests__build_table_linear_stack.snap | 10 +- ...down__tests__build_table_mixed_states.snap | 10 +- ...arkdown__tests__build_table_single_pr.snap | 6 +- ...kdown__tests__build_table_with_badges.snap | 9 + ..._build_table_with_badges_mixed_states.snap | 10 + ...wn__tests__build_table_with_closed_pr.snap | 6 +- ...own__tests__build_table_with_draft_pr.snap | 6 +- ...wn__tests__build_table_with_merged_pr.snap | 6 +- 11 files changed, 214 insertions(+), 107 deletions(-) create mode 100644 src/snapshots/gh_stack__markdown__tests__build_table_with_badges.snap create mode 100644 src/snapshots/gh_stack__markdown__tests__build_table_with_badges_mixed_states.snap diff --git a/src/main.rs b/src/main.rs index bc04cf0..f27eb6e 100644 --- a/src/main.rs +++ b/src/main.rs @@ -219,8 +219,13 @@ async fn main() -> Result<(), Box> { build_pr_stack_for_repo(&identifier, repository, &credentials, get_excluded(m)) .await?; - let table = - markdown::build_table(&stack, &identifier, m.value_of("prelude"), repository); + let table = markdown::build_table( + &stack, + &identifier, + m.value_of("prelude"), + repository, + false, + ); for (pr, _) in stack.iter() { println!("{}: {}", pr.number(), pr.title()); diff --git a/src/markdown.rs b/src/markdown.rs index 6bbf691..fa66e08 100644 --- a/src/markdown.rs +++ b/src/markdown.rs @@ -8,6 +8,7 @@ pub fn build_table( title: &str, prelude_path: Option<&str>, repository: &str, + use_badges: bool, ) -> String { let is_complete = deps .iter() @@ -27,89 +28,104 @@ pub fn build_table( out.push('\n'); } - out.push_str("| PR | Title | Status | Merges Into |\n"); - out.push_str("|:--:|:------|:-------|:-------------:|\n"); + if use_badges { + out.push_str("| PR | Title | Status | Merges Into |\n"); + out.push_str("|:--:|:------|:-------|:-----------:|\n"); + } else { + out.push_str("| PR | Title | Merges Into |\n"); + out.push_str("|:--:|:------|:-----------:|\n"); + } for (node, parent) in deps { - let review_state = match node.review_state() { - PullRequestReviewState::APPROVED => { - format!( - "![](https://img.shields.io/github/pulls/detail/state/{}/{}?label={})", - repository, - &node.number().to_string(), - "Approved" - ) - } - PullRequestReviewState::MERGED => { - format!( - "![](https://img.shields.io/github/pulls/detail/state/{}/{}?label={})", - repository, - &node.number().to_string(), - "%20" - ) - } - PullRequestReviewState::PENDING => { - format!( - "![](https://img.shields.io/github/pulls/detail/state/{}/{}?label={})", - repository, - &node.number().to_string(), - "Pending" - ) - } - PullRequestReviewState::CHANGES_REQUESTED => { - format!( - "![](https://img.shields.io/github/pulls/detail/state/{}/{}?label={})", - repository, - &node.number().to_string(), - "Changes Requested" - ) - } - PullRequestReviewState::DISMISSED => { - format!( - "![](https://img.shields.io/github/pulls/detail/state/{}/{}?label={})", - repository, - &node.number().to_string(), - "Dismissed" - ) - } - PullRequestReviewState::COMMENTED => { + let review_state = if use_badges { + let state = match node.review_state() { + PullRequestReviewState::APPROVED => { + format!( + "![](https://img.shields.io/github/pulls/detail/state/{}/{}?label={})", + repository, + &node.number().to_string(), + "Approved" + ) + } + PullRequestReviewState::MERGED => { + format!( + "![](https://img.shields.io/github/pulls/detail/state/{}/{}?label={})", + repository, + &node.number().to_string(), + "%20" + ) + } + PullRequestReviewState::PENDING => { + format!( + "![](https://img.shields.io/github/pulls/detail/state/{}/{}?label={})", + repository, + &node.number().to_string(), + "Pending" + ) + } + PullRequestReviewState::CHANGES_REQUESTED => { + format!( + "![](https://img.shields.io/github/pulls/detail/state/{}/{}?label={})", + repository, + &node.number().to_string(), + "Changes Requested" + ) + } + PullRequestReviewState::DISMISSED => { + format!( + "![](https://img.shields.io/github/pulls/detail/state/{}/{}?label={})", + repository, + &node.number().to_string(), + "Dismissed" + ) + } + PullRequestReviewState::COMMENTED => { + format!( + "![](https://img.shields.io/github/pulls/detail/state/{}/{}?label={})", + repository, + &node.number().to_string(), + "Commented" + ) + } + }; + + if node.review_state() != PullRequestReviewState::MERGED + && *node.state() == PullRequestStatus::Closed + { format!( "![](https://img.shields.io/github/pulls/detail/state/{}/{}?label={})", repository, &node.number().to_string(), - "Commented" + "Closed" ) + } else { + state } - }; - - let review_state = if node.review_state() != PullRequestReviewState::MERGED - && *node.state() == PullRequestStatus::Closed - { - format!( - "![](https://img.shields.io/github/pulls/detail/state/{}/{}?label={})", - repository, - &node.number().to_string(), - "Closed" - ) } else { - review_state + String::new() }; - let row = match (node.state(), parent) { - (_, None) => format!( - "|#{}|{}|{}|{}|\n", - node.number(), - node.title(), - review_state, - "-" - ), - (_, Some(parent)) => format!( - "|#{}|{}|{}|#{}|\n", - node.number(), - node.title(), - review_state, - parent.number(), - ), + let row = if use_badges { + match parent { + None => format!("|#{}|{}|{}|-|\n", node.number(), node.title(), review_state), + Some(parent) => format!( + "|#{}|{}|{}|#{}|\n", + node.number(), + node.title(), + review_state, + parent.number() + ), + } + } else { + match parent { + None => format!("|#{}|{}|-|\n", node.number(), node.title()), + Some(parent) => format!( + "|#{}|{}|#{}|\n", + node.number(), + node.title(), + parent.number() + ), + } }; out.push_str(&row); @@ -158,7 +174,7 @@ mod tests { ); let deps: FlatDep = vec![(pr, None)]; - let table = build_table(&deps, "JIRA-123", None, "user/repo"); + let table = build_table(&deps, "JIRA-123", None, "user/repo", false); insta::assert_snapshot!(table); } @@ -198,7 +214,7 @@ mod tests { (pr3.clone(), Some(pr2.clone())), ]; - let table = build_table(&deps, "STACK-456", None, "org/project"); + let table = build_table(&deps, "STACK-456", None, "org/project", false); insta::assert_snapshot!(table); } @@ -215,7 +231,7 @@ mod tests { ); let deps: FlatDep = vec![(pr, None)]; - let table = build_table(&deps, "DRAFT-TEST", None, "user/repo"); + let table = build_table(&deps, "DRAFT-TEST", None, "user/repo", false); insta::assert_snapshot!(table); } @@ -232,7 +248,7 @@ mod tests { ); let deps: FlatDep = vec![(pr, None)]; - let table = build_table(&deps, "CLOSED-TEST", None, "user/repo"); + let table = build_table(&deps, "CLOSED-TEST", None, "user/repo", false); insta::assert_snapshot!(table); } @@ -249,7 +265,7 @@ mod tests { ); let deps: FlatDep = vec![(pr, None)]; - let table = build_table(&deps, "MERGED-TEST", None, "user/repo"); + let table = build_table(&deps, "MERGED-TEST", None, "user/repo", false); insta::assert_snapshot!(table); } @@ -276,7 +292,7 @@ mod tests { let deps: FlatDep = vec![(pr1.clone(), None), (pr2.clone(), Some(pr1.clone()))]; - let table = build_table(&deps, "COMPLETE-STACK", None, "user/repo"); + let table = build_table(&deps, "COMPLETE-STACK", None, "user/repo", false); insta::assert_snapshot!(table); } @@ -316,7 +332,74 @@ mod tests { (pr3.clone(), Some(pr2.clone())), ]; - let table = build_table(&deps, "MIXED-STACK", None, "org/repo"); + let table = build_table(&deps, "MIXED-STACK", None, "org/repo", false); + insta::assert_snapshot!(table); + } + + #[test] + fn test_build_table_with_badges() { + let pr1 = make_pr( + 1, + "feature-1", + "main", + "Base feature", + PullRequestStatus::Open, + false, + None, + ); + let pr2 = make_pr( + 2, + "feature-2", + "feature-1", + "Second feature", + PullRequestStatus::Open, + false, + None, + ); + + let deps: FlatDep = vec![(pr1.clone(), None), (pr2.clone(), Some(pr1.clone()))]; + + let table = build_table(&deps, "BADGES-TEST", None, "org/project", true); + insta::assert_snapshot!(table); + } + + #[test] + fn test_build_table_with_badges_mixed_states() { + let pr1 = make_pr( + 1, + "feature-1", + "main", + "Merged base", + PullRequestStatus::Closed, + false, + Some("2024-01-15T10:00:00Z".to_string()), + ); + let pr2 = make_pr( + 2, + "feature-2", + "feature-1", + "Open follow-up", + PullRequestStatus::Open, + false, + None, + ); + let pr3 = make_pr( + 3, + "feature-3", + "feature-2", + "Draft WIP", + PullRequestStatus::Open, + true, + None, + ); + + let deps: FlatDep = vec![ + (pr1.clone(), None), + (pr2.clone(), Some(pr1.clone())), + (pr3.clone(), Some(pr2.clone())), + ]; + + let table = build_table(&deps, "BADGES-MIXED", None, "org/repo", true); insta::assert_snapshot!(table); } } diff --git a/src/snapshots/gh_stack__markdown__tests__build_table_all_closed_shows_checkmark.snap b/src/snapshots/gh_stack__markdown__tests__build_table_all_closed_shows_checkmark.snap index dc594f1..c63d242 100644 --- a/src/snapshots/gh_stack__markdown__tests__build_table_all_closed_shows_checkmark.snap +++ b/src/snapshots/gh_stack__markdown__tests__build_table_all_closed_shows_checkmark.snap @@ -3,7 +3,7 @@ source: src/markdown.rs expression: table --- ### ✅ Stacked PR Chain: COMPLETE-STACK -| PR | Title | Status | Merges Into | -|:--:|:------|:-------|:-------------:| -|#1|~~First~~|![](https://img.shields.io/github/pulls/detail/state/user/repo/1?label=Closed)|-| -|#2|~~Second~~|![](https://img.shields.io/github/pulls/detail/state/user/repo/2?label=Closed)|#1| +| PR | Title | Merges Into | +|:--:|:------|:-----------:| +|#1|~~First~~|-| +|#2|~~Second~~|#1| diff --git a/src/snapshots/gh_stack__markdown__tests__build_table_linear_stack.snap b/src/snapshots/gh_stack__markdown__tests__build_table_linear_stack.snap index 5fc97f8..c74ff11 100644 --- a/src/snapshots/gh_stack__markdown__tests__build_table_linear_stack.snap +++ b/src/snapshots/gh_stack__markdown__tests__build_table_linear_stack.snap @@ -3,8 +3,8 @@ source: src/markdown.rs expression: table --- ### Stacked PR Chain: STACK-456 -| PR | Title | Status | Merges Into | -|:--:|:------|:-------|:-------------:| -|#1|Base feature|![](https://img.shields.io/github/pulls/detail/state/org/project/1?label=Pending)|-| -|#2|Second feature|![](https://img.shields.io/github/pulls/detail/state/org/project/2?label=Pending)|#1| -|#3|Third feature|![](https://img.shields.io/github/pulls/detail/state/org/project/3?label=Pending)|#2| +| PR | Title | Merges Into | +|:--:|:------|:-----------:| +|#1|Base feature|-| +|#2|Second feature|#1| +|#3|Third feature|#2| diff --git a/src/snapshots/gh_stack__markdown__tests__build_table_mixed_states.snap b/src/snapshots/gh_stack__markdown__tests__build_table_mixed_states.snap index c140dac..52c1045 100644 --- a/src/snapshots/gh_stack__markdown__tests__build_table_mixed_states.snap +++ b/src/snapshots/gh_stack__markdown__tests__build_table_mixed_states.snap @@ -3,8 +3,8 @@ source: src/markdown.rs expression: table --- ### Stacked PR Chain: MIXED-STACK -| PR | Title | Status | Merges Into | -|:--:|:------|:-------|:-------------:| -|#1|~~Merged base~~|![](https://img.shields.io/github/pulls/detail/state/org/repo/1?label=%20)|-| -|#2|Open follow-up|![](https://img.shields.io/github/pulls/detail/state/org/repo/2?label=Pending)|#1| -|#3|*(Draft) Draft WIP*|![](https://img.shields.io/github/pulls/detail/state/org/repo/3?label=Pending)|#2| +| PR | Title | Merges Into | +|:--:|:------|:-----------:| +|#1|~~Merged base~~|-| +|#2|Open follow-up|#1| +|#3|*(Draft) Draft WIP*|#2| diff --git a/src/snapshots/gh_stack__markdown__tests__build_table_single_pr.snap b/src/snapshots/gh_stack__markdown__tests__build_table_single_pr.snap index 48fd35f..1848327 100644 --- a/src/snapshots/gh_stack__markdown__tests__build_table_single_pr.snap +++ b/src/snapshots/gh_stack__markdown__tests__build_table_single_pr.snap @@ -3,6 +3,6 @@ source: src/markdown.rs expression: table --- ### Stacked PR Chain: JIRA-123 -| PR | Title | Status | Merges Into | -|:--:|:------|:-------|:-------------:| -|#1|Add new feature|![](https://img.shields.io/github/pulls/detail/state/user/repo/1?label=Pending)|-| +| PR | Title | Merges Into | +|:--:|:------|:-----------:| +|#1|Add new feature|-| diff --git a/src/snapshots/gh_stack__markdown__tests__build_table_with_badges.snap b/src/snapshots/gh_stack__markdown__tests__build_table_with_badges.snap new file mode 100644 index 0000000..1f28de4 --- /dev/null +++ b/src/snapshots/gh_stack__markdown__tests__build_table_with_badges.snap @@ -0,0 +1,9 @@ +--- +source: src/markdown.rs +expression: table +--- +### Stacked PR Chain: BADGES-TEST +| PR | Title | Status | Merges Into | +|:--:|:------|:-------|:-----------:| +|#1|Base feature|![](https://img.shields.io/github/pulls/detail/state/org/project/1?label=Pending)|-| +|#2|Second feature|![](https://img.shields.io/github/pulls/detail/state/org/project/2?label=Pending)|#1| diff --git a/src/snapshots/gh_stack__markdown__tests__build_table_with_badges_mixed_states.snap b/src/snapshots/gh_stack__markdown__tests__build_table_with_badges_mixed_states.snap new file mode 100644 index 0000000..44d305b --- /dev/null +++ b/src/snapshots/gh_stack__markdown__tests__build_table_with_badges_mixed_states.snap @@ -0,0 +1,10 @@ +--- +source: src/markdown.rs +expression: table +--- +### Stacked PR Chain: BADGES-MIXED +| PR | Title | Status | Merges Into | +|:--:|:------|:-------|:-----------:| +|#1|~~Merged base~~|![](https://img.shields.io/github/pulls/detail/state/org/repo/1?label=%20)|-| +|#2|Open follow-up|![](https://img.shields.io/github/pulls/detail/state/org/repo/2?label=Pending)|#1| +|#3|*(Draft) Draft WIP*|![](https://img.shields.io/github/pulls/detail/state/org/repo/3?label=Pending)|#2| diff --git a/src/snapshots/gh_stack__markdown__tests__build_table_with_closed_pr.snap b/src/snapshots/gh_stack__markdown__tests__build_table_with_closed_pr.snap index 97bb560..448e147 100644 --- a/src/snapshots/gh_stack__markdown__tests__build_table_with_closed_pr.snap +++ b/src/snapshots/gh_stack__markdown__tests__build_table_with_closed_pr.snap @@ -3,6 +3,6 @@ source: src/markdown.rs expression: table --- ### ✅ Stacked PR Chain: CLOSED-TEST -| PR | Title | Status | Merges Into | -|:--:|:------|:-------|:-------------:| -|#1|~~Completed feature~~|![](https://img.shields.io/github/pulls/detail/state/user/repo/1?label=Closed)|-| +| PR | Title | Merges Into | +|:--:|:------|:-----------:| +|#1|~~Completed feature~~|-| diff --git a/src/snapshots/gh_stack__markdown__tests__build_table_with_draft_pr.snap b/src/snapshots/gh_stack__markdown__tests__build_table_with_draft_pr.snap index 88b5f95..fbcdc2b 100644 --- a/src/snapshots/gh_stack__markdown__tests__build_table_with_draft_pr.snap +++ b/src/snapshots/gh_stack__markdown__tests__build_table_with_draft_pr.snap @@ -3,6 +3,6 @@ source: src/markdown.rs expression: table --- ### Stacked PR Chain: DRAFT-TEST -| PR | Title | Status | Merges Into | -|:--:|:------|:-------|:-------------:| -|#1|*(Draft) Work in progress*|![](https://img.shields.io/github/pulls/detail/state/user/repo/1?label=Pending)|-| +| PR | Title | Merges Into | +|:--:|:------|:-----------:| +|#1|*(Draft) Work in progress*|-| diff --git a/src/snapshots/gh_stack__markdown__tests__build_table_with_merged_pr.snap b/src/snapshots/gh_stack__markdown__tests__build_table_with_merged_pr.snap index 0de16d2..3401944 100644 --- a/src/snapshots/gh_stack__markdown__tests__build_table_with_merged_pr.snap +++ b/src/snapshots/gh_stack__markdown__tests__build_table_with_merged_pr.snap @@ -3,6 +3,6 @@ source: src/markdown.rs expression: table --- ### ✅ Stacked PR Chain: MERGED-TEST -| PR | Title | Status | Merges Into | -|:--:|:------|:-------|:-------------:| -|#1|~~Merged feature~~|![](https://img.shields.io/github/pulls/detail/state/user/repo/1?label=%20)|-| +| PR | Title | Merges Into | +|:--:|:------|:-----------:| +|#1|~~Merged feature~~|-|