Radish alpha
h
rad:z3gqcJUoA1n9HaHKufZs5FCSGazv5
Radicle Heartwood Protocol & Stack
Radicle
Git
cli: add un/resolve for patch review comments
Merged fintohaps opened 1 year ago

Patch review comments can be resolved and unresolved as part of the Patch COB API. This functionality was missing from the rad patch command.

Add a subcommand rad patch resolve which can resolve a review comment, and unresolve given the --unresolve flag.

2 files changed +106 -0 84aaf9ef fa19105f
modified radicle-cli/src/commands/patch.rs
@@ -22,6 +22,8 @@ mod list;
mod ready;
#[path = "patch/redact.rs"]
mod redact;
+
#[path = "patch/resolve.rs"]
+
mod resolve;
#[path = "patch/review.rs"]
mod review;
#[path = "patch/show.rs"]
@@ -61,6 +63,7 @@ Usage
    rad patch update <patch-id> [<option>...]
    rad patch checkout <patch-id> [<option>...]
    rad patch review <patch-id> [--accept | --reject] [-m [<string>]] [-d | --delete] [<option>...]
+
    rad patch resolve <patch-id> [--review <review-id>] [--comment <comment-id>] [--unresolve] [<option>...]
    rad patch delete <patch-id> [<option>...]
    rad patch redact <revision-id> [<option>...]
    rad patch assign <revision-id> [--add <did>] [--delete <did>] [<option>...]
@@ -101,6 +104,12 @@ Review options
    -d, --delete               Delete a review draft
    -m, --message [<string>]   Provide a comment with the review (default: prompt)

+
Resolve options
+

+
    --review <id>              The review id which the comment is under
+
    --comment <id>             The comment to (un)resolve
+
    --undo                     Unresolve the comment
+

Assign options

    -a, --add    <did>         Add an assignee to the patch (may be specified multiple times).
