upleb.uk

Public git repos — served from a NIP-34 GRASP relay at git.upleb.uk

summaryrefslogtreecommitdiff
path: root/src/bin/git_remote_nostr/fetch.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/bin/git_remote_nostr/fetch.rs')
-rw-r--r--src/bin/git_remote_nostr/fetch.rs178
1 files changed, 138 insertions, 40 deletions
diff --git a/src/bin/git_remote_nostr/fetch.rs b/src/bin/git_remote_nostr/fetch.rs
index 5fd8816..6836456 100644
--- a/src/bin/git_remote_nostr/fetch.rs
+++ b/src/bin/git_remote_nostr/fetch.rs
@@ -1,10 +1,14 @@
1use std::{collections::HashMap, io::Stdin}; 1use std::io::Stdin;
2 2
3use anyhow::{anyhow, bail, Result}; 3use anyhow::{anyhow, bail, Context, Result};
4use auth_git2::GitAuthenticator; 4use auth_git2::GitAuthenticator;
5use git2::Repository; 5use git2::Repository;
6use ngit::{ 6use ngit::{
7 git::{Repo, RepoActions}, 7 git::{
8 nostr_url::{CloneUrl, NostrUrlDecoded, ServerProtocol},
9 utils::check_ssh_keys,
10 Repo, RepoActions,
11 },
8 git_events::tag_value, 12 git_events::tag_value,
9 login::get_curent_user, 13 login::get_curent_user,
10 repo_ref::RepoRef, 14 repo_ref::RepoRef,
@@ -12,12 +16,12 @@ use ngit::{
12 16
13use crate::utils::{ 17use crate::utils::{
14 find_proposal_and_patches_by_branch_name, get_oids_from_fetch_batch, get_open_proposals, 18 find_proposal_and_patches_by_branch_name, get_oids_from_fetch_batch, get_open_proposals,
15 get_short_git_server_name, switch_clone_url_between_ssh_and_https,
16}; 19};
17 20
18pub async fn run_fetch( 21pub async fn run_fetch(
19 git_repo: &Repo, 22 git_repo: &Repo,
20 repo_ref: &RepoRef, 23 repo_ref: &RepoRef,
24 decoded_nostr_url: &NostrUrlDecoded,
21 stdin: &Stdin, 25 stdin: &Stdin,
22 oid: &str, 26 oid: &str,
23 refstr: &str, 27 refstr: &str,
@@ -30,43 +34,19 @@ pub async fn run_fetch(
30 .map(|(_, oid)| oid.clone()) 34 .map(|(_, oid)| oid.clone())
31 .collect::<Vec<String>>(); 35 .collect::<Vec<String>>();
32 36
33 let mut errors = HashMap::new(); 37 let mut errors = vec![];
34 let term = console::Term::stderr(); 38 let term = console::Term::stderr();
35 39
36 for git_server_url in &repo_ref.git_server { 40 for git_server_url in &repo_ref.git_server {
37 let term = console::Term::stderr(); 41 let term = console::Term::stderr();
38 let short_name = get_short_git_server_name(git_repo, git_server_url); 42 if let Err(error) = fetch_from_git_server(
39 term.write_line(format!("fetching from {short_name}...").as_str())?; 43 &git_repo.git_repo,
40 let res = fetch_from_git_server(&git_repo.git_repo, &oids_from_git_servers, git_server_url); 44 &oids_from_git_servers,
41 term.clear_last_lines(1)?; 45 git_server_url,
42 if let Err(error1) = res { 46 decoded_nostr_url,
43 if let Ok(alternative_url) = switch_clone_url_between_ssh_and_https(git_server_url) { 47 &term,
44 let res2 = fetch_from_git_server( 48 ) {
45 &git_repo.git_repo, 49 errors.push(error);
46 &oids_from_git_servers,
47 &alternative_url,
48 );
49 if let Err(error2) = res2 {
50 term.write_line(
51 format!(
52 "WARNING: failed to fetch from {short_name} error:{error1}\r\nand using alternative protocol {alternative_url}: {error2}"
53 ).as_str()
54 )?;
55 errors.insert(
56 short_name.to_string(),
57 anyhow!(
58 "{error1} and using alternative protocol {alternative_url}: {error2}"
59 ),
60 );
61 } else {
62 break;
63 }
64 } else {
65 term.write_line(
66 format!("WARNING: failed to fetch from {short_name} error:{error1}").as_str(),
67 )?;
68 errors.insert(short_name.to_string(), error1);
69 }
70 } else { 50 } else {
71 break; 51 break;
72 } 52 }
@@ -81,7 +61,7 @@ pub async fn run_fetch(
81 "failed to fetch objects in nostr state event from:\r\n{}", 61 "failed to fetch objects in nostr state event from:\r\n{}",
82 errors 62 errors
83 .iter() 63 .iter()
84 .map(|(url, error)| format!("{url}: {error}")) 64 .map(std::string::ToString::to_string)
85 .collect::<Vec<String>>() 65 .collect::<Vec<String>>()
86 .join("\r\n") 66 .join("\r\n")
87 ); 67 );
@@ -129,17 +109,135 @@ fn fetch_from_git_server(
129 git_repo: &Repository, 109 git_repo: &Repository,
130 oids: &[String], 110 oids: &[String],
131 git_server_url: &str, 111 git_server_url: &str,
112 decoded_nostr_url: &NostrUrlDecoded,
113 term: &console::Term,
132) -> Result<()> { 114) -> Result<()> {
133 let git_config = git_repo.config()?; 115 let server_url = git_server_url.parse::<CloneUrl>()?;
116
117 // if protocol is local - just try local
118 if server_url.protocol() == ServerProtocol::Local {
119 let formatted_url = server_url.format_as(&ServerProtocol::Local, &None)?;
120 term.write_line(format!("fetching from {formatted_url}...").as_str())?;
121 if let Err(error) = fetch_from_git_server_url(git_repo, oids, &formatted_url) {
122 term.write_line(
123 format!("WARNING: failed to fetch from {formatted_url} error:{error}").as_str(),
124 )?;
125 return Err(error).context(format!("{formatted_url}: failed to fetch"));
126 }
127 return Ok(());
128 }
129
130 term.write_line(format!("fetching from {}...", server_url.domain()).as_str())?;
131
132 // use overide protocol if specified
133 if let Some(protocol) = &decoded_nostr_url.protocol {
134 let formatted_url = server_url.format_as(protocol, &decoded_nostr_url.user)?;
135 let res = fetch_from_git_server_url(git_repo, oids, &formatted_url);
136 term.clear_last_lines(1)?;
137 if let Err(error) = res {
138 term.write_line(
139 format!(
140 "WARNING: {formatted_url} failed to fetch over {protocol}{} as specified in nostr url. error:{error}",
141 if let Some(user) = &decoded_nostr_url.user {
142 format!(" with user '{user}'")
143 } else {
144 String::new()
145 }
146 ).as_str(),
147 )?;
148 return Err(error).context(format!("{formatted_url}: failed to fetch"));
149 }
150 return Ok(());
151 }
152
153 // Try https unauthenticated
154 let formatted_url = server_url.format_as(&ServerProtocol::Https, &None)?;
155 let res = fetch_from_git_server_url_unauthenticated(git_repo, oids, &formatted_url);
156 term.clear_last_lines(1)?;
157 if let Err(unauth_error) = res {
158 term.write_line(
159 format!(
160 "WARNING: {formatted_url} failed to fetch over unauthenticated https. {unauth_error}",
161 ).as_str(),
162 )?;
163 // TODO what about timeout errors?
164 // try over ssh
165 let mut ssh_error = None;
166 if check_ssh_keys() {
167 term.write_line(format!("fetching from {} over ssh...", server_url.domain()).as_str())?;
168 let formatted_url = server_url.format_as(&ServerProtocol::Ssh, &None)?;
169 let res = fetch_from_git_server_url(git_repo, oids, &formatted_url);
170 term.clear_last_lines(1)?;
171 if let Err(error) = res {
172 term.write_line(
173 format!("WARNING: {formatted_url} failed to fetch over ssh. error:{error}")
174 .as_str(),
175 )?;
176 term.write_line(
177 format!("fetching from {} over ssh...", server_url.domain()).as_str(),
178 )?;
179 ssh_error = Some(error);
180 } else {
181 return Ok(());
182 }
183 }
184 // try over https authenticated
185 term.write_line(
186 format!(
187 "fetching from {} over authenticated https...",
188 server_url.domain()
189 )
190 .as_str(),
191 )?;
192 let formatted_url = server_url.format_as(&ServerProtocol::Ssh, &None)?;
193 let res = fetch_from_git_server_url(git_repo, oids, &formatted_url);
194 term.clear_last_lines(1)?;
195 if let Err(auth_https_error) = res {
196 term.write_line(
197 format!("WARNING: {formatted_url} failed to fetch over authenticated https. error:{auth_https_error}",)
198 .as_str(),
199 )?;
200 let error_message = format!(
201 "{} failed to fetch over unauthenticated https ({unauth_error}), ssh ({}) and authenticated https ({auth_https_error})",
202 server_url.format_as(&ServerProtocol::Unspecified, &None)?,
203 ssh_error.unwrap_or(anyhow!("no keys found"))
204 );
205
206 bail!(error_message)
207 }
208 }
209 Ok(())
210}
134 211
212fn fetch_from_git_server_url(
213 git_repo: &Repository,
214 oids: &[String],
215 git_server_url: &str,
216) -> Result<()> {
217 let git_config = git_repo.config()?;
135 let mut git_server_remote = git_repo.remote_anonymous(git_server_url)?; 218 let mut git_server_remote = git_repo.remote_anonymous(git_server_url)?;
136 // authentication may be required (and will be requird if clone url is ssh)
137 let auth = GitAuthenticator::default(); 219 let auth = GitAuthenticator::default();
138 let mut fetch_options = git2::FetchOptions::new(); 220 let mut fetch_options = git2::FetchOptions::new();
139 let mut remote_callbacks = git2::RemoteCallbacks::new(); 221 let mut remote_callbacks = git2::RemoteCallbacks::new();
222 // TODO status update callback
140 remote_callbacks.credentials(auth.credentials(&git_config)); 223 remote_callbacks.credentials(auth.credentials(&git_config));
141 fetch_options.remote_callbacks(remote_callbacks); 224 fetch_options.remote_callbacks(remote_callbacks);
142 git_server_remote.download(oids, Some(&mut fetch_options))?; 225 git_server_remote.download(oids, Some(&mut fetch_options))?;
143 git_server_remote.disconnect()?; 226 git_server_remote.disconnect()?;
144 Ok(()) 227 Ok(())
145} 228}
229
230fn fetch_from_git_server_url_unauthenticated(
231 git_repo: &Repository,
232 oids: &[String],
233 git_server_url: &str,
234) -> Result<()> {
235 let mut git_server_remote = git_repo.remote_anonymous(git_server_url)?;
236 let mut fetch_options = git2::FetchOptions::new();
237 let remote_callbacks = git2::RemoteCallbacks::new();
238 // TODO status update callback
239 fetch_options.remote_callbacks(remote_callbacks);
240 git_server_remote.download(oids, Some(&mut fetch_options))?;
241 git_server_remote.disconnect()?;
242 Ok(())
243}