1use anyhow::{anyhow, Result};
2use console::{Emoji, Style};
3use std::path::Path;
4
5pub(crate) fn clone(
6 repository: &str,
7 reference_opt: Option<&str>,
8 target_dir: &Path,
9 private_key_path: Option<&Path>,
10) -> Result<()> {
11 let cyan = Style::new().cyan();
12 println!(
13 "{} {}",
14 Emoji("🔄", ""),
15 cyan.apply_to("Cloning repository…"),
16 );
17
18 let mut auth = auth_git2::GitAuthenticator::default();
19 if let Some(private_key_path) = private_key_path {
20 auth = auth.add_ssh_key_from_file(private_key_path, None)
21 }
22
23 let git_config = git2::Config::open_default()
24 .map_err(|e| anyhow!(e).context("Opening git configuration"))?;
25
26 let mut fetch_options = git2::FetchOptions::new();
27
28 let mut callbacks = git2::RemoteCallbacks::new();
30 callbacks.credentials(auth.credentials(&git_config));
31 fetch_options.remote_callbacks(callbacks);
32
33 if reference_opt.is_some() {
34 fetch_options.download_tags(git2::AutotagOption::All);
35 }
36
37 fetch_options.depth(1);
39
40 let mut builder = git2::build::RepoBuilder::new();
42 builder.fetch_options(fetch_options);
43
44 let repo = builder.clone(repository, target_dir)?;
46
47 if let Some(git_reference) = reference_opt {
49 match repo.revparse_ext(git_reference) {
50 Ok((obj, reference)) => {
51 repo.checkout_tree(&obj, None)?;
52 match reference {
53 Some(reporef) => repo.set_head(reporef.name().expect("tag has a name; qed")),
55 None => repo.set_head_detached(obj.id()),
57 }?;
58 }
59 Err(_) => {
60 std::fs::remove_dir_all(target_dir)?;
62 builder.branch(git_reference);
63 let _repo = builder.clone(repository, target_dir)?;
64 }
65 }
66 }
67
68 Ok(())
69}
70
71#[cfg(test)]
72mod tests {
73 use super::*;
74 use tempfile::tempdir;
75
76 #[test]
77 fn clone_http() {
78 let template_path = "https://github.com/http-rs/surf.git";
79 let tmp_dir = tempdir().unwrap();
80 clone(template_path, None, tmp_dir.path(), None).unwrap();
81 }
82
83 #[test]
84 fn clone_http_commit() {
85 let commit = Some("8f0039488b3877ca59592900bc7ad645a83e2886");
86 let template_path = "https://github.com/http-rs/surf.git";
87 let tmp_dir = tempdir().unwrap();
88 clone(template_path, commit, tmp_dir.path(), None).unwrap();
89 }
90
91 #[test]
92 fn clone_http_branch() {
93 let branch = Some("main");
94 let template_path = "https://github.com/apollographql/router.git";
95 let tmp_dir = tempdir().unwrap();
96 clone(template_path, branch, tmp_dir.path(), None).unwrap();
97 }
98
99 #[test]
100 fn clone_ssh() {
102 let template_path = "git@github.com:http-rs/surf.git";
103 let tmp_dir = tempdir().unwrap();
104 clone(template_path, None, tmp_dir.path(), None).unwrap();
105 }
106
107 #[test]
108 fn clone_ssh_commit() {
110 let commit = Some("8f0039488b3877ca59592900bc7ad645a83e2886");
111 let template_path = "git@github.com:http-rs/surf.git";
112 let tmp_dir = tempdir().unwrap();
113 clone(template_path, commit, tmp_dir.path(), None).unwrap();
114 }
115}