Skip to content
This repository has been archived by the owner on Aug 3, 2023. It is now read-only.

Commit

Permalink
feat: message if wrangler needs updating
Browse files Browse the repository at this point in the history
  • Loading branch information
EverlastingBugstopper committed Apr 3, 2020
1 parent c9c5bbe commit fd30320
Show file tree
Hide file tree
Showing 7 changed files with 159 additions and 3 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ url = "2.1.0"
uuid = { version = "0.8", features = ["v4"] }
which = "2.0.1"
ws = "0.9.0"
semver = "0.9.0"

[dev-dependencies]
assert_cmd = "0.11.1"
Expand Down
19 changes: 18 additions & 1 deletion src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,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.
Expand All @@ -34,7 +36,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> {
Expand Down
9 changes: 7 additions & 2 deletions src/settings/global_user.rs
Original file line number Diff line number Diff line change
Expand Up @@ -130,8 +130,8 @@ impl From<GlobalUser> for Credentials {
}
}

pub fn get_global_config_path() -> Result<PathBuf, failure::Error> {
let home_dir = if let Ok(value) = env::var("WRANGLER_HOME") {
pub fn get_wrangler_home_dir() -> Result<PathBuf, failure::Error> {
let config_dir = if let Ok(value) = env::var("WRANGLER_HOME") {
log::info!("Using $WRANGLER_HOME: {}", value);
Path::new(&value).to_path_buf()
} else {
Expand All @@ -140,6 +140,11 @@ pub fn get_global_config_path() -> Result<PathBuf, failure::Error> {
.expect("Could not find home directory")
.join(".wrangler")
};
Ok(config_dir)
}

pub fn get_global_config_path() -> Result<PathBuf, failure::Error> {
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)
Expand Down
File renamed without changes.
5 changes: 5 additions & 0 deletions src/util/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
mod block;
mod version;

pub use block::GuardedCommand;
pub use version::background_check_for_updates;
127 changes: 127 additions & 0 deletions src/util/version.rs
Original file line number Diff line number Diff line change
@@ -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<Self, Self::Err> {
toml::from_str(serialized_toml)
}
}

fn get_installed_version() -> Result<Version, failure::Error> {
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<WranglerVersion, failure::Error> {
let current = get_installed_version()?;
let latest = get_latest_version(&current.to_string())?;
Ok(WranglerVersion { current, latest })
}

fn get_latest_version(installed_version: &str) -> Result<Version, failure::Error> {
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<Version, failure::Error> {
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<Version> {
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
}

0 comments on commit fd30320

Please sign in to comment.