cli: allow directory in rad clone
Allow the user to specify a directory to clone into when performing `rad clone`. Signed-off-by: Fintan Halpenny <Fintan Halpenny@gmail.com> X-Clacks-Overhead: GNU Terry Pratchett
This commit is contained in:
parent
c761d03a5a
commit
070e912b3e
|
|
@ -0,0 +1,27 @@
|
||||||
|
We can specify where a repository gets cloned into on our filesystem
|
||||||
|
by specifying the directory in the `rad clone` command:
|
||||||
|
|
||||||
|
```
|
||||||
|
$ rad clone rad:z42hL2jL4XNk6K8oHQaSWfMgCL7ji --scope followed Developer/Radicle
|
||||||
|
✓ Seeding policy updated for rad:z42hL2jL4XNk6K8oHQaSWfMgCL7ji with scope 'followed'
|
||||||
|
✓ Fetching rad:z42hL2jL4XNk6K8oHQaSWfMgCL7ji from z6MknSL…StBU8Vi..
|
||||||
|
✓ Creating checkout in ./Developer/Radicle..
|
||||||
|
✓ Remote alice@z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi added
|
||||||
|
✓ Remote-tracking branch alice@z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi/master created for z6MknSL…StBU8Vi
|
||||||
|
✓ Repository successfully cloned under [..]/Developer/Radicle/
|
||||||
|
╭────────────────────────────────────╮
|
||||||
|
│ heartwood │
|
||||||
|
│ Radicle Heartwood Protocol & Stack │
|
||||||
|
│ 0 issues · 0 patches │
|
||||||
|
╰────────────────────────────────────╯
|
||||||
|
Run `cd [..]/Developer/Radicle` to go to the project directory.
|
||||||
|
```
|
||||||
|
|
||||||
|
Note that attempting to clone into a directory that already exists,
|
||||||
|
and is not empty, will fail:
|
||||||
|
|
||||||
|
``` (fail)
|
||||||
|
$ rad clone rad:z42hL2jL4XNk6K8oHQaSWfMgCL7ji --scope followed Developer/Radicle
|
||||||
|
✓ Fetching rad:z42hL2jL4XNk6K8oHQaSWfMgCL7ji from z6MknSL…StBU8Vi..
|
||||||
|
✗ Error: the directory path "Developer/Radicle" already exists
|
||||||
|
```
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
#![allow(clippy::or_fun_call)]
|
#![allow(clippy::or_fun_call)]
|
||||||
use std::ffi::OsString;
|
use std::ffi::OsString;
|
||||||
use std::path::Path;
|
use std::path::{Path, PathBuf};
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
use std::time;
|
use std::time;
|
||||||
|
|
||||||
|
|
@ -34,7 +34,7 @@ pub const HELP: Help = Help {
|
||||||
usage: r#"
|
usage: r#"
|
||||||
Usage
|
Usage
|
||||||
|
|
||||||
rad clone <rid> [--scope <scope>] [<option>...]
|
rad clone <rid> [<directory>] [--scope <scope>] [<option>...]
|
||||||
|
|
||||||
Options
|
Options
|
||||||
|
|
||||||
|
|
@ -46,7 +46,11 @@ Options
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Options {
|
pub struct Options {
|
||||||
|
/// The RID of the repository.
|
||||||
id: Id,
|
id: Id,
|
||||||
|
/// The target directory for the repository to be cloned into.
|
||||||
|
directory: Option<PathBuf>,
|
||||||
|
/// The seeding scope of the repository.
|
||||||
scope: Scope,
|
scope: Scope,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -57,6 +61,7 @@ impl Args for Options {
|
||||||
let mut parser = lexopt::Parser::from_args(args);
|
let mut parser = lexopt::Parser::from_args(args);
|
||||||
let mut id: Option<Id> = None;
|
let mut id: Option<Id> = None;
|
||||||
let mut scope = Scope::All;
|
let mut scope = Scope::All;
|
||||||
|
let mut directory = None;
|
||||||
|
|
||||||
while let Some(arg) = parser.next()? {
|
while let Some(arg) = parser.next()? {
|
||||||
match arg {
|
match arg {
|
||||||
|
|
@ -79,13 +84,24 @@ impl Args for Options {
|
||||||
|
|
||||||
id = Some(val);
|
id = Some(val);
|
||||||
}
|
}
|
||||||
|
// Parse <directory> once <rid> has been parsed
|
||||||
|
Value(val) if id.is_some() && directory.is_none() => {
|
||||||
|
directory = Some(Path::new(&val).to_path_buf());
|
||||||
|
}
|
||||||
_ => return Err(anyhow!(arg.unexpected())),
|
_ => return Err(anyhow!(arg.unexpected())),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let id =
|
let id =
|
||||||
id.ok_or_else(|| anyhow!("to clone, an RID must be provided; see `rad clone --help`"))?;
|
id.ok_or_else(|| anyhow!("to clone, an RID must be provided; see `rad clone --help`"))?;
|
||||||
|
|
||||||
Ok((Options { id, scope }, vec![]))
|
Ok((
|
||||||
|
Options {
|
||||||
|
id,
|
||||||
|
directory,
|
||||||
|
scope,
|
||||||
|
},
|
||||||
|
vec![],
|
||||||
|
))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -102,6 +118,7 @@ pub fn run(options: Options, ctx: impl term::Context) -> anyhow::Result<()> {
|
||||||
|
|
||||||
let (working, repo, doc, proj) = clone(
|
let (working, repo, doc, proj) = clone(
|
||||||
options.id,
|
options.id,
|
||||||
|
options.directory.clone(),
|
||||||
options.scope,
|
options.scope,
|
||||||
&mut node,
|
&mut node,
|
||||||
&signer,
|
&signer,
|
||||||
|
|
@ -150,9 +167,12 @@ pub fn run(options: Options, ctx: impl term::Context) -> anyhow::Result<()> {
|
||||||
])]);
|
])]);
|
||||||
info.print();
|
info.print();
|
||||||
|
|
||||||
|
let location = options
|
||||||
|
.directory
|
||||||
|
.map_or(proj.name().to_string(), |loc| loc.display().to_string());
|
||||||
term::info!(
|
term::info!(
|
||||||
"Run {} to go to the project directory.",
|
"Run {} to go to the project directory.",
|
||||||
term::format::command(format!("cd ./{}", proj.name())),
|
term::format::command(format!("cd ./{location}")),
|
||||||
);
|
);
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
@ -160,6 +180,8 @@ pub fn run(options: Options, ctx: impl term::Context) -> anyhow::Result<()> {
|
||||||
|
|
||||||
#[derive(Error, Debug)]
|
#[derive(Error, Debug)]
|
||||||
pub enum CloneError {
|
pub enum CloneError {
|
||||||
|
#[error("the directory path {path:?} already exists")]
|
||||||
|
Exists { path: PathBuf },
|
||||||
#[error("node: {0}")]
|
#[error("node: {0}")]
|
||||||
Node(#[from] node::Error),
|
Node(#[from] node::Error),
|
||||||
#[error("storage: {0}")]
|
#[error("storage: {0}")]
|
||||||
|
|
@ -180,6 +202,7 @@ pub enum CloneError {
|
||||||
|
|
||||||
pub fn clone<G: Signer>(
|
pub fn clone<G: Signer>(
|
||||||
id: Id,
|
id: Id,
|
||||||
|
directory: Option<PathBuf>,
|
||||||
scope: Scope,
|
scope: Scope,
|
||||||
node: &mut Node,
|
node: &mut Node,
|
||||||
signer: &G,
|
signer: &G,
|
||||||
|
|
@ -221,7 +244,14 @@ pub fn clone<G: Signer>(
|
||||||
|
|
||||||
let doc = repository.identity_doc()?;
|
let doc = repository.identity_doc()?;
|
||||||
let proj = doc.project()?;
|
let proj = doc.project()?;
|
||||||
let path = Path::new(proj.name());
|
let path = directory.unwrap_or(Path::new(proj.name()).to_path_buf());
|
||||||
|
|
||||||
|
// N.b. fail if the path exists and is not empty
|
||||||
|
if path.exists() {
|
||||||
|
if path.read_dir().map_or(true, |mut dir| dir.next().is_some()) {
|
||||||
|
return Err(CloneError::Exists { path });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if results.success().next().is_none() {
|
if results.success().next().is_none() {
|
||||||
if results.failed().next().is_some() {
|
if results.failed().next().is_some() {
|
||||||
|
|
|
||||||
|
|
@ -748,6 +748,32 @@ fn rad_clone() {
|
||||||
test("examples/rad-clone.md", working, Some(&bob.home), []).unwrap();
|
test("examples/rad-clone.md", working, Some(&bob.home), []).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn rad_clone_directory() {
|
||||||
|
let mut environment = Environment::new();
|
||||||
|
let mut alice = environment.node(Config::test(Alias::new("alice")));
|
||||||
|
let bob = environment.node(Config::test(Alias::new("bob")));
|
||||||
|
let working = environment.tmp().join("working");
|
||||||
|
|
||||||
|
// Setup a test project.
|
||||||
|
let acme = alice.project("heartwood", "Radicle Heartwood Protocol & Stack");
|
||||||
|
|
||||||
|
let mut alice = alice.spawn();
|
||||||
|
let mut bob = bob.spawn();
|
||||||
|
// Prevent Alice from fetching Bob's fork, as we're not testing that and it may cause errors.
|
||||||
|
alice.handle.seed(acme, Scope::Followed).unwrap();
|
||||||
|
|
||||||
|
bob.connect(&alice).converge([&alice]);
|
||||||
|
|
||||||
|
test(
|
||||||
|
"examples/rad-clone-directory.md",
|
||||||
|
working,
|
||||||
|
Some(&bob.home),
|
||||||
|
[],
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn rad_clone_all() {
|
fn rad_clone_all() {
|
||||||
let mut environment = Environment::new();
|
let mut environment = Environment::new();
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue