upleb.uk

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

summaryrefslogtreecommitdiff
path: root/src/bin
diff options
context:
space:
mode:
Diffstat (limited to 'src/bin')
-rw-r--r--src/bin/git_remote_nostr/list.rs56
-rw-r--r--src/bin/git_remote_nostr/main.rs18
2 files changed, 65 insertions, 9 deletions
diff --git a/src/bin/git_remote_nostr/list.rs b/src/bin/git_remote_nostr/list.rs
index 4a7c1ec..a32ed67 100644
--- a/src/bin/git_remote_nostr/list.rs
+++ b/src/bin/git_remote_nostr/list.rs
@@ -4,13 +4,14 @@ use anyhow::{Context, Result};
4use client::get_state_from_cache; 4use client::get_state_from_cache;
5use git::RepoActions; 5use git::RepoActions;
6use ngit::{ 6use ngit::{
7 client::{self, is_verbose}, 7 client::{self, FetchReport, is_verbose},
8 fetch::fetch_from_git_server, 8 fetch::fetch_from_git_server,
9 git::{self}, 9 git::{self},
10 git_events::{KIND_PULL_REQUEST, KIND_PULL_REQUEST_UPDATE, event_to_cover_letter, tag_value}, 10 git_events::{KIND_PULL_REQUEST, KIND_PULL_REQUEST_UPDATE, event_to_cover_letter, tag_value},
11 list::list_from_remotes, 11 list::list_from_remotes,
12 login::get_curent_user, 12 login::get_curent_user,
13 repo_ref::{self}, 13 repo_ref::{self},
14 repo_state::RepoState,
14 utils::{get_all_proposals, get_open_or_draft_proposals}, 15 utils::{get_all_proposals, get_open_or_draft_proposals},
15}; 16};
16use repo_ref::RepoRef; 17use repo_ref::RepoRef;
@@ -22,6 +23,7 @@ pub async fn run_list(
22 git_repo: &Repo, 23 git_repo: &Repo,
23 repo_ref: &RepoRef, 24 repo_ref: &RepoRef,
24 for_push: bool, 25 for_push: bool,
26 fetch_report: &FetchReport,
25) -> Result<HashMap<String, (HashMap<String, String>, bool)>> { 27) -> Result<HashMap<String, (HashMap<String, String>, bool)>> {
26 let nostr_state = (get_state_from_cache(Some(git_repo.get_path()?), repo_ref).await).ok(); 28 let nostr_state = (get_state_from_cache(Some(git_repo.get_path()?), repo_ref).await).ok();
27 29
@@ -30,6 +32,8 @@ pub async fn run_list(
30 if is_verbose() { 32 if is_verbose() {
31 term.write_line("git servers: listing refs...")?; 33 term.write_line("git servers: listing refs...")?;
32 } 34 }
35 // nostr_state is passed to list_from_remotes only for the sync-status
36 // display; the actual ref state we advertise is determined below.
33 let remote_states = list_from_remotes( 37 let remote_states = list_from_remotes(
34 &term, 38 &term,
35 git_repo, 39 git_repo,
@@ -39,9 +43,55 @@ pub async fn run_list(
39 ) 43 )
40 .await; 44 .await;
41 45
42 let mut state = if let Some(nostr_state) = nostr_state { 46 // Collect all OIDs confirmed present on at least one git server.
43 nostr_state.state 47 let git_server_oids: std::collections::HashSet<String> = remote_states
48 .values()
49 .flat_map(|(state, _)| state.values())
50 .filter(|v| !v.starts_with("ref: "))
51 .cloned()
52 .collect();
53
54 // From the per-relay state events captured during the nostr fetch, find
55 // the newest state event whose every OID is either:
56 // (a) confirmed present on at least one git server, or
57 // (b) already available locally.
58 // This prevents advertising refs whose git objects haven't been pushed to
59 // any server yet, which would cause `git clone` / `git fetch` to fail.
60 let mut candidates: Vec<&nostr::Event> = fetch_report
61 .state_per_relay
62 .values()
63 .filter_map(|maybe| maybe.as_ref())
64 .collect();
65 // Sort newest-first (by created_at, then by id for tie-breaking).
66 candidates.sort_by(|a, b| {
67 b.created_at
68 .cmp(&a.created_at)
69 .then_with(|| b.id.cmp(&a.id))
70 });
71 // Deduplicate by event id so we don't check the same event twice.
72 candidates.dedup_by_key(|e| e.id);
73
74 let best_state: Option<HashMap<String, String>> = candidates.into_iter().find_map(|event| {
75 if let Ok(rs) = RepoState::try_from(vec![event.clone()]) {
76 let all_resolvable = rs.state.values().all(|v| {
77 v.starts_with("ref: ")
78 || git_server_oids.contains(v)
79 || git_repo.does_commit_exist(v).is_ok_and(|exists| exists)
80 });
81 if all_resolvable { Some(rs.state) } else { None }
82 } else {
83 None
84 }
85 });
86
87 let mut state = if let Some(state) = best_state {
88 state
44 } else { 89 } else {
90 // No relay returned a state event whose OIDs are all resolvable
91 // (either no state events were seen on any relay, or every candidate
92 // references git objects not yet on any server). Fall back to
93 // whatever the git servers actually report so we never advertise OIDs
94 // that cannot be fetched.
45 let (state, _is_grasp_server) = repo_ref 95 let (state, _is_grasp_server) = repo_ref
46 .git_server 96 .git_server
47 .iter() 97 .iter()
diff --git a/src/bin/git_remote_nostr/main.rs b/src/bin/git_remote_nostr/main.rs
index 6186ed3..dad8a99 100644
--- a/src/bin/git_remote_nostr/main.rs
+++ b/src/bin/git_remote_nostr/main.rs
@@ -12,7 +12,9 @@ use std::{
12}; 12};
13 13
14use anyhow::{Context, Result, bail}; 14use anyhow::{Context, Result, bail};
15use client::{Connect, consolidate_fetch_reports, get_repo_ref_from_cache, is_verbose}; 15use client::{
16 Connect, FetchReport, consolidate_fetch_reports, get_repo_ref_from_cache, is_verbose,
17};
16use git::{RepoActions, nostr_url::NostrUrlDecoded}; 18use git::{RepoActions, nostr_url::NostrUrlDecoded};
17use ngit::{ 19use ngit::{
18 client::{self, Params}, 20 client::{self, Params},
@@ -149,7 +151,9 @@ async fn main() -> Result<()> {
149 client.set_signer(signer).await; 151 client.set_signer(signer).await;
150 } 152 }
151 153
152 fetching_with_report_for_helper(git_repo_path, &client, &decoded_nostr_url.coordinate).await?; 154 let fetch_report =
155 fetching_with_report_for_helper(git_repo_path, &client, &decoded_nostr_url.coordinate)
156 .await?;
153 157
154 let mut repo_ref = 158 let mut repo_ref =
155 get_repo_ref_from_cache(Some(git_repo_path), &decoded_nostr_url.coordinate).await?; 159 get_repo_ref_from_cache(Some(git_repo_path), &decoded_nostr_url.coordinate).await?;
@@ -221,10 +225,12 @@ async fn main() -> Result<()> {
221 push_options = PushOptions::default(); 225 push_options = PushOptions::default();
222 } 226 }
223 ["list"] => { 227 ["list"] => {
224 list_outputs = Some(list::run_list(&git_repo, &repo_ref, false).await?); 228 list_outputs =
229 Some(list::run_list(&git_repo, &repo_ref, false, &fetch_report).await?);
225 } 230 }
226 ["list", "for-push"] => { 231 ["list", "for-push"] => {
227 list_outputs = Some(list::run_list(&git_repo, &repo_ref, true).await?); 232 list_outputs =
233 Some(list::run_list(&git_repo, &repo_ref, true, &fetch_report).await?);
228 } 234 }
229 [] => { 235 [] => {
230 return Ok(()); 236 return Ok(());
@@ -283,7 +289,7 @@ async fn fetching_with_report_for_helper(
283 git_repo_path: &Path, 289 git_repo_path: &Path,
284 client: &Client, 290 client: &Client,
285 trusted_maintainer_coordinate: &Nip19Coordinate, 291 trusted_maintainer_coordinate: &Nip19Coordinate,
286) -> Result<()> { 292) -> Result<FetchReport> {
287 let term = console::Term::stderr(); 293 let term = console::Term::stderr();
288 let verbose = is_verbose(); 294 let verbose = is_verbose();
289 if verbose { 295 if verbose {
@@ -308,7 +314,7 @@ async fn fetching_with_report_for_helper(
308 } else { 314 } else {
309 term.write_line(&format!("nostr updates: {report}"))?; 315 term.write_line(&format!("nostr updates: {report}"))?;
310 } 316 }
311 Ok(()) 317 Ok(report)
312} 318}
313 319
314#[cfg(test)] 320#[cfg(test)]