Radish alpha
h
Radicle Heartwood Protocol & Stack
Radicle
Git (anonymous pull)
Log in to clone via SSH
cli: Improve `rad remote add`
cloudhead committed 2 years ago
commit e4f06e9c9dea7b60bd8d565a4173873c0fa0d1c4
parent 3a3820b06d7d49f01a0cabea44eec105524e3405
15 files changed +130 -85
modified radicle-cli/examples/git/git-push-diverge.md
@@ -7,9 +7,6 @@ First we add a second delegate, Bob, to our repo:
``` ~alice
$ rad id update --title "Add Bob" --description "" --delegate did:key:z6Mkt67GdsW7715MEfRuP4pSZxJRJh6kj6Y48WRqVv4N1tRk --repo rad:z42hL2jL4XNk6K8oHQaSWfMgCL7ji -q
c036c0d89ce26aef3ad7da402157dba16b5163b4
-
$ rad remote add did:key:z6Mkt67GdsW7715MEfRuP4pSZxJRJh6kj6Y48WRqVv4N1tRk
-
✓ Remote bob@z6Mkt67GdsW7715MEfRuP4pSZxJRJh6kj6Y48WRqVv4N1tRk added
-
✓ Remote-tracking branch bob@z6Mkt67GdsW7715MEfRuP4pSZxJRJh6kj6Y48WRqVv4N1tRk/master created for z6Mkt67…v4N1tRk
```

Then, as Bob, we commit some code on top of the canonical head:
@@ -32,7 +29,9 @@ As Alice, we fetch that code, but commit on top of our own master, which is no
longer canonical, since Bob pushed a more recent commit, and the threshold is 1:

``` ~alice
-
$ git fetch bob@z6Mkt67GdsW7715MEfRuP4pSZxJRJh6kj6Y48WRqVv4N1tRk
+
$ rad remote add did:key:z6Mkt67GdsW7715MEfRuP4pSZxJRJh6kj6Y48WRqVv4N1tRk --fetch --no-sync
+
✓ Remote bob@z6Mkt67GdsW7715MEfRuP4pSZxJRJh6kj6Y48WRqVv4N1tRk added
+
✓ Remote-tracking branch bob@z6Mkt67GdsW7715MEfRuP4pSZxJRJh6kj6Y48WRqVv4N1tRk/master created for z6Mkt67…v4N1tRk
$ git branch -arv
  bob@z6Mkt67GdsW7715MEfRuP4pSZxJRJh6kj6Y48WRqVv4N1tRk/master 319a7dc Third commit
  rad/master                                                  f2de534 Second commit
modified radicle-cli/examples/rad-clone-all.md
@@ -48,18 +48,9 @@ z6Mkt67GdsW7715MEfRuP4pSZxJRJh6kj6Y48WRqVv4N1tRk
We can then setup a git remote for `bob`:

```
-
$ rad remote add z6Mkt67GdsW7715MEfRuP4pSZxJRJh6kj6Y48WRqVv4N1tRk --name bob
+
$ rad remote add z6Mkt67GdsW7715MEfRuP4pSZxJRJh6kj6Y48WRqVv4N1tRk --name bob --no-sync --fetch
✓ Remote bob added
✓ Remote-tracking branch bob/master created for z6Mkt67…v4N1tRk
-
```
-

-
And fetch his refs:
-

-
```
-
$ git fetch --all
-
Fetching rad
-
Fetching alice@z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi
-
Fetching bob
$ git branch --remotes
  alice@z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi/master
  bob/master
