From bc71a1b1366067e2db8722a5d305f291976bc909 Mon Sep 17 00:00:00 2001
From: Jiahao XU <Jiahao_XU@outlook.com>
Date: Thu, 6 Jun 2024 22:57:55 +1000
Subject: [PATCH] Fix `get_repo_info`: Retry on rate limit

Signed-off-by: Jiahao XU <Jiahao_XU@outlook.com>
---
 crates/binstalk-fetchers/src/gh_crate_meta.rs |  6 ++---
 crates/binstalk-fetchers/src/lib.rs           | 25 +++++++++++++------
 2 files changed, 20 insertions(+), 11 deletions(-)

diff --git a/crates/binstalk-fetchers/src/gh_crate_meta.rs b/crates/binstalk-fetchers/src/gh_crate_meta.rs
index fdba68cc..d2d85f50 100644
--- a/crates/binstalk-fetchers/src/gh_crate_meta.rs
+++ b/crates/binstalk-fetchers/src/gh_crate_meta.rs
@@ -1,4 +1,4 @@
-use std::{borrow::Cow, fmt, iter, path::Path, sync::Arc, time::Duration};
+use std::{borrow::Cow, fmt, iter, path::Path, sync::Arc};
 
 use binstalk_git_repo_api::gh_api_client::{GhApiError, GhReleaseArtifact, GhReleaseArtifactUrl};
 use compact_str::{CompactString, ToCompactString};
@@ -12,11 +12,9 @@ use url::Url;
 
 use crate::{
     common::*, futures_resolver::FuturesResolver, Data, FetchError, InvalidPkgFmtError, RepoInfo,
-    SignaturePolicy, SignatureVerifier, TargetDataErased,
+    SignaturePolicy, SignatureVerifier, TargetDataErased, DEFAULT_GH_API_RETRY_DURATION,
 };
 
-static DEFAULT_GH_API_RETRY_DURATION: Duration = Duration::from_secs(1);
-
 pub(crate) mod hosting;
 
 pub struct GhCrateMeta {
diff --git a/crates/binstalk-fetchers/src/lib.rs b/crates/binstalk-fetchers/src/lib.rs
index 4c1de54a..688bfbbb 100644
--- a/crates/binstalk-fetchers/src/lib.rs
+++ b/crates/binstalk-fetchers/src/lib.rs
@@ -1,12 +1,12 @@
 #![cfg_attr(docsrs, feature(doc_auto_cfg))]
 
-use std::{path::Path, sync::Arc};
+use std::{path::Path, sync::Arc, time::Duration};
 
 use binstalk_downloader::{download::DownloadError, remote::Error as RemoteError};
 use binstalk_git_repo_api::gh_api_client::{GhApiError, GhRepo};
 use binstalk_types::cargo_toml_binstall::SigningAlgorithm;
 use thiserror::Error as ThisError;
-use tokio::sync::OnceCell;
+use tokio::{sync::OnceCell, time::sleep};
 pub use url::ParseError as UrlParseError;
 
 mod gh_crate_meta;
@@ -27,6 +27,8 @@ mod futures_resolver;
 
 use gh_crate_meta::hosting::RepositoryHost;
 
+static DEFAULT_GH_API_RETRY_DURATION: Duration = Duration::from_secs(1);
+
 #[derive(Debug, ThisError)]
 #[error("Invalid pkg-url {pkg_url} for {crate_name}@{version} on {target}: {reason}")]
 pub struct InvalidPkgFmtError {
@@ -204,11 +206,20 @@ impl Data {
                     let mut is_private = false;
                     if repository_host == RepositoryHost::GitHub && client.has_gh_token() {
                         if let Some(gh_repo) = GhRepo::try_extract_from_url(&repo) {
-                            let Some(gh_repo_info) = client.get_repo_info(&gh_repo).await? else {
-                                return Err(GhApiError::NotFound.into());
-                            };
-
-                            is_private = gh_repo_info.is_private();
+                            loop {
+                                match client.get_repo_info(&gh_repo).await {
+                                    Ok(Some(gh_repo_info)) => {
+                                        is_private = gh_repo_info.is_private();
+                                        break;
+                                    }
+                                    Ok(None) => return Err(GhApiError::NotFound.into()),
+                                    Err(GhApiError::RateLimit { retry_after }) => {
+                                        sleep(retry_after.unwrap_or(DEFAULT_GH_API_RETRY_DURATION))
+                                            .await
+                                    }
+                                    Err(err) => return Err(err.into()),
+                                }
+                            }
                         }
                     }