update/gupax: retry on failed bytes, add progress bar/spinner

This commit is contained in:
hinto-janaiyo 2022-10-27 11:52:18 -04:00
parent 6418a0ad2c
commit 8780b0684d
No known key found for this signature in database
GPG key ID: D7483F6CA27D1B1D
4 changed files with 58 additions and 30 deletions

View file

@ -56,7 +56,7 @@ pub const HUGEPAGES_1GB: bool = true;
// Gupax
pub const GUPAX_UPDATE: &'static str = "Check for update on Gupax, P2Pool, and XMRig via GitHub's API and upgrade automatically";
pub const GUPAX_AUTO_UPDATE: &'static str = "Automatically check for updates at startup";
pub const GUPAX_UPDATE_VIA_TOR: &'static str = "Update through the Tor network. Gupax has Tor embedded, a Tor system proxy is not required.";
pub const GUPAX_UPDATE_VIA_TOR: &'static str = "Update through the Tor network. Tor is embedded within Gupax; a Tor system proxy is not required";
pub const GUPAX_AUTO_NODE: &'static str = "Automatically ping the community Monero nodes and select the fastest at startup for P2Pool";
pub const GUPAX_ASK_BEFORE_QUIT: &'static str = "Ask before quitting if processes are still alive or if an update is in progress";
pub const GUPAX_SAVE_BEFORE_QUIT: &'static str = "Automatically save any changed settings before quitting";

View file

@ -35,27 +35,41 @@ impl Gupax {
// I have to pick one. This one seperates them though.
let height = height/6.0;
let width = width - SPACE;
let updating = *update.updating.lock().unwrap();
ui.vertical(|ui| {
ui.set_enabled(!*update.updating.lock().unwrap());
ui.set_enabled(!updating);
if ui.add_sized([width, height], egui::Button::new("Check for updates")).on_hover_text(GUPAX_UPDATE).clicked() {
update.path_p2pool = state.absolute_p2pool_path.display().to_string();
update.path_xmrig = state.absolute_xmrig_path.display().to_string();
update.tor = state.update_via_tor;
let u = Arc::new(Mutex::new(update.clone()));
let u = Arc::clone(&u);
let u2 = Arc::new(Mutex::new(update.clone()));
let u2 = Arc::clone(&u);
thread::spawn(move|| {
info!("Spawning update thread...");
let handle = Update::start(u, version);
info!("...........>");
match Update::start(u, version) {
Err(e) => {
info!("Update | {} ... FAIL", e);
*u2.lock().unwrap().msg.lock().unwrap() = MSG_FAILED.to_string();
*u2.lock().unwrap().updating.lock().unwrap() = false;
},
_ => {
info!("Update ... OK");
*u2.lock().unwrap().msg.lock().unwrap() = MSG_SUCCESS.to_string();
*u2.lock().unwrap().prog.lock().unwrap() = 100;
*u2.lock().unwrap().updating.lock().unwrap() = false;
},
}
});
}
});
ui.vertical(|ui| {
ui.set_enabled(*update.updating.lock().unwrap());
ui.set_enabled(updating);
let height = height/2.0;
let msg = format!("{}{}{}{}", *update.msg.lock().unwrap(), " ... ", *update.prog.lock().unwrap(), "%");
ui.add_sized([width, height], egui::Label::new(msg));
// let range = *update.prog.lock().unwrap() as f32 / 100.0;
if updating { ui.add_sized([width, height], egui::Spinner::new().size(height)); }
ui.add_sized([width, height], egui::ProgressBar::new(*update.prog.lock().unwrap() as f32 / 100.0));
});
});

View file

