Watch
0
0
Fork
You've already forked hyperhive
0

hive-forge: review-reply adds the comment to the answered comment's review, as the web UI does

This commit is contained in:
damocles 2026-11-05 07:52:03 +01:00 • committed by mara
commit 3ff4dc7be0

View file

@ -1,12 +1,12 @@
//! `pr review-reply <number> <comment-id>` - answer an inline review comment. //! `pr review-reply <number> <comment-id>` - answer an inline review comment.
//! //!
//! Forgejo's API has no reply endpoint, so the reply is a one-comment //! Forgejo's API has no reply endpoint. The web UI stores a reply in the same
//! `COMMENT` review anchored at the same path and line as the comment it //! review as the comment it answers, so this adds a comment to that review at
//! answers; the forge shows comments with one anchor as one conversation. //! the same path and line. A new review at the same anchor is not enough: the
//! forge shows it as a conversation of its own.
use anyhow::{Result, bail}; use anyhow::{Result, bail};
use clap::Args as ClapArgs; use clap::Args as ClapArgs;
use forgejo_api::structs::{CreatePullReviewComment, CreatePullReviewOptions};
use serde_json::{Value, json}; use serde_json::{Value, json};
use crate::body; use crate::body;
@ -66,8 +66,9 @@ fn anchor_of(comment: &Value) -> Result<Anchor> {
}) })
} }
/// Find the inline comment with `id` among the PR's reviews. /// Find the inline comment with `id` among the PR's reviews, with the id of the
fn find_comment(client: &Client, number: u64, id: i64) -> Result<Value> { /// review it belongs to.
fn find_comment(client: &Client, number: u64, id: i64) -> Result<(i64, Value)> {
let (owner, name) = client.owner_repo()?; let (owner, name) = client.owner_repo()?;
let reviews = all_pages(|page, size| { let reviews = all_pages(|page, size| {
let (_, batch) = client let (_, batch) = client
@ -86,7 +87,7 @@ fn find_comment(client: &Client, number: u64, id: i64) -> Result<Value> {
.into_iter() .into_iter()
.find(|c| c.get("id").and_then(Value::as_i64) == Some(id)); .find(|c| c.get("id").and_then(Value::as_i64) == Some(id));
if let Some(c) = found { if let Some(c) = found {
return Ok(c); return Ok((review_id, c));
} }
} }
bail!("no inline review comment #{id} on PR #{number}; `pr reviews {number}` lists their ids") bail!("no inline review comment #{id} on PR #{number}; `pr reviews {number}` lists their ids")
@ -102,34 +103,28 @@ pub fn run(client: &Client, args: Args) -> Result<()> {
args.body_file.as_deref(), args.body_file.as_deref(),
"pr review-reply", "pr review-reply",
)?; )?;
let target = find_comment(client, args.number, args.comment_id)?; let (review_id, target) = find_comment(client, args.number, args.comment_id)?;
let anchor = anchor_of(&target)?; let anchor = anchor_of(&target)?;
let (owner, name) = client.owner_repo()?; let (owner, name) = client.owner_repo()?;
let review = client let comment = client
.api() .api()
.repo_create_pull_review( .repo_create_pull_review_comment(
owner, owner,
name, name,
index(args.number)?, index(args.number)?,
CreatePullReviewOptions { review_id,
body: Some(String::new()), json!({
comments: Some(vec![CreatePullReviewComment { "body": text,
body: Some(text), "extra_lines_count": anchor.extra_lines,
extra_lines_count: Some(anchor.extra_lines), "new_position": anchor.line,
new_position: Some(anchor.line), "old_position": 0,
old_position: Some(0), "path": anchor.path,
path: Some(anchor.path), }),
}]),
commit_id: target
.get("commit_id")
.and_then(Value::as_str)
.map(str::to_owned),
event: Some("COMMENT".to_owned()),
},
) )
.send()?; .send()?;
print_json(&json!({ print_json(&json!({
"review": review.id, "comment": comment.id,
"review": review_id,
"in_reply_to": args.comment_id, "in_reply_to": args.comment_id,
"at": comment_location(&target), "at": comment_location(&target),
})) }))