Radish alpha
h
Radicle Heartwood Protocol & Stack
Radicle
Git (anonymous pull)
Log in to clone via SSH
cli: Add private repo support to `rad sync`
cloudhead committed 2 years ago
commit a1df6d37488c01b6db5a791a87d6f15279068c3e
parent 87c38118d19ce3891ff7c0d50107e28a81e04b97
4 files changed +99 -20
added radicle-cli/examples/rad-init-private.md
@@ -0,0 +1,49 @@
+
Alice initializes a *private* repo.
+

+
``` ~alice
+
$ rad init --name heartwood --description "radicle heartwood protocol & stack" --no-confirm --announce --private
+

+
Initializing private radicle 👾 project in .
+

+
✓ Project heartwood created
+
✓ Syncing inventory..
+
✓ Announcing inventory..
+

+
Your project's Repository ID (RID) is rad:z2ug5mwNKZB8KGpBDRTrWHAMbvHCu.
+
You can show it any time by running `rad .`
+
```
+

+
Bob tries to clone it, and even though he's connected to Alice, it fails.
+

+
``` ~bob
+
$ rad track rad:z2ug5mwNKZB8KGpBDRTrWHAMbvHCu
+
✓ Tracking policy updated for rad:z2ug5mwNKZB8KGpBDRTrWHAMbvHCu with scope 'trusted'
+
```
+
``` ~bob (fail)
+
$ rad sync rad:z2ug5mwNKZB8KGpBDRTrWHAMbvHCu --fetch --seed z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi --timeout 3
+
✗ Fetching rad:z2ug5mwNKZB8KGpBDRTrWHAMbvHCu from z6MknSL…StBU8Vi.. <canceled>
+
✗ Sync failed: failed to call node: i/o: timed out reading from control socket
+
```
+

+
She allows Bob to view the repository. And when she syncs, one node (Bob) gets
+
the refs.
+

+
``` ~alice
+
$ rad id edit --allow did:key:z6Mkt67GdsW7715MEfRuP4pSZxJRJh6kj6Y48WRqVv4N1tRk -q
+
e98cd6a0a3e94837b382e59e02b3ea83991a8244
+
$ rad id accept e98cd6a0a3e94837b382e59e02b3ea83991a8244 -q
+
$ rad id commit e98cd6a0a3e94837b382e59e02b3ea83991a8244 -q
+
c568f8aac97db40a5e63e1261872bfbd9a3a61e4
+
$ rad sync --announce --timeout 1
+
✓ Synced with 1 node(s)
+
```
+

+
Bob can now fetch the private repo:
+

+
``` ~bob
+
$ rad sync rad:z2ug5mwNKZB8KGpBDRTrWHAMbvHCu --fetch
+
✓ Fetching rad:z2ug5mwNKZB8KGpBDRTrWHAMbvHCu from z6MknSL…StBU8Vi..
+
✓ Fetched repository from 1 seed(s)
+
$ rad ls --private
+
heartwood rad:z2ug5mwNKZB8KGpBDRTrWHAMbvHCu f2de534 radicle heartwood protocol & stack
+
```
modified radicle-cli/src/commands/sync.rs
@@ -6,7 +6,8 @@ use anyhow::{anyhow, Context as _};

use radicle::node;
use radicle::node::{FetchResult, FetchResults, Handle as _, Node};
-
use radicle::prelude::{Id, NodeId};
+
use radicle::prelude::{Id, NodeId, Profile};
+
use radicle::storage::{ReadRepository, ReadStorage};

use crate::terminal as term;
use crate::terminal::args::{Args, Error, Help};
@@ -192,13 +193,13 @@ pub fn run(options: Options, ctx: impl term::Context) -> anyhow::Result<()> {
        let failed = results.failed().count();

        if success == 0 {
-
            term::error(format!("Failed to fetch repository from {failed} seed(s)"));
+
            anyhow::bail!("repository fetch from {failed} seed(s) failed");
        } else {
            term::success!("Fetched repository from {success} seed(s)");
        }
    }
    if [SyncDirection::Announce, SyncDirection::Both].contains(&options.sync.direction) {
-
        announce(rid, mode, options.timeout, node)?;
+
        announce(rid, mode, options.timeout, node, &profile)?;
    }
    Ok(())
}
@@ -208,9 +209,21 @@ fn announce(
    _mode: SyncMode,
    timeout: time::Duration,
    mut node: Node,
+
    profile: &Profile,
) -> anyhow::Result<()> {
-
    let seeds = node.seeds(rid)?;
-
    let connected = seeds.connected().map(|s| s.nid).collect::<Vec<_>>();
+
    let repo = profile.storage.repository(rid)?;
+
    let (_, doc) = repo.identity_doc()?;
+
    let connected: Vec<_> = if doc.visibility.is_public() {
+
        let seeds = node.seeds(rid)?;
+
        seeds.connected().map(|s| s.nid).collect()
+
    } else {
+
        node.sessions()?
+
            .into_iter()
+
            .filter(|s| s.state.is_connected() && doc.is_visible_to(&s.nid))
+
            .map(|s| s.nid)
+
            .collect()
+
    };
+

    if connected.is_empty() {
        term::info!("Not connected to any seeds.");
        return Ok(());
modified radicle-cli/tests/commands.rs
@@ -1160,6 +1160,38 @@ fn rad_patch_pull_update() {
}

#[test]
+
fn rad_init_private() {
+
    logger::init(log::Level::Debug);
+

+
    let mut environment = Environment::new();
+
    let alice = environment.node(Config::test(Alias::new("alice")));
+
    let bob = environment.node(Config::test(Alias::new("bob")));
+
    let working = environment.tmp().join("working");
+

+
    fixtures::repository(working.join("alice"));
+

+
    let alice = alice.spawn();
+
    let mut bob = bob.spawn();
+

+
    bob.connect(&alice).converge([&alice]);
+

+
    formula(&environment.tmp(), "examples/rad-init-private.md")
+
        .unwrap()
+
        .home(
+
            "alice",
+
            working.join("alice"),
+
            [("RAD_HOME", alice.home.path().display())],
+
        )
+
        .home(
+
            "bob",
+
            bob.home.path(),
+
            [("RAD_HOME", bob.home.path().display())],
+
        )
+
        .run()
+
        .unwrap();
+
}
+

+
#[test]
fn framework_home() {
    logger::init(log::Level::Debug);

modified radicle-node/src/service.rs
@@ -722,21 +722,6 @@ where
        }
    }

-
    /// Called when a remote requests a repository be uploaded to it.
-
    /// The upload is authorized if this function returns `true`.
-
    pub fn upload(&mut self, rid: Id, remote: &NodeId) -> bool {
-
        let Ok(repo) = self.storage.repository(rid) else {
-
            return false;
-
        };
-
        let Ok((_, doc)) = repo.identity_doc() else {
-
            return false;
-
        };
-
        if !doc.is_visible_to(remote) {
-
            return false;
-
        }
-
        true
-
    }
-

    /// Inbound connection attempt.
    pub fn accepted(&mut self, addr: Address) -> bool {
        // Always accept trusted connections.