Radish alpha
h
Radicle Heartwood Protocol & Stack
Radicle
Git (anonymous pull)
Log in to clone via SSH
cli: Implement `rad block` command
cloudhead committed 2 years ago
commit e5fcbba4c04369e1124859ed845f711489910342
parent d37c52aadead4173a67e81b1e73b64cfbb275145
6 files changed +161 -0
added radicle-cli/examples/rad-block.md
@@ -0,0 +1,43 @@
+
When using an open seeding policy, it can be useful to block individual
+
repositories from being seeded.
+

+
For instance, if our default policy is to seed, any unknown repository will
+
have its policy set to allow seeding:
+
```
+
$ rad inspect rad:z42hL2jL4XNk6K8oHQaSWfMgCL7ji --policy
+
Repository rad:z42hL2jL4XNk6K8oHQaSWfMgCL7ji is being seeded with scope `followed`
+
```
+

+
Since there is no policy specific to this repository, there's nothing to be
+
removed.
+

+
```
+
$ rad seed
+
No seeding policies to show.
+
```
+

+
But if we wanted to prevent this repository from being seeded, while
+
allowing all other repositories, we could use `rad block`:
+

+
```
+
$ rad block rad:z42hL2jL4XNk6K8oHQaSWfMgCL7ji
+
✓ Policy for rad:z42hL2jL4XNk6K8oHQaSWfMgCL7ji set to 'block'
+
```
+

+
We can see that it is now no longer seeded:
+

+
```
+
$ rad inspect rad:z42hL2jL4XNk6K8oHQaSWfMgCL7ji --policy
+
Repository rad:z42hL2jL4XNk6K8oHQaSWfMgCL7ji is not being seeded
+
```
+

+
And a 'block' policy was added:
+

+
```
+
$ rad seed
+
╭───────────────────────────────────────────────────────╮
+
│ RID                                 Scope      Policy │
+
├───────────────────────────────────────────────────────┤
+
│ rad:z42hL2jL4XNk6K8oHQaSWfMgCL7ji   followed   block  │
+
╰───────────────────────────────────────────────────────╯
+
```
modified radicle-cli/src/commands.rs
@@ -1,5 +1,7 @@
#[path = "commands/auth.rs"]
pub mod rad_auth;
+
#[path = "commands/block.rs"]
+
pub mod rad_block;
#[path = "commands/checkout.rs"]
pub mod rad_checkout;
#[path = "commands/clean.rs"]
added radicle-cli/src/commands/block.rs
@@ -0,0 +1,96 @@
+
use std::ffi::OsString;
+

+
use radicle::node::policy::Policy;
+
use radicle::prelude::{NodeId, RepoId};
+

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

+
pub const HELP: Help = Help {
+
    name: "block",
+
    description: "Block repositories or nodes from being seeded or followed",
+
    version: env!("CARGO_PKG_VERSION"),
+
    usage: r#"
+
Usage
+

+
    rad block <rid> [<option>...]
+
    rad block <nid> [<option>...]
+

+
    Blocks a repository from being seeded or a node from being followed.
+

+
Options
+

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

+
enum Target {
+
    Node(NodeId),
+
    Repo(RepoId),
+
}
+

+
impl std::fmt::Display for Target {
+
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+
        match self {
+
            Self::Node(nid) => nid.fmt(f),
+
            Self::Repo(rid) => rid.fmt(f),
+
        }
+
    }
+
}
+

+
pub struct Options {
+
    target: Target,
+
}
+

+
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 target = None;
+

+
        while let Some(arg) = parser.next()? {
+
            match arg {
+
                Long("help") | Short('h') => {
+
                    return Err(Error::Help.into());
+
                }
+
                Value(val) if target.is_none() => {
+
                    if let Ok(rid) = args::rid(&val) {
+
                        target = Some(Target::Repo(rid));
+
                    } else if let Ok(nid) = args::nid(&val) {
+
                        target = Some(Target::Node(nid));
+
                    } else {
+
                        return Err(anyhow::anyhow!(
+
                            "invalid repository or node specified, see `rad block --help`"
+
                        ));
+
                    }
+
                }
+
                _ => return Err(anyhow::anyhow!(arg.unexpected())),
+
            }
+
        }
+

+
        Ok((
+
            Options {
+
                target: target.ok_or(anyhow::anyhow!(
+
                    "a repository or node to block must be specified, see `rad block --help`"
+
                ))?,
+
            },
+
            vec![],
+
        ))
+
    }
+
}
+

+
pub fn run(options: Options, ctx: impl term::Context) -> anyhow::Result<()> {
+
    let profile = ctx.profile()?;
+
    let mut policies = profile.policies_mut()?;
+

+
    let updated = match options.target {
+
        Target::Node(nid) => policies.set_follow_policy(&nid, Policy::Block)?,
+
        Target::Repo(rid) => policies.set_seed_policy(&rid, Policy::Block)?,
+
    };
+
    if updated {
+
        term::success!("Policy for {} set to 'block'", options.target);
+
    }
+
    Ok(())
+
}
modified radicle-cli/src/commands/help.rs
@@ -16,6 +16,7 @@ pub const HELP: Help = Help {

const COMMANDS: &[Help] = &[
    rad_auth::HELP,
+
    rad_block::HELP,
    rad_checkout::HELP,
    rad_clone::HELP,
    rad_config::HELP,
modified radicle-cli/src/main.rs
@@ -110,6 +110,13 @@ fn run_other(exe: &str, args: &[OsString]) -> Result<(), Option<anyhow::Error>>
                args.to_vec(),
            );
        }
+
        "block" => {
+
            term::run_command_args::<rad_block::Options, _>(
+
                rad_block::HELP,
+
                rad_block::run,
+
                args.to_vec(),
+
            );
+
        }
        "checkout" => {
            term::run_command_args::<rad_checkout::Options, _>(
                rad_checkout::HELP,
modified radicle-cli/tests/commands.rs
@@ -974,6 +974,18 @@ fn rad_unseed() {
}

#[test]
+
fn rad_block() {
+
    let mut environment = Environment::new();
+
    let alice = environment.node(Config {
+
        policy: Policy::Allow,
+
        ..Config::test(Alias::new("alice"))
+
    });
+
    let working = tempfile::tempdir().unwrap();
+

+
    test("examples/rad-block.md", working, Some(&alice.home), []).unwrap();
+
}
+

+
#[test]
fn rad_clone() {
    let mut environment = Environment::new();
    let mut alice = environment.node(Config::test(Alias::new("alice")));