Radish alpha
h
Radicle Heartwood Protocol & Stack
Radicle
Git (anonymous pull)
Log in to clone via SSH
cli: Use library for opening patch
Alexis Sellier committed 3 years ago
commit 51dced480853442d085747c262d2778a8e82ab26
parent 39239fea512da7bbe2b15160dec90a2e21e81b77
9 files changed +71 -27
modified Cargo.lock
@@ -2092,6 +2092,7 @@ name = "radicle-remote-helper"
version = "0.2.0"
dependencies = [
 "radicle",
+
 "radicle-cli",
 "radicle-crypto",
 "radicle-git-ext",
 "thiserror",
modified 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
```
modified 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,
modified 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;
modified 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<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))
modified 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"
modified 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,
                )
modified 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<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)?;
-

-
    let result = match execute(
-
        "rad",
-
        ["patch", "open", "--quiet", "--no-push"],
-
        process::Stdio::piped(),
+
    push_ref(src, dst, false, nid, working, stored.raw())?;
+

+
    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();
+
    stored
+
        .raw()
+
        .find_reference(dst)
+
        .map(|mut r| r.delete())
+
        .ok();

-
    result
+
    result.map_err(Error::from)
}

/// Update an existing patch.
modified 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;