From 897ef20a5995c0afa568cb8c4320f2dfa0e666eb Mon Sep 17 00:00:00 2001 From: Avery Harnish Date: Wed, 30 Jun 2021 16:40:03 -0500 Subject: [PATCH] chore: refactor release update checker --- Cargo.lock | 2 +- Cargo.toml | 1 - crates/rover-client/Cargo.toml | 1 + crates/rover-client/src/error.rs | 10 +++++--- crates/rover-client/src/lib.rs | 3 ++- crates/rover-client/src/releases.rs | 40 +++++++++++++++-------------- installers/binstall/src/error.rs | 7 ++++- installers/binstall/src/install.rs | 12 +++++---- src/error/metadata/mod.rs | 3 ++- src/utils/version.rs | 18 ++++--------- 10 files changed, 52 insertions(+), 45 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 9a2e9d7b2f..e15f9bcb72 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1949,7 +1949,6 @@ dependencies = [ "rover-client", "rustversion", "sdl-encoder", - "semver 1.0.3", "serde", "serde_json", "serde_yaml", @@ -1986,6 +1985,7 @@ dependencies = [ "regex", "reqwest", "sdl-encoder", + "semver 1.0.3", "serde", "serde_json", "thiserror", diff --git a/Cargo.toml b/Cargo.toml index 0d56929ebd..50ac889460 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -58,7 +58,6 @@ opener = "0.5.0" os_info = "3.0" prettytable-rs = "0.8.0" regex = "1" -semver = "1" serde = "1.0" serde_json = "1.0" serde_yaml = "0.8" diff --git a/crates/rover-client/Cargo.toml b/crates/rover-client/Cargo.toml index 7c3ee3dc02..b9e329fe64 100644 --- a/crates/rover-client/Cargo.toml +++ b/crates/rover-client/Cargo.toml @@ -22,6 +22,7 @@ http = "0.2" regex = "1.5.4" reqwest = {version = "0.11", default-features = false, features = ["json", "blocking", "rustls-tls", "gzip"]} sdl-encoder = {path = "../sdl-encoder"} +semver = "1" serde = "1" serde_json = "1" thiserror = "1" diff --git a/crates/rover-client/src/error.rs b/crates/rover-client/src/error.rs index 09941f3f07..f2ddd2a610 100644 --- a/crates/rover-client/src/error.rs +++ b/crates/rover-client/src/error.rs @@ -142,9 +142,13 @@ pub enum RoverClientError { #[error("The registry did not recognize the provided API key")] InvalidKey, - /// could not parse the latest version - #[error("Could not get the latest release version")] - UnparseableReleaseVersion, + /// Could not parse the latest version + #[error("Could not parse the latest release version")] + UnparseableReleaseVersion { source: semver::Error }, + + /// Encountered an error while processing the request for the latest version + #[error("There's something wrong with the latest GitHub release URL")] + BadReleaseUrl, #[error("This endpoint doesn't support subgraph introspection via the Query._service field")] SubgraphIntrospectionNotAvailable, diff --git a/crates/rover-client/src/lib.rs b/crates/rover-client/src/lib.rs index 943267dce1..aa15757c71 100644 --- a/crates/rover-client/src/lib.rs +++ b/crates/rover-client/src/lib.rs @@ -2,9 +2,10 @@ //! HTTP client for making GraphQL requests for the Rover CLI tool. +mod error; + /// Module related to blocking http client. pub mod blocking; -mod error; /// Module for client related errors. pub use error::RoverClientError; diff --git a/crates/rover-client/src/releases.rs b/crates/rover-client/src/releases.rs index 3656481695..306bf9b459 100644 --- a/crates/rover-client/src/releases.rs +++ b/crates/rover-client/src/releases.rs @@ -1,26 +1,28 @@ use crate::RoverClientError; -use regex::Regex; -use reqwest::blocking; + +use reqwest::blocking::Client; +pub use semver::Version; const LATEST_RELEASE_URL: &str = "https://github.com/apollographql/rover/releases/latest"; -/// Looks up the latest release version, and returns it as a string -pub fn get_latest_release() -> Result { - let res = blocking::Client::new().head(LATEST_RELEASE_URL).send()?; +/// Looks up and parses the latest release version +pub fn get_latest_release() -> Result { + // send a request to the latest GitHub release + let response = Client::new().head(LATEST_RELEASE_URL).send()?; + + // this will return a response with a redirect to the latest tagged release + let url_path_segments = response + .url() + .path_segments() + .ok_or(RoverClientError::BadReleaseUrl)?; - let release_url = res.url().to_string(); - let release_url_parts: Vec<&str> = release_url.split('/').collect(); + // the last section of the URL will have the latest version in `v0.1.1` format + let version_string = url_path_segments + .last() + .ok_or(RoverClientError::BadReleaseUrl)? + .to_string(); - match release_url_parts.last() { - Some(version) => { - // Parse out the semver version (ex. v1.0.0 -> 1.0.0) - let re = Regex::new(r"^v[0-9]*\.[0-9]*\.[0-9]*$").unwrap(); - if re.is_match(version) { - Ok(version.to_string().replace('v', "")) - } else { - Err(RoverClientError::UnparseableReleaseVersion) - } - } - None => Err(RoverClientError::UnparseableReleaseVersion), - } + // strip the `v` prefix from the last section of the URL before parsing + Version::parse(&version_string[1..]) + .map_err(|source| RoverClientError::UnparseableReleaseVersion { source }) } diff --git a/installers/binstall/src/error.rs b/installers/binstall/src/error.rs index 26ca51377c..dc6f923532 100644 --- a/installers/binstall/src/error.rs +++ b/installers/binstall/src/error.rs @@ -2,21 +2,26 @@ use thiserror::Error; use std::io; -/// InstallerError is the type of Error that occured while installing. +/// InstallerError is the type of Error that occurred while installing. #[derive(Error, Debug)] pub enum InstallerError { + /// Something went wrong with system I/O #[error(transparent)] IoError(#[from] io::Error), + /// Couldn't find a valid install location on Unix #[error("Could not find the home directory of the current user")] NoHomeUnix, + /// Couldn't find a valid install location on Windows #[error("Could not find the user profile folder")] NoHomeWindows, + /// Something went wrong while adding the executable to zsh config #[error("Zsh setup failed")] ZshSetup, + /// A specified path was not valid UTF-8 #[error(transparent)] PathNotUtf8(#[from] camino::FromPathBufError), } diff --git a/installers/binstall/src/install.rs b/installers/binstall/src/install.rs index ecf494167a..4ea6e51b92 100644 --- a/installers/binstall/src/install.rs +++ b/installers/binstall/src/install.rs @@ -15,12 +15,19 @@ pub struct Installer { } impl Installer { + /// Installs the executable and returns the location it was installed. pub fn install(&self) -> Result, InstallerError> { let install_path = self.do_install()?; Ok(install_path) } + /// Gets the location the executable will be installed to + pub fn get_bin_dir_path(&self) -> Result { + let bin_dir = self.get_base_dir_path()?.join("bin"); + Ok(bin_dir) + } + fn do_install(&self) -> Result, InstallerError> { let bin_destination = self.get_bin_path()?; @@ -52,11 +59,6 @@ impl Installer { Ok(base_dir.join(&format!(".{}", &self.binary_name))) } - pub fn get_bin_dir_path(&self) -> Result { - let bin_dir = self.get_base_dir_path()?.join("bin"); - Ok(bin_dir) - } - fn create_bin_dir(&self) -> Result<(), InstallerError> { fs::create_dir_all(self.get_bin_dir_path()?)?; Ok(()) diff --git a/src/error/metadata/mod.rs b/src/error/metadata/mod.rs index 32649ab6c1..911b44b43e 100644 --- a/src/error/metadata/mod.rs +++ b/src/error/metadata/mod.rs @@ -121,9 +121,10 @@ impl From<&mut anyhow::Error> for Metadata { RoverClientError::ClientError { msg: _ } => (None, Some(Code::E012)), RoverClientError::InvalidKey => (Some(Suggestion::CheckKey), Some(Code::E013)), RoverClientError::MalformedKey => (Some(Suggestion::ProperKey), Some(Code::E014)), - RoverClientError::UnparseableReleaseVersion => { + RoverClientError::UnparseableReleaseVersion { source: _ } => { (Some(Suggestion::SubmitIssue), Some(Code::E015)) } + RoverClientError::BadReleaseUrl => (Some(Suggestion::SubmitIssue), None), RoverClientError::NoCompositionPublishes { graph: _, composition_errors, diff --git a/src/utils/version.rs b/src/utils/version.rs index 9de8228041..8076e81cec 100644 --- a/src/utils/version.rs +++ b/src/utils/version.rs @@ -3,11 +3,10 @@ use std::{fs, time::SystemTime}; use ansi_term::Colour::{Cyan, Yellow}; use billboard::{Alignment, Billboard}; use camino::Utf8PathBuf; -use semver::Version; use crate::{Result, PKG_VERSION}; use houston as config; -use rover_client::releases::get_latest_release; +use rover_client::releases::{get_latest_release, Version}; const ONE_HOUR: u64 = 60 * 60; const ONE_DAY: u64 = ONE_HOUR * 24; @@ -50,12 +49,11 @@ pub fn check_for_update(config: config::Config, force: bool) -> Result<()> { } fn do_update_check(checked: &mut bool, should_output_if_updated: bool) -> Result<()> { - let latest = get_latest_release()?; - let pretty_latest = Cyan.normal().paint(format!("v{}", latest)); - let update_available = is_latest_newer(&latest, PKG_VERSION)?; - if update_available { + let latest_version = get_latest_release()?; + let pretty_latest = Cyan.normal().paint(format!("v{}", latest_version)); + if latest_version > Version::parse(PKG_VERSION)? { let message = format!( - "There is a newer version of Rover available: {} (currently running v{})\n\nFor instructions on how to install, run {}", + "There is a newer version of Rover available: {} (currently running v{})\n\nFor instructions on how to install, run {}", &pretty_latest, PKG_VERSION, Yellow.normal().paint("`rover docs open start`") @@ -90,9 +88,3 @@ fn get_last_checked_time_from_disk(version_file: &Utf8PathBuf) -> Option Result { - let latest = Version::parse(latest)?; - let running = Version::parse(running)?; - Ok(latest > running) -}