Skip to content

Commit da34b3b

Browse files
committed
Treat 404 repo responses as skip, not fail
1 parent 3d6b645 commit da34b3b

File tree

1 file changed

+26
-10
lines changed

1 file changed

+26
-10
lines changed

ci/pr-check/src/main.rs

Lines changed: 26 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -145,7 +145,7 @@ impl GithubClient {
145145
///
146146
/// Returns an error on network failure or if the response cannot be
147147
/// deserialised as `T`.
148-
async fn get<T: for<'de> Deserialize<'de>>(&self, url: &str) -> Result<T> {
148+
async fn get<T: for<'de> Deserialize<'de>>(&self, url: &str) -> Result<Option<T>> {
149149
let resp = self
150150
.client
151151
.get(url)
@@ -157,6 +157,9 @@ impl GithubClient {
157157
.with_context(|| format!("GET {url} failed"))?;
158158

159159
let status = resp.status();
160+
if status == reqwest::StatusCode::NOT_FOUND {
161+
return Ok(None);
162+
}
160163
if !status.is_success() {
161164
let body = resp.text().await.unwrap_or_default();
162165
bail!("GET {url} returned {status}: {body}");
@@ -165,14 +168,15 @@ impl GithubClient {
165168
resp.json::<T>()
166169
.await
167170
.with_context(|| format!("Failed to deserialise response from {url}"))
171+
.map(Some)
168172
}
169173

170174
/// Fetches repository metadata.
171175
///
172176
/// # Errors
173177
///
174178
/// Returns an error if the API call fails.
175-
async fn repo_info(&self, owner: &str, repo: &str) -> Result<RepoInfo> {
179+
async fn repo_info(&self, owner: &str, repo: &str) -> Result<Option<RepoInfo>> {
176180
let url = format!("https://api.github.com/repos/{owner}/{repo}");
177181
self.get::<RepoInfo>(&url).await
178182
}
@@ -183,16 +187,18 @@ impl GithubClient {
183187
/// # Errors
184188
///
185189
/// Returns an error if the API call fails.
186-
async fn contributor_count(&self, owner: &str, repo: &str) -> Result<usize> {
190+
async fn contributor_count(&self, owner: &str, repo: &str) -> Result<Option<usize>> {
187191
let url =
188192
format!("https://api.github.com/repos/{owner}/{repo}/contributors?per_page=100&anon=0");
189-
let contributors = self.get::<Vec<Contributor>>(&url).await?;
193+
let Some(contributors) = self.get::<Vec<Contributor>>(&url).await? else {
194+
return Ok(None);
195+
};
190196
// Exclude bot accounts from the contributor count.
191197
let human_count = contributors
192198
.iter()
193199
.filter(|c| c.account_type != "Bot")
194200
.count();
195-
Ok(human_count)
201+
Ok(Some(human_count))
196202
}
197203

198204
/// Lists all comments on a PR/issue.
@@ -202,7 +208,9 @@ impl GithubClient {
202208
/// Returns an error if the API call fails.
203209
async fn list_pr_comments(&self, repo: &str, pr: u64) -> Result<Vec<IssueComment>> {
204210
let url = format!("https://api.github.com/repos/{repo}/issues/{pr}/comments?per_page=100");
205-
self.get::<Vec<IssueComment>>(&url).await
211+
self.get::<Vec<IssueComment>>(&url)
212+
.await?
213+
.with_context(|| format!("PR {pr} not found in {repo}"))
206214
}
207215

208216
/// Creates a new PR comment.
@@ -310,19 +318,20 @@ async fn check_tool(client: &GithubClient, tool: &ToolEntry) -> Result<ToolRepor
310318
let contributors_result = client.contributor_count(&owner, &repo).await;
311319

312320
let stars_check = match &repo_result {
313-
Ok(info) => {
321+
Ok(Some(info)) => {
314322
let s = info.stargazers_count;
315323
if s >= MIN_STARS {
316324
CheckResult::Pass(format!("{s} stars"))
317325
} else {
318326
CheckResult::Fail(format!("{s} stars (minimum is {MIN_STARS})"))
319327
}
320328
}
329+
Ok(None) => CheckResult::Skip("repository not found".into()),
321330
Err(e) => CheckResult::Fail(format!("Could not fetch repo info: {e}")),
322331
};
323332

324333
let age_check = match &repo_result {
325-
Ok(info) => {
334+
Ok(Some(info)) => {
326335
let age = Utc::now().signed_duration_since(info.created_at);
327336
let days = age.num_days();
328337
let months = days / 30;
@@ -335,11 +344,12 @@ async fn check_tool(client: &GithubClient, tool: &ToolEntry) -> Result<ToolRepor
335344
))
336345
}
337346
}
347+
Ok(None) => CheckResult::Skip("repository not found".into()),
338348
Err(_) => CheckResult::Skip("Could not determine age (repo info unavailable)".into()),
339349
};
340350

341351
let contributors_check = match contributors_result {
342-
Ok(count) => {
352+
Ok(Some(count)) => {
343353
if count >= MIN_CONTRIBUTORS {
344354
CheckResult::Pass(format!("{count} contributors"))
345355
} else {
@@ -348,16 +358,22 @@ async fn check_tool(client: &GithubClient, tool: &ToolEntry) -> Result<ToolRepor
348358
))
349359
}
350360
}
361+
Ok(None) => CheckResult::Skip("repository not found".into()),
351362
Err(e) => CheckResult::Fail(format!("Could not fetch contributors: {e}")),
352363
};
353364

365+
let repo_not_found = matches!(repo_result, Ok(None));
366+
let note = repo_not_found.then_some(
367+
"The source URL returned a 404. Please check that the repository exists and is public.",
368+
);
369+
354370
Ok(ToolReport {
355371
name: tool.name.to_string(),
356372
source,
357373
stars: stars_check,
358374
contributors: contributors_check,
359375
age: age_check,
360-
note: None,
376+
note: note.map(str::to_owned),
361377
})
362378
} else {
363379
// No source or non-GitHub source. This is fine for proprietary or

0 commit comments

Comments
 (0)