diff --git a/src/cmd/clone.rs b/src/cmd/clone.rs index 93bf06c..0d6a533 100644 --- a/src/cmd/clone.rs +++ b/src/cmd/clone.rs @@ -1,13 +1,14 @@ use std::path::PathBuf; +use std::time::Duration; use anyhow::{anyhow, Result}; use async_hofs::iter::AsyncMapExt; use clap::Parser; use console::style; use git2::Repository; -use itertools::Itertools; +use tokio::time::sleep; use tokio_stream::StreamExt; -use tracing::info; +use tracing::{info, warn}; use crate::config::Config; use crate::console::Spinner; @@ -16,6 +17,11 @@ use crate::path::Path; use crate::root::Root; use crate::url::Url; +// Constant values taken from implementation of GitHub Cli (gh) +// ref: https://github.com/cli/cli/blob/350011/pkg/cmd/repo/fork/fork.go#L328-L344 +const CLONE_RETRY_COUNT: u32 = 3; +const CLONE_RETRY_DURATION: Duration = Duration::from_secs(2); + #[derive(Debug, Parser)] pub struct Cmd { /// URL or pattern of the repository to clone. @@ -53,11 +59,12 @@ impl Cmd { let repo: Vec = Spinner::new("Cloning the repository...") .spin_while(|| async move { urls.into_iter() - .map(|url| { + .async_map(|url| async { info!("Cloning from '{}'", url.to_string()); - self.clone(&root, &config, url) + self.clone(&root, &config, url).await }) - .try_collect() + .collect::>>() + .await }) .await?; @@ -113,20 +120,32 @@ impl Cmd { Ok(url) } - fn clone(&self, root: &Root, config: &Config, url: Url) -> Result { + async fn clone(&self, root: &Root, config: &Config, url: Url) -> Result { let path = PathBuf::from(Path::resolve(root, &url)); let profile = config .rules .resolve(&url) .and_then(|r| config.profiles.resolve(&r.profile)); - config.git.strategy.clone.clone_repository( - url, + let mut retries = 0; + while let Err(e) = config.git.strategy.clone.clone_repository( + url.clone(), &path, &CloneOptions { recursive: self.recursive, }, - )?; + ) { + retries += 1; + if retries > CLONE_RETRY_COUNT { + return Err(e); + } else { + warn!( + "Cloning failed. Retrying in {} seconds", + CLONE_RETRY_DURATION.as_secs(), + ); + sleep(CLONE_RETRY_DURATION).await; + } + } let repo = Repository::open(&path)?; let profile = if let Some((name, p)) = profile {