From f30074cd1bd4fc427795192e27614f5302593b8e Mon Sep 17 00:00:00 2001 From: tsutoringo <33830878+tsutoringo@users.noreply.github.com> Date: Thu, 30 Apr 2026 23:17:32 +0900 Subject: [PATCH] Fix GitHub bot avatar URL (#55297) ## Summary - Fix GitHub avatar URL generation for bot noreply commit authors - Fall back to the GitHub commit author API when the CDN email avatar endpoint cannot resolve bot noreply emails - Add tests covering bot noreply and regular user noreply author emails - Before image - After image ## Test Plan - `cargo test -p git_hosting_providers --features gpui/test-support` Self-Review Checklist: - [x] I've reviewed my own diff for quality, security, and reliability - [x] Unsafe blocks (if any) have justifying comments - [x] The content is consistent with the [UI/UX checklist](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist) - [x] Tests cover the new/changed behavior - [x] Performance impact has been considered and is acceptable Release Notes: - Fixed GitHub avatar lookup for bot noreply commit authors. --------- Co-authored-by: Christopher Biscardi --- .../src/providers/github.rs | 43 +++++++++++++++++-- 1 file changed, 40 insertions(+), 3 deletions(-) diff --git a/crates/git_hosting_providers/src/providers/github.rs b/crates/git_hosting_providers/src/providers/github.rs index 23aff64de9..df3637c49d 100644 --- a/crates/git_hosting_providers/src/providers/github.rs +++ b/crates/git_hosting_providers/src/providers/github.rs @@ -68,8 +68,12 @@ pub struct Github { base_url: Url, } +fn normalize_author_email(email: &str) -> &str { + email.trim_start_matches('<').trim_end_matches('>') +} + fn build_cdn_avatar_url(email: &str) -> Result { - let email = email.trim_start_matches('<').trim_end_matches('>'); + let email = normalize_author_email(email); Url::parse(&format!( "https://avatars.githubusercontent.com/u/e?email={}&s=128", encode(email) @@ -77,6 +81,15 @@ fn build_cdn_avatar_url(email: &str) -> Result { .context("failed to construct avatar URL") } +fn build_cdn_avatar_url_for_author_email(email: &str) -> Result> { + let email = normalize_author_email(email); + if email.ends_with("[bot]@users.noreply.github.com") { + return Ok(None); + } + + build_cdn_avatar_url(email).map(Some) +} + impl Github { pub fn new(name: impl Into, base_url: Url) -> Self { Self { @@ -267,8 +280,10 @@ impl GitHostingProvider for Github { author_email: Option, http_client: Arc, ) -> Result> { - if let Some(email) = author_email { - return Ok(Some(build_cdn_avatar_url(&email)?)); + if let Some(email) = author_email + && let Some(avatar_url) = build_cdn_avatar_url_for_author_email(&email)? + { + return Ok(Some(avatar_url)); } let commit = commit.to_string(); @@ -629,4 +644,26 @@ mod tests { "https://avatars.githubusercontent.com/u/e?email=user%2Btag%40example.com&s=128" ); } + + #[test] + fn test_build_cdn_avatar_url_for_author_email_skips_bot_noreply_emails() { + for email in [ + "41898282+github-actions[bot]@users.noreply.github.com", + "<41898282+github-actions[bot]@users.noreply.github.com>", + ] { + assert_eq!(build_cdn_avatar_url_for_author_email(email).unwrap(), None); + } + } + + #[test] + fn test_build_cdn_avatar_url_for_author_email_uses_user_noreply_emails() { + let url = build_cdn_avatar_url_for_author_email("12345+octocat@users.noreply.github.com") + .unwrap() + .unwrap(); + + assert_eq!( + url.as_str(), + "https://avatars.githubusercontent.com/u/e?email=12345%2Boctocat%40users.noreply.github.com&s=128" + ); + } }