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.rs145
1 files changed, 145 insertions, 0 deletions
diff --git a/src/bin/git_remote_nostr/fetch.rs b/src/bin/git_remote_nostr/fetch.rs
new file mode 100644
index 0000000..5fd8816
--- /dev/null
+++ b/src/bin/git_remote_nostr/fetch.rs
@@ -0,0 +1,145 @@
1use std::{collections::HashMap, io::Stdin};
2
3use anyhow::{anyhow, bail, Result};
4use auth_git2::GitAuthenticator;
5use git2::Repository;
6use ngit::{
7 git::{Repo, RepoActions},
8 git_events::tag_value,
9 login::get_curent_user,
10 repo_ref::RepoRef,
11};
12
13use crate::utils::{
14 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};
17
18pub async fn run_fetch(
19 git_repo: &Repo,
20 repo_ref: &RepoRef,
21 stdin: &Stdin,
22 oid: &str,
23 refstr: &str,
24) -> Result<()> {
25 let mut fetch_batch = get_oids_from_fetch_batch(stdin, oid, refstr)?;
26
27 let oids_from_git_servers = fetch_batch
28 .iter()
29 .filter(|(refstr, _)| !refstr.contains("refs/heads/pr/"))
30 .map(|(_, oid)| oid.clone())
31 .collect::<Vec<String>>();
32
33 let mut errors = HashMap::new();
34 let term = console::Term::stderr();
35
36 for git_server_url in &repo_ref.git_server {
37 let term = console::Term::stderr();
38 let short_name = get_short_git_server_name(git_repo, git_server_url);
39 term.write_line(format!("fetching from {short_name}...").as_str())?;
40 let res = fetch_from_git_server(&git_repo.git_repo, &oids_from_git_servers, git_server_url);
41 term.clear_last_lines(1)?;
42 if let Err(error1) = res {
43 if let Ok(alternative_url) = switch_clone_url_between_ssh_and_https(git_server_url) {
44 let res2 = fetch_from_git_server(
45 &git_repo.git_repo,
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 {
71 break;
72 }
73 }
74
75 if oids_from_git_servers
76 .iter()
77 .any(|oid| !git_repo.does_commit_exist(oid).unwrap())
78 && !errors.is_empty()
79 {
80 bail!(
81 "failed to fetch objects in nostr state event from:\r\n{}",
82 errors
83 .iter()
84 .map(|(url, error)| format!("{url}: {error}"))
85 .collect::<Vec<String>>()
86 .join("\r\n")
87 );
88 }
89
90 fetch_batch.retain(|refstr, _| refstr.contains("refs/heads/pr/"));
91
92 if !fetch_batch.is_empty() {
93 let open_proposals = get_open_proposals(git_repo, repo_ref).await?;
94
95 let current_user = get_curent_user(git_repo)?;
96
97 for (refstr, oid) in fetch_batch {
98 if let Some((_, (_, patches))) =
99 find_proposal_and_patches_by_branch_name(&refstr, &open_proposals, &current_user)
100 {
101 if !git_repo.does_commit_exist(&oid)? {
102 let mut patches_ancestor_first = patches.clone();
103 patches_ancestor_first.reverse();
104 if git_repo.does_commit_exist(&tag_value(
105 patches_ancestor_first.first().unwrap(),
106 "parent-commit",
107 )?)? {
108 for patch in &patches_ancestor_first {
109 git_repo.create_commit_from_patch(patch)?;
110 }
111 } else {
112 term.write_line(
113 format!("WARNING: cannot find parent commit for {refstr}").as_str(),
114 )?;
115 }
116 }
117 } else {
118 term.write_line(format!("WARNING: cannot find proposal for {refstr}").as_str())?;
119 }
120 }
121 }
122
123 term.flush()?;
124 println!();
125 Ok(())
126}
127
128fn fetch_from_git_server(
129 git_repo: &Repository,
130 oids: &[String],
131 git_server_url: &str,
132) -> Result<()> {
133 let git_config = git_repo.config()?;
134
135 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();
138 let mut fetch_options = git2::FetchOptions::new();
139 let mut remote_callbacks = git2::RemoteCallbacks::new();
140 remote_callbacks.credentials(auth.credentials(&git_config));
141 fetch_options.remote_callbacks(remote_callbacks);
142 git_server_remote.download(oids, Some(&mut fetch_options))?;
143 git_server_remote.disconnect()?;
144 Ok(())
145}