diff --git a/.vscode/launch.json b/.vscode/launch.json index 4b0c159..8ab6d4d 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -21,7 +21,7 @@ "type": "lldb", "request": "launch", "program": "${workspaceRoot}/target/debug/tmpo", - "args": ["init", "-r", "https://github.com/perryrh0dan/templates", "-t", "typescript"], + // "args": ["init", "-r", "https://github.com/perryrh0dan/templates", "-t", "typescript"], // "args": ["repository", "remove"], "cwd": "${workspaceRoot}", } diff --git a/Cargo.lock b/Cargo.lock index 135b230..7790e08 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1539,7 +1539,7 @@ dependencies = [ [[package]] name = "tmpo" -version = "1.2.3" +version = "1.3.0" dependencies = [ "clap 3.0.0-beta.1 (git+https://github.com/clap-rs/clap/)", "colored 1.9.3 (registry+https://github.com/rust-lang/crates.io-index)", diff --git a/Cargo.toml b/Cargo.toml index e4df971..5873113 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "tmpo" -version = "1.2.3" +version = "1.3.0" authors = ["Thomas Pöhlmann "] edition = "2018" diff --git a/src/action/default/update.rs b/src/action/default/update.rs index 276f7f7..c084a7e 100644 --- a/src/action/default/update.rs +++ b/src/action/default/update.rs @@ -1,103 +1,20 @@ -use std::fs::File; -use log; - -use crate::out; use crate::cli::input::confirm; +use crate::update; -extern crate tar; -extern crate self_update; -extern crate flate2; -use clap::{crate_version}; -use tar::Archive; -use flate2::read::GzDecoder; - -pub fn update(interactive: bool) { - let releases = self_update::backends::github::ReleaseList::configure() - .repo_owner("perryrh0dan") - .repo_name("tmpo") - .build().unwrap() - .fetch().unwrap(); - - // check version - let version = crate_version!(); - if releases[0].version == version { - if interactive { out::info::no_app_update() }; - return; - } - - println!("Checking target-arch: {}", &self_update::get_target()); - println!("Checking current version: {}", &version); - println!("New release found! {} --> {}", &version, &releases[0].version); - - let asset = match releases[0].asset_for(&self_update::get_target()) { - Some(value) => value, - None => { - println!("New release is not compatible"); - return; - } +pub fn update() { + let asset = match update::check_version() { + Some(asset) => asset, + None => return, }; - println!("New release is compatible"); - println!(); - // user input - if interactive { - let update = confirm("The new release will be downloaded/extraced and the existing binary will be replaced. Do you want to continue?"); - if !update { + let update = confirm("The new release will be downloaded/extraced and the existing binary will be replaced.\nDo you want to continue?"); + if !update { return; - } } - let tmp_dir = tempfile::Builder::new().tempdir_in(::std::env::current_dir().unwrap()).unwrap(); - let tmp_tarball_path = tmp_dir.path().join(&asset.name); - std::fs::File::create(&tmp_tarball_path).unwrap(); - let tmp_tarball = std::fs::OpenOptions::new().create(true).append(true).open(&tmp_tarball_path).unwrap(); - - // download asset - let mut headers = reqwest::header::HeaderMap::new(); - headers.insert(reqwest::header::ACCEPT, "application/octet-stream".parse().unwrap()); - match self_update::Download::from_url(&asset.download_url).show_progress(true).set_headers(headers).download_to(&tmp_tarball) { - Ok(_) => (), - Err(error) => println!("{}", error) - }; - - // extract tar.gz archive - let tar_gz = File::open(tmp_tarball_path).unwrap(); - let tar = GzDecoder::new(tar_gz); - let mut archive = Archive::new(tar); - match archive.unpack(&tmp_dir) { - Ok(_) => (), - Err(error) => println!("{}", error) - }; - - // move file to current executable - let bin_name = "tmpo"; - let tmp_file = tmp_dir.path().join("replacement_tmp"); - let bin_path = tmp_dir.path().join(bin_name); - let success = match self_update::Move::from_source(&bin_path) - .replace_using_temp(&tmp_file) - .to_dest(&::std::env::current_exe().unwrap()) { - Ok(_) => ( true ), - Err(error) => match error { - self_update::errors::Error::Io { .. } => { - out::error::selfupdate_no_permission(); - false - }, - _ => { - log::error!("{}", error); - false - } - } - }; - - // remove tmp folder - match std::fs::remove_dir_all(tmp_dir) { - Ok(_) => (), - Err(error) => println!("{}", error), - }; + println!(); - if success { - out::success::app_updated(); - } + update::update(asset); } diff --git a/src/main.rs b/src/main.rs index 2187b66..59b516f 100644 --- a/src/main.rs +++ b/src/main.rs @@ -7,6 +7,7 @@ mod meta; mod out; mod repository; mod template; +mod update; mod utils; use clap::{crate_version, App, AppSettings, Arg}; @@ -128,7 +129,7 @@ fn main() { action::default::init::init(&config, init_matches); } ("update", Some(_update_matches)) => { - action::default::update::update(true); + action::default::update::update(); } ("repository", Some(repository_matches)) => { match repository_matches.subcommand() { diff --git a/src/update/mod.rs b/src/update/mod.rs new file mode 100644 index 0000000..bd352f1 --- /dev/null +++ b/src/update/mod.rs @@ -0,0 +1,143 @@ +use log; +use std::fs::File; + +use crate::out; + +extern crate flate2; +extern crate self_update; +extern crate tar; +use clap::crate_version; +use flate2::read::GzDecoder; +use tar::Archive; + +#[cfg(windows)] +const BIN_NAME: &str = "tmpo.exe"; + +#[cfg(not(windows))] +const BIN_NAME: &str = "tmpo"; + +pub fn check_version() -> Option { + log::info!("Fetch release list"); + let releases = self_update::backends::github::ReleaseList::configure() + .repo_owner("perryrh0dan") + .repo_name("tmpo") + .build() + .unwrap() + .fetch() + .unwrap(); + + // check version + let version = crate_version!(); + if releases[0].version == version { + log::info!("No update"); + out::info::no_app_update(); + return None; + } + + println!("Checking target-arch: {}", &self_update::get_target()); + println!("Checking current version: {}", &version); + println!( + "New release found! {} --> {}", + &version, &releases[0].version + ); + + log::info!( + "New release found! {} --> {}", + &version, + &releases[0].version + ); + + let asset = match releases[0].asset_for(&self_update::get_target()) { + Some(value) => value, + None => { + println!("New release is not compatible"); + return None; + } + }; + + println!("New release is compatible"); + println!(); + + return Some(asset); +} + +pub fn update(asset: self_update::update::ReleaseAsset) { + let tmp_dir = tempfile::Builder::new() + .tempdir_in(::std::env::current_dir().unwrap()) + .unwrap(); + let tmp_tarball_path = tmp_dir.path().join(&asset.name); + std::fs::File::create(&tmp_tarball_path).unwrap(); + let tmp_tarball = std::fs::OpenOptions::new() + .create(true) + .append(true) + .open(&tmp_tarball_path) + .unwrap(); + + // download asset + let mut headers = reqwest::header::HeaderMap::new(); + headers.insert( + reqwest::header::ACCEPT, + "application/octet-stream".parse().unwrap(), + ); + + log::info!( + "Download asset to: {}", + &tmp_tarball_path.to_owned().to_str().unwrap() + ); + match self_update::Download::from_url(&asset.download_url) + .show_progress(true) + .set_headers(headers) + .download_to(&tmp_tarball) + { + Ok(_) => (), + Err(error) => { + log::error!("{}", error); + return; + } + }; + + // extract tar.gz archive + log::info!("Extract tar.gz archive"); + let tar_gz = File::open(tmp_tarball_path).unwrap(); + let tar = GzDecoder::new(tar_gz); + let mut archive = Archive::new(tar); + match archive.unpack(&tmp_dir) { + Ok(_) => (), + Err(error) => { + log::error!("{}", error); + return; + } + }; + + // move file to current executable + let tmp_file = tmp_dir.path().join("replacement_tmp"); + let bin_path = tmp_dir.path().join(BIN_NAME); + let dest_path = std::env::current_exe().unwrap(); + + log::info!( + "Move {} to {}", + bin_path.to_owned().to_str().unwrap(), + dest_path.to_owned().to_str().unwrap() + ); + match self_update::Move::from_source(&bin_path) + .replace_using_temp(&tmp_file) + .to_dest(&dest_path) + { + Ok(_) => (), + Err(error) => { + log::error!("{}", error); + match error { + self_update::errors::Error::Io { .. } => { + out::error::selfupdate_no_permission(); + return; + } + _ => { + out::error::unknown(); + return; + } + } + } + }; + + out::success::app_updated(); +}