@ -582,6 +582,7 @@ impl eframe::App for App {
ui.style_mut().override_text_style = Some(egui::TextStyle::Body);
match self.tab {
Tab::About => {
info!("");
ui.add_space(10.0);
ui.vertical_centered(|ui| {
// Display [Gupax] banner at max, 1/4 the available length

View file

@ -163,7 +163,10 @@ const MSG_TMP: &'static str = "Creating temporary directory";
const MSG_TOR: &'static str = "Creating Tor+HTTPS client";
const MSG_HTTPS: &'static str = "Creating HTTPS client";
const MSG_METADATA: &'static str = "Fetching package metadata";
const MSG_ARCHIVE: &'static str = "Downloading packages";
const MSG_COMPARE: &'static str = "Compare package versions";
const MSG_DOWNLOAD: &'static str = "Downloading packages";
pub const MSG_SUCCESS: &'static str = "Update successful";
pub const MSG_FAILED: &'static str = "Update failed";
// These two are sequential and not async so no need for a constant message.
// The package in question will be known at runtime, so that will be printed.
@ -173,9 +176,10 @@ const MSG_ARCHIVE: &'static str = "Downloading packages";
//---------------------------------------------------------------------------------------------------- Update struct/impl
// Contains values needed during update
// Progress bar structure:
// 10% | Create tmp directory and pkg list
// 15% | Create Tor/HTTPS client
// 0% | Create tmp directory and pkg list
// 10% | Create Tor/HTTPS client
// 15% | Download Metadata (x3)
// 15% | Compare Versions (x3)
// 30% | Download Archive (x3)
// 15% | Extract (x3)
// 15% | Upgrade (x3)
@ -273,7 +277,6 @@ impl Update {
// so there will be some intermediate variables.
info!("Update | Init | {} ... {}%", MSG_TMP.to_string(), *update.lock().unwrap().prog.lock().unwrap());
let tmp_dir = Self::get_tmp_dir();
*update.lock().unwrap().prog.lock().unwrap() += 10;
// Make Pkg vector
let prog = update.lock().unwrap().prog.clone();
@ -294,7 +297,8 @@ impl Update {
let prog = *update.lock().unwrap().prog.lock().unwrap();
info!("Update | Init | {} ... {}%", *update.lock().unwrap().msg.lock().unwrap(), prog);
let client = Self::get_client(update.lock().unwrap().tor).await?;
*update.lock().unwrap().prog.lock().unwrap() += 15;
*update.lock().unwrap().prog.lock().unwrap() += 10;
info!("Update | Init ... OK");
// Loop for metadata
*update.lock().unwrap().msg.lock().unwrap() = MSG_METADATA.to_string();
@ -322,6 +326,7 @@ impl Update {
info!("Update | Metadata ... OK");
// Loop for version comparison
*update.lock().unwrap().msg.lock().unwrap() = MSG_COMPARE.to_string();
info!("Update | Compare | Starting version comparison...");
let prog = update.lock().unwrap().prog.clone();
let msg = update.lock().unwrap().msg.clone();
@ -354,10 +359,13 @@ impl Update {
}
}
}
*update.lock().unwrap().prog.lock().unwrap() += 5;
}
info!("Update | Compare ... OK");
// Loop for download
let mut handles: Vec<JoinHandle<()>> = vec![];
*update.lock().unwrap().msg.lock().unwrap() = MSG_DOWNLOAD.to_string();
info!("Update | Download | Starting download...");
for pkg in vec2.iter() {
// Clone data before async
@ -380,11 +388,10 @@ impl Update {
link = pkg.link_prefix.to_string() + &version + &pkg.link_suffix + &version + &pkg.link_extension;
}
info!("Update | Download | {} ... {}", pkg.name, link);
let request = Pkg::get_request(link)?;
let handle: JoinHandle<()> = tokio::spawn(async move {
match client {
ClientEnum::Tor(t) => Pkg::get_bytes(name, bytes, prog, t, request).await,
ClientEnum::Https(h) => Pkg::get_bytes(name, bytes, prog, h, request).await,
ClientEnum::Tor(t) => Pkg::get_bytes(name, bytes, prog, t, link).await,
ClientEnum::Https(h) => Pkg::get_bytes(name, bytes, prog, h, link).await,
};
});
handles.push(handle);
@ -411,7 +418,6 @@ impl Update {
}
info!("Update | Extract ... OK");
*update.lock().unwrap().updating.lock().unwrap() = false;
std::process::exit(0);
Ok(())
}
}
@ -491,8 +497,7 @@ impl Pkg {
pub async fn get_metadata<C>(name: Name, new_ver: Arc<Mutex<String>>, prog: Arc<Mutex<u8>>, client: Client<C>, link: String) -> Result<(), Error>
where C: hyper::client::connect::Connect + Clone + Send + Sync + 'static, {
// Retry [3] times if version is not [v*]
let mut n = 0;
while n < 3 {
for i in 0..3 {
let request = Pkg::get_request(link.clone())?;
let mut response = client.request(request).await?;
let body = hyper::body::to_bytes(response.body_mut()).await?;
@ -503,28 +508,36 @@ impl Pkg {
info!("Update | Metadata | {} {} ... {}%", name, body.tag_name, *prog.lock().unwrap());
return Ok(())
}
warn!("Update | Metadata | {} metadata fetch failed, retry [{}/3]...", name, n);
n += 1;
warn!("Update | Metadata | {} failed, retry [{}/3]...", name, i);
}
error!("Update | Metadata | {} metadata fetch failed", name);
Err(anyhow!("Metadata fetch failed"))
error!("Update | Metadata | {} failed", name);
Err(anyhow!("{} | Metadata fetch failed", name))
}
// Takes a [Request], fills the appropriate [Pkg]
// [bytes] field with the [Archive/Standalone]
pub async fn get_bytes<C>(name: Name, bytes: Arc<Mutex<bytes::Bytes>>, prog: Arc<Mutex<u8>>, client: Client<C>, request: Request<Body>) -> Result<(), anyhow::Error>
pub async fn get_bytes<C>(name: Name, bytes: Arc<Mutex<bytes::Bytes>>, prog: Arc<Mutex<u8>>, client: Client<C>, link: String) -> Result<(), anyhow::Error>
where C: hyper::client::connect::Connect + Clone + Send + Sync + 'static, {
// GitHub sends a 302 redirect, so we must follow
// the [Location] header... only if Reqwest had custom
// connectors so I didn't have to manually do this...
let response = client.request(request).await?;
let request = Self::get_request(response.headers().get(hyper::header::LOCATION).unwrap().to_str()?.to_string())?;
let response = client.request(request).await?;
let body = hyper::body::to_bytes(response.into_body()).await?;
*bytes.lock().unwrap() = body;
*prog.lock().unwrap() += 10;
info!("Update | Download | {} ... {}%", name, *prog.lock().unwrap());
Ok(())
// Also, retry [3] times if [Bytes] == empty
for i in 0..3 {
let request = Pkg::get_request(link.clone())?;
let response = client.request(request).await?;
let request = Self::get_request(response.headers().get(hyper::header::LOCATION).unwrap().to_str()?.to_string())?;
let response = client.request(request).await?;
let body = hyper::body::to_bytes(response.into_body()).await?;
if ! body.is_empty() {
*bytes.lock().unwrap() = body;
*prog.lock().unwrap() += 10;
info!("Update | Download | {} ... {}%", name, *prog.lock().unwrap());
return Ok(())
}
warn!("Update | Metadata | {} download bytes are empty, retry [{}/3]...", name, i);
}
error!("Update | Download | {} failed", name);
Err(anyhow!("{} | Download failed", name))
}
}