cli: Use library for opening patch

Instead of calling `rad patch open`, use `patch::create` directly.

Also outputs the patch id for the user.
This commit is contained in:
Alexis Sellier 2023-05-12 16:05:26 +02:00
parent 39239fea51
commit 51dced4808
No known key found for this signature in database
9 changed files with 71 additions and 27 deletions

1
Cargo.lock generated
View File

@ -2092,6 +2092,7 @@ name = "radicle-remote-helper"
version = "0.2.0"
dependencies = [
"radicle",
"radicle-cli",
"radicle-crypto",
"radicle-git-ext",
"thiserror",

View File

@ -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
```

View File

@ -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,

View File

@ -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;

View File

@ -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<String> {
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))

View File

@ -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"

View File

@ -111,7 +111,7 @@ pub fn run(profile: radicle::Profile) -> Result<(), Error> {
vec![refspec.to_string()],
&working,
url,
stored,
&stored,
&profile,
&stdin,
)

View File

@ -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<R: WriteRepository>(
pub fn run(
mut specs: Vec<String>,
working: &Path,
url: Url,
stored: R,
stored: &storage::git::Repository,
profile: &Profile,
stdin: &io::Stdin,
) -> Result<(), Error> {
@ -148,16 +154,15 @@ pub fn run<R: WriteRepository>(
}
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<R: WriteRepository>(
// 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<R: WriteRepository>(
}
/// Open a new patch.
fn patch_open(
fn patch_open<G: Signer>(
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/<nid>/refs/heads/patches/<patch-id>
//
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.

View File

@ -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;