Radish alpha
h
Radicle Heartwood Protocol & Stack
Radicle
Git (anonymous pull)
Log in to clone via SSH
Add `rad-track`, `rad-untrack` commands
xphoniex committed 3 years ago
commit 1d51afad285943849653053282c8adc0dde78aa1
parent f05a040be65b7495855441fb13175841710b12e9
6 files changed +221 -4
modified radicle-cli/src/commands.rs
@@ -10,3 +10,7 @@ pub mod rad_help;
pub mod rad_init;
#[path = "commands/self.rs"]
pub mod rad_self;
+
#[path = "commands/track.rs"]
+
pub mod rad_track;
+
#[path = "commands/untrack.rs"]
+
pub mod rad_untrack;
modified radicle-cli/src/commands/help.rs
@@ -3,10 +3,7 @@ use std::ffi::OsString;
use crate::terminal as term;
use crate::terminal::args::{Args, Error, Help};

-
use super::rad_auth;
-
use super::rad_checkout;
-
use super::rad_init;
-
use super::rad_self;
+
use super::*;

pub const HELP: Help = Help {
    name: "help",
@@ -20,6 +17,8 @@ const COMMANDS: &[Help] = &[
    rad_init::HELP,
    rad_checkout::HELP,
    rad_self::HELP,
+
    rad_track::HELP,
+
    rad_untrack::HELP,
    HELP,
];

added radicle-cli/src/commands/track.rs
@@ -0,0 +1,101 @@
+
use std::ffi::OsString;
+

+
use anyhow::{anyhow, Context as _};
+

+
use radicle::identity::project::Id;
+
use radicle::node::Handle;
+
use radicle::prelude::*;
+
use radicle::storage::WriteStorage;
+

+
use crate::terminal as term;
+
use crate::terminal::args::{Args, Error, Help};
+

+
pub const HELP: Help = Help {
+
    name: "track",
+
    description: "Manage radicle project tracking relationships",
+
    version: env!("CARGO_PKG_VERSION"),
+
    usage: r#"
+
Usage
+

+
    rad track [<id>] [--fetch]
+

+
Options
+

+
    --fetch                Fetch the peer's refs into the working copy
+
    --verbose, -v          Verbose output
+
    --help                 Print help
+
"#,
+
};
+

+
#[derive(Debug)]
+
pub struct Options {
+
    pub id: Option<Id>,
+
    pub fetch: bool,
+
    pub verbose: bool,
+
}
+

+
impl Args for Options {
+
    fn from_args(args: Vec<OsString>) -> anyhow::Result<(Self, Vec<OsString>)> {
+
        use lexopt::prelude::*;
+

+
        let mut parser = lexopt::Parser::from_args(args);
+
        let mut id: Option<Id> = None;
+
        let mut fetch = true;
+
        let mut verbose = false;
+

+
        while let Some(arg) = parser.next()? {
+
            match arg {
+
                Long("no-fetch") => fetch = false,
+
                Long("verbose") | Short('v') => verbose = true,
+
                Value(val) if id.is_none() => {
+
                    let val = val.to_string_lossy();
+

+
                    if let Ok(val) = Id::from_human(&val) {
+
                        id = Some(val);
+
                    } else {
+
                        return Err(anyhow!("invalid ID '{}'", val));
+
                    }
+
                }
+
                Long("help") => {
+
                    return Err(Error::Help.into());
+
                }
+
                _ => {
+
                    return Err(anyhow!(arg.unexpected()));
+
                }
+
            }
+
        }
+

+
        Ok((Options { id, fetch, verbose }, vec![]))
+
    }
+
}
+

+
pub fn run(options: Options, ctx: impl term::Context) -> anyhow::Result<()> {
+
    let id = options
+
        .id
+
        .or_else(|| radicle::rad::cwd().ok().map(|(_, id)| id))
+
        .context("current directory is not a git repository; please supply an `<id>`")?;
+
    let profile = ctx.profile()?;
+
    let storage = &profile.storage;
+
    let Doc { payload, .. } = storage.repository(id)?.project_of(profile.id())?;
+
    let node = radicle::node::connect(&profile.node())?;
+

+
    term::info!(
+
        "Establishing 🌱 tracking relationship for {}",
+
        term::format::highlight(&payload.name)
+
    );
+
    term::blank();
+

+
    let tracked = node.track(&id)?;
+
    term::success!(
+
        "Tracking relationship for {} ({}) {}",
+
        term::format::tertiary(&payload.name),
+
        &id.to_human(),
+
        if tracked { "established" } else { "exists" }
+
    );
+

+
    if options.fetch {
+
        node.fetch(&id)?;
+
    }
+

+
    Ok(())
+
}
added radicle-cli/src/commands/untrack.rs
@@ -0,0 +1,89 @@
+
use std::ffi::OsString;
+

+
use anyhow::{anyhow, Context as _};
+

+
use radicle::identity::project::Id;
+
use radicle::node::Handle;
+
use radicle::prelude::*;
+
use radicle::storage::WriteStorage;
+

+
use crate::terminal as term;
+
use crate::terminal::args::{Args, Error, Help};
+

+
pub const HELP: Help = Help {
+
    name: "untrack",
+
    description: "Untrack radicle project peers",
+
    version: env!("CARGO_PKG_VERSION"),
+
    usage: r#"
+
Usage
+

+
    rad untrack [<id>]
+

+
Options
+

+
    --help              Print help
+
"#,
+
};
+

+
#[derive(Debug)]
+
pub struct Options {
+
    pub id: Option<Id>,
+
}
+

+
impl Args for Options {
+
    fn from_args(args: Vec<OsString>) -> anyhow::Result<(Self, Vec<OsString>)> {
+
        use lexopt::prelude::*;
+

+
        let mut parser = lexopt::Parser::from_args(args);
+
        let mut id: Option<Id> = None;
+

+
        while let Some(arg) = parser.next()? {
+
            match arg {
+
                Value(val) if id.is_none() => {
+
                    let val = val.to_string_lossy();
+

+
                    if let Ok(val) = Id::from_human(&val) {
+
                        id = Some(val);
+
                    } else {
+
                        return Err(anyhow!("invalid ID '{}'", val));
+
                    }
+
                }
+
                Long("help") => {
+
                    return Err(Error::Help.into());
+
                }
+
                _ => {
+
                    return Err(anyhow!(arg.unexpected()));
+
                }
+
            }
+
        }
+

+
        Ok((Options { id }, vec![]))
+
    }
+
}
+

+
pub fn run(options: Options, ctx: impl term::Context) -> anyhow::Result<()> {
+
    let id = options
+
        .id
+
        .or_else(|| radicle::rad::cwd().ok().map(|(_, id)| id))
+
        .context("current directory is not a git repository; please supply an `<id>`")?;
+
    let profile = ctx.profile()?;
+
    let storage = &profile.storage;
+
    let Doc { payload, .. } = storage.repository(id)?.project_of(profile.id())?;
+
    let node = radicle::node::connect(&profile.node())?;
+

+
    if node.untrack(&id)? {
+
        term::success!(
+
            "Tracking relationships for {} ({}) removed",
+
            term::format::highlight(payload.name),
+
            &id.to_human()
+
        );
+
    } else {
+
        term::info!(
+
            "Tracking relationships for {} ({}) doesn't exist",
+
            term::format::highlight(payload.name),
+
            &id.to_human()
+
        );
+
    }
+

+
    Ok(())
+
}
modified radicle-cli/src/main.rs
@@ -142,6 +142,22 @@ fn run_other(exe: &str, args: &[OsString]) -> Result<(), Option<anyhow::Error>>
                args.to_vec(),
            );
        }
+
        "track" => {
+
            term::run_command_args::<rad_track::Options, _>(
+
                rad_track::HELP,
+
                "Track",
+
                rad_track::run,
+
                args.to_vec(),
+
            );
+
        }
+
        "untrack" => {
+
            term::run_command_args::<rad_untrack::Options, _>(
+
                rad_untrack::HELP,
+
                "Untrack",
+
                rad_untrack::run,
+
                args.to_vec(),
+
            );
+
        }
        _ => {
            let exe = format!("{}-{}", NAME, exe);
            let status = process::Command::new(exe.clone()).args(args).status();
modified radicle/src/rad.rs
@@ -324,6 +324,14 @@ pub fn remote(repo: &git2::Repository) -> Result<(git2::Remote<'_>, Id), RemoteE
    Ok((remote, url.repo))
}

+
/// Get the Id of project in current working directory
+
pub fn cwd() -> Result<(git2::Repository, Id), RemoteError> {
+
    let repo = git2::Repository::open(Path::new("."))?;
+
    let (_, id) = remote(&repo)?;
+

+
    Ok((repo, id))
+
}
+

#[cfg(test)]
mod tests {
    use std::collections::HashMap;