modified radicle-cli/examples/rad-push-and-pull-patches.md
@@ -8,9 +8,6 @@ $ git push -o patch.message="Changes" rad HEAD:refs/patches
```

``` ~alice
-
$ rad sync -f
-
✓ Fetching rad:z42hL2jL4XNk6K8oHQaSWfMgCL7ji from z6Mkt67…v4N1tRk..
-
✓ Fetched repository from 1 seed(s)
$ git checkout -b alice/1 -q
$ git rev-parse HEAD
f2de534b5e81d7c6e2dcaf58c3dd91573c0a0354
@@ -19,13 +16,12 @@ $ rad patch checkout 0fd67a0
✓ Switched to branch patch/0fd67a0
✓ Branch patch/0fd67a0 setup to track rad/patches/0fd67a0364af1f79ed8770a35ed09d85571d4c21
$ rad remote add z6Mkt67GdsW7715MEfRuP4pSZxJRJh6kj6Y48WRqVv4N1tRk
+
✓ Follow policy updated for z6Mkt67GdsW7715MEfRuP4pSZxJRJh6kj6Y48WRqVv4N1tRk
+
✓ Fetching rad:z42hL2jL4XNk6K8oHQaSWfMgCL7ji from z6Mkt67…v4N1tRk..
✓ Remote bob@z6Mkt67GdsW7715MEfRuP4pSZxJRJh6kj6Y48WRqVv4N1tRk added
✓ Remote-tracking branch bob@z6Mkt67GdsW7715MEfRuP4pSZxJRJh6kj6Y48WRqVv4N1tRk/master created for z6Mkt67…v4N1tRk
$ git checkout master -q
-
$ git fetch --all -q
$ cat .git/FETCH_HEAD
-
f2de534b5e81d7c6e2dcaf58c3dd91573c0a0354		branch 'master' of rad://z42hL2jL4XNk6K8oHQaSWfMgCL7ji
-
8d5f1bae4b69d8e3f6cbfc6f4bd675ed19990afc	not-for-merge	branch 'patches/0fd67a0364af1f79ed8770a35ed09d85571d4c21' of rad://z42hL2jL4XNk6K8oHQaSWfMgCL7ji
f2de534b5e81d7c6e2dcaf58c3dd91573c0a0354	not-for-merge	branch 'master' of rad://z42hL2jL4XNk6K8oHQaSWfMgCL7ji/z6Mkt67GdsW7715MEfRuP4pSZxJRJh6kj6Y48WRqVv4N1tRk
8d5f1bae4b69d8e3f6cbfc6f4bd675ed19990afc	not-for-merge	branch 'patches/0fd67a0364af1f79ed8770a35ed09d85571d4c21' of rad://z42hL2jL4XNk6K8oHQaSWfMgCL7ji/z6Mkt67GdsW7715MEfRuP4pSZxJRJh6kj6Y48WRqVv4N1tRk
$ git rev-parse master
modified radicle-cli/examples/rad-remote.md
@@ -1,7 +1,7 @@
Now, let's add a bob as a new remote:

```
-
$ rad remote add did:key:z6Mkt67GdsW7715MEfRuP4pSZxJRJh6kj6Y48WRqVv4N1tRk --name bob
+
$ rad remote add did:key:z6Mkt67GdsW7715MEfRuP4pSZxJRJh6kj6Y48WRqVv4N1tRk --name bob --no-sync
✓ Remote bob added
✓ Remote-tracking branch bob/master created for z6Mkt67…v4N1tRk
```
@@ -18,14 +18,6 @@ rad z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi (push)
You can see both `bob` and `rad` as remotes.  The `rad` remote is our personal
remote of the project.

-
For the remote-tracking branch to work, we fetch bob:
-

-
``` RAD_SOCKET=/dev/null (stderr)
-
$ git fetch bob
-
From rad://z42hL2jL4XNk6K8oHQaSWfMgCL7ji/z6Mkt67GdsW7715MEfRuP4pSZxJRJh6kj6Y48WRqVv4N1tRk
-
 * [new branch]      master     -> bob/master
-
```
-

We can now see the remote-tracking branch that was setup:

