From 7205a94ec3e0fba3701df2c9751336c1a5b62213 Mon Sep 17 00:00:00 2001 From: Avery Harnish Date: Fri, 3 Apr 2020 14:00:00 -0500 Subject: [PATCH 01/12] feat: message if wrangler needs updating --- Cargo.lock | 1 + Cargo.toml | 1 + src/main.rs | 19 ++++- src/settings/global_user.rs | 9 ++- src/{util.rs => util/block.rs} | 0 src/util/mod.rs | 5 ++ src/util/version.rs | 127 +++++++++++++++++++++++++++++++++ 7 files changed, 159 insertions(+), 3 deletions(-) rename src/{util.rs => util/block.rs} (100%) create mode 100644 src/util/mod.rs create mode 100644 src/util/version.rs diff --git a/Cargo.lock b/Cargo.lock index 927c3add2..e02d75664 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2811,6 +2811,7 @@ dependencies = [ "rand 0.6.5", "regex", "reqwest", + "semver", "serde 1.0.105", "serde_json", "serde_with", diff --git a/Cargo.toml b/Cargo.toml index e84b31490..f8cad537a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -58,6 +58,7 @@ uuid = { version = "0.8", features = ["v4"] } which = "2.0.1" ws = "0.9.0" twox-hash = "1.5.0" +semver = "0.9.0" [dev-dependencies] assert_cmd = "0.11.1" diff --git a/src/main.rs b/src/main.rs index 9ff6bf6b6..db08f4e23 100644 --- a/src/main.rs +++ b/src/main.rs @@ -21,9 +21,11 @@ use wrangler::settings; use wrangler::settings::global_user::GlobalUser; use wrangler::settings::toml::TargetType; use wrangler::terminal::{emoji, interactive, message}; +use wrangler::util::background_check_for_updates; fn main() -> Result<(), ExitFailure> { env_logger::init(); + let latest_version_receiver = background_check_for_updates(); if let Ok(me) = env::current_exe() { // If we're actually running as the installer then execute our // self-installation, otherwise just continue as usual. @@ -36,7 +38,22 @@ fn main() -> Result<(), ExitFailure> { installer::install(); } } - Ok(run()?) + run()?; + if let Ok(latest_version) = latest_version_receiver.try_recv() { + let latest_version = style(latest_version).green().bold(); + let new_version_available = format!( + "A new version of Wrangler ({}) is available!", + latest_version + ); + let update_message = "You can learn more about updating here:".to_string(); + let update_docs_url = + style("https://developers.cloudflare.com/workers/quickstart#updating-the-cli") + .blue() + .bold() + .to_string(); + message::billboard(&[new_version_available, update_message, update_docs_url].join("\n")); + } + Ok(()) } fn run() -> Result<(), failure::Error> { diff --git a/src/settings/global_user.rs b/src/settings/global_user.rs index 8ff5e112f..d4fcc7ae5 100644 --- a/src/settings/global_user.rs +++ b/src/settings/global_user.rs @@ -130,8 +130,8 @@ impl From for Credentials { } } -pub fn get_global_config_path() -> Result { - let home_dir = if let Ok(value) = env::var("WRANGLER_HOME") { +pub fn get_wrangler_home_dir() -> Result { + let config_dir = if let Ok(value) = env::var("WRANGLER_HOME") { log::info!("Using $WRANGLER_HOME: {}", value); Path::new(&value).to_path_buf() } else { @@ -140,6 +140,11 @@ pub fn get_global_config_path() -> Result { .expect("Could not find home directory") .join(".wrangler") }; + Ok(config_dir) +} + +pub fn get_global_config_path() -> Result { + let home_dir = get_wrangler_home_dir()?; let global_config_file = home_dir.join("config").join(DEFAULT_CONFIG_FILE_NAME); log::info!("Using global config file: {}", global_config_file.display()); Ok(global_config_file) diff --git a/src/util.rs b/src/util/block.rs similarity index 100% rename from src/util.rs rename to src/util/block.rs diff --git a/src/util/mod.rs b/src/util/mod.rs new file mode 100644 index 000000000..1c2574961 --- /dev/null +++ b/src/util/mod.rs @@ -0,0 +1,5 @@ +mod block; +mod version; + +pub use block::GuardedCommand; +pub use version::background_check_for_updates; diff --git a/src/util/version.rs b/src/util/version.rs new file mode 100644 index 000000000..36ca9d4fe --- /dev/null +++ b/src/util/version.rs @@ -0,0 +1,127 @@ +use std::fs; +use std::str::FromStr; +use std::sync::mpsc; +use std::thread; +use std::time::SystemTime; + +use crate::settings::global_user::get_wrangler_home_dir; + +use reqwest::header::USER_AGENT; +use semver::Version; +use serde::{Deserialize, Serialize}; + +const ONE_DAY: u64 = 60 * 60 * 24; + +#[derive(Debug)] +pub struct WranglerVersion { + /// currently installed version of wrangler + pub current: Version, + + /// latest version of wrangler on crates.io + pub latest: Version, +} + +#[derive(Clone, Debug, Deserialize, Serialize)] +struct LastCheckedVersion { + /// latest version as of last time we checked + latest_version: String, + + /// the last time we asked crates.io for the latest version + last_checked: SystemTime, +} + +impl FromStr for LastCheckedVersion { + type Err = toml::de::Error; + + fn from_str(serialized_toml: &str) -> Result { + toml::from_str(serialized_toml) + } +} + +fn get_installed_version() -> Result { + let version = option_env!("CARGO_PKG_VERSION").unwrap_or_else(|| "unknown"); + let parsed_version = Version::parse(version)?; + Ok(parsed_version) +} + +fn check_wrangler_versions() -> Result { + let current = get_installed_version()?; + let latest = get_latest_version(¤t.to_string())?; + Ok(WranglerVersion { current, latest }) +} + +fn get_latest_version(installed_version: &str) -> Result { + let config_dir = get_wrangler_home_dir()?; + let version_file = config_dir.join("version.toml"); + let current_time = SystemTime::now(); + let latest_version = match fs::read_to_string(&version_file) { + Ok(contents) => { + let last_checked_version = LastCheckedVersion::from_str(&contents)?; + let time_since_last_checked = + current_time.duration_since(last_checked_version.last_checked)?; + if time_since_last_checked.as_secs() < ONE_DAY { + let version = Version::parse(&last_checked_version.latest_version)?; + Some(version) + } else { + None + } + } + Err(_) => None, + }; + match latest_version { + Some(latest_version) => Ok(latest_version), + None => { + let latest_version = get_latest_version_from_api(installed_version)?; + let updated_file_contents = toml::to_string(&LastCheckedVersion { + latest_version: latest_version.to_string(), + last_checked: current_time, + })?; + fs::write(&version_file, updated_file_contents)?; + Ok(latest_version) + } + } +} + +fn get_latest_version_from_api(installed_version: &str) -> Result { + let url = "https://crates.io/api/v1/crates/wrangler"; + let user_agent = format!( + "wrangler/{} ({})", + installed_version, + env!("CARGO_PKG_REPOSITORY") + ); + let response = reqwest::blocking::Client::new() + .get(url) + .header(USER_AGENT, user_agent) + .send()? + .error_for_status()?; + let text = response.text()?; + let crt: ApiResponse = serde_json::from_str(&text)?; + let version = Version::parse(&crt.info.max_version)?; + Ok(version) +} + +#[derive(Deserialize, Debug)] +struct ApiResponse { + #[serde(rename = "crate")] + info: CrateInformation, +} + +#[derive(Deserialize, Debug)] +struct CrateInformation { + max_version: String, +} + +pub fn background_check_for_updates() -> mpsc::Receiver { + let (sender, receiver) = mpsc::channel(); + + let _detached_thread = thread::spawn(move || match check_wrangler_versions() { + Ok(wrangler_versions) => { + if wrangler_versions.current != wrangler_versions.latest { + let _ = sender.send(wrangler_versions.latest); + } + } + Err(e) => log::debug!("could not determine if update is needed:\n{}", e), + }); + + receiver +} From 06eb5453ed662edfaa04da043e162944339a72e1 Mon Sep 17 00:00:00 2001 From: jspspike Date: Tue, 26 May 2020 15:45:02 -0500 Subject: [PATCH 02/12] Made new version only check once a day --- src/version/version.rs | 61 +++++++++++++++++++++++++----------------- 1 file changed, 37 insertions(+), 24 deletions(-) diff --git a/src/version/version.rs b/src/version/version.rs index 36ca9d4fe..d6b7fe8d3 100644 --- a/src/version/version.rs +++ b/src/version/version.rs @@ -1,4 +1,5 @@ use std::fs; +use std::path::PathBuf; use std::str::FromStr; use std::sync::mpsc; use std::thread; @@ -45,41 +46,53 @@ fn get_installed_version() -> Result { } fn check_wrangler_versions() -> Result { - let current = get_installed_version()?; - let latest = get_latest_version(¤t.to_string())?; - Ok(WranglerVersion { current, latest }) -} - -fn get_latest_version(installed_version: &str) -> Result { let config_dir = get_wrangler_home_dir()?; let version_file = config_dir.join("version.toml"); let current_time = SystemTime::now(); - let latest_version = match fs::read_to_string(&version_file) { - Ok(contents) => { - let last_checked_version = LastCheckedVersion::from_str(&contents)?; + + let current = get_installed_version()?; + + let latest = match get_version_disk(&version_file)? { + Some(last_checked_version) => { let time_since_last_checked = current_time.duration_since(last_checked_version.last_checked)?; if time_since_last_checked.as_secs() < ONE_DAY { - let version = Version::parse(&last_checked_version.latest_version)?; - Some(version) + current.clone() } else { - None + Version::parse(&last_checked_version.latest_version)? } } - Err(_) => None, + None => get_latest_version(¤t.to_string(), &version_file, current_time)?, }; - match latest_version { - Some(latest_version) => Ok(latest_version), - None => { - let latest_version = get_latest_version_from_api(installed_version)?; - let updated_file_contents = toml::to_string(&LastCheckedVersion { - latest_version: latest_version.to_string(), - last_checked: current_time, - })?; - fs::write(&version_file, updated_file_contents)?; - Ok(latest_version) + + Ok(WranglerVersion { current, latest }) +} + +fn get_version_disk(version_file: &PathBuf) -> Result, failure::Error> { + let local_version = match fs::read_to_string(&version_file) { + Ok(contents) => { + let last_checked_version = LastCheckedVersion::from_str(&contents)?; + + Some(last_checked_version) } - } + Err(_) => None, + }; + + Ok(local_version) +} + +fn get_latest_version( + installed_version: &str, + version_file: &PathBuf, + current_time: SystemTime, +) -> Result { + let latest_version = get_latest_version_from_api(installed_version)?; + let updated_file_contents = toml::to_string(&LastCheckedVersion { + latest_version: latest_version.to_string(), + last_checked: current_time, + })?; + fs::write(&version_file, updated_file_contents)?; + Ok(latest_version) } fn get_latest_version_from_api(installed_version: &str) -> Result { From bb027905693263074d56273ac29d353d197e07be Mon Sep 17 00:00:00 2001 From: jspspike Date: Tue, 26 May 2020 15:49:02 -0500 Subject: [PATCH 03/12] Added comments for checking version --- src/version/version.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/version/version.rs b/src/version/version.rs index d6b7fe8d3..1b95d0eb4 100644 --- a/src/version/version.rs +++ b/src/version/version.rs @@ -53,10 +53,12 @@ fn check_wrangler_versions() -> Result { let current = get_installed_version()?; let latest = match get_version_disk(&version_file)? { + // If version.toml doesn't exist, fetch latest version Some(last_checked_version) => { let time_since_last_checked = current_time.duration_since(last_checked_version.last_checked)?; if time_since_last_checked.as_secs() < ONE_DAY { + // If checked version within last day, current and latest will be the same current.clone() } else { Version::parse(&last_checked_version.latest_version)? From 2b67a5c5c55e8c3548071292c5fa9cab582eea6a Mon Sep 17 00:00:00 2001 From: jspspike Date: Tue, 26 May 2020 16:19:34 -0500 Subject: [PATCH 04/12] Passed bool instead of setting latest and current to the same thing --- src/version/version.rs | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/version/version.rs b/src/version/version.rs index 1b95d0eb4..92e25720f 100644 --- a/src/version/version.rs +++ b/src/version/version.rs @@ -45,29 +45,28 @@ fn get_installed_version() -> Result { Ok(parsed_version) } -fn check_wrangler_versions() -> Result { +fn check_wrangler_versions() -> Result<(WranglerVersion, bool), failure::Error> { let config_dir = get_wrangler_home_dir()?; let version_file = config_dir.join("version.toml"); let current_time = SystemTime::now(); + let mut checked = false; let current = get_installed_version()?; let latest = match get_version_disk(&version_file)? { - // If version.toml doesn't exist, fetch latest version Some(last_checked_version) => { let time_since_last_checked = current_time.duration_since(last_checked_version.last_checked)?; if time_since_last_checked.as_secs() < ONE_DAY { - // If checked version within last day, current and latest will be the same - current.clone() - } else { - Version::parse(&last_checked_version.latest_version)? + checked = true; } + Version::parse(&last_checked_version.latest_version)? } + // If version.toml doesn't exist, fetch latest version None => get_latest_version(¤t.to_string(), &version_file, current_time)?, }; - Ok(WranglerVersion { current, latest }) + Ok((WranglerVersion { current, latest }, checked)) } fn get_version_disk(version_file: &PathBuf) -> Result, failure::Error> { @@ -130,8 +129,9 @@ pub fn background_check_for_updates() -> mpsc::Receiver { let (sender, receiver) = mpsc::channel(); let _detached_thread = thread::spawn(move || match check_wrangler_versions() { - Ok(wrangler_versions) => { - if wrangler_versions.current != wrangler_versions.latest { + Ok(version) => { + let (wrangler_versions, checked) = version; + if !checked && (wrangler_versions.current != wrangler_versions.latest) { let _ = sender.send(wrangler_versions.latest); } } From 7c06122efa0f234b662959dabe801e760bfc54b3 Mon Sep 17 00:00:00 2001 From: jspspike Date: Tue, 26 May 2020 17:51:12 -0500 Subject: [PATCH 05/12] Added checked to WranglerVersion struct and added more comments --- src/version/version.rs | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/src/version/version.rs b/src/version/version.rs index 92e25720f..d6e36fc9c 100644 --- a/src/version/version.rs +++ b/src/version/version.rs @@ -20,6 +20,9 @@ pub struct WranglerVersion { /// latest version of wrangler on crates.io pub latest: Version, + + /// set to true if wrangler version has been checked within a day + pub checked: bool, } #[derive(Clone, Debug, Deserialize, Serialize)] @@ -45,7 +48,7 @@ fn get_installed_version() -> Result { Ok(parsed_version) } -fn check_wrangler_versions() -> Result<(WranglerVersion, bool), failure::Error> { +fn check_wrangler_versions() -> Result { let config_dir = get_wrangler_home_dir()?; let version_file = config_dir.join("version.toml"); let current_time = SystemTime::now(); @@ -57,6 +60,7 @@ fn check_wrangler_versions() -> Result<(WranglerVersion, bool), failure::Error> Some(last_checked_version) => { let time_since_last_checked = current_time.duration_since(last_checked_version.last_checked)?; + if time_since_last_checked.as_secs() < ONE_DAY { checked = true; } @@ -66,9 +70,14 @@ fn check_wrangler_versions() -> Result<(WranglerVersion, bool), failure::Error> None => get_latest_version(¤t.to_string(), &version_file, current_time)?, }; - Ok((WranglerVersion { current, latest }, checked)) + Ok(WranglerVersion { + current, + latest, + checked, + }) } +/// Reads version out of version file, is `None` if file does not exist fn get_version_disk(version_file: &PathBuf) -> Result, failure::Error> { let local_version = match fs::read_to_string(&version_file) { Ok(contents) => { @@ -129,9 +138,11 @@ pub fn background_check_for_updates() -> mpsc::Receiver { let (sender, receiver) = mpsc::channel(); let _detached_thread = thread::spawn(move || match check_wrangler_versions() { - Ok(version) => { - let (wrangler_versions, checked) = version; - if !checked && (wrangler_versions.current != wrangler_versions.latest) { + Ok(wrangler_versions) => { + // If the wrangler version has not been checked within the last day and the versions + // are different, print out an update message + if !wrangler_versions.checked && (wrangler_versions.current != wrangler_versions.latest) + { let _ = sender.send(wrangler_versions.latest); } } From e1153fe557fb53965ea25e609653daef6e5f4cb2 Mon Sep 17 00:00:00 2001 From: jspspike Date: Wed, 27 May 2020 10:07:12 -0500 Subject: [PATCH 06/12] Changed get_version_disk to return an Option and added is_outdated --- src/version/version.rs | 28 ++++++++++++++++------------ 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/src/version/version.rs b/src/version/version.rs index d6e36fc9c..5e8008c7c 100644 --- a/src/version/version.rs +++ b/src/version/version.rs @@ -25,6 +25,12 @@ pub struct WranglerVersion { pub checked: bool, } +impl WranglerVersion { + pub fn is_outdated(&self) -> bool { + return self.checked; + } +} + #[derive(Clone, Debug, Deserialize, Serialize)] struct LastCheckedVersion { /// latest version as of last time we checked @@ -56,7 +62,7 @@ fn check_wrangler_versions() -> Result { let mut checked = false; let current = get_installed_version()?; - let latest = match get_version_disk(&version_file)? { + let latest = match get_version_disk(&version_file) { Some(last_checked_version) => { let time_since_last_checked = current_time.duration_since(last_checked_version.last_checked)?; @@ -78,17 +84,14 @@ fn check_wrangler_versions() -> Result { } /// Reads version out of version file, is `None` if file does not exist -fn get_version_disk(version_file: &PathBuf) -> Result, failure::Error> { - let local_version = match fs::read_to_string(&version_file) { - Ok(contents) => { - let last_checked_version = LastCheckedVersion::from_str(&contents)?; - - Some(last_checked_version) - } +fn get_version_disk(version_file: &PathBuf) -> Option { + match fs::read_to_string(&version_file) { + Ok(contents) => match LastCheckedVersion::from_str(&contents) { + Ok(last_checked_version) => Some(last_checked_version), + Err(_) => None, + }, Err(_) => None, - }; - - Ok(local_version) + } } fn get_latest_version( @@ -141,7 +144,8 @@ pub fn background_check_for_updates() -> mpsc::Receiver { Ok(wrangler_versions) => { // If the wrangler version has not been checked within the last day and the versions // are different, print out an update message - if !wrangler_versions.checked && (wrangler_versions.current != wrangler_versions.latest) + if !wrangler_versions.is_outdated() + && (wrangler_versions.current != wrangler_versions.latest) { let _ = sender.send(wrangler_versions.latest); } From cae858406a0adcd1d4c84d0834d764f1807df2a9 Mon Sep 17 00:00:00 2001 From: jspspike Date: Wed, 27 May 2020 12:09:42 -0500 Subject: [PATCH 07/12] Made check happen in is_outdated --- src/version/version.rs | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/src/version/version.rs b/src/version/version.rs index 5e8008c7c..39610ec85 100644 --- a/src/version/version.rs +++ b/src/version/version.rs @@ -27,7 +27,7 @@ pub struct WranglerVersion { impl WranglerVersion { pub fn is_outdated(&self) -> bool { - return self.checked; + return !self.checked && (self.current != self.latest); } } @@ -86,10 +86,12 @@ fn check_wrangler_versions() -> Result { /// Reads version out of version file, is `None` if file does not exist fn get_version_disk(version_file: &PathBuf) -> Option { match fs::read_to_string(&version_file) { - Ok(contents) => match LastCheckedVersion::from_str(&contents) { - Ok(last_checked_version) => Some(last_checked_version), - Err(_) => None, - }, + Ok(contents) => { + match LastCheckedVersion::from_str(&contents) { + Ok(last_checked_version) => Some(last_checked_version), + Err(_) => None, + } + } Err(_) => None, } } @@ -144,8 +146,7 @@ pub fn background_check_for_updates() -> mpsc::Receiver { Ok(wrangler_versions) => { // If the wrangler version has not been checked within the last day and the versions // are different, print out an update message - if !wrangler_versions.is_outdated() - && (wrangler_versions.current != wrangler_versions.latest) + if wrangler_versions.is_outdated() { let _ = sender.send(wrangler_versions.latest); } From 54ed30de57766e6ee67681eda067f51d45045d60 Mon Sep 17 00:00:00 2001 From: jspspike Date: Wed, 27 May 2020 12:16:23 -0500 Subject: [PATCH 08/12] Fixed undoing lint --- src/version/version.rs | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/src/version/version.rs b/src/version/version.rs index 39610ec85..95edd6fa5 100644 --- a/src/version/version.rs +++ b/src/version/version.rs @@ -86,12 +86,10 @@ fn check_wrangler_versions() -> Result { /// Reads version out of version file, is `None` if file does not exist fn get_version_disk(version_file: &PathBuf) -> Option { match fs::read_to_string(&version_file) { - Ok(contents) => { - match LastCheckedVersion::from_str(&contents) { - Ok(last_checked_version) => Some(last_checked_version), - Err(_) => None, - } - } + Ok(contents) => match LastCheckedVersion::from_str(&contents) { + Ok(last_checked_version) => Some(last_checked_version), + Err(_) => None, + }, Err(_) => None, } } @@ -146,8 +144,7 @@ pub fn background_check_for_updates() -> mpsc::Receiver { Ok(wrangler_versions) => { // If the wrangler version has not been checked within the last day and the versions // are different, print out an update message - if wrangler_versions.is_outdated() - { + if wrangler_versions.is_outdated() { let _ = sender.send(wrangler_versions.latest); } } From b1a0b867532ed2e3be61e2a235bf47155b31137d Mon Sep 17 00:00:00 2001 From: Avery Harnish Date: Wed, 27 May 2020 15:04:45 -0500 Subject: [PATCH 09/12] addresses clippy warnings --- src/version/mod.rs | 4 ++-- src/version/{version.rs => wrangler_version.rs} | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) rename src/version/{version.rs => wrangler_version.rs} (98%) diff --git a/src/version/mod.rs b/src/version/mod.rs index 1c2574961..be9bfe7bd 100644 --- a/src/version/mod.rs +++ b/src/version/mod.rs @@ -1,5 +1,5 @@ mod block; -mod version; +mod wrangler_version; pub use block::GuardedCommand; -pub use version::background_check_for_updates; +pub use wrangler_version::background_check_for_updates; diff --git a/src/version/version.rs b/src/version/wrangler_version.rs similarity index 98% rename from src/version/version.rs rename to src/version/wrangler_version.rs index 95edd6fa5..db0a0bb8a 100644 --- a/src/version/version.rs +++ b/src/version/wrangler_version.rs @@ -27,7 +27,7 @@ pub struct WranglerVersion { impl WranglerVersion { pub fn is_outdated(&self) -> bool { - return !self.checked && (self.current != self.latest); + !self.checked && (self.current != self.latest) } } From df1047c0235e934164d61bb1a3cace5567b64441 Mon Sep 17 00:00:00 2001 From: jspspike Date: Wed, 27 May 2020 15:39:19 -0500 Subject: [PATCH 10/12] Fetches latest version if version file is older than a day --- src/version/wrangler_version.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/version/wrangler_version.rs b/src/version/wrangler_version.rs index db0a0bb8a..85a82bfe2 100644 --- a/src/version/wrangler_version.rs +++ b/src/version/wrangler_version.rs @@ -69,8 +69,10 @@ fn check_wrangler_versions() -> Result { if time_since_last_checked.as_secs() < ONE_DAY { checked = true; + Version::parse(&last_checked_version.latest_version)? + } else { + get_latest_version(¤t.to_string(), &version_file, current_time)? } - Version::parse(&last_checked_version.latest_version)? } // If version.toml doesn't exist, fetch latest version None => get_latest_version(¤t.to_string(), &version_file, current_time)?, From a518de2ce3e8ea29b3cb76835b0d0fe889a4579a Mon Sep 17 00:00:00 2001 From: Avery Harnish Date: Wed, 27 May 2020 15:47:48 -0500 Subject: [PATCH 11/12] restructure version module --- src/version/block.rs | 21 ----- src/version/mod.rs | 160 +++++++++++++++++++++++++++++++- src/version/wrangler_version.rs | 157 ------------------------------- 3 files changed, 156 insertions(+), 182 deletions(-) delete mode 100644 src/version/block.rs delete mode 100644 src/version/wrangler_version.rs diff --git a/src/version/block.rs b/src/version/block.rs deleted file mode 100644 index 2c72b40a5..000000000 --- a/src/version/block.rs +++ /dev/null @@ -1,21 +0,0 @@ -use std::process::{Child, Command}; - -// wrapper around spawning child processes such that they -// have the same behavior as spawned threads i.e. a spawned -// child process using GuardedChild has the same lifetime as -// the main thread. -pub struct GuardedCommand(Child); - -impl GuardedCommand { - pub fn spawn(mut command: Command) -> GuardedCommand { - GuardedCommand(command.spawn().expect("failed to execute child command")) - } -} - -impl Drop for GuardedCommand { - fn drop(&mut self) { - if let Err(e) = self.0.kill() { - panic!("Failed to kill child process: {:?}", e); - } - } -} diff --git a/src/version/mod.rs b/src/version/mod.rs index be9bfe7bd..6e4d23f1a 100644 --- a/src/version/mod.rs +++ b/src/version/mod.rs @@ -1,5 +1,157 @@ -mod block; -mod wrangler_version; +use std::fs; +use std::path::PathBuf; +use std::str::FromStr; +use std::sync::mpsc; +use std::thread; +use std::time::SystemTime; -pub use block::GuardedCommand; -pub use wrangler_version::background_check_for_updates; +use crate::settings::global_user::get_wrangler_home_dir; + +use reqwest::header::USER_AGENT; +use semver::Version; +use serde::{Deserialize, Serialize}; + +const ONE_DAY: u64 = 60 * 60 * 24; + +pub fn background_check_for_updates() -> mpsc::Receiver { + let (sender, receiver) = mpsc::channel(); + + let _detached_thread = thread::spawn(move || match check_wrangler_versions() { + Ok(wrangler_versions) => { + // If the wrangler version has not been checked within the last day and the versions + // are different, print out an update message + if wrangler_versions.is_outdated() { + let _ = sender.send(wrangler_versions.latest); + } + } + Err(e) => log::debug!("could not determine if update is needed:\n{}", e), + }); + + receiver +} + +#[derive(Debug, Clone)] +struct WranglerVersion { + /// currently installed version of wrangler + pub current: Version, + + /// latest version of wrangler on crates.io + pub latest: Version, + + /// set to true if wrangler version has been checked within a day + pub checked: bool, +} + +impl WranglerVersion { + pub fn is_outdated(&self) -> bool { + !self.checked && (self.current != self.latest) + } +} + +#[derive(Clone, Debug, Deserialize, Serialize)] +struct LastCheckedVersion { + /// latest version as of last time we checked + latest_version: String, + + /// the last time we asked crates.io for the latest version + last_checked: SystemTime, +} + +impl FromStr for LastCheckedVersion { + type Err = toml::de::Error; + + fn from_str(serialized_toml: &str) -> Result { + toml::from_str(serialized_toml) + } +} + +fn get_installed_version() -> Result { + let version = option_env!("CARGO_PKG_VERSION").unwrap_or_else(|| "unknown"); + let parsed_version = Version::parse(version)?; + Ok(parsed_version) +} + +fn check_wrangler_versions() -> Result { + let config_dir = get_wrangler_home_dir()?; + let version_file = config_dir.join("version.toml"); + let current_time = SystemTime::now(); + + let mut checked = false; + let current = get_installed_version()?; + + let latest = match get_version_disk(&version_file) { + Some(last_checked_version) => { + let time_since_last_checked = + current_time.duration_since(last_checked_version.last_checked)?; + + if time_since_last_checked.as_secs() < ONE_DAY { + checked = true; + Version::parse(&last_checked_version.latest_version)? + } else { + get_latest_version(¤t.to_string(), &version_file, current_time)? + } + } + // If version.toml doesn't exist, fetch latest version + None => get_latest_version(¤t.to_string(), &version_file, current_time)?, + }; + + Ok(WranglerVersion { + current, + latest, + checked, + }) +} + +/// Reads version out of version file, is `None` if file does not exist or is corrupted +fn get_version_disk(version_file: &PathBuf) -> Option { + match fs::read_to_string(&version_file) { + Ok(contents) => match LastCheckedVersion::from_str(&contents) { + Ok(last_checked_version) => Some(last_checked_version), + Err(_) => None, + }, + Err(_) => None, + } +} + +fn get_latest_version( + installed_version: &str, + version_file: &PathBuf, + current_time: SystemTime, +) -> Result { + let latest_version = get_latest_version_from_api(installed_version)?; + let updated_file_contents = toml::to_string(&LastCheckedVersion { + latest_version: latest_version.to_string(), + last_checked: current_time, + })?; + fs::write(&version_file, updated_file_contents)?; + Ok(latest_version) +} + +fn get_latest_version_from_api(installed_version: &str) -> Result { + let url = "https://crates.io/api/v1/crates/wrangler"; + let user_agent = format!( + "wrangler/{} ({})", + installed_version, + env!("CARGO_PKG_REPOSITORY") + ); + let response = reqwest::blocking::Client::new() + .get(url) + .header(USER_AGENT, user_agent) + .send()? + .error_for_status()?; + let text = response.text()?; + let crt: ApiResponse = serde_json::from_str(&text)?; + let version = Version::parse(&crt.info.max_version)?; + Ok(version) +} + +#[derive(Deserialize, Debug)] +struct ApiResponse { + #[serde(rename = "crate")] + info: CrateInformation, +} + +#[derive(Deserialize, Debug)] +struct CrateInformation { + max_version: String, +} diff --git a/src/version/wrangler_version.rs b/src/version/wrangler_version.rs deleted file mode 100644 index 85a82bfe2..000000000 --- a/src/version/wrangler_version.rs +++ /dev/null @@ -1,157 +0,0 @@ -use std::fs; -use std::path::PathBuf; -use std::str::FromStr; -use std::sync::mpsc; -use std::thread; -use std::time::SystemTime; - -use crate::settings::global_user::get_wrangler_home_dir; - -use reqwest::header::USER_AGENT; -use semver::Version; -use serde::{Deserialize, Serialize}; - -const ONE_DAY: u64 = 60 * 60 * 24; - -#[derive(Debug)] -pub struct WranglerVersion { - /// currently installed version of wrangler - pub current: Version, - - /// latest version of wrangler on crates.io - pub latest: Version, - - /// set to true if wrangler version has been checked within a day - pub checked: bool, -} - -impl WranglerVersion { - pub fn is_outdated(&self) -> bool { - !self.checked && (self.current != self.latest) - } -} - -#[derive(Clone, Debug, Deserialize, Serialize)] -struct LastCheckedVersion { - /// latest version as of last time we checked - latest_version: String, - - /// the last time we asked crates.io for the latest version - last_checked: SystemTime, -} - -impl FromStr for LastCheckedVersion { - type Err = toml::de::Error; - - fn from_str(serialized_toml: &str) -> Result { - toml::from_str(serialized_toml) - } -} - -fn get_installed_version() -> Result { - let version = option_env!("CARGO_PKG_VERSION").unwrap_or_else(|| "unknown"); - let parsed_version = Version::parse(version)?; - Ok(parsed_version) -} - -fn check_wrangler_versions() -> Result { - let config_dir = get_wrangler_home_dir()?; - let version_file = config_dir.join("version.toml"); - let current_time = SystemTime::now(); - - let mut checked = false; - let current = get_installed_version()?; - - let latest = match get_version_disk(&version_file) { - Some(last_checked_version) => { - let time_since_last_checked = - current_time.duration_since(last_checked_version.last_checked)?; - - if time_since_last_checked.as_secs() < ONE_DAY { - checked = true; - Version::parse(&last_checked_version.latest_version)? - } else { - get_latest_version(¤t.to_string(), &version_file, current_time)? - } - } - // If version.toml doesn't exist, fetch latest version - None => get_latest_version(¤t.to_string(), &version_file, current_time)?, - }; - - Ok(WranglerVersion { - current, - latest, - checked, - }) -} - -/// Reads version out of version file, is `None` if file does not exist -fn get_version_disk(version_file: &PathBuf) -> Option { - match fs::read_to_string(&version_file) { - Ok(contents) => match LastCheckedVersion::from_str(&contents) { - Ok(last_checked_version) => Some(last_checked_version), - Err(_) => None, - }, - Err(_) => None, - } -} - -fn get_latest_version( - installed_version: &str, - version_file: &PathBuf, - current_time: SystemTime, -) -> Result { - let latest_version = get_latest_version_from_api(installed_version)?; - let updated_file_contents = toml::to_string(&LastCheckedVersion { - latest_version: latest_version.to_string(), - last_checked: current_time, - })?; - fs::write(&version_file, updated_file_contents)?; - Ok(latest_version) -} - -fn get_latest_version_from_api(installed_version: &str) -> Result { - let url = "https://crates.io/api/v1/crates/wrangler"; - let user_agent = format!( - "wrangler/{} ({})", - installed_version, - env!("CARGO_PKG_REPOSITORY") - ); - let response = reqwest::blocking::Client::new() - .get(url) - .header(USER_AGENT, user_agent) - .send()? - .error_for_status()?; - let text = response.text()?; - let crt: ApiResponse = serde_json::from_str(&text)?; - let version = Version::parse(&crt.info.max_version)?; - Ok(version) -} - -#[derive(Deserialize, Debug)] -struct ApiResponse { - #[serde(rename = "crate")] - info: CrateInformation, -} - -#[derive(Deserialize, Debug)] -struct CrateInformation { - max_version: String, -} - -pub fn background_check_for_updates() -> mpsc::Receiver { - let (sender, receiver) = mpsc::channel(); - - let _detached_thread = thread::spawn(move || match check_wrangler_versions() { - Ok(wrangler_versions) => { - // If the wrangler version has not been checked within the last day and the versions - // are different, print out an update message - if wrangler_versions.is_outdated() { - let _ = sender.send(wrangler_versions.latest); - } - } - Err(e) => log::debug!("could not determine if update is needed:\n{}", e), - }); - - receiver -} From 1f4cc7116257956fb8c51c8d9bf8f53175db2074 Mon Sep 17 00:00:00 2001 From: Avery Harnish Date: Thu, 28 May 2020 10:03:47 -0500 Subject: [PATCH 12/12] decouple global_user from global_config --- src/commands/config.rs | 2 +- src/settings/global_config.rs | 24 ++++++++++++++++++++++++ src/settings/global_user.rs | 27 ++------------------------- src/settings/mod.rs | 2 ++ src/version/mod.rs | 2 +- 5 files changed, 30 insertions(+), 27 deletions(-) create mode 100644 src/settings/global_config.rs diff --git a/src/commands/config.rs b/src/commands/config.rs index f5c2a612d..855dedb18 100644 --- a/src/commands/config.rs +++ b/src/commands/config.rs @@ -9,7 +9,7 @@ use cloudflare::endpoints::user::{GetUserDetails, GetUserTokenStatus}; use cloudflare::framework::apiclient::ApiClient; use crate::http; -use crate::settings::global_user::{get_global_config_path, GlobalUser}; +use crate::settings::{get_global_config_path, global_user::GlobalUser}; use crate::terminal::{message, styles}; // set the permissions on the dir, we want to avoid that other user reads to file diff --git a/src/settings/global_config.rs b/src/settings/global_config.rs new file mode 100644 index 000000000..70402fe61 --- /dev/null +++ b/src/settings/global_config.rs @@ -0,0 +1,24 @@ +use std::env; +use std::path::{Path, PathBuf}; + +pub const DEFAULT_CONFIG_FILE_NAME: &str = "default.toml"; + +pub fn get_wrangler_home_dir() -> Result { + let config_dir = if let Ok(value) = env::var("WRANGLER_HOME") { + log::info!("Using $WRANGLER_HOME: {}", value); + Path::new(&value).to_path_buf() + } else { + log::info!("No $WRANGLER_HOME detected, using $HOME"); + dirs::home_dir() + .expect("Could not find home directory") + .join(".wrangler") + }; + Ok(config_dir) +} + +pub fn get_global_config_path() -> Result { + let home_dir = get_wrangler_home_dir()?; + let global_config_file = home_dir.join("config").join(DEFAULT_CONFIG_FILE_NAME); + log::info!("Using global config file: {}", global_config_file.display()); + Ok(global_config_file) +} diff --git a/src/settings/global_user.rs b/src/settings/global_user.rs index c20d55b8a..5bb6e1d7a 100644 --- a/src/settings/global_user.rs +++ b/src/settings/global_user.rs @@ -1,15 +1,12 @@ -use std::env; use std::fs; use std::path::{Path, PathBuf}; use cloudflare::framework::auth::Credentials; use serde::{Deserialize, Serialize}; -use crate::settings::{Environment, QueryEnvironment}; +use crate::settings::{get_global_config_path, Environment, QueryEnvironment}; use crate::terminal::{emoji, styles}; -const DEFAULT_CONFIG_FILE_NAME: &str = "default.toml"; - const CF_API_TOKEN: &str = "CF_API_TOKEN"; const CF_API_KEY: &str = "CF_API_KEY"; const CF_EMAIL: &str = "CF_EMAIL"; @@ -133,33 +130,13 @@ impl From for Credentials { } } -pub fn get_wrangler_home_dir() -> Result { - let config_dir = if let Ok(value) = env::var("WRANGLER_HOME") { - log::info!("Using $WRANGLER_HOME: {}", value); - Path::new(&value).to_path_buf() - } else { - log::info!("No $WRANGLER_HOME detected, using $HOME"); - dirs::home_dir() - .expect("Could not find home directory") - .join(".wrangler") - }; - Ok(config_dir) -} - -pub fn get_global_config_path() -> Result { - let home_dir = get_wrangler_home_dir()?; - let global_config_file = home_dir.join("config").join(DEFAULT_CONFIG_FILE_NAME); - log::info!("Using global config file: {}", global_config_file.display()); - Ok(global_config_file) -} - #[cfg(test)] mod tests { use super::*; use std::fs::File; use tempfile::tempdir; - use crate::settings::environment::MockEnvironment; + use crate::settings::{environment::MockEnvironment, DEFAULT_CONFIG_FILE_NAME}; #[test] fn it_can_prioritize_token_input() { diff --git a/src/settings/mod.rs b/src/settings/mod.rs index 5e2285e4c..3cfe1c803 100644 --- a/src/settings/mod.rs +++ b/src/settings/mod.rs @@ -1,7 +1,9 @@ pub mod binding; mod environment; +mod global_config; pub mod global_user; pub mod metadata; pub mod toml; pub use environment::{Environment, QueryEnvironment}; +pub use global_config::{get_global_config_path, get_wrangler_home_dir, DEFAULT_CONFIG_FILE_NAME}; diff --git a/src/version/mod.rs b/src/version/mod.rs index 6e4d23f1a..53baec09a 100644 --- a/src/version/mod.rs +++ b/src/version/mod.rs @@ -5,7 +5,7 @@ use std::sync::mpsc; use std::thread; use std::time::SystemTime; -use crate::settings::global_user::get_wrangler_home_dir; +use crate::settings::get_wrangler_home_dir; use reqwest::header::USER_AGENT; use semver::Version;