1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
use crate::errors::*;
use crate::Guidon;
use crate::TryNew;
use derive_builder::Builder;
use git2::build::RepoBuilder;
use git2::{AutotagOption, Cred, CredentialType, FetchOptions, ProxyOptions};
use log::debug;
use std::env;
use std::fs::create_dir_all;
use std::fs::remove_dir_all;
use url::Url;
#[derive(Debug, Clone, Builder, Default)]
#[builder(default)]
#[builder(field(private))]
#[builder(setter(into, strip_option))]
pub struct GitOptions {
repo: String,
rev: Option<String>,
unsecure: bool,
auto_proxy: bool,
}
impl GitOptions {
pub fn builder() -> GitOptionsBuilder {
GitOptionsBuilder::default()
}
}
impl<'a> TryNew<GitOptions> for Guidon<'a> {
fn try_new(git: GitOptions) -> Result<Self> {
let mut cb = git2::RemoteCallbacks::new();
cb.credentials(move |_url, _user_from_url, _cred| {
if _cred.contains(CredentialType::USERNAME) {
debug!("CredentialType::USERNAME");
return Cred::username(_user_from_url.unwrap_or("git"));
}
if _cred.contains(CredentialType::USER_PASS_PLAINTEXT) {
debug!("CredentialType::USER_PASS_PLAINTEXT");
let url = Url::parse(_url).map_err(|e| {
git2::Error::from_str(&format!("Malformed URL: {}", e.to_string()))
})?;
let user = url.username();
let password = url.password().unwrap_or("");
if user.is_empty() && password.is_empty() {
return Cred::default();
}
return Cred::userpass_plaintext(user, password);
}
if _cred.contains(CredentialType::SSH_KEY) {
debug!("CredentialType::SSH_KEY");
return Cred::ssh_key_from_agent("git");
}
debug!("CredentialType::None");
Err(git2::Error::from_str("No credential type found"))
});
if git.unsecure {
cb.certificate_check(|_, _| true);
}
let mut fo = FetchOptions::new();
fo.remote_callbacks(cb)
.download_tags(AutotagOption::All)
.update_fetchhead(true);
if git.auto_proxy {
let mut po = ProxyOptions::new();
po.auto();
fo.proxy_options(po);
}
let dest = env::temp_dir().join("guidon");
let _ = remove_dir_all(&dest);
create_dir_all(&dest)?;
let repo = RepoBuilder::new()
.fetch_options(fo)
.clone(&git.repo, dest.as_path())?;
let local =
repo.revparse_single(&git.rev.unwrap_or_else(|| "refs/heads/master".to_owned()))?;
let mut opts = git2::build::CheckoutBuilder::new();
opts.force();
opts.use_theirs(true);
repo.checkout_tree(&local, Some(&mut opts))?;
Guidon::try_new(dest)
}
}
impl From<git2::Error> for GuidonError {
fn from(error: git2::Error) -> Self {
GuidonError {
kind: ErrorKind::GitError,
message: error.to_string(),
}
}
}