```
@@ -47,7 +39,7 @@ Now, add another time `bob` but without specify the `name`, so we should be
able to fetch the node alias from our db!

```
-
$ rad remote add did:key:z6Mkt67GdsW7715MEfRuP4pSZxJRJh6kj6Y48WRqVv4N1tRk
+
$ rad remote add did:key:z6Mkt67GdsW7715MEfRuP4pSZxJRJh6kj6Y48WRqVv4N1tRk --no-sync
✓ Remote bob@z6Mkt67GdsW7715MEfRuP4pSZxJRJh6kj6Y48WRqVv4N1tRk added
✓ Remote-tracking branch bob@z6Mkt67GdsW7715MEfRuP4pSZxJRJh6kj6Y48WRqVv4N1tRk/master created for z6Mkt67…v4N1tRk
```
@@ -76,7 +68,7 @@ As we can see, we have also have another remote namespace `eve`, so
let's add them to our set of working copy remotes:

```
-
$ rad remote add did:key:z6Mkux1aUQD2voWWukVb5nNUR7thrHveQG4pDQua8nVhib7Z --name eve
+
$ rad remote add did:key:z6Mkux1aUQD2voWWukVb5nNUR7thrHveQG4pDQua8nVhib7Z --name eve --no-sync
✓ Remote eve added
✓ Remote-tracking branch eve/master created for z6Mkux1…nVhib7Z
```
modified radicle-cli/examples/workflow/5-patching-maintainer.md
@@ -1,29 +1,16 @@
Back to being the project maintainer.

-
Changes have been proposed by another person (or peer) via a radicle patch.  To
-
follow changes by another, we must 'follow' them.
+
Changes have been proposed by another peer via a radicle patch. To track
+
changes from another peer, we must first follow them, and then create
+
a tracking branch in our working copy. The `rad remote add` command does all
+
of this.

```
-
$ rad follow did:key:z6Mkt67GdsW7715MEfRuP4pSZxJRJh6kj6Y48WRqVv4N1tRk --alias bob
+
$ rad remote add z6Mkt67GdsW7715MEfRuP4pSZxJRJh6kj6Y48WRqVv4N1tRk --name bob --sync --fetch
✓ Follow policy updated for z6Mkt67GdsW7715MEfRuP4pSZxJRJh6kj6Y48WRqVv4N1tRk (bob)
-
```
-

-
Additionally, we need to add a new 'git remote' to our working copy for the
-
peer.  Upcoming versions of radicle will not require this step.
-

-
```
-
$ rad remote add z6Mkt67GdsW7715MEfRuP4pSZxJRJh6kj6Y48WRqVv4N1tRk --name bob
+
✓ Fetching rad:z42hL2jL4XNk6K8oHQaSWfMgCL7ji from z6Mkt67…v4N1tRk..
✓ Remote bob added
✓ Remote-tracking branch bob/master created for z6Mkt67…v4N1tRk
-
$ rad sync -f
-
✓ Fetching rad:z42hL2jL4XNk6K8oHQaSWfMgCL7ji from z6Mkt67…v4N1tRk..
-
✓ Fetched repository from 1 seed(s)
-
```
-

-
``` (stderr)
-
$ git fetch bob
-
From rad://z42hL2jL4XNk6K8oHQaSWfMgCL7ji/z6Mkt67GdsW7715MEfRuP4pSZxJRJh6kj6Y48WRqVv4N1tRk
-
 * [new branch]      patches/3581e83ad18f5cdd806ab50fa11cfd5dd4e8ae1c -> bob/patches/3581e83ad18f5cdd806ab50fa11cfd5dd4e8ae1c
