From 51dced480853442d085747c262d2778a8e82ab26 Mon Sep 17 00:00:00 2001 From: Alexis Sellier Date: Fri, 12 May 2023 16:05:26 +0200 Subject: [PATCH] cli: Use library for opening patch Instead of calling `rad patch open`, use `patch::create` directly. Also outputs the patch id for the user. --- Cargo.lock | 1 + radicle-cli/examples/rad-patch-via-push.md | 2 + radicle-cli/src/commands/patch/create.rs | 1 - radicle-cli/src/terminal.rs | 3 +- radicle-cli/src/terminal/patch.rs | 17 +++++- radicle-remote-helper/Cargo.toml | 4 ++ radicle-remote-helper/src/lib.rs | 2 +- radicle-remote-helper/src/push.rs | 67 +++++++++++++++------- radicle-term/src/lib.rs | 1 + 9 files changed, 71 insertions(+), 27 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 6e6e1dee..40d12d31 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2092,6 +2092,7 @@ name = "radicle-remote-helper" version = "0.2.0" dependencies = [ "radicle", + "radicle-cli", "radicle-crypto", "radicle-git-ext", "thiserror", diff --git a/radicle-cli/examples/rad-patch-via-push.md b/radicle-cli/examples/rad-patch-via-push.md index b021f693..9cb14a60 100644 --- a/radicle-cli/examples/rad-patch-via-push.md +++ b/radicle-cli/examples/rad-patch-via-push.md @@ -8,6 +8,7 @@ $ git checkout -b feature/1 Switched to a new branch 'feature/1' $ git commit -a -m "Add things" -q --allow-empty $ git push rad HEAD:refs/patches +✓ Patch 8f0e8ecb47a17e8f3219f33150a4092d645e4875 opened To rad://z42hL2jL4XNk6K8oHQaSWfMgCL7ji/z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi * [new reference] HEAD -> refs/patches ``` @@ -72,6 +73,7 @@ simpler: $ git checkout -b feature/2 -q $ git commit -a -m "Add more things" -q --allow-empty $ git push rad/patches +✓ Patch 8678aafff1d1e28430952abf431e60b87e28023c opened To rad://z42hL2jL4XNk6K8oHQaSWfMgCL7ji/z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi * [new reference] HEAD -> refs/patches ``` diff --git a/radicle-cli/src/commands/patch/create.rs b/radicle-cli/src/commands/patch/create.rs index 6ab87e99..b0ede200 100644 --- a/radicle-cli/src/commands/patch/create.rs +++ b/radicle-cli/src/commands/patch/create.rs @@ -60,7 +60,6 @@ pub fn run( let (target_ref, target_oid) = get_merge_target(storage, &head_branch)?; if head_branch.upstream().is_err() { - // TODO(cloudhead): Disable this when invoked from remote helper. radicle::git::set_upstream( workdir, &*radicle::rad::REMOTE_NAME, diff --git a/radicle-cli/src/terminal.rs b/radicle-cli/src/terminal.rs index 428c0dd0..8e6d8f94 100644 --- a/radicle-cli/src/terminal.rs +++ b/radicle-cli/src/terminal.rs @@ -4,11 +4,12 @@ pub mod format; pub mod io; pub use io::{proposal, signer}; pub mod patch; -pub use radicle_term::*; use std::ffi::OsString; use std::process; +pub use radicle_term::*; + use radicle::profile::Profile; use crate::terminal; diff --git a/radicle-cli/src/terminal/patch.rs b/radicle-cli/src/terminal/patch.rs index a1cf236e..6aee1d87 100644 --- a/radicle-cli/src/terminal/patch.rs +++ b/radicle-cli/src/terminal/patch.rs @@ -1,3 +1,5 @@ +use std::io; + use radicle::git; use crate::terminal as term; @@ -18,7 +20,13 @@ impl Message { /// Get the `Message` as a string according to the method. pub fn get(self, help: &str) -> std::io::Result { let comment = match self { - Message::Edit => term::Editor::new().extension("markdown").edit(help)?, + Message::Edit => { + if term::is_terminal(&io::stderr()) { + term::Editor::new().extension("markdown").edit(help)? + } else { + Some(help.to_owned()) + } + } Message::Blank => None, Message::Text(c) => Some(c), }; @@ -68,7 +76,7 @@ pub fn message(title: &str, description: &str) -> String { pub fn get_message( message: term::patch::Message, default_msg: &str, -) -> anyhow::Result<(String, String)> { +) -> io::Result<(String, String)> { let display_msg = default_msg.trim_end(); let message = message.get(&format!("{display_msg}\n{PATCH_MSG}"))?; @@ -78,7 +86,10 @@ pub fn get_message( let (title, description) = (title.trim().to_string(), description.trim().to_string()); if title.is_empty() { - anyhow::bail!("a patch title must be provided"); + return Err(io::Error::new( + io::ErrorKind::InvalidInput, + "a patch title must be provided", + )); } Ok((title, description)) diff --git a/radicle-remote-helper/Cargo.toml b/radicle-remote-helper/Cargo.toml index d21c41b6..87090a6a 100644 --- a/radicle-remote-helper/Cargo.toml +++ b/radicle-remote-helper/Cargo.toml @@ -17,6 +17,10 @@ version = "0" path = "../radicle-crypto" version = "0" +[dependencies.radicle-cli] +path = "../radicle-cli" +version = "0" + [[bin]] name = "git-remote-rad" path = "src/git-remote-rad.rs" diff --git a/radicle-remote-helper/src/lib.rs b/radicle-remote-helper/src/lib.rs index acdaf797..6ebf54cd 100644 --- a/radicle-remote-helper/src/lib.rs +++ b/radicle-remote-helper/src/lib.rs @@ -111,7 +111,7 @@ pub fn run(profile: radicle::Profile) -> Result<(), Error> { vec![refspec.to_string()], &working, url, - stored, + &stored, &profile, &stdin, ) diff --git a/radicle-remote-helper/src/push.rs b/radicle-remote-helper/src/push.rs index 35b4c2f3..0b955630 100644 --- a/radicle-remote-helper/src/push.rs +++ b/radicle-remote-helper/src/push.rs @@ -8,12 +8,15 @@ use std::{io, process}; use radicle::storage::git::cob::object::ParseObjectId; use thiserror::Error; -use radicle::crypto::PublicKey; +use radicle::cob::patch; +use radicle::crypto::{PublicKey, Signer}; use radicle::node::{Handle, NodeId}; use radicle::storage::git::transport::local::Url; use radicle::storage::WriteRepository; +use radicle::storage::{self, ReadRepository}; use radicle::Profile; use radicle::{git, rad}; +use radicle_cli::terminal as cli; use crate::read_line; @@ -52,6 +55,9 @@ pub enum Error { /// Parse error for object IDs. #[error(transparent)] ParseObjectId(#[from] ParseObjectId), + /// Patch COB error. + #[error(transparent)] + Patch(#[from] radicle::cob::patch::Error), } enum Command { @@ -94,11 +100,11 @@ impl FromStr for Command { } /// Run a git push command. -pub fn run( +pub fn run( mut specs: Vec, working: &Path, url: Url, - stored: R, + stored: &storage::git::Repository, profile: &Profile, stdin: &io::Stdin, ) -> Result<(), Error> { @@ -148,16 +154,15 @@ pub fn run( } Command::Push(git::Refspec { src, dst, force }) => { let working = git::raw::Repository::open(working)?; - let stored = stored.raw(); if let Some(oid) = dst.strip_prefix(git::refname!("refs/heads/patches")) { let oid = git::Oid::from_str(oid)?; - patch_update(src, dst, *force, &oid, &nid, &working, stored) + patch_update(src, dst, *force, &oid, &nid, &working, stored.raw()) } else if dst == &*rad::PATCHES_REFNAME { - patch_open(src, &nid, &working, stored) + patch_open(src, &nid, &working, stored, &signer) } else { - push_ref(src, dst, *force, &nid, &working, stored) + push_ref(src, dst, *force, &nid, &working, stored.raw()) } } }; @@ -182,7 +187,7 @@ pub fn run( // If our node is not running, we simply skip this step, as the // refs will be announced eventually, when the node restarts. if radicle::Node::new(profile.socket()).is_running() { - let rid = stored.id().to_string(); + let rid = stored.id.to_string(); let stderr = io::stderr().as_raw_fd(); // Nb. allow this to fail. The push to local storage was still successful. execute("rad", ["sync", &rid, "--verbose"], unsafe { @@ -199,11 +204,12 @@ pub fn run( } /// Open a new patch. -fn patch_open( +fn patch_open( src: &git::RefStr, nid: &NodeId, working: &git::raw::Repository, - stored: &git::raw::Repository, + stored: &storage::git::Repository, + signer: &G, ) -> Result<(), Error> { let reference = working.find_reference(src.as_str())?; let commit = reference.peel_to_commit()?; @@ -216,23 +222,37 @@ fn patch_open( // // In case the reference is not properly deleted, the next attempt to open a patch should // not fail, since the reference will already exist with the correct OID. - push_ref(src, dst, false, nid, working, stored)?; + push_ref(src, dst, false, nid, working, stored.raw())?; - let result = match execute( - "rad", - ["patch", "open", "--quiet", "--no-push"], - process::Stdio::piped(), + let mut patches = patch::Patches::open(stored).unwrap(); + let message = commit.message().unwrap_or_default(); + let (title, description) = cli::patch::get_message(cli::patch::Message::Edit, message)?; + let (_, target) = stored.canonical_head()?; + let base = stored.backend.merge_base(*target, commit.id())?; + let result = match patches.create( + title, + &description, + patch::MergeTarget::default(), + base, + commit.id(), + &[], + signer, ) { Ok(patch) => { - let patch = patch.trim(); - let patch = radicle::cob::ObjectId::from_str(patch)?; + let patch = patch.id; + + eprintln!( + "{} Patch {} opened", + cli::format::positive("✓"), + cli::format::tertiary(patch) + ); // Create long-lived patch head reference, now that we know the Patch ID. // // refs/namespaces//refs/heads/patches/ // let refname = git::refs::storage::patch(nid, &patch); - let _ = stored.reference( + let _ = stored.raw().reference( refname.as_str(), commit.id(), true, @@ -271,10 +291,15 @@ fn patch_open( } Err(e) => Err(e), }; - // Delete short-lived patch head reference. - stored.find_reference(dst).map(|mut r| r.delete()).ok(); - result + // Delete short-lived patch head reference. + stored + .raw() + .find_reference(dst) + .map(|mut r| r.delete()) + .ok(); + + result.map_err(Error::from) } /// Update an existing patch. diff --git a/radicle-term/src/lib.rs b/radicle-term/src/lib.rs index bc5d3ce8..272c0f2f 100644 --- a/radicle-term/src/lib.rs +++ b/radicle-term/src/lib.rs @@ -20,6 +20,7 @@ pub use element::{Element, Line, Max, Size}; pub use hstack::HStack; pub use inquire::ui::Styled; pub use io::*; +pub use is_terminal::is_terminal; pub use label::{label, Label}; pub use spinner::{spinner, Spinner}; pub use table::Table;