codeberg_cli/actions/repo/
fork.rs1use crate::actions::GeneralArgs;
2use crate::render::spinner::{spin_and_try_every_second_for, spin_until_ready};
3use crate::render::ui::confirm_with_prompt;
4use crate::types::context::BergContext;
5use anyhow::Context;
6use forgejo_api::structs::CreateForkOption;
7use url::Url;
8
9use super::parse_owner_and_repo;
10
11use clap::Parser;
12
13#[derive(Parser, Debug)]
15pub struct RepoForkArgs {
16 #[arg(value_name = "OWNER/REPO")]
18 pub owner_and_repo: String,
19}
20
21impl RepoForkArgs {
22 pub async fn run(self, general_args: GeneralArgs) -> anyhow::Result<()> {
23 let _ = general_args;
24 let ctx = BergContext::new(self, general_args).await?;
25
26 let (owner, repo) = parse_owner_and_repo(ctx.args.owner_and_repo.as_str())?;
27 let ssh_url =
28 spin_until_ready(start_fork_repo(&ctx, owner.as_str(), repo.as_str())).await?;
29 ask_confirm_clone(repo.as_str())?;
30 start_clone_repo(ssh_url)?;
31 Ok(())
32 }
33}
34
35async fn start_fork_repo(
36 ctx: &BergContext<RepoForkArgs>,
37 owner: &str,
38 repo: &str,
39) -> anyhow::Result<Url> {
40 let _ssh_url_original = get_ssh_url(ctx, owner, repo).await?;
42
43 ctx.client
44 .create_fork(
45 owner,
46 repo,
47 CreateForkOption {
48 name: None,
49 organization: None,
50 },
51 )
52 .await?;
53 let user = ctx.client.user_get_current().await?;
54 let username = user
55 .login
56 .as_ref()
57 .cloned()
58 .context("Current user has no username")?;
59 let new_url = spin_and_try_every_second_for(|| get_ssh_url(ctx, &username, repo), 10).await?;
60 tracing::debug!("Forked Repo SSH URL: {new_url:?}");
61 Ok(new_url)
62}
63
64async fn get_ssh_url(
65 ctx: &BergContext<RepoForkArgs>,
66 owner: &str,
67 repo: &str,
68) -> anyhow::Result<Url> {
69 ctx.client
70 .repo_get(owner, repo)
71 .await
72 .map_err(anyhow::Error::from)
73 .and_then(|repo| repo.ssh_url.context("No SSH url on repo"))
74 .context("User doesn't own the repo that was specified.")
75}
76
77fn ask_confirm_clone(repo: &str) -> anyhow::Result<()> {
78 let current_path = std::env::current_dir()?;
79 if !confirm_with_prompt(
80 format!("Do you really to fork {repo} into the directory {current_path:?}").as_str(),
81 )? {
82 anyhow::bail!("Abort cloning the repository.")
83 }
84 Ok(())
85}
86
87fn start_clone_repo(ssh_url: Url) -> anyhow::Result<()> {
88 let mut cmd = std::process::Command::new("git");
89 cmd.arg("clone").arg(ssh_url.to_string());
90 tracing::debug!("cmd: {cmd:?}");
91 let mut child = cmd.stdout(std::process::Stdio::inherit()).spawn()?;
92 child.wait()?;
93 Ok(())
94}