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:
parent
39239fea51
commit
51dced4808
|
|
@ -2092,6 +2092,7 @@ name = "radicle-remote-helper"
|
||||||
version = "0.2.0"
|
version = "0.2.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"radicle",
|
"radicle",
|
||||||
|
"radicle-cli",
|
||||||
"radicle-crypto",
|
"radicle-crypto",
|
||||||
"radicle-git-ext",
|
"radicle-git-ext",
|
||||||
"thiserror",
|
"thiserror",
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,7 @@ $ git checkout -b feature/1
|
||||||
Switched to a new branch 'feature/1'
|
Switched to a new branch 'feature/1'
|
||||||
$ git commit -a -m "Add things" -q --allow-empty
|
$ git commit -a -m "Add things" -q --allow-empty
|
||||||
$ git push rad HEAD:refs/patches
|
$ git push rad HEAD:refs/patches
|
||||||
|
✓ Patch 8f0e8ecb47a17e8f3219f33150a4092d645e4875 opened
|
||||||
To rad://z42hL2jL4XNk6K8oHQaSWfMgCL7ji/z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi
|
To rad://z42hL2jL4XNk6K8oHQaSWfMgCL7ji/z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi
|
||||||
* [new reference] HEAD -> refs/patches
|
* [new reference] HEAD -> refs/patches
|
||||||
```
|
```
|
||||||
|
|
@ -72,6 +73,7 @@ simpler:
|
||||||
$ git checkout -b feature/2 -q
|
$ git checkout -b feature/2 -q
|
||||||
$ git commit -a -m "Add more things" -q --allow-empty
|
$ git commit -a -m "Add more things" -q --allow-empty
|
||||||
$ git push rad/patches
|
$ git push rad/patches
|
||||||
|
✓ Patch 8678aafff1d1e28430952abf431e60b87e28023c opened
|
||||||
To rad://z42hL2jL4XNk6K8oHQaSWfMgCL7ji/z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi
|
To rad://z42hL2jL4XNk6K8oHQaSWfMgCL7ji/z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi
|
||||||
* [new reference] HEAD -> refs/patches
|
* [new reference] HEAD -> refs/patches
|
||||||
```
|
```
|
||||||
|
|
|
||||||
|
|
@ -60,7 +60,6 @@ pub fn run(
|
||||||
let (target_ref, target_oid) = get_merge_target(storage, &head_branch)?;
|
let (target_ref, target_oid) = get_merge_target(storage, &head_branch)?;
|
||||||
|
|
||||||
if head_branch.upstream().is_err() {
|
if head_branch.upstream().is_err() {
|
||||||
// TODO(cloudhead): Disable this when invoked from remote helper.
|
|
||||||
radicle::git::set_upstream(
|
radicle::git::set_upstream(
|
||||||
workdir,
|
workdir,
|
||||||
&*radicle::rad::REMOTE_NAME,
|
&*radicle::rad::REMOTE_NAME,
|
||||||
|
|
|
||||||
|
|
@ -4,11 +4,12 @@ pub mod format;
|
||||||
pub mod io;
|
pub mod io;
|
||||||
pub use io::{proposal, signer};
|
pub use io::{proposal, signer};
|
||||||
pub mod patch;
|
pub mod patch;
|
||||||
pub use radicle_term::*;
|
|
||||||
|
|
||||||
use std::ffi::OsString;
|
use std::ffi::OsString;
|
||||||
use std::process;
|
use std::process;
|
||||||
|
|
||||||
|
pub use radicle_term::*;
|
||||||
|
|
||||||
use radicle::profile::Profile;
|
use radicle::profile::Profile;
|
||||||
|
|
||||||
use crate::terminal;
|
use crate::terminal;
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,5 @@
|
||||||
|
use std::io;
|
||||||
|
|
||||||
use radicle::git;
|
use radicle::git;
|
||||||
|
|
||||||
use crate::terminal as term;
|
use crate::terminal as term;
|
||||||
|
|
@ -18,7 +20,13 @@ impl Message {
|
||||||
/// Get the `Message` as a string according to the method.
|
/// Get the `Message` as a string according to the method.
|
||||||
pub fn get(self, help: &str) -> std::io::Result<String> {
|
pub fn get(self, help: &str) -> std::io::Result<String> {
|
||||||
let comment = match self {
|
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::Blank => None,
|
||||||
Message::Text(c) => Some(c),
|
Message::Text(c) => Some(c),
|
||||||
};
|
};
|
||||||
|
|
@ -68,7 +76,7 @@ pub fn message(title: &str, description: &str) -> String {
|
||||||
pub fn get_message(
|
pub fn get_message(
|
||||||
message: term::patch::Message,
|
message: term::patch::Message,
|
||||||
default_msg: &str,
|
default_msg: &str,
|
||||||
) -> anyhow::Result<(String, String)> {
|
) -> io::Result<(String, String)> {
|
||||||
let display_msg = default_msg.trim_end();
|
let display_msg = default_msg.trim_end();
|
||||||
|
|
||||||
let message = message.get(&format!("{display_msg}\n{PATCH_MSG}"))?;
|
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());
|
let (title, description) = (title.trim().to_string(), description.trim().to_string());
|
||||||
|
|
||||||
if title.is_empty() {
|
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))
|
Ok((title, description))
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,10 @@ version = "0"
|
||||||
path = "../radicle-crypto"
|
path = "../radicle-crypto"
|
||||||
version = "0"
|
version = "0"
|
||||||
|
|
||||||
|
[dependencies.radicle-cli]
|
||||||
|
path = "../radicle-cli"
|
||||||
|
version = "0"
|
||||||
|
|
||||||
[[bin]]
|
[[bin]]
|
||||||
name = "git-remote-rad"
|
name = "git-remote-rad"
|
||||||
path = "src/git-remote-rad.rs"
|
path = "src/git-remote-rad.rs"
|
||||||
|
|
|
||||||
|
|
@ -111,7 +111,7 @@ pub fn run(profile: radicle::Profile) -> Result<(), Error> {
|
||||||
vec![refspec.to_string()],
|
vec![refspec.to_string()],
|
||||||
&working,
|
&working,
|
||||||
url,
|
url,
|
||||||
stored,
|
&stored,
|
||||||
&profile,
|
&profile,
|
||||||
&stdin,
|
&stdin,
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -8,12 +8,15 @@ use std::{io, process};
|
||||||
use radicle::storage::git::cob::object::ParseObjectId;
|
use radicle::storage::git::cob::object::ParseObjectId;
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
|
||||||
use radicle::crypto::PublicKey;
|
use radicle::cob::patch;
|
||||||
|
use radicle::crypto::{PublicKey, Signer};
|
||||||
use radicle::node::{Handle, NodeId};
|
use radicle::node::{Handle, NodeId};
|
||||||
use radicle::storage::git::transport::local::Url;
|
use radicle::storage::git::transport::local::Url;
|
||||||
use radicle::storage::WriteRepository;
|
use radicle::storage::WriteRepository;
|
||||||
|
use radicle::storage::{self, ReadRepository};
|
||||||
use radicle::Profile;
|
use radicle::Profile;
|
||||||
use radicle::{git, rad};
|
use radicle::{git, rad};
|
||||||
|
use radicle_cli::terminal as cli;
|
||||||
|
|
||||||
use crate::read_line;
|
use crate::read_line;
|
||||||
|
|
||||||
|
|
@ -52,6 +55,9 @@ pub enum Error {
|
||||||
/// Parse error for object IDs.
|
/// Parse error for object IDs.
|
||||||
#[error(transparent)]
|
#[error(transparent)]
|
||||||
ParseObjectId(#[from] ParseObjectId),
|
ParseObjectId(#[from] ParseObjectId),
|
||||||
|
/// Patch COB error.
|
||||||
|
#[error(transparent)]
|
||||||
|
Patch(#[from] radicle::cob::patch::Error),
|
||||||
}
|
}
|
||||||
|
|
||||||
enum Command {
|
enum Command {
|
||||||
|
|
@ -94,11 +100,11 @@ impl FromStr for Command {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Run a git push command.
|
/// Run a git push command.
|
||||||
pub fn run<R: WriteRepository>(
|
pub fn run(
|
||||||
mut specs: Vec<String>,
|
mut specs: Vec<String>,
|
||||||
working: &Path,
|
working: &Path,
|
||||||
url: Url,
|
url: Url,
|
||||||
stored: R,
|
stored: &storage::git::Repository,
|
||||||
profile: &Profile,
|
profile: &Profile,
|
||||||
stdin: &io::Stdin,
|
stdin: &io::Stdin,
|
||||||
) -> Result<(), Error> {
|
) -> Result<(), Error> {
|
||||||
|
|
@ -148,16 +154,15 @@ pub fn run<R: WriteRepository>(
|
||||||
}
|
}
|
||||||
Command::Push(git::Refspec { src, dst, force }) => {
|
Command::Push(git::Refspec { src, dst, force }) => {
|
||||||
let working = git::raw::Repository::open(working)?;
|
let working = git::raw::Repository::open(working)?;
|
||||||
let stored = stored.raw();
|
|
||||||
|
|
||||||
if let Some(oid) = dst.strip_prefix(git::refname!("refs/heads/patches")) {
|
if let Some(oid) = dst.strip_prefix(git::refname!("refs/heads/patches")) {
|
||||||
let oid = git::Oid::from_str(oid)?;
|
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 {
|
} else if dst == &*rad::PATCHES_REFNAME {
|
||||||
patch_open(src, &nid, &working, stored)
|
patch_open(src, &nid, &working, stored, &signer)
|
||||||
} else {
|
} 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
|
// If our node is not running, we simply skip this step, as the
|
||||||
// refs will be announced eventually, when the node restarts.
|
// refs will be announced eventually, when the node restarts.
|
||||||
if radicle::Node::new(profile.socket()).is_running() {
|
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();
|
let stderr = io::stderr().as_raw_fd();
|
||||||
// Nb. allow this to fail. The push to local storage was still successful.
|
// Nb. allow this to fail. The push to local storage was still successful.
|
||||||
execute("rad", ["sync", &rid, "--verbose"], unsafe {
|
execute("rad", ["sync", &rid, "--verbose"], unsafe {
|
||||||
|
|
@ -199,11 +204,12 @@ pub fn run<R: WriteRepository>(
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Open a new patch.
|
/// Open a new patch.
|
||||||
fn patch_open(
|
fn patch_open<G: Signer>(
|
||||||
src: &git::RefStr,
|
src: &git::RefStr,
|
||||||
nid: &NodeId,
|
nid: &NodeId,
|
||||||
working: &git::raw::Repository,
|
working: &git::raw::Repository,
|
||||||
stored: &git::raw::Repository,
|
stored: &storage::git::Repository,
|
||||||
|
signer: &G,
|
||||||
) -> Result<(), Error> {
|
) -> Result<(), Error> {
|
||||||
let reference = working.find_reference(src.as_str())?;
|
let reference = working.find_reference(src.as_str())?;
|
||||||
let commit = reference.peel_to_commit()?;
|
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
|
// 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.
|
// 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(
|
let mut patches = patch::Patches::open(stored).unwrap();
|
||||||
"rad",
|
let message = commit.message().unwrap_or_default();
|
||||||
["patch", "open", "--quiet", "--no-push"],
|
let (title, description) = cli::patch::get_message(cli::patch::Message::Edit, message)?;
|
||||||
process::Stdio::piped(),
|
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) => {
|
Ok(patch) => {
|
||||||
let patch = patch.trim();
|
let patch = patch.id;
|
||||||
let patch = radicle::cob::ObjectId::from_str(patch)?;
|
|
||||||
|
eprintln!(
|
||||||
|
"{} Patch {} opened",
|
||||||
|
cli::format::positive("✓"),
|
||||||
|
cli::format::tertiary(patch)
|
||||||
|
);
|
||||||
|
|
||||||
// Create long-lived patch head reference, now that we know the Patch ID.
|
// Create long-lived patch head reference, now that we know the Patch ID.
|
||||||
//
|
//
|
||||||
// refs/namespaces/<nid>/refs/heads/patches/<patch-id>
|
// refs/namespaces/<nid>/refs/heads/patches/<patch-id>
|
||||||
//
|
//
|
||||||
let refname = git::refs::storage::patch(nid, &patch);
|
let refname = git::refs::storage::patch(nid, &patch);
|
||||||
let _ = stored.reference(
|
let _ = stored.raw().reference(
|
||||||
refname.as_str(),
|
refname.as_str(),
|
||||||
commit.id(),
|
commit.id(),
|
||||||
true,
|
true,
|
||||||
|
|
@ -271,10 +291,15 @@ fn patch_open(
|
||||||
}
|
}
|
||||||
Err(e) => Err(e),
|
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.
|
/// Update an existing patch.
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,7 @@ pub use element::{Element, Line, Max, Size};
|
||||||
pub use hstack::HStack;
|
pub use hstack::HStack;
|
||||||
pub use inquire::ui::Styled;
|
pub use inquire::ui::Styled;
|
||||||
pub use io::*;
|
pub use io::*;
|
||||||
|
pub use is_terminal::is_terminal;
|
||||||
pub use label::{label, Label};
|
pub use label::{label, Label};
|
||||||
pub use spinner::{spinner, Spinner};
|
pub use spinner::{spinner, Spinner};
|
||||||
pub use table::Table;
|
pub use table::Table;
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue