diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/bin/git_remote_nostr/list.rs | 56 | ||||
| -rw-r--r-- | src/bin/git_remote_nostr/main.rs | 18 |
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}; | |||
| 4 | use client::get_state_from_cache; | 4 | use client::get_state_from_cache; |
| 5 | use git::RepoActions; | 5 | use git::RepoActions; |
| 6 | use ngit::{ | 6 | use 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 | }; |
| 16 | use repo_ref::RepoRef; | 17 | use 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 | ||
| 14 | use anyhow::{Context, Result, bail}; | 14 | use anyhow::{Context, Result, bail}; |
| 15 | use client::{Connect, consolidate_fetch_reports, get_repo_ref_from_cache, is_verbose}; | 15 | use client::{ |
| 16 | Connect, FetchReport, consolidate_fetch_reports, get_repo_ref_from_cache, is_verbose, | ||
| 17 | }; | ||
| 16 | use git::{RepoActions, nostr_url::NostrUrlDecoded}; | 18 | use git::{RepoActions, nostr_url::NostrUrlDecoded}; |
| 17 | use ngit::{ | 19 | use 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)] |