cli: Add private repo support to `rad sync`
We can't use `node::seeds` for private repos.
This commit is contained in:
parent
87c38118d1
commit
a1df6d3748
|
|
@ -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
|
||||||
|
```
|
||||||
|
|
@ -6,7 +6,8 @@ use anyhow::{anyhow, Context as _};
|
||||||
|
|
||||||
use radicle::node;
|
use radicle::node;
|
||||||
use radicle::node::{FetchResult, FetchResults, Handle as _, 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 as term;
|
||||||
use crate::terminal::args::{Args, Error, Help};
|
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();
|
let failed = results.failed().count();
|
||||||
|
|
||||||
if success == 0 {
|
if success == 0 {
|
||||||
term::error(format!("Failed to fetch repository from {failed} seed(s)"));
|
anyhow::bail!("repository fetch from {failed} seed(s) failed");
|
||||||
} else {
|
} else {
|
||||||
term::success!("Fetched repository from {success} seed(s)");
|
term::success!("Fetched repository from {success} seed(s)");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if [SyncDirection::Announce, SyncDirection::Both].contains(&options.sync.direction) {
|
if [SyncDirection::Announce, SyncDirection::Both].contains(&options.sync.direction) {
|
||||||
announce(rid, mode, options.timeout, node)?;
|
announce(rid, mode, options.timeout, node, &profile)?;
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
@ -208,9 +209,21 @@ fn announce(
|
||||||
_mode: SyncMode,
|
_mode: SyncMode,
|
||||||
timeout: time::Duration,
|
timeout: time::Duration,
|
||||||
mut node: Node,
|
mut node: Node,
|
||||||
|
profile: &Profile,
|
||||||
) -> anyhow::Result<()> {
|
) -> anyhow::Result<()> {
|
||||||
|
let repo = profile.storage.repository(rid)?;
|
||||||
|
let (_, doc) = repo.identity_doc()?;
|
||||||
|
let connected: Vec<_> = if doc.visibility.is_public() {
|
||||||
let seeds = node.seeds(rid)?;
|
let seeds = node.seeds(rid)?;
|
||||||
let connected = seeds.connected().map(|s| s.nid).collect::<Vec<_>>();
|
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() {
|
if connected.is_empty() {
|
||||||
term::info!("Not connected to any seeds.");
|
term::info!("Not connected to any seeds.");
|
||||||
return Ok(());
|
return Ok(());
|
||||||
|
|
|
||||||
|
|
@ -1159,6 +1159,38 @@ fn rad_patch_pull_update() {
|
||||||
.unwrap();
|
.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]
|
#[test]
|
||||||
fn framework_home() {
|
fn framework_home() {
|
||||||
logger::init(log::Level::Debug);
|
logger::init(log::Level::Debug);
|
||||||
|
|
|
||||||
|
|
@ -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.
|
/// Inbound connection attempt.
|
||||||
pub fn accepted(&mut self, addr: Address) -> bool {
|
pub fn accepted(&mut self, addr: Address) -> bool {
|
||||||
// Always accept trusted connections.
|
// Always accept trusted connections.
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue