cli: Add private repo support to `rad sync`

We can't use `node::seeds` for private repos.
This commit is contained in:
cloudhead 2023-08-29 16:10:17 +02:00
parent 87c38118d1
commit a1df6d3748
No known key found for this signature in database
4 changed files with 99 additions and 20 deletions

View File

@ -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
```

View File

@ -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(());

View File

@ -1159,6 +1159,38 @@ fn rad_patch_pull_update() {
.unwrap();
}
#[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);

View File

@ -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.