Add `rad-clone` command
Signed-off-by: Alexis Sellier <alexis@radicle.xyz>
This commit is contained in:
parent
ca1b1f4453
commit
b040098447
|
|
@ -2,6 +2,8 @@
|
|||
pub mod rad_auth;
|
||||
#[path = "commands/checkout.rs"]
|
||||
pub mod rad_checkout;
|
||||
#[path = "commands/clone.rs"]
|
||||
pub mod rad_clone;
|
||||
#[path = "commands/help.rs"]
|
||||
pub mod rad_help;
|
||||
#[path = "commands/init.rs"]
|
||||
|
|
|
|||
|
|
@ -115,8 +115,8 @@ pub fn execute(options: Options, profile: &Profile) -> anyhow::Result<PathBuf> {
|
|||
.filter(|id| id != profile.id())
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
// Setup tracking for project delegates.
|
||||
setup_tracking(
|
||||
// Setup remote tracking branches for project delegates.
|
||||
setup_remotes(
|
||||
project::SetupRemote {
|
||||
project: id,
|
||||
default_branch: payload.default_branch,
|
||||
|
|
@ -131,7 +131,7 @@ pub fn execute(options: Options, profile: &Profile) -> anyhow::Result<PathBuf> {
|
|||
}
|
||||
|
||||
/// Setup a remote and tracking branch for each given remote.
|
||||
pub fn setup_tracking(setup: project::SetupRemote, remotes: &[NodeId]) -> anyhow::Result<()> {
|
||||
pub fn setup_remotes(setup: project::SetupRemote, remotes: &[NodeId]) -> anyhow::Result<()> {
|
||||
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.
|
||||
|
|
|
|||
|
|
@ -0,0 +1,127 @@
|
|||
#![allow(clippy::or_fun_call)]
|
||||
use std::ffi::OsString;
|
||||
use std::path::Path;
|
||||
use std::str::FromStr;
|
||||
|
||||
use anyhow::anyhow;
|
||||
use anyhow::Context as _;
|
||||
|
||||
use radicle::node::Handle;
|
||||
use radicle::prelude::*;
|
||||
use radicle::rad;
|
||||
use radicle::storage::WriteStorage;
|
||||
|
||||
use crate::commands::rad_checkout::setup_remotes;
|
||||
use crate::project;
|
||||
use crate::terminal as term;
|
||||
use crate::terminal::args::{Args, Error, Help};
|
||||
use crate::terminal::Interactive;
|
||||
|
||||
pub const HELP: Help = Help {
|
||||
name: "clone",
|
||||
description: "Clone radicle projects",
|
||||
version: env!("CARGO_PKG_VERSION"),
|
||||
usage: r#"
|
||||
Usage
|
||||
|
||||
rad clone <id> [<option>...]
|
||||
|
||||
Options
|
||||
|
||||
--no-confirm Don't ask for confirmation during clone
|
||||
--help Print help
|
||||
|
||||
"#,
|
||||
};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Options {
|
||||
id: Id,
|
||||
interactive: Interactive,
|
||||
}
|
||||
|
||||
impl Args for Options {
|
||||
fn from_args(args: Vec<OsString>) -> anyhow::Result<(Self, Vec<OsString>)> {
|
||||
use lexopt::prelude::*;
|
||||
|
||||
let mut parser = lexopt::Parser::from_args(args);
|
||||
let mut id: Option<Id> = None;
|
||||
let mut interactive = Interactive::Yes;
|
||||
|
||||
while let Some(arg) = parser.next()? {
|
||||
match arg {
|
||||
Long("no-confirm") => {
|
||||
interactive = Interactive::No;
|
||||
}
|
||||
Long("help") => {
|
||||
return Err(Error::Help.into());
|
||||
}
|
||||
Value(val) if id.is_none() => {
|
||||
let val = val.to_string_lossy();
|
||||
let val = val.strip_prefix("rad://").unwrap_or(&val);
|
||||
let val = Id::from_str(val)?;
|
||||
|
||||
id = Some(val);
|
||||
}
|
||||
_ => return Err(anyhow!(arg.unexpected())),
|
||||
}
|
||||
}
|
||||
let id = id.ok_or_else(|| {
|
||||
anyhow!("to clone, a radicle id must be provided; see `rad clone --help`")
|
||||
})?;
|
||||
|
||||
Ok((Options { id, interactive }, vec![]))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn run(options: Options, ctx: impl term::Context) -> anyhow::Result<()> {
|
||||
clone(options.id, options.interactive, ctx)
|
||||
}
|
||||
|
||||
pub fn clone(id: Id, _interactive: Interactive, ctx: impl term::Context) -> anyhow::Result<()> {
|
||||
let profile = ctx.profile()?;
|
||||
let node = radicle::node::connect(profile.node())?;
|
||||
let signer = term::signer(&profile)?;
|
||||
|
||||
// Track & fetch project.
|
||||
node.track(&id).context("track")?;
|
||||
node.fetch(&id).context("fetch")?;
|
||||
|
||||
// Create a local fork of the project, under our own id.
|
||||
rad::fork(id, &signer, &profile.storage).context("fork")?;
|
||||
|
||||
let doc = profile
|
||||
.storage
|
||||
.repository(id)?
|
||||
.project_of(profile.id())
|
||||
.map_err(|_e| anyhow!("couldn't load project {} from local state", id))?;
|
||||
|
||||
let path = Path::new(&doc.name);
|
||||
let repo = rad::checkout(id, profile.id(), path, &profile.storage)?;
|
||||
let delegates = doc
|
||||
.delegates
|
||||
.iter()
|
||||
.map(|d| *d.id)
|
||||
.filter(|id| id != profile.id())
|
||||
.collect::<Vec<_>>();
|
||||
let default_branch = doc.payload.default_branch.clone();
|
||||
|
||||
// Setup tracking for project delegates.
|
||||
setup_remotes(
|
||||
project::SetupRemote {
|
||||
project: id,
|
||||
default_branch,
|
||||
repo: &repo,
|
||||
fetch: true,
|
||||
tracking: true,
|
||||
},
|
||||
&delegates,
|
||||
)?;
|
||||
|
||||
term::headline(&format!(
|
||||
"🌱 Project successfully cloned under {}",
|
||||
term::format::highlight(Path::new(".").join(path).display())
|
||||
));
|
||||
|
||||
Ok(())
|
||||
}
|
||||
Loading…
Reference in New Issue