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:
authorDanConwayDev <DanConwayDev@protonmail.com>2024-09-23 10:46:26 +0100
committerDanConwayDev <DanConwayDev@protonmail.com>2024-09-23 13:36:53 +0100
commit2cdbb8c7ac6c98af6e36c4458c08bef2299794e1 (patch)
treefcf7c56d2f5eda0776643f8a52090965f1de06a8 /src/bin/git_remote_nostr/fetch.rs
parent51358320c50afece31fc25945a09e3d7aac8f39c (diff)
fix(remote): add resilience to `prs`
make poorly formatted patches fail silently. we stop trusting that the `commit` tag in the latest patch can be produced by apply the patches. to achieve this we must recreate the commit during the list command, which require fetching the parent oids. support patches without optional `commit` and `parent-commit` tags.
Diffstat (limited to 'src/bin/git_remote_nostr/fetch.rs')
-rw-r--r--src/bin/git_remote_nostr/fetch.rs117
1 files changed, 75 insertions, 42 deletions
diff --git a/src/bin/git_remote_nostr/fetch.rs b/src/bin/git_remote_nostr/fetch.rs
index 2e16297..46e7ad3 100644
--- a/src/bin/git_remote_nostr/fetch.rs
+++ b/src/bin/git_remote_nostr/fetch.rs
@@ -1,11 +1,12 @@
1use core::str; 1use core::str;
2use std::{ 2use std::{
3 collections::HashMap,
3 io::Stdin, 4 io::Stdin,
4 sync::{Arc, Mutex}, 5 sync::{Arc, Mutex},
5 time::Instant, 6 time::Instant,
6}; 7};
7 8
8use anyhow::{anyhow, bail, Result}; 9use anyhow::{anyhow, bail, Context, Result};
9use auth_git2::GitAuthenticator; 10use auth_git2::GitAuthenticator;
10use git2::{Progress, Repository}; 11use git2::{Progress, Repository};
11use ngit::{ 12use ngit::{
@@ -19,7 +20,7 @@ use ngit::{
19 repo_ref::RepoRef, 20 repo_ref::RepoRef,
20}; 21};
21use nostr::nips::nip19; 22use nostr::nips::nip19;
22use nostr_sdk::ToBech32; 23use nostr_sdk::{Event, ToBech32};
23 24
24use crate::utils::{ 25use crate::utils::{
25 count_lines_per_msg_vec, fetch_or_list_error_is_not_authentication_failure, 26 count_lines_per_msg_vec, fetch_or_list_error_is_not_authentication_failure,
@@ -78,65 +79,97 @@ pub async fn run_fetch(
78 79
79 fetch_batch.retain(|refstr, _| refstr.contains("refs/heads/pr/")); 80 fetch_batch.retain(|refstr, _| refstr.contains("refs/heads/pr/"));
80 81
81 if !fetch_batch.is_empty() { 82 fetch_proposals(git_repo, &term, repo_ref, &fetch_batch).await?;
83 term.flush()?;
84 println!();
85 Ok(())
86}
87
88pub fn make_commits_for_proposal(
89 git_repo: &Repo,
90 repo_ref: &RepoRef,
91 patches_ancestor_last: &[Event],
92) -> Result<String> {
93 let patches_ancestor_first: Vec<&Event> = patches_ancestor_last.iter().rev().collect();
94 let mut tip_commit_id = if let Ok(parent_commit) = tag_value(
95 patches_ancestor_first
96 .first()
97 .context("proposal should have at least one patch")?,
98 "parent-commit",
99 ) {
100 parent_commit
101 } else {
102 // TODO choose most recent commit on master before patch timestamp so it doesnt
103 // constantly get rebased
104 let (_, hash) = git_repo.get_main_or_master_branch()?;
105 hash.to_string()
106 };
107
108 for patch in &patches_ancestor_first {
109 let commit_id = git_repo
110 .create_commit_from_patch(patch, Some(tip_commit_id.clone()))
111 .context(format!(
112 "cannot create commit for patch {}",
113 nip19::Nip19Event {
114 event_id: patch.id(),
115 author: Some(patch.author()),
116 kind: Some(patch.kind()),
117 relays: if let Some(relay) = repo_ref.relays.first() {
118 vec![relay.to_string()]
119 } else {
120 vec![]
121 },
122 }
123 .to_bech32()
124 .unwrap_or_default()
125 ))?;
126 tip_commit_id = commit_id.to_string();
127 }
128 Ok(tip_commit_id)
129}
130
131async fn fetch_proposals(
132 git_repo: &Repo,
133 term: &console::Term,
134 repo_ref: &RepoRef,
135 proposal_refs: &HashMap<String, String>,
136) -> Result<()> {
137 if !proposal_refs.is_empty() {
82 let open_proposals = get_open_proposals(git_repo, repo_ref).await?; 138 let open_proposals = get_open_proposals(git_repo, repo_ref).await?;
83 139
84 let current_user = get_curent_user(git_repo)?; 140 let current_user = get_curent_user(git_repo)?;
85 141
86 for (refstr, oid) in fetch_batch { 142 for refstr in proposal_refs.keys() {
87 if let Some((_, (_, patches))) = 143 if let Some((_, (_, patches))) =
88 find_proposal_and_patches_by_branch_name(&refstr, &open_proposals, &current_user) 144 find_proposal_and_patches_by_branch_name(refstr, &open_proposals, &current_user)
89 { 145 {
90 if !git_repo.does_commit_exist(&oid)? { 146 if let Err(error) = make_commits_for_proposal(git_repo, repo_ref, patches) {
91 let mut patches_ancestor_first = patches.clone(); 147 term.write_line(
92 patches_ancestor_first.reverse(); 148 format!("WARNING: cannot create branch for {refstr}, error: {error}",)
93 if git_repo.does_commit_exist(&tag_value( 149 .as_str(),
94 patches_ancestor_first.first().unwrap(), 150 )?;
95 "parent-commit", 151 break;
96 )?)? {
97 for patch in &patches_ancestor_first {
98 if let Err(error) = git_repo.create_commit_from_patch(patch) {
99 term.write_line(
100 format!(
101 "WARNING: cannot create branch for {refstr}, error: {error} for patch {}",
102 nip19::Nip19Event {
103 event_id: patch.id(),
104 author: Some(patch.author()),
105 kind: Some(patch.kind()),
106 relays: if let Some(relay) = repo_ref.relays.first() {
107 vec![relay.to_string()]
108 } else { vec![]},
109 }.to_bech32().unwrap_or_default()
110 )
111 .as_str(),
112 )?;
113 break;
114 }
115 }
116 } else {
117 term.write_line(
118 format!("WARNING: cannot find parent commit for {refstr}").as_str(),
119 )?;
120 }
121 } 152 }
122 } else {
123 term.write_line(format!("WARNING: cannot find proposal for {refstr}").as_str())?;
124 } 153 }
125 } 154 }
126 } 155 }
127
128 term.flush()?;
129 println!();
130 Ok(()) 156 Ok(())
131} 157}
132 158
133fn fetch_from_git_server( 159pub fn fetch_from_git_server(
134 git_repo: &Repo, 160 git_repo: &Repo,
135 oids: &[String], 161 oids: &[String],
136 git_server_url: &str, 162 git_server_url: &str,
137 decoded_nostr_url: &NostrUrlDecoded, 163 decoded_nostr_url: &NostrUrlDecoded,
138 term: &console::Term, 164 term: &console::Term,
139) -> Result<()> { 165) -> Result<()> {
166 let already_have_oids = oids
167 .iter()
168 .all(|oid| git_repo.does_commit_exist(oid).is_ok_and(|outcome| outcome));
169 if already_have_oids {
170 return Ok(());
171 }
172
140 let server_url = git_server_url.parse::<CloneUrl>()?; 173 let server_url = git_server_url.parse::<CloneUrl>()?;
141 174
142 let protocols_to_attempt = get_read_protocols_to_try(git_repo, &server_url, decoded_nostr_url); 175 let protocols_to_attempt = get_read_protocols_to_try(git_repo, &server_url, decoded_nostr_url);