```

The contributor's changes are now visible to us.
modified radicle-cli/src/commands/checkout.rs
@@ -156,7 +156,7 @@ pub fn setup_remote(
    remote_id: &NodeId,
    remote_name: Option<git::RefString>,
    aliases: &impl AliasStore,
-
) -> anyhow::Result<()> {
+
) -> anyhow::Result<git::RefString> {
    let remote_name = if let Some(name) = remote_name {
        name
    } else {
@@ -168,7 +168,7 @@ pub fn setup_remote(
        git::RefString::try_from(name.as_str())
            .map_err(|_| anyhow!("invalid remote name: '{name}'"))?
    };
-
    let (remote, branch) = setup.run(remote_name, *remote_id)?;
+
    let (remote, branch) = setup.run(&remote_name, *remote_id)?;

    term::success!("Remote {} added", term::format::tertiary(remote.name));

@@ -179,5 +179,5 @@ pub fn setup_remote(
            term::format::tertiary(term::format::node(remote_id))
        );
    }
-
    Ok(())
+
    Ok(remote_name)
}
modified radicle-cli/src/commands/issue.rs
@@ -528,7 +528,7 @@ pub fn run(options: Options, ctx: impl term::Context) -> anyhow::Result<()> {

    if announce {
        let mut node = Node::new(profile.socket());
-
        node::sync(rid, &mut node)?;
+
        node::announce(rid, &mut node)?;
    }

    Ok(())
modified radicle-cli/src/commands/ls.rs
@@ -106,7 +106,7 @@ pub fn run(options: Options, ctx: impl term::Context) -> anyhow::Result<()> {
        if refs.is_none() && !options.all {
            continue;
        }
-
        let seeded = policy.is_repo_seeded(&rid)?;
+
        let seeded = policy.is_seeded(&rid)?;

        if !seeded && !options.all {
            continue;
modified radicle-cli/src/commands/patch.rs
@@ -794,7 +794,7 @@ pub fn run(options: Options, ctx: impl term::Context) -> anyhow::Result<()> {

    if announce {
        let mut node = Node::new(profile.socket());
-
        node::sync(id, &mut node)?;
+
        node::announce(id, &mut node)?;
    }
    Ok(())
}
modified radicle-cli/src/commands/remote.rs
@@ -39,6 +39,8 @@ List options
Add options

    --name        Override the name of the remote that by default is set to the node alias
+
    --[no-]fetch  Fetch the remote from local storage (default: fetch)
+
    --[no-]sync   Sync the remote refs from the network (default: sync)

Options

@@ -56,9 +58,18 @@ pub enum OperationName {

#[derive(Debug)]
pub enum Operation {
-
    Add { id: NodeId, name: Option<RefString> },
-
    Rm { name: RefString },
-
    List { option: ListOption },
+
    Add {
+
        id: NodeId,
+
        name: Option<RefString>,
+
        fetch: bool,
+
        sync: bool,
+
    },
+
    Rm {
+
        name: RefString,
+
    },
+
    List {
+
        option: ListOption,
+
    },
}

#[derive(Debug, Default)]
@@ -83,6 +94,8 @@ impl Args for Options {
        let mut id: Option<NodeId> = None;
        let mut name: Option<RefString> = None;
        let mut list_op: ListOption = ListOption::default();
+
        let mut fetch = true;
+
        let mut sync = true;

        while let Some(arg) = parser.next()? {
            match arg {
@@ -114,6 +127,18 @@ impl Args for Options {
                }

                // Add options
+
                Long("sync") if op == Some(OperationName::Add) => {
+
                    sync = true;
+
                }
+
                Long("no-sync") if op == Some(OperationName::Add) => {
+
                    sync = false;
+
                }
+
                Long("fetch") if op == Some(OperationName::Add) => {
+
                    fetch = true;
+
                }
+
                Long("no-fetch") if op == Some(OperationName::Add) => {
+
                    fetch = false;
+
                }
                Value(val) if op == Some(OperationName::Add) && id.is_none() => {
                    let nid = args::pubkey(&val)?;
                    id = Some(nid);
@@ -137,6 +162,8 @@ impl Args for Options {
                    "`DID` required, try running `rad remote add <did>`"
                ))?,
                name,
+
                fetch,
+
                sync,
            },
            OperationName::List => Operation::List { option: list_op },
            OperationName::Rm => Operation::Rm {
@@ -154,11 +181,25 @@ pub fn run(options: Options, ctx: impl Context) -> anyhow::Result<()> {
    let profile = ctx.profile()?;

    match options.op {
-
        Operation::Add { ref id, name } => {
+
        Operation::Add {
+
            ref id,
+
            name,
+
            fetch,
+
            sync,
+
        } => {
            let proj = profile.storage.repository(rid)?.project()?;
            let branch = proj.default_branch();

-
            self::add::run(rid, id, name, Some(branch.clone()), &profile, &working)?
+
            self::add::run(
+
                rid,
+
                id,
+
                name,
+
                Some(branch.clone()),
+
                &profile,
+
                &working,
+
                fetch,
+
                sync,
+
            )?
        }
        Operation::Rm { ref name } => self::rm::run(name, &working)?,
        Operation::List { option } => match option {
modified radicle-cli/src/commands/remote/add.rs
@@ -1,10 +1,15 @@
+
use std::str::FromStr;
+
use std::time;
+

+
use radicle::git;
use radicle::git::RefString;
use radicle::prelude::*;
use radicle::Profile;
use radicle_crypto::PublicKey;

use crate::commands::rad_checkout as checkout;
-
use crate::git;
+
use crate::commands::rad_follow as follow;
+
use crate::commands::rad_sync as sync;
use crate::project::SetupRemote;

pub fn run(
@@ -13,14 +18,33 @@ pub fn run(
    name: Option<RefString>,
    tracking: Option<BranchName>,
    profile: &Profile,
-
    repo: &git::Repository,
+
    repo: &git::raw::Repository,
+
    fetch: bool,
+
    sync: bool,
) -> anyhow::Result<()> {
+
    if sync {
+
        let mut node = radicle::Node::new(profile.socket());
+

+
        if !profile.policies()?.is_followed(nid)? {
+
            let alias = name.as_ref().and_then(|n| Alias::from_str(n.as_str()).ok());
+

+
            follow::follow(*nid, alias, &mut node, profile)?;
+
            sync::fetch(
+
                rid,
+
                sync::RepoSync::default(),
+
                time::Duration::from_secs(9),
+
                &mut node,
+
            )?;
+
        }
+
    }
    let aliases = profile.aliases();
    let setup = SetupRemote {
        rid,
        tracking,
-
        fetch: false,
+
        fetch,
        repo,
    };
-
    checkout::setup_remote(&setup, nid, name, &aliases)
+
    checkout::setup_remote(&setup, nid, name, &aliases)?;
+

+
    Ok(())
}
modified radicle-cli/src/commands/sync.rs
@@ -280,7 +280,7 @@ pub fn run(options: Options, ctx: impl term::Context) -> anyhow::Result<()> {
        }
        Operation::Synchronize(SyncMode::Repo { mode, direction }) => {
            if [SyncDirection::Fetch, SyncDirection::Both].contains(&direction) {
-
                if !profile.policies()?.is_repo_seeded(&rid)? {
+
                if !profile.policies()?.is_seeded(&rid)? {
                    anyhow::bail!("repository {rid} is not seeded");
                }
                let results = fetch(rid, mode.clone(), options.timeout, &mut node)?;
modified radicle-cli/src/node.rs
@@ -7,8 +7,9 @@ use radicle::Node;

use crate::terminal as term;

-
pub fn sync(rid: Id, node: &mut Node) -> anyhow::Result<()> {
-
    match sync_(rid, node) {
+
/// Announce changes to the network.
+
pub fn announce(rid: Id, node: &mut Node) -> anyhow::Result<()> {
+
    match announce_(rid, node) {
        Ok(()) => Ok(()),
        Err(e) if e.is_connection_err() => {
            term::hint("Node is stopped. To announce changes to the network, start it with `rad node start`.");
@@ -18,8 +19,7 @@ pub fn sync(rid: Id, node: &mut Node) -> anyhow::Result<()> {
    }
}

-
/// Sync with the network.
-
fn sync_(rid: Id, node: &mut Node) -> Result<(), radicle::node::Error> {
+
fn announce_(rid: Id, node: &mut Node) -> Result<(), radicle::node::Error> {
    let seeds = node.seeds(rid)?;
    let connected = seeds.connected().map(|s| s.nid).collect::<Vec<_>>();

modified radicle/src/node/policy/store.rs
@@ -204,7 +204,7 @@ impl Store<Write> {
/// `Config<Write>` can access these functions as well.
impl<T> Store<T> {
    /// Check if a node is followed.
-
    pub fn is_node_followed(&self, id: &NodeId) -> Result<bool, Error> {
+
    pub fn is_followed(&self, id: &NodeId) -> Result<bool, Error> {
        Ok(matches!(
            self.follow_policy(id)?,
            Some(Node {
@@ -215,7 +215,7 @@ impl<T> Store<T> {
    }

    /// Check if a repository is seeded.
-
    pub fn is_repo_seeded(&self, id: &Id) -> Result<bool, Error> {
+
    pub fn is_seeded(&self, id: &Id) -> Result<bool, Error> {
        Ok(matches!(
            self.seed_policy(id)?,
            Some(Repo {
@@ -335,10 +335,10 @@ mod test {
        let mut db = Store::open(":memory:").unwrap();

        assert!(db.follow(&id, Some("eve")).unwrap());
-
        assert!(db.is_node_followed(&id).unwrap());
+
        assert!(db.is_followed(&id).unwrap());
        assert!(!db.follow(&id, Some("eve")).unwrap());
        assert!(db.unfollow(&id).unwrap());
-
        assert!(!db.is_node_followed(&id).unwrap());
+
        assert!(!db.is_followed(&id).unwrap());
    }

    #[test]
@@ -347,10 +347,10 @@ mod test {
        let mut db = Store::open(":memory:").unwrap();

        assert!(db.seed(&id, Scope::All).unwrap());
-
        assert!(db.is_repo_seeded(&id).unwrap());
+
        assert!(db.is_seeded(&id).unwrap());
        assert!(!db.seed(&id, Scope::All).unwrap());
        assert!(db.unseed(&id).unwrap());
-
        assert!(!db.is_repo_seeded(&id).unwrap());
+
        assert!(!db.is_seeded(&id).unwrap());
    }

    #[test]
modified radicle/src/profile.rs
@@ -20,6 +20,7 @@ use thiserror::Error;
use crate::crypto::ssh::agent::Agent;
use crate::crypto::ssh::{keystore, Keystore, Passphrase};
use crate::crypto::{PublicKey, Signer};
+
use crate::node::policy::config::store::Read;
use crate::node::{policy, Alias, AliasStore};
use crate::prelude::Did;
use crate::prelude::{Id, NodeId};
@@ -346,6 +347,17 @@ impl Profile {
        &self.home
    }

+
    /// Return a read-only handle to the policies of the node.
+
    pub fn policies(&self) -> Result<policy::config::Config<Read>, policy::store::Error> {
+
        let path = self.node().join(node::POLICIES_DB_FILE);
+
        let config = policy::config::Config::new(
+
            self.config.node.policy,
+
            self.config.node.scope,
+
            policy::store::Store::reader(path)?,
+
        );
+
        Ok(config)
+
    }
+

    /// Return a multi-source store for aliases.
    pub fn aliases(&self) -> Aliases {
        let policies = self.home.policies().ok();
@@ -471,15 +483,7 @@ impl Home {
            .unwrap_or_else(|| self.node().join(node::DEFAULT_SOCKET_NAME))
    }

-
    /// Return a read-only handle to the policies of the node.
-
    pub fn policies(&self) -> Result<policy::store::StoreReader, policy::store::Error> {
-
        let path = self.node().join(node::POLICIES_DB_FILE);
-
        let config = policy::store::Store::reader(path)?;
-

-
        Ok(config)
-
    }
-

-
    /// Return a read-write handle to the policies of the node.
+
    /// Return a read-write handle to the policies store of the node.
    pub fn policies_mut(&self) -> Result<policy::store::StoreWriter, policy::store::Error> {
        let path = self.node().join(node::POLICIES_DB_FILE);
        let config = policy::store::Store::open(path)?;
@@ -504,6 +508,17 @@ impl Home {
    }
}

+
// Private methods.
+
impl Home {
+
    /// Return a read-only handle to the policies store of the node.
+
    fn policies(&self) -> Result<policy::store::StoreReader, policy::store::Error> {
+
        let path = self.node().join(node::POLICIES_DB_FILE);
+
        let config = policy::store::Store::reader(path)?;
+

+
        Ok(config)
+
    }
+
}
+

#[cfg(test)]
#[cfg(not(target_os = "macos"))]
mod test {