Skip to content

Commit 4efd93c

Browse files
Enhance GitHub API error handling (#496)
* feat: Enhance GitHub API error handling * docs: Update changelog
1 parent 6d6e3d0 commit 4efd93c

File tree

4 files changed

+54
-17
lines changed

4 files changed

+54
-17
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
88
## Unreleased
99

1010
### Changed
11+
- Improved GitHub API error handling (#496)
1112
- update `zip` dependency to 3.0.0
1213

1314
## [0.15.0] - 2025-04-08

src/error.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,10 @@ pub enum Error {
1414
#[error("Failed to query GitHub API: Invalid Github token")]
1515
GithubTokenInvalid,
1616

17+
#[diagnostic(code(espup::toolchain::rust::query_github))]
18+
#[error("Failed to connect to GitHub API: {0}")]
19+
GithubConnectivityError(String),
20+
1721
#[diagnostic(code(espup::toolchain::http_error))]
1822
#[error("HTTP GET Error: {0}")]
1923
HttpError(String),

src/toolchain/mod.rs

Lines changed: 24 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -237,10 +237,11 @@ pub async fn install(args: InstallOpts, install_mode: InstallMode) -> Result<()>
237237
toolchain_version.clone()
238238
}
239239
} else {
240-
// Get the latest version of the Xtensa Rust toolchain. If that fails, return an error::GithubTokenInvalid
241-
XtensaRust::get_latest_version()
242-
.await
243-
.map_err(|_| Error::GithubTokenInvalid)?
240+
// Get the latest version of the Xtensa Rust toolchain
241+
XtensaRust::get_latest_version().await.map_err(|e| {
242+
warn!("Failed to get latest Xtensa Rust version: {}", e);
243+
e
244+
})?
244245
};
245246
let toolchain_dir = get_rustup_home().join("toolchains").join(args.name);
246247
let llvm: Llvm = Llvm::new(
@@ -382,27 +383,42 @@ pub fn github_query(url: &str) -> Result<serde_json::Value, Error> {
382383
.unwrap(),
383384
);
384385
}
386+
385387
let client = build_proxy_blocking_client()?;
388+
386389
let json: Result<serde_json::Value, Error> = retry(
387390
Fixed::from_millis(100).take(5),
388391
|| -> Result<serde_json::Value, Error> {
389-
let res = client.get(url).headers(headers.clone()).send()?.text()?;
392+
let response = client.get(url).headers(headers.clone()).send()?;
393+
let status = response.status();
394+
395+
if !status.is_success() {
396+
return Err(Error::HttpError(format!(
397+
"GitHub API returned status code: {}",
398+
status
399+
)));
400+
}
401+
402+
let res = response.text()?;
403+
404+
// Check for rate limiting response
390405
if res.contains(
391406
"https://docs.github.com/rest/overview/resources-in-the-rest-api#rate-limiting",
392407
) {
393408
return Err(Error::GithubRateLimit);
394409
}
395410

411+
// Check for authentication errors
396412
if res.contains("Bad credentials") {
397413
return Err(Error::GithubTokenInvalid);
398414
}
399415

400-
let json: serde_json::Value =
401-
serde_json::from_str(&res).map_err(|_| Error::SerializeJson)?;
402-
Ok(json)
416+
// Try to parse the JSON
417+
serde_json::from_str(&res).map_err(|_| Error::SerializeJson)
403418
},
404419
)
405420
.map_err(|err| err.error);
421+
406422
json
407423
}
408424

src/toolchain/rust.rs

Lines changed: 25 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -72,19 +72,35 @@ pub struct XtensaRust {
7272

7373
impl XtensaRust {
7474
/// Get the latest version of Xtensa Rust toolchain.
75-
pub async fn get_latest_version() -> Result<String> {
76-
let json = tokio::task::spawn_blocking(|| github_query(XTENSA_RUST_LATEST_API_URL))
75+
pub async fn get_latest_version() -> Result<String, Error> {
76+
debug!("Querying latest Xtensa Rust version from GitHub API");
77+
78+
// First, handle the spawn_blocking result
79+
let query_result = tokio::task::spawn_blocking(|| github_query(XTENSA_RUST_LATEST_API_URL))
7780
.await
78-
.unwrap()?;
79-
let mut version = json["tag_name"].to_string();
81+
.map_err(|e| {
82+
Error::GithubConnectivityError(format!("Failed to query GitHub API: {}", e))
83+
})?;
84+
85+
// Then handle the github_query result
86+
let json = query_result?;
87+
88+
if !json.is_object() || !json["tag_name"].is_string() {
89+
return Err(Error::SerializeJson);
90+
}
8091

92+
let mut version = json["tag_name"].to_string();
8193
version.retain(|c| c != 'v' && c != '"');
82-
let borrowed = version.clone();
83-
tokio::task::spawn_blocking(move || Self::parse_version(&borrowed))
94+
95+
// Validate the version format - handle both spawning and parsing errors
96+
let parse_task = tokio::task::spawn_blocking(move || Self::parse_version(&version))
8497
.await
85-
.expect("Join blocking task error")?;
86-
debug!("Latest Xtensa Rust version: {}", version);
87-
Ok(version)
98+
.map_err(|_| Error::SerializeJson)?;
99+
100+
let validated_version = parse_task?;
101+
102+
debug!("Latest Xtensa Rust version: {}", validated_version);
103+
Ok(validated_version)
88104
}
89105

90106
/// Create a new instance.

0 commit comments

Comments
 (0)