upleb.uk

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

summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDanConwayDev <DanConwayDev@protonmail.com>2024-08-01 09:39:08 +0100
committerDanConwayDev <DanConwayDev@protonmail.com>2024-08-01 10:19:31 +0100
commitc494242fbf7308e50e38ed7b4750a4fe2638ed18 (patch)
tree506cbe2c95a1fc80902b665055dbeadebc644000
parent5c973c672f79510b19cd394a33751d93b7f9f5ee (diff)
feat(remote): `fetch` uses state event
and falls back to git server is state event cant be found
-rw-r--r--src/git_remote_helper.rs43
-rw-r--r--test_utils/src/git.rs9
-rw-r--r--tests/git_remote_helper.rs315
3 files changed, 291 insertions, 76 deletions
diff --git a/src/git_remote_helper.rs b/src/git_remote_helper.rs
index 08a59c1..38a0aa5 100644
--- a/src/git_remote_helper.rs
+++ b/src/git_remote_helper.rs
@@ -108,10 +108,10 @@ async fn main() -> Result<()> {
108 .await?; 108 .await?;
109 } 109 }
110 ["list"] => { 110 ["list"] => {
111 list(&git_repo.git_repo, &repo_ref, false)?; 111 list(&git_repo, &repo_ref, false).await?;
112 } 112 }
113 ["list", "for-push"] => { 113 ["list", "for-push"] => {
114 list(&git_repo.git_repo, &repo_ref, true)?; 114 list(&git_repo, &repo_ref, true).await?;
115 } 115 }
116 [] => { 116 [] => {
117 return Ok(()); 117 return Ok(());
@@ -148,19 +148,36 @@ fn nostr_git_url_to_repo_coordinates(url: &str) -> Result<HashSet<Coordinate>> {
148 Ok(repo_coordinattes) 148 Ok(repo_coordinattes)
149} 149}
150 150
151fn list(git_repo: &Repository, repo_ref: &RepoRef, for_push: bool) -> Result<()> { 151async fn list(git_repo: &Repo, repo_ref: &RepoRef, for_push: bool) -> Result<()> {
152 let git_server_remote_url = repo_ref 152 if let Ok(repo_state) = get_state_from_cache(git_repo.get_path()?, repo_ref).await {
153 .git_server 153 for (name, value) in &repo_state.state {
154 .first() 154 if value.starts_with("ref: ") {
155 .context("no git server listed in nostr repository announcement")?; 155 if !for_push {
156 let mut git_server_remote = git_repo.remote_anonymous(git_server_remote_url)?; 156 println!("{} {name}", value.replace("ref: ", "@"));
157 git_server_remote.connect(git2::Direction::Fetch)?; 157 }
158 for head in git_server_remote.list()? { 158 } else {
159 if !for_push || head.name() != "HEAD" { 159 println!("{value} {name}");
160 println!("{} {}", head.oid(), head.name()); 160 }
161 } 161 }
162 } else {
163 let git_server_remote_url = repo_ref
164 .git_server
165 .first()
166 .context("no git server listed in nostr repository announcement")?;
167 let mut git_server_remote = git_repo.git_repo.remote_anonymous(git_server_remote_url)?;
168 git_server_remote.connect(git2::Direction::Fetch)?;
169
170 for head in git_server_remote.list()? {
171 if let Some(symbolic_reference) = head.symref_target() {
172 if !for_push {
173 println!("@{} {}", symbolic_reference, head.name());
174 }
175 } else {
176 println!("{} {}", head.oid(), head.name());
177 }
178 }
179 git_server_remote.disconnect()?;
162 } 180 }
163 git_server_remote.disconnect()?;
164 println!(); 181 println!();
165 Ok(()) 182 Ok(())
166} 183}
diff --git a/test_utils/src/git.rs b/test_utils/src/git.rs
index 7dfa62b..4a4aaa8 100644
--- a/test_utils/src/git.rs
+++ b/test_utils/src/git.rs
@@ -123,6 +123,15 @@ impl GitTestRepo {
123 }) 123 })
124 } 124 }
125 125
126 pub fn clone_repo(existing_repo: &GitTestRepo) -> Result<Self> {
127 let path = current_dir()?.join(format!("tmpgit-{}", rand::random::<u64>()));
128 let git_repo = git2::Repository::clone(existing_repo.dir.to_str().unwrap(), path.clone())?;
129 Ok(Self {
130 dir: path,
131 git_repo,
132 })
133 }
134
126 pub fn initial_commit(&self) -> Result<Oid> { 135 pub fn initial_commit(&self) -> Result<Oid> {
127 let oid = self.git_repo.index()?.write_tree()?; 136 let oid = self.git_repo.index()?.write_tree()?;
128 let tree = self.git_repo.find_tree(oid)?; 137 let tree = self.git_repo.find_tree(oid)?;
diff --git a/tests/git_remote_helper.rs b/tests/git_remote_helper.rs
index 1ca4296..20ecad7 100644
--- a/tests/git_remote_helper.rs
+++ b/tests/git_remote_helper.rs
@@ -54,7 +54,6 @@ fn cli_tester_after_fetch(git_repo: &GitTestRepo) -> Result<CliTester> {
54async fn generate_repo_with_state_event() -> Result<(nostr::Event, GitTestRepo)> { 54async fn generate_repo_with_state_event() -> Result<(nostr::Event, GitTestRepo)> {
55 let git_repo = prep_git_repo()?; 55 let git_repo = prep_git_repo()?;
56 git_repo.create_branch("example-branch")?; 56 git_repo.create_branch("example-branch")?;
57
58 let main_commit_id = git_repo.get_tip_of_local_branch("main")?.to_string(); 57 let main_commit_id = git_repo.get_tip_of_local_branch("main")?.to_string();
59 // TODO recreate_as_bare isn't creating other branches 58 // TODO recreate_as_bare isn't creating other branches
60 let source_git_repo = GitTestRepo::recreate_as_bare(&git_repo)?; 59 let source_git_repo = GitTestRepo::recreate_as_bare(&git_repo)?;
@@ -185,74 +184,234 @@ mod list {
185 184
186 use super::*; 185 use super::*;
187 186
188 async fn async_run_test() -> Result<()> { 187 mod without_state_announcement {
189 let source_git_repo = prep_git_repo()?;
190 std::fs::write(source_git_repo.dir.join("commit.md"), "some content")?;
191 let main_commit_id = source_git_repo.stage_and_commit("commit.md")?;
192
193 source_git_repo.create_branch("vnext")?;
194 source_git_repo.checkout("vnext")?;
195 std::fs::write(source_git_repo.dir.join("vnext.md"), "some content")?;
196 let vnext_commit_id = source_git_repo.stage_and_commit("vnext.md")?;
197
198 let head_commit_id = source_git_repo.checkout("main")?;
199 188
200 let git_repo = prep_git_repo()?; 189 use super::*;
201 let events = vec![
202 generate_test_key_1_metadata_event("fred"),
203 generate_test_key_1_relay_list_event(),
204 generate_repo_ref_event_with_git_server(source_git_repo.dir.to_str().unwrap()),
205 ];
206 // fallback (51,52) user write (53, 55) repo (55, 56) blaster (57)
207 let (mut r51, mut r52, mut r53, mut r55, mut r56, mut r57) = (
208 Relay::new(8051, None, None),
209 Relay::new(8052, None, None),
210 Relay::new(8053, None, None),
211 Relay::new(8055, None, None),
212 Relay::new(8056, None, None),
213 Relay::new(8057, None, None),
214 );
215 r51.events = events.clone();
216 r55.events = events;
217 190
218 let cli_tester_handle = std::thread::spawn(move || -> Result<()> { 191 async fn async_run_test() -> Result<()> {
219 let mut p = cli_tester_after_fetch(&git_repo)?; 192 let source_git_repo = prep_git_repo()?;
220 p.send_line("list")?; 193 std::fs::write(source_git_repo.dir.join("commit.md"), "some content")?;
221 // println!("{}", p.expect_eventually("\r\n\r\n")?); 194 let main_commit_id = source_git_repo.stage_and_commit("commit.md")?;
222 assert_eq!( 195
223 p.expect_eventually("\r\n\r\n")? 196 source_git_repo.create_branch("vnext")?;
224 .split("\r\n") 197 source_git_repo.checkout("vnext")?;
225 .map(|e| e.to_string()) 198 std::fs::write(source_git_repo.dir.join("vnext.md"), "some content")?;
226 .collect::<HashSet<String>>(), 199 let vnext_commit_id = source_git_repo.stage_and_commit("vnext.md")?;
227 HashSet::from([ 200 source_git_repo.checkout("main")?;
228 format!("{} HEAD", head_commit_id), 201
229 format!("{} refs/heads/main", main_commit_id), 202 let git_repo = prep_git_repo()?;
230 format!("{} refs/heads/vnext", vnext_commit_id), 203 let events = vec![
231 ]), 204 generate_test_key_1_metadata_event("fred"),
205 generate_test_key_1_relay_list_event(),
206 generate_repo_ref_event_with_git_server(source_git_repo.dir.to_str().unwrap()),
207 ];
208 // fallback (51,52) user write (53, 55) repo (55, 56) blaster (57)
209 let (mut r51, mut r52, mut r53, mut r55, mut r56, mut r57) = (
210 Relay::new(8051, None, None),
211 Relay::new(8052, None, None),
212 Relay::new(8053, None, None),
213 Relay::new(8055, None, None),
214 Relay::new(8056, None, None),
215 Relay::new(8057, None, None),
232 ); 216 );
233 p.exit()?; 217 r51.events = events.clone();
234 for p in [51, 52, 53, 55, 56, 57] { 218 r55.events = events;
235 relay::shutdown_relay(8000 + p)?; 219
236 } 220 let cli_tester_handle = std::thread::spawn(move || -> Result<()> {
221 let mut p = cli_tester_after_fetch(&git_repo)?;
222 p.send_line("list")?;
223 // println!("{}", p.expect_eventually("\r\n\r\n")?);
224 assert_eq!(
225 p.expect_eventually("\r\n\r\n")?
226 .split("\r\n")
227 .map(|e| e.to_string())
228 .collect::<HashSet<String>>(),
229 HashSet::from([
230 "@refs/heads/main HEAD".to_string(),
231 format!("{} refs/heads/main", main_commit_id),
232 format!("{} refs/heads/vnext", vnext_commit_id),
233 ]),
234 );
235 p.exit()?;
236 for p in [51, 52, 53, 55, 56, 57] {
237 relay::shutdown_relay(8000 + p)?;
238 }
239 Ok(())
240 });
241 // launch relays
242 let _ = join!(
243 r51.listen_until_close(),
244 r52.listen_until_close(),
245 r53.listen_until_close(),
246 r55.listen_until_close(),
247 r56.listen_until_close(),
248 r57.listen_until_close(),
249 );
250 cli_tester_handle.join().unwrap()?;
237 Ok(()) 251 Ok(())
238 }); 252 }
239 // launch relays 253
240 let _ = join!( 254 #[tokio::test]
241 r51.listen_until_close(), 255 #[serial]
242 r52.listen_until_close(), 256 async fn lists_head_and_2_branches_and_commit_ids_from_git_server() -> Result<()> {
243 r53.listen_until_close(), 257 async_run_test().await
244 r55.listen_until_close(), 258 }
245 r56.listen_until_close(),
246 r57.listen_until_close(),
247 );
248 cli_tester_handle.join().unwrap()?;
249 Ok(())
250 } 259 }
260 mod with_state_announcement {
251 261
252 #[tokio::test] 262 use super::*;
253 #[serial] 263
254 async fn lists_head_and_2_branches_and_commit_ids_from_git_server() -> Result<()> { 264 mod when_announcement_matches_git_server {
255 async_run_test().await 265
266 use super::*;
267
268 async fn async_run_test() -> Result<()> {
269 let (state_event, source_git_repo) = generate_repo_with_state_event().await?;
270
271 let main_commit_id = source_git_repo.get_tip_of_local_branch("main")?;
272 let example_commit_id =
273 source_git_repo.get_tip_of_local_branch("example-branch")?;
274
275 let git_repo = prep_git_repo()?;
276 let events = vec![
277 generate_test_key_1_metadata_event("fred"),
278 generate_test_key_1_relay_list_event(),
279 generate_repo_ref_event_with_git_server(source_git_repo.dir.to_str().unwrap()),
280 state_event,
281 ];
282 // fallback (51,52) user write (53, 55) repo (55, 56) blaster (57)
283 let (mut r51, mut r52, mut r53, mut r55, mut r56, mut r57) = (
284 Relay::new(8051, None, None),
285 Relay::new(8052, None, None),
286 Relay::new(8053, None, None),
287 Relay::new(8055, None, None),
288 Relay::new(8056, None, None),
289 Relay::new(8057, None, None),
290 );
291 r51.events = events.clone();
292 r55.events = events;
293
294 let cli_tester_handle = std::thread::spawn(move || -> Result<()> {
295 let mut p = cli_tester_after_fetch(&git_repo)?;
296 p.send_line("list")?;
297 // println!("{}", p.expect_eventually("\r\n\r\n")?);
298 assert_eq!(
299 p.expect_eventually("\r\n\r\n")?
300 .split("\r\n")
301 .map(|e| e.to_string())
302 .collect::<HashSet<String>>(),
303 HashSet::from([
304 "@refs/heads/main HEAD".to_string(),
305 format!("{} refs/heads/main", main_commit_id),
306 format!("{} refs/heads/example-branch", example_commit_id),
307 ]),
308 );
309 p.exit()?;
310 for p in [51, 52, 53, 55, 56, 57] {
311 relay::shutdown_relay(8000 + p)?;
312 }
313 Ok(())
314 });
315 // launch relays
316 let _ = join!(
317 r51.listen_until_close(),
318 r52.listen_until_close(),
319 r53.listen_until_close(),
320 r55.listen_until_close(),
321 r56.listen_until_close(),
322 r57.listen_until_close(),
323 );
324 cli_tester_handle.join().unwrap()?;
325 Ok(())
326 }
327
328 #[tokio::test]
329 #[serial]
330 async fn lists_head_and_2_branches_and_commit_ids_announcement() -> Result<()> {
331 async_run_test().await
332 }
333 }
334 mod when_announcement_doesnt_match_git_server {
335
336 use super::*;
337
338 async fn async_run_test() -> Result<()> {
339 let (state_event, source_git_repo) = generate_repo_with_state_event().await?;
340 let main_original_commit_id = source_git_repo.get_tip_of_local_branch("main")?;
341
342 {
343 // add commit to main on git server
344 let tmp_repo = GitTestRepo::clone_repo(&source_git_repo)?;
345 std::fs::write(tmp_repo.dir.join("commitx.md"), "some content")?;
346 tmp_repo.stage_and_commit("commitx.md")?;
347 let mut remote = tmp_repo.git_repo.find_remote("origin")?;
348 remote.push(&["refs/heads/main:refs/heads/main"], None)?;
349 }
350
351 let main_updated_commit_id = source_git_repo.get_tip_of_local_branch("main")?;
352 assert_ne!(main_original_commit_id, main_updated_commit_id);
353 let example_commit_id =
354 source_git_repo.get_tip_of_local_branch("example-branch")?;
355
356 let git_repo = prep_git_repo()?;
357 let events = vec![
358 generate_test_key_1_metadata_event("fred"),
359 generate_test_key_1_relay_list_event(),
360 generate_repo_ref_event_with_git_server(source_git_repo.dir.to_str().unwrap()),
361 state_event,
362 ];
363 // fallback (51,52) user write (53, 55) repo (55, 56) blaster (57)
364 let (mut r51, mut r52, mut r53, mut r55, mut r56, mut r57) = (
365 Relay::new(8051, None, None),
366 Relay::new(8052, None, None),
367 Relay::new(8053, None, None),
368 Relay::new(8055, None, None),
369 Relay::new(8056, None, None),
370 Relay::new(8057, None, None),
371 );
372 r51.events = events.clone();
373 r55.events = events;
374
375 let cli_tester_handle = std::thread::spawn(move || -> Result<()> {
376 let mut p = cli_tester_after_fetch(&git_repo)?;
377 p.send_line("list")?;
378 // println!("{}", p.expect_eventually("\r\n\r\n")?);
379 assert_eq!(
380 p.expect_eventually("\r\n\r\n")?
381 .split("\r\n")
382 .map(|e| e.to_string())
383 .collect::<HashSet<String>>(),
384 HashSet::from([
385 "@refs/heads/main HEAD".to_string(),
386 format!("{} refs/heads/main", main_original_commit_id),
387 format!("{} refs/heads/example-branch", example_commit_id),
388 ]),
389 );
390 p.exit()?;
391 for p in [51, 52, 53, 55, 56, 57] {
392 relay::shutdown_relay(8000 + p)?;
393 }
394 Ok(())
395 });
396 // launch relays
397 let _ = join!(
398 r51.listen_until_close(),
399 r52.listen_until_close(),
400 r53.listen_until_close(),
401 r55.listen_until_close(),
402 r56.listen_until_close(),
403 r57.listen_until_close(),
404 );
405 cli_tester_handle.join().unwrap()?;
406 Ok(())
407 }
408
409 #[tokio::test]
410 #[serial]
411 async fn anouncement_state_is_used() -> Result<()> {
412 async_run_test().await
413 }
414 }
256 } 415 }
257} 416}
258 417
@@ -656,6 +815,24 @@ mod push {
656 p.expect("ok refs/heads/vnext\r\n")?; 815 p.expect("ok refs/heads/vnext\r\n")?;
657 p.expect("\r\n")?; 816 p.expect("\r\n")?;
658 p.exit()?; 817 p.exit()?;
818 // local refs updated
819 assert_eq!(
820 git_repo
821 .git_repo
822 .find_reference("refs/remotes/nostr/main")?
823 .peel_to_commit()?
824 .id(),
825 main_commit_id,
826 );
827
828 assert_eq!(
829 git_repo
830 .git_repo
831 .find_reference("refs/remotes/nostr/vnext")?
832 .peel_to_commit()?
833 .id(),
834 vnext_commit_id
835 );
659 for p in [51, 52, 53, 55, 56, 57] { 836 for p in [51, 52, 53, 55, 56, 57] {
660 relay::shutdown_relay(8000 + p)?; 837 relay::shutdown_relay(8000 + p)?;
661 } 838 }
@@ -673,6 +850,18 @@ mod push {
673 850
674 cli_tester_handle.join().unwrap()?; 851 cli_tester_handle.join().unwrap()?;
675 852
853 // git_server updated
854 assert_eq!(
855 source_git_repo.get_tip_of_local_branch("main")?,
856 main_commit_id
857 );
858
859 assert_eq!(
860 source_git_repo.get_tip_of_local_branch("vnext")?,
861 vnext_commit_id
862 );
863
864 // state annoucement updated
676 let state_event = r56 865 let state_event = r56
677 .events 866 .events
678 .iter() 867 .iter()