upleb.uk

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

summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/bin/git_remote_nostr/fetch.rs105
-rw-r--r--src/bin/ngit/sub_commands/list.rs6
-rw-r--r--src/lib/client.rs10
-rw-r--r--src/lib/git_events.rs13
4 files changed, 118 insertions, 16 deletions
diff --git a/src/bin/git_remote_nostr/fetch.rs b/src/bin/git_remote_nostr/fetch.rs
index b191850..f3d4362 100644
--- a/src/bin/git_remote_nostr/fetch.rs
+++ b/src/bin/git_remote_nostr/fetch.rs
@@ -1,6 +1,6 @@
1use core::str; 1use core::str;
2use std::{ 2use std::{
3 collections::HashMap, 3 collections::{HashMap, HashSet},
4 io::Stdin, 4 io::Stdin,
5 sync::{Arc, Mutex}, 5 sync::{Arc, Mutex},
6 time::Instant, 6 time::Instant,
@@ -16,7 +16,7 @@ use ngit::{
16 nostr_url::{CloneUrl, NostrUrlDecoded, ServerProtocol}, 16 nostr_url::{CloneUrl, NostrUrlDecoded, ServerProtocol},
17 utils::check_ssh_keys, 17 utils::check_ssh_keys,
18 }, 18 },
19 git_events::tag_value, 19 git_events::{KIND_PULL_REQUEST, KIND_PULL_REQUEST_UPDATE, tag_value},
20 login::get_curent_user, 20 login::get_curent_user,
21 repo_ref::{RepoRef, is_grasp_server}, 21 repo_ref::{RepoRef, is_grasp_server},
22}; 22};
@@ -37,38 +37,78 @@ pub async fn run_fetch(
37) -> Result<()> { 37) -> Result<()> {
38 let mut fetch_batch = get_oids_from_fetch_batch(stdin, oid, refstr)?; 38 let mut fetch_batch = get_oids_from_fetch_batch(stdin, oid, refstr)?;
39 39
40 let oids_from_git_servers = fetch_batch 40 let oids_from_state = fetch_batch
41 .iter() 41 .iter()
42 .filter(|(refstr, _)| !refstr.contains("refs/heads/pr/")) 42 .filter(|(refstr, _)| !refstr.contains("refs/heads/pr/"))
43 .map(|(_, oid)| oid.clone()) 43 .map(|(_, oid)| oid.clone())
44 .collect::<Vec<String>>(); 44 .collect::<Vec<String>>();
45 45
46 let pr_oid_clone_url_map = identify_clone_urls_for_oids_from_pr_pr_update_events(
47 fetch_batch.values().collect::<Vec<&String>>(),
48 git_repo,
49 repo_ref,
50 )
51 .await?;
52
53 let oids_to_fetch_from_git_servers = [
54 oids_from_state.clone(),
55 pr_oid_clone_url_map
56 .keys()
57 .cloned()
58 .collect::<Vec<String>>(),
59 ]
60 .concat();
61
62 let git_servers = {
63 let mut seen: HashSet<String> = HashSet::new();
64 let mut out: Vec<String> = vec![];
65 for server in &repo_ref.git_server {
66 if seen.insert(server.clone()) {
67 out.push(server.clone());
68 }
69 }
70 for url in pr_oid_clone_url_map.values().flatten() {
71 if seen.insert(url.clone()) {
72 out.push(url.clone());
73 }
74 }
75 out
76 };
77
46 let mut errors = vec![]; 78 let mut errors = vec![];
47 let term = console::Term::stderr(); 79 let term = console::Term::stderr();
48 80
49 for git_server_url in &repo_ref.git_server { 81 for git_server_url in &git_servers {
82 let oids_to_fetch_from_server = oids_to_fetch_from_git_servers
83 .clone()
84 .into_iter()
85 .filter(|oid| !git_repo.does_commit_exist(oid).unwrap_or(false))
86 .collect::<Vec<String>>();
87
88 if oids_to_fetch_from_server.is_empty() {
89 continue;
90 }
91
50 let term = console::Term::stderr(); 92 let term = console::Term::stderr();
51 if let Err(error) = fetch_from_git_server( 93 if let Err(error) = fetch_from_git_server(
52 git_repo, 94 git_repo,
53 &oids_from_git_servers, 95 &oids_from_state,
54 git_server_url, 96 git_server_url,
55 &repo_ref.to_nostr_git_url(&None), 97 &repo_ref.to_nostr_git_url(&None),
56 &term, 98 &term,
57 is_grasp_server(git_server_url, &repo_ref.grasp_servers()), 99 is_grasp_server(git_server_url, &repo_ref.grasp_servers()),
58 ) { 100 ) {
59 errors.push(error); 101 errors.push(error);
60 } else {
61 break;
62 } 102 }
63 } 103 }
64 104
65 if oids_from_git_servers 105 if oids_from_state
66 .iter() 106 .iter()
67 .any(|oid| !git_repo.does_commit_exist(oid).unwrap()) 107 .any(|oid| !git_repo.does_commit_exist(oid).unwrap())
68 && !errors.is_empty() 108 && !errors.is_empty()
69 { 109 {
70 bail!( 110 bail!(
71 "fetch: failed to fetch objects in nostr state event from:\r\n{}", 111 "fetch: failed to fetch objects from:\r\n{}",
72 errors 112 errors
73 .iter() 113 .iter()
74 .map(|e| format!(" - {e}")) 114 .map(|e| format!(" - {e}"))
@@ -79,12 +119,43 @@ pub async fn run_fetch(
79 119
80 fetch_batch.retain(|refstr, _| refstr.contains("refs/heads/pr/")); 120 fetch_batch.retain(|refstr, _| refstr.contains("refs/heads/pr/"));
81 121
82 fetch_open_or_draft_proposals(git_repo, &term, repo_ref, &fetch_batch).await?; 122 fetch_open_or_draft_proposals_from_patches(git_repo, &term, repo_ref, &fetch_batch).await?;
123 // TODO fetch_open_or_draft_proposals just needs to do it for patches
83 term.flush()?; 124 term.flush()?;
84 println!(); 125 println!();
85 Ok(()) 126 Ok(())
86} 127}
87 128
129async fn identify_clone_urls_for_oids_from_pr_pr_update_events(
130 oids: Vec<&String>,
131 git_repo: &Repo,
132 repo_ref: &RepoRef,
133) -> Result<HashMap<String, Vec<String>>> {
134 let mut map: HashMap<String, Vec<String>> = HashMap::new();
135
136 let open_and_draft_proposals = get_open_or_draft_proposals(git_repo, repo_ref).await?;
137
138 for (_, (_, events)) in open_and_draft_proposals {
139 for event in events {
140 if [KIND_PULL_REQUEST, KIND_PULL_REQUEST_UPDATE].contains(&event.kind) {
141 if let Ok(c) = tag_value(&event, "c") {
142 if oids.contains(&&c) {
143 for tag in event.tags.as_slice() {
144 if tag.kind().eq(&nostr::event::TagKind::Clone) {
145 for clone_url in tag.as_slice().iter().skip(1) {
146 map.entry(c.clone()).or_default().push(clone_url.clone());
147 }
148 }
149 }
150 }
151 }
152 }
153 }
154 }
155
156 Ok(map)
157}
158
88pub fn make_commits_for_proposal( 159pub fn make_commits_for_proposal(
89 git_repo: &Repo, 160 git_repo: &Repo,
90 repo_ref: &RepoRef, 161 repo_ref: &RepoRef,
@@ -128,7 +199,7 @@ pub fn make_commits_for_proposal(
128 Ok(tip_commit_id) 199 Ok(tip_commit_id)
129} 200}
130 201
131async fn fetch_open_or_draft_proposals( 202async fn fetch_open_or_draft_proposals_from_patches(
132 git_repo: &Repo, 203 git_repo: &Repo,
133 term: &console::Term, 204 term: &console::Term,
134 repo_ref: &RepoRef, 205 repo_ref: &RepoRef,
@@ -140,12 +211,19 @@ async fn fetch_open_or_draft_proposals(
140 let current_user = get_curent_user(git_repo)?; 211 let current_user = get_curent_user(git_repo)?;
141 212
142 for refstr in proposal_refs.keys() { 213 for refstr in proposal_refs.keys() {
143 if let Some((_, (_, patches))) = find_proposal_and_patches_by_branch_name( 214 if let Some((_, (_, events_to_apply))) = find_proposal_and_patches_by_branch_name(
144 refstr, 215 refstr,
145 &open_and_draft_proposals, 216 &open_and_draft_proposals,
146 current_user.as_ref(), 217 current_user.as_ref(),
147 ) { 218 ) {
148 if let Err(error) = make_commits_for_proposal(git_repo, repo_ref, patches) { 219 if events_to_apply
220 .iter()
221 .any(|e| e.kind.eq(&KIND_PULL_REQUEST) || e.kind.eq(&KIND_PULL_REQUEST_UPDATE))
222 {
223 // do nothing - we fetch these oids as part of run_fetch
224 } else if let Err(error) =
225 make_commits_for_proposal(git_repo, repo_ref, events_to_apply)
226 {
149 term.write_line( 227 term.write_line(
150 format!("WARNING: failed to create branch for {refstr}, error: {error}",) 228 format!("WARNING: failed to create branch for {refstr}, error: {error}",)
151 .as_str(), 229 .as_str(),
@@ -429,6 +507,7 @@ fn fetch_from_git_server_url(
429 remote_callbacks.credentials(auth.credentials(&git_config)); 507 remote_callbacks.credentials(auth.credentials(&git_config));
430 } 508 }
431 fetch_options.remote_callbacks(remote_callbacks); 509 fetch_options.remote_callbacks(remote_callbacks);
510
432 git_server_remote.download(oids, Some(&mut fetch_options))?; 511 git_server_remote.download(oids, Some(&mut fetch_options))?;
433 512
434 git_server_remote.disconnect()?; 513 git_server_remote.disconnect()?;
diff --git a/src/bin/ngit/sub_commands/list.rs b/src/bin/ngit/sub_commands/list.rs
index a90b28e..9e35b33 100644
--- a/src/bin/ngit/sub_commands/list.rs
+++ b/src/bin/ngit/sub_commands/list.rs
@@ -219,7 +219,11 @@ pub async fn launch() -> Result<()> {
219 PromptChoiceParms::default() 219 PromptChoiceParms::default()
220 .with_prompt("this is new PR event kind which ngit doesnt yet support") 220 .with_prompt("this is new PR event kind which ngit doesnt yet support")
221 .with_default(0) 221 .with_default(0)
222 .with_choices(vec!["back to proposals".to_string()]), 222 .with_choices(vec![
223 // TODO enable checkout by fetching oids, creating / updating branch and
224 // checking out
225 "back to proposals".to_string(),
226 ]),
223 )? { 227 )? {
224 0 => continue, 228 0 => continue,
225 _ => { 229 _ => {
diff --git a/src/lib/client.rs b/src/lib/client.rs
index 1f3b08c..091d68d 100644
--- a/src/lib/client.rs
+++ b/src/lib/client.rs
@@ -49,7 +49,8 @@ use crate::{
49 git::{Repo, RepoActions, get_git_config_item}, 49 git::{Repo, RepoActions, get_git_config_item},
50 git_events::{ 50 git_events::{
51 KIND_PULL_REQUEST, KIND_PULL_REQUEST_UPDATE, event_is_cover_letter, 51 KIND_PULL_REQUEST, KIND_PULL_REQUEST_UPDATE, event_is_cover_letter,
52 event_is_patch_set_root, event_is_revision_root, status_kinds, 52 event_is_patch_set_root, event_is_revision_root, event_is_valid_pr_or_pr_update,
53 status_kinds,
53 }, 54 },
54 login::{get_likely_logged_in_user, user::get_user_ref_from_cache}, 55 login::{get_likely_logged_in_user, user::get_user_ref_from_cache},
55 repo_ref::RepoRef, 56 repo_ref::RepoRef,
@@ -1824,6 +1825,7 @@ pub async fn get_proposals_and_revisions_from_cache(
1824 .await? 1825 .await?
1825 .iter() 1826 .iter()
1826 .filter(|e| event_is_patch_set_root(e) || e.kind.eq(&KIND_PULL_REQUEST)) 1827 .filter(|e| event_is_patch_set_root(e) || e.kind.eq(&KIND_PULL_REQUEST))
1828 .filter(|e| e.kind.eq(&Kind::GitPatch) || event_is_valid_pr_or_pr_update(e))
1827 .cloned() 1829 .cloned()
1828 .collect::<Vec<nostr::Event>>(); 1830 .collect::<Vec<nostr::Event>>();
1829 proposals.sort_by_key(|e| e.created_at); 1831 proposals.sort_by_key(|e| e.created_at);
@@ -1874,7 +1876,11 @@ pub async fn get_all_proposal_patch_pr_pr_update_events_from_cache(
1874 .iter() 1876 .iter()
1875 .copied() 1877 .copied()
1876 .collect(); 1878 .collect();
1877 commit_events.retain(|e| permissioned_users.contains(&e.pubkey)); 1879
1880 commit_events.retain(|e| {
1881 permissioned_users.contains(&e.pubkey)
1882 && (e.kind.eq(&Kind::GitPatch) || event_is_valid_pr_or_pr_update(e))
1883 });
1878 1884
1879 let revision_roots: HashSet<nostr::EventId> = commit_events 1885 let revision_roots: HashSet<nostr::EventId> = commit_events
1880 .iter() 1886 .iter()
diff --git a/src/lib/git_events.rs b/src/lib/git_events.rs
index 7b25daf..09ec040 100644
--- a/src/lib/git_events.rs
+++ b/src/lib/git_events.rs
@@ -90,6 +90,19 @@ pub fn patch_supports_commit_ids(event: &Event) -> bool {
90 .any(|t| !t.as_slice().is_empty() && t.as_slice()[0].eq("commit-pgp-sig")) 90 .any(|t| !t.as_slice().is_empty() && t.as_slice()[0].eq("commit-pgp-sig"))
91} 91}
92 92
93pub fn event_is_valid_pr_or_pr_update(event: &Event) -> bool {
94 [KIND_PULL_REQUEST, KIND_PULL_REQUEST_UPDATE].contains(&event.kind)
95 && event.tags.iter().any(|t| {
96 t.as_slice().len().gt(&1)
97 && t.as_slice()[0].eq("c")
98 && git2::Oid::from_str(&t.as_slice()[1]).is_ok()
99 })
100 && event
101 .tags
102 .iter()
103 .any(|t| t.as_slice().len().gt(&1) && t.as_slice()[0].eq("clone"))
104}
105
93#[allow(clippy::too_many_arguments)] 106#[allow(clippy::too_many_arguments)]
94#[allow(clippy::too_many_lines)] 107#[allow(clippy::too_many_lines)]
95pub async fn generate_patch_event( 108pub async fn generate_patch_event(