@@ -169,6 +178,7 @@ pub enum OperationName {
    Comment,
    Ready,
    Review,
+
    Resolve,
    Label,
    #[default]
    List,
@@ -232,6 +242,12 @@ pub enum Operation {
        revision_id: Option<Rev>,
        opts: review::Options,
    },
+
    Resolve {
+
        patch_id: Rev,
+
        review_id: Rev,
+
        comment_id: Rev,
+
        undo: bool,
+
    },
    Assign {
        patch_id: Rev,
        opts: AssignOptions,
@@ -268,6 +284,7 @@ impl Operation {
            | Operation::Delete { .. }
            | Operation::Comment { .. }
            | Operation::Review { .. }
+
            | Operation::Resolve { .. }
            | Operation::Assign { .. }
            | Operation::Label { .. }
            | Operation::Edit { .. }
@@ -306,6 +323,8 @@ impl Args for Options {
        let mut announce = true;
        let mut patch_id = None;
        let mut revision_id = None;
+
        let mut review_id = None;
+
        let mut comment_id = None;
        let mut message = Message::default();
        let mut filter = Some(patch::Status::Open);
        let mut diff = false;
@@ -442,6 +461,23 @@ impl Args for Options {
                    }
                }

+
                // Resolve options
+
                Long("undo") if op == Some(OperationName::Resolve) => {
+
                    undo = true;
+
                }
+
                Long("review") if op == Some(OperationName::Resolve) => {
+
                    let val = parser.value()?;
+
                    let rev = term::args::rev(&val)?;
+

+
                    review_id = Some(rev);
+
                }
+
                Long("comment") if op == Some(OperationName::Resolve) => {
+
                    let val = parser.value()?;
+
                    let rev = term::args::rev(&val)?;
+

+
                    comment_id = Some(rev);
+
                }
+

                // Checkout options
                Long("revision") if op == Some(OperationName::Checkout) => {
                    let val = parser.value()?;
@@ -545,6 +581,7 @@ impl Args for Options {
                    "label" => op = Some(OperationName::Label),
                    "comment" => op = Some(OperationName::Comment),
                    "review" => op = Some(OperationName::Review),
+
                    "resolve" => op = Some(OperationName::Resolve),
                    "set" => op = Some(OperationName::Set),
                    "cache" => op = Some(OperationName::Cache),
                    unknown => anyhow::bail!("unknown operation '{}'", unknown),
@@ -565,6 +602,7 @@ impl Args for Options {
                            Some(OperationName::Checkout),
                            Some(OperationName::Comment),
                            Some(OperationName::Review),
+
                            Some(OperationName::Resolve),
                            Some(OperationName::Edit),
                            Some(OperationName::Set),
                            Some(OperationName::Assign),
@@ -623,6 +661,13 @@ impl Args for Options {
                    op: review_op,
                },
            },
+
            OperationName::Resolve => Operation::Resolve {
+
                patch_id: patch_id
+
                    .ok_or_else(|| anyhow!("a patch or revision must be provided"))?,
+
                review_id: review_id.ok_or_else(|| anyhow!("a review must be provided"))?,
+
                comment_id: comment_id.ok_or_else(|| anyhow!("a comment must be provided"))?,
+
                undo,
+
            },
            OperationName::Ready => Operation::Ready {
                patch_id: patch_id.ok_or_else(|| anyhow!("a patch must be provided"))?,
                undo,
@@ -804,6 +849,25 @@ pub fn run(options: Options, ctx: impl term::Context) -> anyhow::Result<()> {
                .map(patch::RevisionId::from);
            review::run(patch_id, revision_id, opts, &profile, &repository)?;
        }
+
        Operation::Resolve {
+
            ref patch_id,
+
            ref review_id,
+
            ref comment_id,
+
            undo,
+
        } => {
+
            let patch = patch_id.resolve(&repository.backend)?;
+
            let review = patch::ReviewId::from(
+
                review_id.resolve::<radicle::cob::EntryId>(&repository.backend)?,
+
            );
+
            let comment = comment_id.resolve(&repository.backend)?;
+
            if undo {
+
                resolve::unresolve(patch, review, comment, &repository, &profile)?;
+
                term::success!("Unresolved comment {comment_id}");
+
            } else {
+
                resolve::resolve(patch, review, comment, &repository, &profile)?;
+
                term::success!("Resolved comment {comment_id}");
+
            }
+
        }
        Operation::Edit {
            patch_id,
            revision_id,
added radicle-cli/src/commands/patch/resolve.rs
@@ -0,0 +1,42 @@
+
use anyhow::anyhow;
+
use radicle::cob::thread::CommentId;
+
use radicle::patch::{self, PatchId};
+
use radicle::patch::{cache::Patches as _, ReviewId};
+
use radicle::storage::git::Repository;
+
use radicle::Profile;
+

+
use crate::terminal as term;
+

+
pub fn resolve(
+
    patch_id: PatchId,
+
    review: ReviewId,
+
    comment: CommentId,
+
    repo: &Repository,
+
    profile: &Profile,
+
) -> anyhow::Result<()> {
+
    let signer = term::signer(profile)?;
+
    let mut patches = profile.patches_mut(repo)?;
+
    let patch = patches
+
        .get(&patch_id)?
+
        .ok_or_else(|| anyhow!("Patch `{patch_id}` not found"))?;
+
    let mut patch = patch::PatchMut::new(patch_id, patch, &mut patches);
+
    patch.resolve_review_comment(review, comment, &signer)?;
+
    Ok(())
+
}
+

+
pub fn unresolve(
+
    patch_id: PatchId,
+
    review: ReviewId,
+
    comment: CommentId,
+
    repo: &Repository,
+
    profile: &Profile,
+
) -> anyhow::Result<()> {
+
    let signer = term::signer(profile)?;
+
    let mut patches = profile.patches_mut(repo)?;
+
    let patch = patches
+
        .get(&patch_id)?
+
        .ok_or_else(|| anyhow!("Patch `{patch_id}` not found"))?;
+
    let mut patch = patch::PatchMut::new(patch_id, patch, &mut patches);
+
    patch.unresolve_review_comment(review, comment, &signer)?;
+
    Ok(())
+
}