hive-forge: point to per-line review comments instead of inlining them, fix pr reviews line numbers
This commit is contained in:
parent
c38778b885
commit
b7a7628845
2 changed files with 35 additions and 15 deletions
|
|
@ -21,8 +21,9 @@
|
||||||
//! same way (see `crate::verbs::MAX_LIMIT`).
|
//! same way (see `crate::verbs::MAX_LIMIT`).
|
||||||
//!
|
//!
|
||||||
//! Review *bodies* on PRs are always merged in too (`pulls/<n>/reviews`
|
//! Review *bodies* on PRs are always merged in too (`pulls/<n>/reviews`
|
||||||
//! isn't the issues/comments thread, so a plain listing used to miss
|
//! isn't the issues/comments thread), tagged `[review: STATE]`, with a
|
||||||
//! them), tagged `[review: STATE]`.
|
//! `(N line comment(s) — see 'pr reviews')` pointer when a review has
|
||||||
|
//! inline comments — this verb never inlines those, `pr reviews` does.
|
||||||
//!
|
//!
|
||||||
//! `--json` output is an object (`{"comments": [...], "more_before": N,
|
//! `--json` output is an object (`{"comments": [...], "more_before": N,
|
||||||
//! "more_after": N, "since_more": bool}`), not a bare array, so a
|
//! "more_after": N, "since_more": bool}`), not a bare array, so a
|
||||||
|
|
@ -106,6 +107,7 @@ pub fn run(client: &Client, args: Args) -> Result<()> {
|
||||||
"url": c.get("html_url"),
|
"url": c.get("html_url"),
|
||||||
"kind": c.get("kind").and_then(Value::as_str).unwrap_or("comment"),
|
"kind": c.get("kind").and_then(Value::as_str).unwrap_or("comment"),
|
||||||
"state": c.get("state"),
|
"state": c.get("state"),
|
||||||
|
"comments_count": c.get("comments_count"),
|
||||||
"attachments": attachment_json_from_value(c),
|
"attachments": attachment_json_from_value(c),
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
@ -128,7 +130,15 @@ pub fn run(client: &Client, args: Args) -> Result<()> {
|
||||||
let body = c.get("body").and_then(Value::as_str).unwrap_or("");
|
let body = c.get("body").and_then(Value::as_str).unwrap_or("");
|
||||||
if c.get("kind").and_then(Value::as_str) == Some("review") {
|
if c.get("kind").and_then(Value::as_str) == Some("review") {
|
||||||
let state = c.get("state").and_then(Value::as_str).unwrap_or("?");
|
let state = c.get("state").and_then(Value::as_str).unwrap_or("?");
|
||||||
println!("**{user} @ {ts}** [review: {state}]: {body}");
|
let n = c.get("comments_count").and_then(Value::as_i64).unwrap_or(0);
|
||||||
|
if n > 0 {
|
||||||
|
println!(
|
||||||
|
"**{user} @ {ts}** [review: {state}] ({n} line comment(s) — see `hive-forge pr reviews {}`): {body}",
|
||||||
|
args.number
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
println!("**{user} @ {ts}** [review: {state}]: {body}");
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
println!("**{user} @ {ts}**: {body}");
|
println!("**{user} @ {ts}**: {body}");
|
||||||
}
|
}
|
||||||
|
|
@ -240,14 +250,19 @@ fn to_values<T: serde::Serialize>(items: Vec<T>) -> Result<Vec<Value>> {
|
||||||
///
|
///
|
||||||
/// Review summaries live in the `pulls/<n>/reviews` object, not the
|
/// Review summaries live in the `pulls/<n>/reviews` object, not the
|
||||||
/// issues/comments thread, so the plain comment listing misses them
|
/// issues/comments thread, so the plain comment listing misses them
|
||||||
/// — the review-body gap this fixes. Best-effort: returns empty on
|
/// — the review-body gap this fixes. Best-effort: returns empty on any
|
||||||
/// any error — notably when
|
/// error — notably when `number` is an issue (no reviews endpoint) —
|
||||||
/// `number` is an issue (no reviews endpoint) — so callers degrade
|
/// so callers degrade gracefully. Skips PENDING reviews (not yet
|
||||||
/// gracefully. Skips PENDING reviews (not yet visible to others) and
|
/// visible to others) and reviews with neither a body nor line
|
||||||
/// empty-body reviews (a bare approval adds nothing to the thread).
|
/// comments (a bare approval adds nothing to the thread). Deliberately
|
||||||
/// `created_at` is synthesised from the review's `submitted_at` so
|
/// does NOT inline the per-line comments themselves: merging them into
|
||||||
/// the chronological merge sorts uniformly; `kind:"review"` + the
|
/// this glance-first thread would spam anyone who just wants status.
|
||||||
/// review `state` tag the entry for display.
|
/// `comments_count` carries a pointer to `pr reviews <n>` instead,
|
||||||
|
/// which prints the full per-line detail — see that verb
|
||||||
|
/// (`pr_reviews.rs`) for the exhaustive form. `created_at`
|
||||||
|
/// is synthesised from the review's `submitted_at` so the chronological
|
||||||
|
/// merge sorts uniformly; `kind:"review"` + the review `state` tag the
|
||||||
|
/// entry for display.
|
||||||
fn fetch_review_bodies(client: &Client, number: u64) -> Vec<Value> {
|
fn fetch_review_bodies(client: &Client, number: u64) -> Vec<Value> {
|
||||||
let Ok((owner, name)) = client.owner_repo() else {
|
let Ok((owner, name)) = client.owner_repo() else {
|
||||||
return Vec::new();
|
return Vec::new();
|
||||||
|
|
@ -268,7 +283,8 @@ fn fetch_review_bodies(client: &Client, number: u64) -> Vec<Value> {
|
||||||
if state == "PENDING" {
|
if state == "PENDING" {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
if r.body.as_deref().unwrap_or("").trim().is_empty() {
|
let comments_count = r.comments_count.unwrap_or(0);
|
||||||
|
if r.body.as_deref().unwrap_or("").trim().is_empty() && comments_count == 0 {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
let submitted = rfc3339(r.submitted_at);
|
let submitted = rfc3339(r.submitted_at);
|
||||||
|
|
@ -281,6 +297,7 @@ fn fetch_review_bodies(client: &Client, number: u64) -> Vec<Value> {
|
||||||
"html_url": r.html_url,
|
"html_url": r.html_url,
|
||||||
"kind": "review",
|
"kind": "review",
|
||||||
"state": state,
|
"state": state,
|
||||||
|
"comments_count": comments_count,
|
||||||
}))
|
}))
|
||||||
})
|
})
|
||||||
.collect()
|
.collect()
|
||||||
|
|
|
||||||
|
|
@ -124,7 +124,7 @@ fn list_reviews_json(client: &Client, number: u64, reviews: &[Value]) -> Result<
|
||||||
json!({
|
json!({
|
||||||
"id": c.get("id"),
|
"id": c.get("id"),
|
||||||
"path": c.get("path"),
|
"path": c.get("path"),
|
||||||
"line": c.get("line"),
|
"line": c.get("position"),
|
||||||
"body": c.get("body"),
|
"body": c.get("body"),
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
@ -187,8 +187,11 @@ fn list_reviews_text(client: &Client, number: u64, reviews: &[Value]) {
|
||||||
for c in &fetch_inline_comments(client, number, id) {
|
for c in &fetch_inline_comments(client, number, id) {
|
||||||
let path = c.get("path").and_then(Value::as_str).unwrap_or("?");
|
let path = c.get("path").and_then(Value::as_str).unwrap_or("?");
|
||||||
let cbody = c.get("body").and_then(Value::as_str).unwrap_or("").trim();
|
let cbody = c.get("body").and_then(Value::as_str).unwrap_or("").trim();
|
||||||
// PR-level comments have no line; omit `:line` when absent.
|
// PR-level comments have no position; omit `:line` when
|
||||||
match c.get("line").and_then(Value::as_u64) {
|
// absent (also true for a comment whose anchored line has
|
||||||
|
// since fallen out of the diff — forgejo drops `position`
|
||||||
|
// in that case too, same display fallback).
|
||||||
|
match c.get("position").and_then(Value::as_u64) {
|
||||||
Some(line) => println!(" [{path}:{line}] {cbody}"),
|
Some(line) => println!(" [{path}:{line}] {cbody}"),
|
||||||
None => println!(" [{path}] {cbody}"),
|
None => println!(" [{path}] {cbody}"),
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue