Skip to content

Commit

Permalink
address PR feedback
Browse files Browse the repository at this point in the history
Co-authored-by: Chris Olszewski <chris.olszewski@vercel.com>
  • Loading branch information
tknickman and chris-olszewski committed Dec 1, 2022
1 parent fa1a096 commit cf3630e
Show file tree
Hide file tree
Showing 8 changed files with 262 additions and 249 deletions.
39 changes: 2 additions & 37 deletions Cargo.lock

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

4 changes: 1 addition & 3 deletions crates/update-notifier/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,12 @@
name = "update-notifier"
version = "0.1.0"
edition = "2021"
description = "Output update notification to the console when new version on npm package is available"
description = "Display update notification when new version of npm package is available"
license = "MPL-2.0"
publish = false


[dependencies]
colored = "2.0"
env_logger = "0.10"
log = "0.4"
reqwest = { version = "0.11", features = ["json"] }
semver = "1.0"
Expand Down
27 changes: 9 additions & 18 deletions crates/update-notifier/src/fetch.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,31 +12,22 @@ struct NpmVersionData {

const REGISTRY_URL: &str = "https://registry.npmjs.org";
const DEFAULT_TAG: &str = "latest";
const TIMEOUT: Duration = Duration::from_millis(800);
const DEFAULT_TIMEOUT: Duration = Duration::from_millis(800);

pub async fn get_latest_version(
package: &str,
tag: Option<&str>,
timeout: Option<Duration>,
) -> Result<String, UpdateNotifierError> {
log::debug!("fetching latest version");
let tag = tag.unwrap_or_else(|| DEFAULT_TAG);
let timeout = timeout.unwrap_or_else(|| TIMEOUT);
let tag = tag.unwrap_or(DEFAULT_TAG);
let timeout = timeout.unwrap_or(DEFAULT_TIMEOUT);
let client: Client = reqwest::Client::new();
let url = format!("{}/{}/{}", REGISTRY_URL, package, tag);

// send request
log::debug!("fetching {:?}", url);
let resp = client.get(url).timeout(timeout).send().await;
match resp {
Ok(r) => {
let json_result = r.json::<NpmVersionData>().await;
match json_result {
Ok(v) => Ok(v.version.to_string()),
Err(err) => Err(UpdateNotifierError::FetchError(err)),
}
}
Err(err) => {
log::error!("failed to fetch latest version {:?}", err);
Err(UpdateNotifierError::FetchError(err))
}
}
let resp = client.get(url).timeout(timeout).send().await?;

let json_result = resp.json::<NpmVersionData>().await?;
Ok(json_result.version)
}
113 changes: 55 additions & 58 deletions crates/update-notifier/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ mod ui;
mod utils;

// default interval to check for new updates (one per day)
const INTERVAL: Duration = Duration::from_secs(60 * 60 * 24);
const DEFAULT_INTERVAL: Duration = Duration::from_secs(60 * 60 * 24);

#[derive(Debug)]
pub struct UpdateNotifier {
Expand All @@ -39,9 +39,52 @@ pub enum UpdateNotifierError {
ParseError(#[from] SemVerError),
#[error("Failed to parse JSON")]
JsonError(#[from] SerdeError),
#[error("Failed to write to terminal")]
DisplayLengthError(#[from] ui::utils::GetDisplayLengthError),
#[error("Unknown Error")]
Internal(),
}

impl UpdateNotifier {
pub fn new(package: String, tag: Option<String>, interval: Option<Duration>) -> Self {
let tmp = utils::get_config_path();

// ignore if it doesn't exist, we don't care
if tmp.try_exists().unwrap_or(false) {
let file = std::fs::File::open(tmp);
let file = match file {
Ok(f) => f,
Err(_) => {
log::debug!("failed to read local config, writing first version");
return Self::first_run(package, tag, interval);
}
};
let reader = std::io::BufReader::new(file);
let json_result: Result<UpdateNotifierConfig, SerdeError> =
serde_json::from_reader(reader);
match json_result {
Ok(v) => {
log::debug!("found local version config {:?}", v);
Self {
config: UpdateNotifierConfig {
current_version: utils::get_version().to_string(),
..v
},
package,
tag,
interval: interval.unwrap_or(DEFAULT_INTERVAL),
}
}
Err(_) => {
log::debug!("failed to find version config");
Self::first_run(package, tag, interval)
}
}
} else {
Self::first_run(package, tag, interval)
}
}

fn should_refresh(&self) -> bool {
if self.config.latest_version.is_none() {
log::debug!("no latest version found in local config");
Expand All @@ -66,36 +109,37 @@ impl UpdateNotifier {
}
}

fn update_message(&self) {
fn update_message(&self) -> Result<(), UpdateNotifierError> {
let turbo = "@turborepo";
let turbo_gradient = turbo.gradient([RGB::new(0, 153, 247), RGB::new(241, 23, 18)]);

let latest_version = match &self.config.latest_version {
Some(v) => v,
None => {
log::error!("no latest version found in local config");
return;
return Err(UpdateNotifierError::Internal());
}
};

let msg = format!(
"
Update available {} ≫ {}
Changelog: https://github.com/vercel/turbo/releases/tag/v{}Run \
\"{}\" to update
Changelog: {}/releases/tag/v{}
Run \"{}\" to update
Follow {} for updates: {}
",
&self.config.current_version.dimmed(),
&latest_version.green().bold(),
"https://github.com/vercel/turbo",
&latest_version,
// TODO: make this package manager aware
"npm i -g turbo".cyan().bold(),
turbo_gradient,
"https://twitter.com/turborepo",
);

ui::rectangle(&msg);
ui::message(&msg)
}

fn first_run(package: String, tag: Option<String>, interval: Option<Duration>) -> Self {
Expand All @@ -106,51 +150,12 @@ impl UpdateNotifier {
latest_version: None,
last_checked: None,
},
interval: interval.unwrap_or(INTERVAL),
interval: interval.unwrap_or(DEFAULT_INTERVAL),
package,
tag,
}
}

pub fn new(package: String, tag: Option<String>, interval: Option<Duration>) -> Self {
let tmp = utils::get_config_path();

// ignore if it doesn't exist, we don't care
if tmp.try_exists().unwrap_or(false) {
let file = std::fs::File::open(tmp);
let file = match file {
Ok(f) => f,
Err(_) => {
log::debug!("failed to open local config, writing first version");
return Self::first_run(package, tag, interval);
}
};
let reader = std::io::BufReader::new(file);
let json_result: Result<UpdateNotifierConfig, SerdeError> =
serde_json::from_reader(reader);
match json_result {
Ok(v) => {
log::debug!("found local version config {:?}", v);
Self {
config: UpdateNotifierConfig {
current_version: utils::get_version().to_string(),
..v
},
package,
tag,
interval: interval.unwrap_or(INTERVAL),
}
}
Err(_) => {
log::debug!("failed to find version config");
Self::first_run(package, tag, interval)
}
}
} else {
Self::first_run(package, tag, interval)
}
}

fn save(&self) {
// get directory
let tmp = utils::get_config_path();
Expand All @@ -173,15 +178,7 @@ impl UpdateNotifier {
#[tokio::main]
async fn update(&mut self) -> Result<(), UpdateNotifierError> {
let latest_version =
fetch::get_latest_version(&self.package, self.tag.as_deref(), None).await;
let latest_version = match latest_version {
Ok(v) => v,
Err(err) => {
log::debug!("failed to fetch latest version {:?}", err);
return Err(err);
}
};

fetch::get_latest_version(&self.package, self.tag.as_deref(), None).await?;
let current_version = String::from(utils::get_version());
let now = utils::ms_since_epoch();

Expand All @@ -192,7 +189,7 @@ impl UpdateNotifier {

// persist
self.save();
return Ok(());
Ok(())
}

pub fn check(&mut self) -> Result<(), UpdateNotifierError> {
Expand All @@ -207,13 +204,13 @@ impl UpdateNotifier {
log::debug!("checking if {} > {}", latest_version, current_version);
if latest_version > current_version {
log::debug!("update available");
self.update_message();
self.update_message()?;
return Ok(());
} else {
log::debug!("no update available");
return Ok(());
}
}
return Ok(());
Ok(())
}
}

0 comments on commit cf3630e

Please sign in to comment.