codeberg_cli/actions/repo/
clone.rs1use 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#[derive(Parser, Debug)]
13pub struct RepoCloneArgs {
14 #[arg(value_name = "OWNER/REPO")]
16 pub owner_and_repo: String,
17 #[arg(long, default_value_t = false)]
19 pub use_ssh: bool,
20 #[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 anyhow::bail!("SSH Cloning under maintenance");
33 #[allow(unreachable_code)]
34 spin_until_ready(get_ssh_url(&ctx, owner.as_str(), repo.as_str())).await?
35 } else {
36 ctx.config
37 .url()?
38 .join(format!("/{owner}/{repo}.git").as_str())?
39 };
40
41 start_clone_repo(url, ctx.args.destination.unwrap_or(repo))?;
44 Ok(())
45 }
46}
47
48async fn get_ssh_url(
49 ctx: &BergContext<RepoCloneArgs>,
50 ownername: &str,
51 reponame: &str,
52) -> anyhow::Result<Url> {
53 ctx.client
54 .repo_get(ownername, reponame)
55 .await
56 .map_err(anyhow::Error::from)
57 .inspect(|x| tracing::debug!("{x:#?}"))
58 .and_then(|repo| repo.ssh_url.context("No SSH url on repo"))
59 .and_then(|url| Url::parse(url.as_str()).context("Url wasn't a valid SSH Url"))
60 .context("User doesn't own the repo that was specified.")
61}
62
63fn start_clone_repo(ssh_url: Url, destination: String) -> anyhow::Result<()> {
64 let mut cmd = std::process::Command::new("git");
65 cmd.arg("clone")
66 .arg(ssh_url.as_str())
67 .arg(destination.as_str());
68 tracing::debug!("cmd: {cmd:?}");
69 let mut child = cmd.stdout(std::process::Stdio::inherit()).spawn()?;
70 child.wait()?;
71 Ok(())
72}