diff options
| author | DanConwayDev <DanConwayDev@protonmail.com> | 2024-08-01 11:09:51 +0100 |
|---|---|---|
| committer | DanConwayDev <DanConwayDev@protonmail.com> | 2024-08-01 11:09:51 +0100 |
| commit | 640fb41902c140a63f46b6126d17416b7dccf344 (patch) | |
| tree | 821005c6a78a320f19730c076851963ce272cc1d | |
| parent | 24125936adb6c152cdb16d55f2639dd374dfee1d (diff) | |
feat(remote): `fetch` fallsback to other servers
listed in the announcement `clone` tag
| -rw-r--r-- | src/git_remote_helper.rs | 35 | ||||
| -rw-r--r-- | test_utils/src/git.rs | 6 | ||||
| -rw-r--r-- | tests/git_remote_helper.rs | 81 |
3 files changed, 114 insertions, 8 deletions
diff --git a/src/git_remote_helper.rs b/src/git_remote_helper.rs index d0c0005..7b471b1 100644 --- a/src/git_remote_helper.rs +++ b/src/git_remote_helper.rs | |||
| @@ -183,15 +183,36 @@ async fn list(git_repo: &Repo, repo_ref: &RepoRef, for_push: bool) -> Result<()> | |||
| 183 | } | 183 | } |
| 184 | 184 | ||
| 185 | fn fetch(git_repo: &Repository, repo_ref: &RepoRef, stdin: &Stdin, oid: &str) -> Result<()> { | 185 | fn fetch(git_repo: &Repository, repo_ref: &RepoRef, stdin: &Stdin, oid: &str) -> Result<()> { |
| 186 | let git_server_remote_url = repo_ref | 186 | let oids = get_oids_from_fetch_batch(stdin, oid)?; |
| 187 | .git_server | 187 | |
| 188 | .first() | 188 | let mut errors = HashMap::new(); |
| 189 | .context("no git server listed in nostr repository announcement")?; | 189 | for git_server_url in &repo_ref.git_server { |
| 190 | let mut git_server_remote = git_repo.remote_anonymous(git_server_remote_url)?; | 190 | if let Err(e) = fetch_from_git_server(git_repo, &oids, git_server_url) { |
| 191 | errors.insert(git_server_url.to_string(), e); | ||
| 192 | } else { | ||
| 193 | println!(); | ||
| 194 | return Ok(()); | ||
| 195 | } | ||
| 196 | } | ||
| 197 | bail!( | ||
| 198 | "failed to fetch objects in nostr state event from:\r\n{}", | ||
| 199 | errors | ||
| 200 | .iter() | ||
| 201 | .map(|(url, error)| format!("{url}: {error}")) | ||
| 202 | .collect::<Vec<String>>() | ||
| 203 | .join("\r\n") | ||
| 204 | ); | ||
| 205 | } | ||
| 206 | |||
| 207 | fn fetch_from_git_server( | ||
| 208 | git_repo: &Repository, | ||
| 209 | oids: &[String], | ||
| 210 | git_server_url: &str, | ||
| 211 | ) -> Result<()> { | ||
| 212 | let mut git_server_remote = git_repo.remote_anonymous(git_server_url)?; | ||
| 191 | git_server_remote.connect(git2::Direction::Fetch)?; | 213 | git_server_remote.connect(git2::Direction::Fetch)?; |
| 192 | git_server_remote.download(&get_oids_from_fetch_batch(stdin, oid)?, None)?; | 214 | git_server_remote.download(oids, None)?; |
| 193 | git_server_remote.disconnect()?; | 215 | git_server_remote.disconnect()?; |
| 194 | println!(); | ||
| 195 | Ok(()) | 216 | Ok(()) |
| 196 | } | 217 | } |
| 197 | 218 | ||
diff --git a/test_utils/src/git.rs b/test_utils/src/git.rs index e522ffd..058eb31 100644 --- a/test_utils/src/git.rs +++ b/test_utils/src/git.rs | |||
| @@ -154,6 +154,12 @@ impl GitTestRepo { | |||
| 154 | self.stage_and_commit("add t2.md") | 154 | self.stage_and_commit("add t2.md") |
| 155 | } | 155 | } |
| 156 | 156 | ||
| 157 | pub fn populate_minus_1(&self) -> Result<Oid> { | ||
| 158 | self.initial_commit()?; | ||
| 159 | fs::write(self.dir.join("t1.md"), "some content")?; | ||
| 160 | self.stage_and_commit("add t1.md") | ||
| 161 | } | ||
| 162 | |||
| 157 | pub fn populate_with_test_branch(&self) -> Result<Oid> { | 163 | pub fn populate_with_test_branch(&self) -> Result<Oid> { |
| 158 | self.populate()?; | 164 | self.populate()?; |
| 159 | self.create_branch("add-example-feature")?; | 165 | self.create_branch("add-example-feature")?; |
diff --git a/tests/git_remote_helper.rs b/tests/git_remote_helper.rs index 69f290a..fda393f 100644 --- a/tests/git_remote_helper.rs +++ b/tests/git_remote_helper.rs | |||
| @@ -39,6 +39,19 @@ fn prep_git_repo() -> Result<GitTestRepo> { | |||
| 39 | Ok(test_repo) | 39 | Ok(test_repo) |
| 40 | } | 40 | } |
| 41 | 41 | ||
| 42 | fn prep_git_repo_minus_1_commit() -> Result<GitTestRepo> { | ||
| 43 | let test_repo = GitTestRepo::without_repo_in_git_config(); | ||
| 44 | let mut config = test_repo | ||
| 45 | .git_repo | ||
| 46 | .config() | ||
| 47 | .context("cannot open git config")?; | ||
| 48 | config.set_str("nostr.nsec", TEST_KEY_1_NSEC)?; | ||
| 49 | config.set_str("nostr.npub", TEST_KEY_1_NPUB)?; | ||
| 50 | test_repo.add_remote(NOSTR_REMOTE_NAME, &get_nostr_remote_url()?)?; | ||
| 51 | test_repo.populate_minus_1()?; | ||
| 52 | Ok(test_repo) | ||
| 53 | } | ||
| 54 | |||
| 42 | fn cli_tester(git_repo: &GitTestRepo) -> CliTester { | 55 | fn cli_tester(git_repo: &GitTestRepo) -> CliTester { |
| 43 | CliTester::new_remote_helper_from_dir(&git_repo.dir, &get_nostr_remote_url().unwrap()) | 56 | CliTester::new_remote_helper_from_dir(&git_repo.dir, &get_nostr_remote_url().unwrap()) |
| 44 | } | 57 | } |
| @@ -493,9 +506,75 @@ mod fetch { | |||
| 493 | 506 | ||
| 494 | #[tokio::test] | 507 | #[tokio::test] |
| 495 | #[serial] | 508 | #[serial] |
| 496 | async fn fetch_downloads_speficied_branch_ref_commits_from_git_server() -> Result<()> { | 509 | async fn fetch_downloads_speficied_commits_from_git_server() -> Result<()> { |
| 497 | async_run_test().await | 510 | async_run_test().await |
| 498 | } | 511 | } |
| 512 | |||
| 513 | mod when_first_git_server_fails_ { | ||
| 514 | use super::*; | ||
| 515 | async fn async_run_test() -> Result<()> { | ||
| 516 | let (state_event, source_git_repo) = generate_repo_with_state_event().await?; | ||
| 517 | |||
| 518 | let main_commit_id = source_git_repo.get_tip_of_local_branch("main")?; | ||
| 519 | |||
| 520 | let git_repo = prep_git_repo_minus_1_commit()?; | ||
| 521 | |||
| 522 | let events = vec![ | ||
| 523 | generate_test_key_1_metadata_event("fred"), | ||
| 524 | generate_test_key_1_relay_list_event(), | ||
| 525 | generate_repo_ref_event_with_git_server(vec![ | ||
| 526 | "./path-doesnt-exist".to_string(), | ||
| 527 | source_git_repo.dir.to_str().unwrap().to_string(), | ||
| 528 | ]), | ||
| 529 | state_event, | ||
| 530 | ]; | ||
| 531 | // fallback (51,52) user write (53, 55) repo (55, 56) blaster (57) | ||
| 532 | let (mut r51, mut r52, mut r53, mut r55, mut r56, mut r57) = ( | ||
| 533 | Relay::new(8051, None, None), | ||
| 534 | Relay::new(8052, None, None), | ||
| 535 | Relay::new(8053, None, None), | ||
| 536 | Relay::new(8055, None, None), | ||
| 537 | Relay::new(8056, None, None), | ||
| 538 | Relay::new(8057, None, None), | ||
| 539 | ); | ||
| 540 | r51.events = events.clone(); | ||
| 541 | r55.events = events; | ||
| 542 | |||
| 543 | let cli_tester_handle = std::thread::spawn(move || -> Result<()> { | ||
| 544 | assert!(git_repo.git_repo.find_commit(main_commit_id).is_err()); | ||
| 545 | |||
| 546 | let mut p = cli_tester_after_fetch(&git_repo)?; | ||
| 547 | p.send_line(format!("fetch {main_commit_id} main").as_str())?; | ||
| 548 | p.send_line("")?; | ||
| 549 | p.expect("\r\n")?; | ||
| 550 | |||
| 551 | assert!(git_repo.git_repo.find_commit(main_commit_id).is_ok()); | ||
| 552 | |||
| 553 | p.exit()?; | ||
| 554 | for p in [51, 52, 53, 55, 56, 57] { | ||
| 555 | relay::shutdown_relay(8000 + p)?; | ||
| 556 | } | ||
| 557 | Ok(()) | ||
| 558 | }); | ||
| 559 | // launch relays | ||
| 560 | let _ = join!( | ||
| 561 | r51.listen_until_close(), | ||
| 562 | r52.listen_until_close(), | ||
| 563 | r53.listen_until_close(), | ||
| 564 | r55.listen_until_close(), | ||
| 565 | r56.listen_until_close(), | ||
| 566 | r57.listen_until_close(), | ||
| 567 | ); | ||
| 568 | cli_tester_handle.join().unwrap()?; | ||
| 569 | Ok(()) | ||
| 570 | } | ||
| 571 | |||
| 572 | #[tokio::test] | ||
| 573 | #[serial] | ||
| 574 | async fn fetch_downloads_speficied_commits_from_second_git_server() -> Result<()> { | ||
| 575 | async_run_test().await | ||
| 576 | } | ||
| 577 | } | ||
| 499 | } | 578 | } |
| 500 | 579 | ||
| 501 | mod push { | 580 | mod push { |