-
Notifications
You must be signed in to change notification settings - Fork 0
[CLI] Retry Download #183
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[CLI] Retry Download #183
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||
|---|---|---|---|---|---|---|---|---|
| @@ -1,6 +1,8 @@ | ||||||||
| use crate::formatter::{error_without_trace, info, success}; | ||||||||
| use bytes::Bytes; | ||||||||
| use log::error; | ||||||||
| use reqwest::header::{ACCEPT, USER_AGENT}; | ||||||||
| use reqwest::{Client, Response}; | ||||||||
| use serde::Deserialize; | ||||||||
| use std::fs; | ||||||||
| use std::fs::File; | ||||||||
|
|
@@ -17,7 +19,6 @@ struct Release { | |||||||
| struct Asset { | ||||||||
| name: String, | ||||||||
| browser_download_url: String, | ||||||||
| size: u64, | ||||||||
| } | ||||||||
|
|
||||||||
| pub async fn handle_download(tag: Option<String>, features: Option<Vec<String>>) { | ||||||||
|
|
@@ -26,13 +27,7 @@ pub async fn handle_download(tag: Option<String>, features: Option<Vec<String>>) | |||||||
|
|
||||||||
| // Download the definitions | ||||||||
| info("Starting download process...".to_string()); | ||||||||
| let bytes = match download_definitions_as_bytes(tag).await { | ||||||||
| Some(bytes) => bytes, | ||||||||
| None => { | ||||||||
| error_without_trace(String::from("Download failed.")); | ||||||||
| return; | ||||||||
| } | ||||||||
| }; | ||||||||
| let bytes = download_definitions_as_bytes(tag).await; | ||||||||
|
|
||||||||
| // Extract the zip file | ||||||||
| convert_bytes_to_folder(bytes, zip_path).await; | ||||||||
|
|
@@ -52,7 +47,8 @@ pub async fn handle_download(tag: Option<String>, features: Option<Vec<String>>) | |||||||
| )); | ||||||||
| } | ||||||||
|
|
||||||||
| async fn download_definitions_as_bytes(tag: Option<String>) -> Option<bytes::Bytes> { | ||||||||
| async fn download_definitions_as_bytes(tag: Option<String>) -> Bytes { | ||||||||
| let max_retries = 3; | ||||||||
| let client = reqwest::Client::new(); | ||||||||
|
|
||||||||
| let url = match tag { | ||||||||
|
|
@@ -66,23 +62,46 @@ async fn download_definitions_as_bytes(tag: Option<String>) -> Option<bytes::Byt | |||||||
| } | ||||||||
| }; | ||||||||
|
|
||||||||
| let release_request = match client | ||||||||
| .get(url) | ||||||||
| .header(USER_AGENT, "code0-definition-cli") | ||||||||
| .header(ACCEPT, "application/vnd.github+json") | ||||||||
| .send() | ||||||||
| .await | ||||||||
| { | ||||||||
| Ok(response) => { | ||||||||
| if response.status().is_success() { | ||||||||
| response | ||||||||
| } else { | ||||||||
| return None; | ||||||||
| } | ||||||||
| async fn download_release(client: &Client, url: String) -> Response { | ||||||||
| match client | ||||||||
| .get(url) | ||||||||
| .header(USER_AGENT, "code0-definition-cli") | ||||||||
| .header(ACCEPT, "application/vnd.github+json") | ||||||||
| .send() | ||||||||
| .await | ||||||||
| { | ||||||||
| Ok(r) => r, | ||||||||
| Err(e) => panic!("Request failed: {:?}", e), | ||||||||
| } | ||||||||
| Err(e) => { | ||||||||
| panic!("Request failed: {}", e); | ||||||||
| } | ||||||||
|
|
||||||||
| let mut retries = 0; | ||||||||
| let mut result = None; | ||||||||
| let mut succeeded = false; | ||||||||
|
|
||||||||
| while !succeeded { | ||||||||
| let release_request = download_release(&client, url.clone()).await; | ||||||||
| if release_request.status().is_success() { | ||||||||
| succeeded = true; | ||||||||
| result = Some(release_request); | ||||||||
| } else { | ||||||||
| if retries >= max_retries { | ||||||||
| panic!("Reached max retries while downloading release.") | ||||||||
|
||||||||
| panic!("Reached max retries while downloading release.") | |
| panic!("Reached max retries while downloading release") |
raphael-goetz marked this conversation as resolved.
Show resolved
Hide resolved
Copilot
AI
Dec 19, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The retry loop performs immediate retries without any delay or backoff between attempts. This can overwhelm the server with rapid successive requests and may violate rate limits. Consider adding exponential backoff or at least a small delay between retry attempts to be more respectful of the GitHub API and increase the likelihood of success.
Copilot
AI
Dec 19, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The variable 'result' is initialized as None and only set to Some when succeeded is true, but the loop exits when succeeded is true. This means the match on line 102-105 will never encounter a None case - it's defensive code that can never execute. Consider removing the Option wrapper and the redundant match, or restructure the loop to make the logic clearer.
raphael-goetz marked this conversation as resolved.
Show resolved
Hide resolved
Copilot
AI
Dec 19, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Similar to the release download, the asset download retry loop has no delay between attempts. This can result in rapid-fire requests that may trigger rate limiting or be rejected by the server. Adding backoff between retries would improve reliability.
Copilot
AI
Dec 19, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Similar to the release download retry logic, 'asset_result' is initialized as None and only set to Some when asset_success is true, making the None case in the match at lines 165-168 unreachable. This defensive code adds unnecessary complexity.
Copilot
AI
Dec 19, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The panic message starts with "Warning:" which is contradictory - panics are fatal errors, not warnings. If creating the parent directory is truly optional or can be skipped, use error_without_trace or just log the error instead of panicking. If it's a fatal error, remove the "Warning:" prefix.
Copilot
AI
Dec 19, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The panic message starts with "Warning:" which is contradictory - panics are fatal errors, not warnings. If extraction failure for individual files is non-fatal, use error_without_trace and continue processing other files. If it's a fatal error that should stop the program, remove the "Warning:" prefix.
Copilot
AI
Dec 19, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The panic message starts with "Warning:" which is contradictory - panics are fatal errors, not warnings. If this is truly a warning that should be logged but not cause the application to crash, use error_without_trace instead of panic (as done on line 282). If it's a fatal error that should panic, remove the "Warning:" prefix.
| panic!("Warning: Failed to read directory entry {:?}", e); | |
| error_without_trace(format!("Warning: Failed to read directory entry {:?}", e)); | |
| return; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The change from returning Option and gracefully handling failures to panicking on errors represents a significant regression in error handling. The previous code allowed users to receive a friendly error message and continue, while the new code will cause the entire application to crash with a panic. This is particularly problematic for a CLI tool where users expect graceful error messages. Consider restoring the Option return type or using Result to allow proper error propagation and user-friendly error messages.