cli: Consolidate working copy remote setup
We consolidate the setup done by `rad checkout`, `rad clone` and `rad remote add`, so that they all check for node aliases, and are all capable of setting up tracking branches etc.
This commit is contained in:
parent
5502676aa7
commit
e9ef0f4aa4
|
|
@ -0,0 +1,76 @@
|
|||
<!-- TODO: Currently, `rad clone`, even with `--scope all` will not fetch all remotes -->
|
||||
<!-- We have to issue a separate `rad sync --fetch` -->
|
||||
|
||||
```
|
||||
$ rad clone rad:z42hL2jL4XNk6K8oHQaSWfMgCL7ji --scope all
|
||||
✓ Tracking relationship established for rad:z42hL2jL4XNk6K8oHQaSWfMgCL7ji with scope 'all'
|
||||
✓ Fetching rad:z42hL2jL4XNk6K8oHQaSWfMgCL7ji from z6MknSL…StBU8Vi..
|
||||
✓ Forking under z6Mkux1…nVhib7Z..
|
||||
✓ Creating checkout in ./heartwood..
|
||||
✓ Remote z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi added
|
||||
✓ Remote-tracking branch z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi/master created for z6MknSL…StBU8Vi
|
||||
✓ Repository successfully cloned under [..]/heartwood/
|
||||
```
|
||||
|
||||
We can now have a look at the new working copy that was created from the cloned
|
||||
repository:
|
||||
|
||||
```
|
||||
$ cd heartwood
|
||||
$ cat README
|
||||
Hello World!
|
||||
```
|
||||
|
||||
```
|
||||
$ rad sync --fetch
|
||||
✓ Fetching rad:z42hL2jL4XNk6K8oHQaSWfMgCL7ji from z6MknSL…StBU8Vi..
|
||||
✓ Fetched repository from 1 seed(s)
|
||||
```
|
||||
|
||||
Let's check that we have all the namespaces in storage:
|
||||
|
||||
```
|
||||
$ rad inspect --refs
|
||||
.
|
||||
|-- z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi
|
||||
| `-- refs
|
||||
| |-- heads
|
||||
| | `-- master
|
||||
| `-- rad
|
||||
| |-- id
|
||||
| `-- sigrefs
|
||||
|-- z6Mkt67GdsW7715MEfRuP4pSZxJRJh6kj6Y48WRqVv4N1tRk
|
||||
| `-- refs
|
||||
| |-- heads
|
||||
| | `-- master
|
||||
| `-- rad
|
||||
| |-- id
|
||||
| `-- sigrefs
|
||||
`-- z6Mkux1aUQD2voWWukVb5nNUR7thrHveQG4pDQua8nVhib7Z
|
||||
`-- refs
|
||||
|-- heads
|
||||
| `-- master
|
||||
`-- rad
|
||||
|-- id
|
||||
`-- sigrefs
|
||||
```
|
||||
|
||||
We can then setup a git remote for `bob`:
|
||||
|
||||
```
|
||||
$ rad remote add z6Mkt67GdsW7715MEfRuP4pSZxJRJh6kj6Y48WRqVv4N1tRk --name bob
|
||||
✓ Remote bob added
|
||||
```
|
||||
|
||||
And fetch his refs:
|
||||
|
||||
```
|
||||
$ git fetch --all
|
||||
Fetching rad
|
||||
Fetching z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi
|
||||
Fetching bob
|
||||
$ git branch --remotes
|
||||
bob/master
|
||||
rad/master
|
||||
z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi/master
|
||||
```
|
||||
|
|
@ -2,12 +2,12 @@ To create a local copy of a repository on the radicle network, we use the
|
|||
`clone` command, followed by the identifier or *RID* of the repository:
|
||||
|
||||
```
|
||||
$ rad clone rad:z42hL2jL4XNk6K8oHQaSWfMgCL7ji
|
||||
✓ Tracking relationship established for rad:z42hL2jL4XNk6K8oHQaSWfMgCL7ji with scope 'all'
|
||||
$ rad clone rad:z42hL2jL4XNk6K8oHQaSWfMgCL7ji --scope trusted
|
||||
✓ Tracking relationship established for rad:z42hL2jL4XNk6K8oHQaSWfMgCL7ji with scope 'trusted'
|
||||
✓ Fetching rad:z42hL2jL4XNk6K8oHQaSWfMgCL7ji from z6MknSL…StBU8Vi..
|
||||
✓ Forking under z6Mkt67…v4N1tRk..
|
||||
✓ Creating checkout in ./heartwood..
|
||||
✓ Remote z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi created
|
||||
✓ Remote z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi added
|
||||
✓ Remote-tracking branch z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi/master created for z6MknSL…StBU8Vi
|
||||
✓ Repository successfully cloned under [..]/heartwood/
|
||||
```
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ Now, let's add a bob as a new remote:
|
|||
|
||||
```
|
||||
$ rad remote add did:key:z6Mkt67GdsW7715MEfRuP4pSZxJRJh6kj6Y48WRqVv4N1tRk --name bob
|
||||
✓ Remote bob added with rad://z42hL2jL4XNk6K8oHQaSWfMgCL7ji/z6Mkt67GdsW7715MEfRuP4pSZxJRJh6kj6Y48WRqVv4N1tRk
|
||||
✓ Remote bob added
|
||||
```
|
||||
|
||||
Now, we can see that there is a new remote in the list of remotes:
|
||||
|
|
@ -14,7 +14,8 @@ rad (canonical upstream) (fetch)
|
|||
rad z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi (push)
|
||||
```
|
||||
|
||||
You can see both `bob` and `rad` as remotes. The `rad` remote is our personal remote of the project.
|
||||
You can see both `bob` and `rad` as remotes. The `rad` remote is our personal
|
||||
remote of the project.
|
||||
|
||||
When we're finished with the `bob` remote, we can remove it:
|
||||
|
||||
|
|
@ -23,9 +24,10 @@ $ rad remote rm bob
|
|||
✓ Remote `bob` removed
|
||||
```
|
||||
|
||||
Now, add another time `bob` but without specify the `name`, so we should be able to fetch the node alias from our db!
|
||||
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
|
||||
✓ Remote bob added with rad://z42hL2jL4XNk6K8oHQaSWfMgCL7ji/z6Mkt67GdsW7715MEfRuP4pSZxJRJh6kj6Y48WRqVv4N1tRk
|
||||
✓ Remote bob added
|
||||
```
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ peer. Upcoming versions of radicle will not require this step.
|
|||
|
||||
```
|
||||
$ rad remote add z6Mkt67GdsW7715MEfRuP4pSZxJRJh6kj6Y48WRqVv4N1tRk --name bob
|
||||
✓ Remote bob added with rad://z42hL2jL4XNk6K8oHQaSWfMgCL7ji/z6Mkt67GdsW7715MEfRuP4pSZxJRJh6kj6Y48WRqVv4N1tRk
|
||||
✓ Remote bob added
|
||||
```
|
||||
|
||||
``` (stderr)
|
||||
|
|
|
|||
|
|
@ -1,9 +1,13 @@
|
|||
#![allow(clippy::box_default)]
|
||||
use std::collections::HashMap;
|
||||
use std::ffi::OsString;
|
||||
use std::path::PathBuf;
|
||||
|
||||
use anyhow::anyhow;
|
||||
use anyhow::Context as _;
|
||||
|
||||
use radicle::git;
|
||||
use radicle::node::AliasStore;
|
||||
use radicle::prelude::*;
|
||||
use radicle::storage::git::transport;
|
||||
|
||||
|
|
@ -118,31 +122,63 @@ fn execute(options: Options, profile: &Profile) -> anyhow::Result<PathBuf> {
|
|||
// Setup remote tracking branches for project delegates.
|
||||
setup_remotes(
|
||||
project::SetupRemote {
|
||||
project: id,
|
||||
default_branch: payload.default_branch().clone(),
|
||||
rid: id,
|
||||
tracking: Some(payload.default_branch().clone()),
|
||||
repo: &repo,
|
||||
fetch: true,
|
||||
tracking: true,
|
||||
},
|
||||
&remotes,
|
||||
profile,
|
||||
)?;
|
||||
|
||||
Ok(path)
|
||||
}
|
||||
|
||||
/// Setup a remote and tracking branch for each given remote.
|
||||
pub fn setup_remotes(setup: project::SetupRemote, remotes: &[NodeId]) -> anyhow::Result<()> {
|
||||
pub fn setup_remotes(
|
||||
setup: project::SetupRemote,
|
||||
remotes: &[NodeId],
|
||||
profile: &Profile,
|
||||
) -> anyhow::Result<()> {
|
||||
let aliases = if let Ok(aliases) = profile.aliases() {
|
||||
Box::new(aliases) as Box<dyn AliasStore>
|
||||
} else {
|
||||
Box::new(HashMap::new()) as Box<dyn AliasStore>
|
||||
};
|
||||
for remote_id in remotes {
|
||||
if let Some((remote, branch)) = setup.run(*remote_id)? {
|
||||
let remote = remote.name().unwrap(); // Only valid UTF-8 is used.
|
||||
|
||||
term::success!("Remote {} created", term::format::tertiary(remote));
|
||||
term::success!(
|
||||
"Remote-tracking branch {} created for {}",
|
||||
term::format::tertiary(branch),
|
||||
term::format::tertiary(term::format::node(remote_id))
|
||||
);
|
||||
if let Err(e) = setup_remote(&setup, remote_id, None, &aliases) {
|
||||
term::warning(format!("Failed to setup remote for {remote_id}: {e}").as_str());
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Setup a remote and tracking branch for the given remote.
|
||||
pub fn setup_remote(
|
||||
setup: &project::SetupRemote,
|
||||
remote_id: &NodeId,
|
||||
remote_name: Option<git::RefString>,
|
||||
aliases: &impl AliasStore,
|
||||
) -> anyhow::Result<()> {
|
||||
let remote_name = if let Some(alias) = remote_name {
|
||||
alias
|
||||
} else {
|
||||
let alias = aliases
|
||||
.alias(remote_id)
|
||||
.unwrap_or_else(|| remote_id.to_string());
|
||||
git::RefString::try_from(alias.clone())
|
||||
.map_err(|_| anyhow!("invalid remote name: '{alias}'"))?
|
||||
};
|
||||
let (remote, branch) = setup.run(remote_name, *remote_id)?;
|
||||
|
||||
term::success!("Remote {} added", term::format::tertiary(remote.name));
|
||||
|
||||
if let Some(branch) = branch {
|
||||
term::success!(
|
||||
"Remote-tracking branch {} created for {}",
|
||||
term::format::tertiary(branch),
|
||||
term::format::tertiary(term::format::node(remote_id))
|
||||
);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
|||
|
|
@ -126,13 +126,13 @@ pub fn run(options: Options, ctx: impl term::Context) -> anyhow::Result<()> {
|
|||
radicle::git::configure_repository(&working)?;
|
||||
checkout::setup_remotes(
|
||||
project::SetupRemote {
|
||||
project: options.id,
|
||||
default_branch,
|
||||
rid: options.id,
|
||||
tracking: Some(default_branch),
|
||||
repo: &working,
|
||||
fetch: true,
|
||||
tracking: true,
|
||||
},
|
||||
&delegates,
|
||||
&profile,
|
||||
)?;
|
||||
|
||||
term::success!(
|
||||
|
|
|
|||
|
|
@ -10,9 +10,10 @@ use std::ffi::OsString;
|
|||
|
||||
use anyhow::anyhow;
|
||||
|
||||
use radicle::git::RefString;
|
||||
use radicle::prelude::NodeId;
|
||||
|
||||
use crate::terminal::args::{self, string, Error};
|
||||
use crate::terminal::args;
|
||||
use crate::terminal::{Args, Context, Help};
|
||||
|
||||
pub const HELP: Help = Help {
|
||||
|
|
@ -44,8 +45,8 @@ pub enum OperationName {
|
|||
|
||||
#[derive(Debug)]
|
||||
pub enum Operation {
|
||||
Add { id: NodeId, name: Option<String> },
|
||||
Rm { name: String },
|
||||
Add { id: NodeId, name: Option<RefString> },
|
||||
Rm { name: RefString },
|
||||
List,
|
||||
}
|
||||
|
||||
|
|
@ -61,16 +62,17 @@ impl Args for Options {
|
|||
let mut parser = lexopt::Parser::from_args(args);
|
||||
let mut op: Option<OperationName> = None;
|
||||
let mut id: Option<NodeId> = None;
|
||||
let mut name: Option<String> = None;
|
||||
let mut name: Option<RefString> = None;
|
||||
|
||||
while let Some(arg) = parser.next()? {
|
||||
match arg {
|
||||
Long("help") => {
|
||||
return Err(Error::Help.into());
|
||||
return Err(args::Error::Help.into());
|
||||
}
|
||||
Long("name") | Short('n') => {
|
||||
let value = parser.value()?;
|
||||
let value = string(&value);
|
||||
let value = args::refstring("name", value)?;
|
||||
|
||||
name = Some(value);
|
||||
}
|
||||
Value(val) if op.is_none() => match val.to_string_lossy().as_ref() {
|
||||
|
|
@ -84,7 +86,10 @@ impl Args for Options {
|
|||
id = Some(nid);
|
||||
}
|
||||
Value(val) if op == Some(OperationName::Rm) && name.is_none() => {
|
||||
let val = string(&val);
|
||||
let val = args::string(&val);
|
||||
let val = RefString::try_from(val)
|
||||
.map_err(|e| anyhow!("invalid remote name specified: {e}"))?;
|
||||
|
||||
name = Some(val);
|
||||
}
|
||||
_ => return Err(anyhow::anyhow!(arg.unexpected())),
|
||||
|
|
@ -114,7 +119,8 @@ pub fn run(options: Options, ctx: impl Context) -> anyhow::Result<()> {
|
|||
let profile = ctx.profile()?;
|
||||
|
||||
match options.op {
|
||||
Operation::Add { ref id, name } => self::add::run(rid, id, name, &profile, &working)?,
|
||||
// TODO: Support remote-tracking.
|
||||
Operation::Add { ref id, name } => self::add::run(rid, id, name, None, &profile, &working)?,
|
||||
Operation::Rm { ref name } => self::rm::run(name, &working)?,
|
||||
Operation::List => self::list::run(&working)?,
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,31 +1,26 @@
|
|||
use radicle::{git::Url, prelude::Id, Profile};
|
||||
use radicle::git::RefString;
|
||||
use radicle::prelude::*;
|
||||
use radicle::Profile;
|
||||
use radicle_crypto::PublicKey;
|
||||
|
||||
use crate::git::add_remote;
|
||||
use crate::{git, terminal as term};
|
||||
use crate::commands::rad_checkout as checkout;
|
||||
use crate::git;
|
||||
use crate::project::SetupRemote;
|
||||
|
||||
pub fn run(
|
||||
id: Id,
|
||||
pubkey: &PublicKey,
|
||||
name: Option<String>,
|
||||
rid: Id,
|
||||
nid: &PublicKey,
|
||||
name: Option<RefString>,
|
||||
tracking: Option<BranchName>,
|
||||
profile: &Profile,
|
||||
repository: &git::Repository,
|
||||
repo: &git::Repository,
|
||||
) -> anyhow::Result<()> {
|
||||
let name = match name {
|
||||
Some(name) => name,
|
||||
_ => profile
|
||||
.tracking()?
|
||||
.node_policy(pubkey)?
|
||||
.and_then(|node| node.alias)
|
||||
.ok_or(anyhow::anyhow!("a `name` needs to be specified"))?,
|
||||
let aliases = profile.aliases()?;
|
||||
let setup = SetupRemote {
|
||||
rid,
|
||||
tracking,
|
||||
fetch: false,
|
||||
repo,
|
||||
};
|
||||
if git::is_remote(repository, &name)? {
|
||||
anyhow::bail!("remote `{name}` already exists");
|
||||
}
|
||||
|
||||
let url = Url::from(id).with_namespace(*pubkey);
|
||||
let remote = add_remote(repository, &name, &url)?;
|
||||
term::success!("Remote {} added with {url}", remote.name,);
|
||||
|
||||
Ok(())
|
||||
checkout::setup_remote(&setup, nid, name, &aliases)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ use std::fmt::Display;
|
|||
use std::fs::{File, OpenOptions};
|
||||
use std::io;
|
||||
use std::io::Write;
|
||||
use std::ops::{Deref, DerefMut};
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::process::Command;
|
||||
use std::str::FromStr;
|
||||
|
|
@ -74,11 +75,10 @@ pub enum RemoteError {
|
|||
|
||||
#[derive(Clone)]
|
||||
pub struct Remote<'a> {
|
||||
pub(crate) name: String,
|
||||
pub(crate) url: radicle::git::Url,
|
||||
pub(crate) pushurl: Option<radicle::git::Url>,
|
||||
pub name: String,
|
||||
pub url: radicle::git::Url,
|
||||
pub pushurl: Option<radicle::git::Url>,
|
||||
|
||||
#[allow(dead_code)]
|
||||
inner: git2::Remote<'a>,
|
||||
}
|
||||
|
||||
|
|
@ -104,6 +104,20 @@ impl<'a> TryFrom<git2::Remote<'a>> for Remote<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'a> Deref for Remote<'a> {
|
||||
type Target = git2::Remote<'a>;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.inner
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> DerefMut for Remote<'a> {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
&mut self.inner
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the git repository in the current directory.
|
||||
pub fn repository() -> Result<Repository, anyhow::Error> {
|
||||
match Repository::open(".") {
|
||||
|
|
@ -273,15 +287,6 @@ pub fn remove_remote(repo: &Repository, rid: &Id) -> anyhow::Result<()> {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn add_remote<'a>(
|
||||
repo: &'a Repository,
|
||||
name: &'a str,
|
||||
url: &'a radicle::git::Url,
|
||||
) -> anyhow::Result<Remote<'a>> {
|
||||
let remote = repo.remote(name, &url.to_string())?;
|
||||
Ok(Remote::try_from(remote)?)
|
||||
}
|
||||
|
||||
/// Setup an upstream tracking branch for the given remote and branch.
|
||||
/// Creates the tracking branch if it does not exist.
|
||||
///
|
||||
|
|
|
|||
|
|
@ -1,46 +1,50 @@
|
|||
use radicle::git::raw::Remote;
|
||||
use radicle::git::RefString;
|
||||
use radicle::prelude::*;
|
||||
|
||||
use crate::git;
|
||||
use radicle::git::RefStr;
|
||||
|
||||
/// Setup a project remote and tracking branch.
|
||||
pub struct SetupRemote<'a> {
|
||||
/// The project id.
|
||||
pub project: Id,
|
||||
/// The project default branch.
|
||||
pub default_branch: BranchName,
|
||||
/// The repository in which to setup the remote.
|
||||
pub repo: &'a git::Repository,
|
||||
pub rid: Id,
|
||||
/// Whether or not to setup a remote tracking branch.
|
||||
pub tracking: Option<BranchName>,
|
||||
/// Whether or not to fetch the remote immediately.
|
||||
pub fetch: bool,
|
||||
/// Whether or not to setup a remote tracking branch.
|
||||
pub tracking: bool,
|
||||
/// The repository in which to setup the remote.
|
||||
pub repo: &'a git::Repository,
|
||||
}
|
||||
|
||||
impl<'a> SetupRemote<'a> {
|
||||
/// Run the setup for the given peer.
|
||||
pub fn run(&self, node: NodeId) -> anyhow::Result<Option<(Remote, RefString)>> {
|
||||
let url = radicle::git::Url::from(self.project).with_namespace(node);
|
||||
let mut remote = radicle::git::configure_remote(self.repo, &node.to_string(), &url, &url)?;
|
||||
pub fn run(
|
||||
&self,
|
||||
name: impl AsRef<RefStr>,
|
||||
node: NodeId,
|
||||
) -> anyhow::Result<(git::Remote, Option<BranchName>)> {
|
||||
let remote_url = radicle::git::Url::from(self.rid).with_namespace(node);
|
||||
let remote_name = name.as_ref();
|
||||
|
||||
if git::is_remote(self.repo, remote_name)? {
|
||||
anyhow::bail!("remote `{remote_name}` already exists");
|
||||
}
|
||||
|
||||
let remote =
|
||||
radicle::git::configure_remote(self.repo, remote_name, &remote_url, &remote_url)?;
|
||||
let mut remote = git::Remote::try_from(remote)?;
|
||||
|
||||
// Fetch the refs into the working copy.
|
||||
if self.fetch {
|
||||
remote.fetch::<&str>(&[], None, None)?;
|
||||
}
|
||||
// Setup remote-tracking branch.
|
||||
if self.tracking {
|
||||
// SAFETY: Node IDs are valid ref strings.
|
||||
let node_ref = RefString::try_from(node.to_string()).unwrap();
|
||||
let node_ref = node_ref.as_refstr();
|
||||
let branch_name = node_ref.join(&self.default_branch);
|
||||
let local_branch = radicle::git::refs::workdir::branch(
|
||||
node_ref.join(&self.default_branch).as_refstr(),
|
||||
);
|
||||
radicle::git::set_upstream(self.repo, node.to_string(), &branch_name, local_branch)?;
|
||||
if let Some(branch) = &self.tracking {
|
||||
let tracking_branch = remote_name.join(branch);
|
||||
let local_branch = radicle::git::refs::workdir::branch(tracking_branch.as_refstr());
|
||||
radicle::git::set_upstream(self.repo, remote_name, &tracking_branch, local_branch)?;
|
||||
|
||||
return Ok(Some((remote, branch_name)));
|
||||
return Ok((remote, Some(tracking_branch)));
|
||||
}
|
||||
Ok(None)
|
||||
Ok((remote, None))
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ use anyhow::anyhow;
|
|||
|
||||
use radicle::cob::{self, issue, patch};
|
||||
use radicle::crypto;
|
||||
use radicle::git::RefString;
|
||||
use radicle::node::Address;
|
||||
use radicle::prelude::{Did, Id, NodeId};
|
||||
|
||||
|
|
@ -77,6 +78,20 @@ pub fn finish(unparsed: Vec<OsString>) -> anyhow::Result<()> {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
pub fn refstring(flag: &str, value: OsString) -> anyhow::Result<RefString> {
|
||||
RefString::try_from(
|
||||
value
|
||||
.into_string()
|
||||
.map_err(|_| anyhow!("the value specified for '--{}' is not valid UTF-8", flag))?,
|
||||
)
|
||||
.map_err(|_| {
|
||||
anyhow!(
|
||||
"the value specified for '--{}' is not a valid ref string",
|
||||
flag
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
pub fn did(val: &OsString) -> anyhow::Result<Did> {
|
||||
let val = val.to_string_lossy();
|
||||
let Ok(peer) = Did::from_str(&val) else {
|
||||
|
|
|
|||
|
|
@ -370,6 +370,47 @@ fn rad_clone() {
|
|||
test("examples/rad-clone.md", working, Some(&bob.home), []).unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn rad_clone_all() {
|
||||
logger::init(log::Level::Debug);
|
||||
|
||||
let mut environment = Environment::new();
|
||||
let mut alice = environment.node("alice");
|
||||
let bob = environment.node("bob");
|
||||
let eve = environment.node("eve");
|
||||
let working = environment.tmp().join("working");
|
||||
|
||||
// Setup a test project.
|
||||
let acme = alice.project("heartwood", "Radicle Heartwood Protocol & Stack");
|
||||
|
||||
let mut alice = alice.spawn(Config::default());
|
||||
let mut bob = bob.spawn(Config::default());
|
||||
let mut eve = eve.spawn(Config::default());
|
||||
|
||||
alice.handle.track_repo(acme, Scope::All).unwrap();
|
||||
bob.connect(&alice).converge([&alice]);
|
||||
eve.connect(&alice).converge([&alice]);
|
||||
|
||||
test(
|
||||
"examples/rad-clone.md",
|
||||
working.join("bob"),
|
||||
Some(&bob.home),
|
||||
[],
|
||||
)
|
||||
.unwrap();
|
||||
bob.has_inventory_of(&acme, &alice.id);
|
||||
alice.has_inventory_of(&acme, &bob.id);
|
||||
|
||||
test(
|
||||
"examples/rad-clone-all.md",
|
||||
working.join("eve"),
|
||||
Some(&eve.home),
|
||||
[],
|
||||
)
|
||||
.unwrap();
|
||||
eve.has_inventory_of(&acme, &bob.id);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn rad_self() {
|
||||
let mut environment = Environment::new();
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ use radicle::node::TRACKING_DB_FILE;
|
|||
use radicle::profile::Home;
|
||||
use radicle::profile::Profile;
|
||||
use radicle::rad;
|
||||
use radicle::storage::{ReadStorage as _, WriteRepository};
|
||||
use radicle::storage::{ReadRepository, ReadStorage as _, WriteRepository};
|
||||
use radicle::test::fixtures;
|
||||
use radicle::Storage;
|
||||
|
||||
|
|
@ -220,6 +220,27 @@ impl<G: Signer + cyphernet::Ecdh> NodeHandle<G> {
|
|||
}
|
||||
}
|
||||
|
||||
/// Wait until this node has the inventory of another node.
|
||||
#[track_caller]
|
||||
pub fn has_inventory_of(&self, rid: &Id, nid: &NodeId) {
|
||||
log::debug!(target: "test", "Waiting for {} to have {rid}/{nid}", self.id);
|
||||
let events = self.handle.events();
|
||||
|
||||
loop {
|
||||
if let Ok(repo) = self.storage.repository(*rid) {
|
||||
if repo.identity_of(nid).is_ok() && repo.remote(nid).is_ok() {
|
||||
break;
|
||||
}
|
||||
}
|
||||
events
|
||||
.wait(
|
||||
|e| matches!(e, Event::RefsFetched { .. }),
|
||||
time::Duration::from_secs(6),
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
/// Run a `rad` CLI command.
|
||||
pub fn rad<P: AsRef<Path>>(&self, cmd: &str, args: &[&str], cwd: P) -> io::Result<()> {
|
||||
let cwd = cwd.as_ref();
|
||||
|
|
|
|||
Loading…
Reference in New Issue