Radish alpha
h
rad:z3gqcJUoA1n9HaHKufZs5FCSGazv5
Radicle Heartwood Protocol & Stack
Radicle
Git
cli: A few improvements
Merged did:key:z6MksFqX...wzpT opened 2 years ago

See commits.

13 files changed +125 -57 0908c65f eff40166
modified radicle-cli/examples/rad-block.md
@@ -36,8 +36,8 @@ And a 'block' policy was added:
```
$ rad seed
╭───────────────────────────────────────────────────────╮
-
│ RID                                 Scope      Policy │
+
│ Repository                          Policy   Scope    │
├───────────────────────────────────────────────────────┤
-
│ rad:z42hL2jL4XNk6K8oHQaSWfMgCL7ji   followed   block  │
+
│ rad:z42hL2jL4XNk6K8oHQaSWfMgCL7ji   block    followed │
╰───────────────────────────────────────────────────────╯
```
modified radicle-cli/examples/rad-node.md
@@ -35,9 +35,9 @@ repository that was already created:
```
$ rad seed
╭───────────────────────────────────────────────────────╮
-
│ RID                                 Scope      Policy │
+
│ Repository                          Policy   Scope    │
├───────────────────────────────────────────────────────┤
-
│ rad:z42hL2jL4XNk6K8oHQaSWfMgCL7ji   followed   allow  │
+
│ rad:z42hL2jL4XNk6K8oHQaSWfMgCL7ji   allow    followed │
╰───────────────────────────────────────────────────────╯
```

modified radicle-cli/examples/rad-seed-and-follow.md
@@ -30,8 +30,8 @@ We can list the repositories we are seeding by omitting the RID:
```
$ rad seed
╭───────────────────────────────────────────────────────╮
-
│ RID                                 Scope      Policy │
+
│ Repository                          Policy   Scope    │
├───────────────────────────────────────────────────────┤
-
│ rad:z42hL2jL4XNk6K8oHQaSWfMgCL7ji   followed   allow  │
+
│ rad:z42hL2jL4XNk6K8oHQaSWfMgCL7ji   allow    followed │
╰───────────────────────────────────────────────────────╯
```
modified radicle-cli/examples/rad-unseed.md
@@ -12,7 +12,7 @@ $ rad ls
We could stop seeding it if we didn't want other nodes to fetch it from us:

```
-
$ rad seed --delete rad:z42hL2jL4XNk6K8oHQaSWfMgCL7ji
+
$ rad unseed rad:z42hL2jL4XNk6K8oHQaSWfMgCL7ji
✓ Seeding policy for rad:z42hL2jL4XNk6K8oHQaSWfMgCL7ji removed
```

modified radicle-cli/src/commands.rs
@@ -52,5 +52,7 @@ pub mod rad_stats;
pub mod rad_sync;
#[path = "commands/unfollow.rs"]
pub mod rad_unfollow;
+
#[path = "commands/unseed.rs"]
+
pub mod rad_unseed;
#[path = "commands/watch.rs"]
pub mod rad_watch;
modified radicle-cli/src/commands/help.rs
@@ -35,6 +35,7 @@ const COMMANDS: &[Help] = &[
    rad_seed::HELP,
    rad_follow::HELP,
    rad_unfollow::HELP,
+
    rad_unseed::HELP,
    rad_remote::HELP,
    rad_stats::HELP,
    rad_sync::HELP,
modified radicle-cli/src/commands/id.rs
@@ -194,7 +194,9 @@ impl Args for Options {
                    let val = values
                        .next()
                        .ok_or(anyhow!("expected payload value, eg. '\"heartwood\"'"))?;
-
                    let val = json::from_str(val.to_string_lossy().to_string().as_str())?;
+
                    let val = val.to_string_lossy().to_string();
+
                    let val = json::from_str(val.as_str())
+
                        .map_err(|e| anyhow!("invalid JSON value `{val}`: {e}"))?;

                    payload.push((id, key, val));
                }
modified radicle-cli/src/commands/seed.rs
@@ -20,14 +20,13 @@ pub const HELP: Help = Help {
    usage: r#"
Usage

-
    rad seed [<rid>] [-d | --delete] [--[no-]fetch] [--scope <scope>] [<option>...]
+
    rad seed [<rid>] [--[no-]fetch] [--scope <scope>] [<option>...]

    The `seed` command, when no Repository ID (<rid>) is provided, will list the
    repositories being seeded.

-
    When a Repository ID (<rid>) is provided it updates the seeding policy for
-
    that repository. By default, a seeding policy will be created or updated.
-
    To delete a policy, use the `--delete` flag.
+
    When a Repository ID (<rid>) is provided it updates or creates the seeding policy for
+
    that repository. To delete a seeding policy, use the `rad unseed` command.

    When seeding a repository, a scope can be specified: this can be either `all` or
    `followed`. When using `all`, all remote nodes will be followed for that repository.
@@ -36,7 +35,6 @@ Usage

Options

-
    --delete, -d           Delete the seeding policy
    --[no-]fetch           Fetch repository after updating seeding policy
    --scope <scope>        Peer follow scope for this repository
    --verbose, -v          Verbose output
@@ -52,16 +50,6 @@ pub enum Operation {
        scope: Scope,
    },
    List,
-
    Unseed {
-
        rid: RepoId,
-
    },
-
}
-

-
#[derive(Copy, Clone, Debug, PartialEq, Eq, Default)]
-
pub enum OperationName {
-
    #[default]
-
    Seed,
-
    Unseed,
}

#[derive(Debug)]
@@ -78,7 +66,6 @@ impl Args for Options {
        let mut rid: Option<RepoId> = None;
        let mut scope: Option<Scope> = None;
        let mut fetch: Option<bool> = None;
-
        let mut op: Option<OperationName> = None;
        let mut verbose = false;

        while let Some(arg) = parser.next()? {
@@ -86,17 +73,14 @@ impl Args for Options {
                Value(val) => {
                    rid = Some(term::args::rid(val)?);
                }
-
                Long("delete") | Short('d') if op.is_none() => {
-
                    op = Some(OperationName::Unseed);
-
                }
-
                Long("scope") if op.unwrap_or_default() == OperationName::Seed => {
+
                Long("scope") => {
                    let val = parser.value()?;
                    scope = Some(term::args::parse_value("scope", val)?);
                }
-
                Long("fetch") if op.unwrap_or_default() == OperationName::Seed => {
+
                Long("fetch") => {
                    fetch = Some(true);
                }
-
                Long("no-fetch") if op.unwrap_or_default() == OperationName::Seed => {
+
                Long("no-fetch") => {
                    fetch = Some(false);
                }
                Long("verbose") | Short('v') => verbose = true,
@@ -110,13 +94,10 @@ impl Args for Options {
        }

        let op = match rid {
-
            Some(rid) => match op.unwrap_or_default() {
-
                OperationName::Seed => Operation::Seed {
-
                    rid,
-
                    fetch: fetch.unwrap_or(true),
-
                    scope: scope.unwrap_or(Scope::All),
-
                },
-
                OperationName::Unseed => Operation::Unseed { rid },
+
            Some(rid) => Operation::Seed {
+
                rid,
+
                fetch: fetch.unwrap_or(true),
+
                scope: scope.unwrap_or(Scope::All),
            },
            None => Operation::List,
        };
@@ -130,7 +111,6 @@ pub fn run(options: Options, ctx: impl term::Context) -> anyhow::Result<()> {
    let mut node = radicle::Node::new(profile.socket());

    match options.op {
-
        Operation::Unseed { rid } => delete(rid, &mut node, &profile)?,
        Operation::Seed { rid, fetch, scope } => {
            update(rid, scope, &mut node, &profile)?;

@@ -177,9 +157,9 @@ pub fn seeding(profile: &Profile) -> anyhow::Result<()> {
    let store = profile.policies()?;
    let mut t = term::Table::new(term::table::TableOptions::bordered());
    t.header([
-
        term::format::default(String::from("RID")),
-
        term::format::default(String::from("Scope")),
+
        term::format::default(String::from("Repository")),
        term::format::default(String::from("Policy")),
+
        term::format::default(String::from("Scope")),
    ]);
    t.divider();

@@ -191,13 +171,9 @@ pub fn seeding(profile: &Profile) -> anyhow::Result<()> {
    {
        let id = id.to_string();
        let scope = scope.to_string();
-
        let policy = policy.to_string();
+
        let policy = term::format::policy(&policy);

-
        t.push([
-
            term::format::highlight(id),
-
            term::format::secondary(scope),
-
            term::format::secondary(policy),
-
        ])
+
        t.push([term::format::tertiary(id), policy, term::format::dim(scope)])
    }

    if t.is_empty() {
added radicle-cli/src/commands/unseed.rs
@@ -0,0 +1,79 @@
+
use std::ffi::OsString;
+

+
use anyhow::anyhow;
+

+
use radicle::{prelude::*, Node};
+

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

+
pub const HELP: Help = Help {
+
    name: "unseed",
+
    description: "Remove repository seeding policies",
+
    version: env!("CARGO_PKG_VERSION"),
+
    usage: r#"
+
Usage
+

+
    rad unseed <rid> [<option>...]
+

+
    The `unseed` command removes the seeding policy, if found,
+
    for the given repository.
+

+
Options
+

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

+
#[derive(Debug)]
+
pub struct Options {
+
    rid: RepoId,
+
}
+

+
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 rid: Option<RepoId> = None;
+

+
        while let Some(arg) = parser.next()? {
+
            match &arg {
+
                Value(val) => {
+
                    rid = Some(term::args::rid(val)?);
+
                }
+
                Long("help") | Short('h') => {
+
                    return Err(Error::Help.into());
+
                }
+
                _ => {
+
                    return Err(anyhow!(arg.unexpected()));
+
                }
+
            }
+
        }
+

+
        Ok((
+
            Options {
+
                rid: rid.ok_or(anyhow!(
+
                    "A Repository ID must be provided; see `rad unseed --help`"
+
                ))?,
+
            },
+
            vec![],
+
        ))
+
    }
+
}
+

+
pub fn run(options: Options, ctx: impl term::Context) -> anyhow::Result<()> {
+
    let profile = ctx.profile()?;
+
    let mut node = radicle::Node::new(profile.socket());
+

+
    delete(options.rid, &mut node, &profile)?;
+

+
    Ok(())
+
}
+

+
pub fn delete(rid: RepoId, node: &mut Node, profile: &Profile) -> anyhow::Result<()> {
+
    if project::unseed(rid, node, profile)? {
+
        term::success!("Seeding policy for {} removed", term::format::tertiary(rid));
+
    }
+
    Ok(())
+
}
modified radicle-cli/src/main.rs
@@ -268,6 +268,13 @@ fn run_other(exe: &str, args: &[OsString]) -> Result<(), Option<anyhow::Error>>
                args.to_vec(),
            );
        }
+
        "unseed" => {
+
            term::run_command_args::<rad_unseed::Options, _>(
+
                rad_unseed::HELP,
+
                rad_unseed::run,
+
                args.to_vec(),
+
            );
+
        }
        "remote" => term::run_command_args::<rad_remote::Options, _>(
            rad_remote::HELP,
            rad_remote::run,
modified radicle-cli/src/terminal/format.rs
@@ -7,6 +7,7 @@ pub use radicle_term::{style, Paint};

use radicle::cob::{ObjectId, Timestamp};
use radicle::identity::Visibility;
+
use radicle::node::policy::Policy;
use radicle::node::{Alias, AliasStore, NodeId};
use radicle::prelude::Did;
use radicle::profile::Profile;
@@ -63,6 +64,14 @@ pub fn visibility(v: &Visibility) -> Paint<&str> {
    }
}

+
/// Format a policy.
+
pub fn policy(p: &Policy) -> Paint<String> {
+
    match p {
+
        Policy::Allow => term::format::positive(p.to_string()),
+
        Policy::Block => term::format::negative(p.to_string()),
+
    }
+
}
+

/// Format a timestamp.
pub fn timestamp(time: impl Into<LocalTime>) -> Paint<String> {
    let time: LocalTime = time.into();
modified radicle/src/node/config.rs
@@ -86,7 +86,7 @@ pub struct Limits {
    /// How long to keep a gossip message entry before pruning it.
    #[serde(with = "crate::serde_ext::localtime::duration")]
    pub gossip_max_age: LocalDuration,
-
    /// Maximum number of concurrent fetches per per connection.
+
    /// Maximum number of concurrent fetches per peer connection.
    pub fetch_concurrency: usize,
    /// Maximum number of open files.
    pub max_open_files: usize,
modified radicle/src/profile.rs
@@ -12,7 +12,7 @@
//!
use std::io::Write;
use std::path::{Path, PathBuf};
-
use std::{fs, io, str::FromStr};
+
use std::{fs, io};

use serde::Serialize;
use thiserror::Error;
@@ -163,15 +163,7 @@ impl Config {
            Ok(cfg) => {
                serde_json::from_reader(cfg).map_err(|e| ConfigError::Load(path.to_path_buf(), e))
            }
-
            Err(e) => {
-
                let Ok(user) = env::var("USER") else {
-
                    return Err(ConfigError::Io(path.to_owned(), e));
-
                };
-
                let Ok(alias) = Alias::from_str(&user) else {
-
                    return Err(ConfigError::Io(path.to_owned(), e));
-
                };
-
                Ok(Config::new(alias))
-
            }
+
            Err(e) => Err(ConfigError::Io(path.to_path_buf(), e)),
        }
    }