git_mirror/provider/
github.rs1use log::trace;
9
10use reqwest::StatusCode;
12use reqwest::blocking::Client;
13use reqwest::header::{ACCEPT, HeaderMap, HeaderValue, USER_AGENT};
14
15use crate::provider::{Desc, Mirror, MirrorError, MirrorResult, Provider};
16
17pub struct GitHub {
18 pub url: String,
19 pub org: String,
20 pub use_http: bool,
21 pub private_token: Option<String>,
22 pub useragent: String,
23}
24
25#[derive(Deserialize, Debug)]
27struct Project {
28 description: Option<String>,
29 url: String,
30 ssh_url: String,
31 clone_url: String,
32}
33
34impl Provider for GitHub {
35 fn get_label(&self) -> String {
36 format!("{}/orgs/{}", self.url, self.org)
37 }
38
39 fn get_mirror_repos(&self) -> Result<Vec<MirrorResult>, String> {
40 let client = Client::new();
41
42 let use_http = self.use_http;
43
44 let mut headers = HeaderMap::new();
45 let useragent = HeaderValue::from_str(&self.useragent).expect("User agent invalid!");
47 headers.insert(USER_AGENT, useragent);
48 let accept = HeaderValue::from_static("application/vnd.github.v3+json");
50 headers.insert(ACCEPT, accept);
51
52 let url = format!("{}/orgs/{}/repos", self.url, self.org);
53 trace!("URL: {url}");
54
55 let res = client
56 .get(&url)
57 .headers(headers)
58 .send()
59 .map_err(|e| format!("Unable to connect to: {url} ({e})"))?;
60
61 if res.status() != StatusCode::OK {
62 if res.status() == StatusCode::UNAUTHORIZED {
63 return Err(format!(
64 "API call received unautorized ({}) for: {}. \
65 Please make sure the `GITHUB_PRIVATE_TOKEN` environment \
66 variable is set.",
67 res.status(),
68 url
69 ));
70 } else {
71 return Err(format!(
72 "API call received invalid status ({}) for : {}",
73 res.status(),
74 url
75 ));
76 }
77 }
78
79 let projects: Vec<Project> = serde_json::from_reader(res)
80 .map_err(|e| format!("Unable to parse response as JSON ({e:?})"))?;
81
82 let mut mirrors: Vec<MirrorResult> = Vec::new();
83
84 for p in projects {
85 match serde_yaml::from_str::<Desc>(&p.description.unwrap_or_default()) {
86 Ok(desc) => {
87 if desc.skip {
88 mirrors.push(Err(MirrorError::Skip(p.url)));
89 continue;
90 }
91 trace!("{0} -> {1}", desc.origin, p.ssh_url);
92 let destination = if use_http { p.clone_url } else { p.ssh_url };
93 let m = Mirror {
94 origin: desc.origin,
95 destination,
96 refspec: desc.refspec,
97 lfs: desc.lfs,
98 };
99 mirrors.push(Ok(m));
100 }
101 Err(e) => {
102 mirrors.push(Err(MirrorError::Description(p.url, e)));
103 }
104 }
105 }
106 Ok(mirrors)
107 }
108}