codeberg_cli/actions/repo/
clone.rs

1use anyhow::Context;
2use url::Url;
3
4use crate::actions::repo::parse_owner_and_repo;
5use crate::actions::GeneralArgs;
6use crate::render::spinner::spin_until_ready;
7use crate::types::context::BergContext;
8
9use clap::Parser;
10
11/// Clone a repository
12#[derive(Parser, Debug)]
13pub struct RepoCloneArgs {
14    /// Repository to be cloned
15    #[arg(value_name = "OWNER/REPO")]
16    pub owner_and_repo: String,
17    /// Whether or not to clone via ssh
18    #[arg(long, default_value_t = false)]
19    pub use_ssh: bool,
20    /// Where to clone into
21    #[arg(value_name = "DESTINATION")]
22    pub destination: Option<String>,
23}
24
25impl RepoCloneArgs {
26    pub async fn run(self, general_args: GeneralArgs) -> anyhow::Result<()> {
27        let _ = general_args;
28        let ctx = BergContext::new(self, general_args).await?;
29
30        let (owner, repo) = parse_owner_and_repo(ctx.args.owner_and_repo.as_str())?;
31        let url = if ctx.args.use_ssh {
32            spin_until_ready(get_ssh_url(&ctx, owner.as_str(), repo.as_str())).await?
33        } else {
34            ctx.config
35                .url()?
36                .join(format!("/{owner}/{repo}.git").as_str())?
37        };
38
39        // TODO: Add back ***opt in*** ask confirm to clone
40
41        start_clone_repo(url, ctx.args.destination.unwrap_or(repo))?;
42        Ok(())
43    }
44}
45
46async fn get_ssh_url(
47    ctx: &BergContext<RepoCloneArgs>,
48    ownername: &str,
49    reponame: &str,
50) -> anyhow::Result<Url> {
51    ctx.client
52        .repo_get(ownername, reponame)
53        .await
54        .map_err(anyhow::Error::from)
55        .inspect(|x| tracing::debug!("{x:#?}"))
56        .and_then(|repo| repo.ssh_url.context("No SSH url on repo"))
57        .and_then(|url| {
58            Url::parse(url.as_str())
59                .with_context(|| format!("Url wasn't a valid SSH Url: {url}", url = url.as_str()))
60        })
61        .context("User doesn't own the repo that was specified or at least doesn't have access'.")
62}
63
64fn start_clone_repo(ssh_url: Url, destination: String) -> anyhow::Result<()> {
65    let mut cmd = std::process::Command::new("git");
66    cmd.arg("clone")
67        .arg(ssh_url.as_str())
68        .arg(destination.as_str());
69    tracing::debug!("cmd: {cmd:?}");
70    let mut child = cmd.stdout(std::process::Stdio::inherit()).spawn()?;
71    child.wait()?;
72    Ok(())
73}