Skip to content

Commit

Permalink
feat: add automatic version checking to pre-run
Browse files Browse the repository at this point in the history
  • Loading branch information
JakeDawkins committed Mar 1, 2021
1 parent f16e6b8 commit 0361f50
Show file tree
Hide file tree
Showing 7 changed files with 124 additions and 32 deletions.
3 changes: 2 additions & 1 deletion Cargo.lock

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

3 changes: 2 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
authors = ["Apollo Developers <opensource@apollographql.com>"]
edition = "2018"
name = "rover"
version = "0.0.1"
version = "0.0.2"
repository = "https://github.com/apollographql/rover/"

[dependencies]
Expand Down Expand Up @@ -35,6 +35,7 @@ tracing = "0.1.22"
regex = "1"
url = "2.2.0"
semver = "0.11"
toml = "0.5"

[dev-dependencies]
assert_cmd = "1.0.1"
Expand Down
12 changes: 11 additions & 1 deletion src/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ use crate::utils::{
env::{RoverEnv, RoverEnvKey},
git::GitContext,
stringify::from_display,
version,
};
use crate::Result;
use config::Config;
Expand Down Expand Up @@ -111,6 +112,15 @@ pub enum Command {

impl Rover {
pub fn run(&self) -> Result<RoverStdout> {
// before running any commands, we check if rover is up to date
// this only happens once a day automatically
// we skip this check for the `rover update` commands, since they
// do their own checks
if let Command::Update(_) = &self.command { /* skip check */
} else {
version::check_for_update(self.get_rover_config()?, false)?;
}

match &self.command {
Command::Config(command) => {
command.run(self.get_rover_config()?, self.get_client_config()?)
Expand All @@ -122,7 +132,7 @@ impl Rover {
Command::Subgraph(command) => {
command.run(self.get_client_config()?, self.get_git_context()?)
}
Command::Update(command) => command.run(),
Command::Update(command) => command.run(self.get_rover_config()?),
Command::Install(command) => command.run(self.get_install_override_path()?),
Command::Info(command) => command.run(),
}
Expand Down
32 changes: 5 additions & 27 deletions src/command/update/check.rs
Original file line number Diff line number Diff line change
@@ -1,41 +1,19 @@
use ansi_term::Colour::Cyan;
use serde::Serialize;
use structopt::StructOpt;

use crate::command::RoverStdout;
use crate::{Result, PKG_VERSION};
use crate::{utils::version, Result};

use semver::Version;

use rover_client::releases::get_latest_release;
use houston as config;

#[derive(Debug, Serialize, StructOpt)]
pub struct Check {
// TODO: support prerelease check through flag
// future: support prerelease check through flag --prerelease
}

impl Check {
pub fn run(&self) -> Result<RoverStdout> {
let latest = get_latest_release()?;
let update_available = needs_update(&latest, PKG_VERSION)?;

if update_available {
eprintln!(
"There is a newer version of Rover available for download: {} (currently running v{})\n\nFor instructions on how to install the latest version of Rover, see {}",
Cyan.normal().paint(format!("v{}", latest)),
PKG_VERSION,
Cyan.normal().paint("https://go.apollo.dev/r/start")
);
} else {
eprintln!("Rover is up to date!");
}

pub fn run(&self, config: config::Config) -> Result<RoverStdout> {
version::check_for_update(config, true)?;
Ok(RoverStdout::None)
}
}

fn needs_update(latest: &str, running: &str) -> Result<bool> {
let latest = Version::parse(latest)?;
let running = Version::parse(running)?;
Ok(latest > running)
}
6 changes: 4 additions & 2 deletions src/command/update/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ use structopt::StructOpt;
use crate::command::RoverStdout;
use crate::Result;

use houston as config;

#[derive(Debug, Serialize, StructOpt)]
pub struct Update {
#[structopt(subcommand)]
Expand All @@ -19,9 +21,9 @@ pub enum Command {
}

impl Update {
pub fn run(&self) -> Result<RoverStdout> {
pub fn run(&self, config: config::Config) -> Result<RoverStdout> {
match &self.command {
Command::Check(command) => command.run(),
Command::Check(command) => command.run(config),
}
}
}
1 change: 1 addition & 0 deletions src/utils/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,4 @@ pub mod parsers;
pub mod pkg;
pub mod stringify;
pub mod telemetry;
pub mod version;
99 changes: 99 additions & 0 deletions src/utils/version.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
use std::{fs, path::PathBuf, time::SystemTime};

use rover_client::releases::get_latest_release;

use ansi_term::Colour::Cyan;
use semver::Version;

use crate::{Result, PKG_VERSION};

use houston as config;

const ONE_HOUR: u64 = 60 * 60;
const ONE_DAY: u64 = ONE_HOUR * 24;

/// check for newer versions of rover.
///
/// If this fn is run explicitly from a user-facing command, we pass `force` to
/// check for newer versions, even if we recently checked for updates.
///
/// If `force` is not passed, we check for updates every day at most
pub fn check_for_update(config: config::Config, force: bool) -> Result<()> {
let version_file = config.home.join("version.toml");
let current_time = SystemTime::now();
// if we don't end up checking, we don't want to overwrite the last checked time
let mut checked = false;

// check fs for last check time
let last_checked_time = get_last_checked_time_from_disk(&version_file);

match last_checked_time {
Some(last_checked_time) => {
let time_since_check = current_time.duration_since(last_checked_time)?.as_secs();
tracing::debug!(
"Time since last update check: {:?}h",
time_since_check / ONE_HOUR
);

if force || time_since_check > ONE_DAY {
do_update_check(&mut checked)?;
} else {
tracing::debug!(
"No need to check for updates. Automatic checks happen once per day"
);
}
}
// we haven't checked for updates before -- check now :)
None => {
do_update_check(&mut checked)?;
}
}

if checked {
tracing::debug!("Checked for available updates. Writing current time to disk");
fs::write(&version_file, toml::to_string(&current_time)?)?;
}

Ok(())
}

fn do_update_check(checked: &mut bool) -> Result<()> {
let latest = get_latest_release()?;
let update_available = is_latest_newer(&latest, PKG_VERSION)?;

if update_available {
eprintln!(
"There is a newer version of Rover available for download: {} (currently running v{})\n\nFor instructions on how to install the latest version of Rover, see {}",
Cyan.normal().paint(format!("v{}", latest)),
PKG_VERSION,
Cyan.normal().paint("https://go.apollo.dev/r/start")
);
} else {
eprintln!("Rover is up to date!");
}

*checked = true;
Ok(())
}

fn get_last_checked_time_from_disk(version_file: &PathBuf) -> Option<SystemTime> {
match fs::read_to_string(&version_file) {
Ok(contents) => match toml::from_str(&contents) {
Ok(last_checked_version) => Some(last_checked_version),
Err(_) => {
tracing::debug!("Failed to parse last update check time from version file");
None
}
},
Err(_) => {
tracing::debug!("Failed to read version file containing last update check time");
None
}
}
}

fn is_latest_newer(latest: &str, running: &str) -> Result<bool> {
let latest = Version::parse(latest)?;
let running = Version::parse(running)?;
Ok(latest > running)
}

0 comments on commit 0361f50

Please sign in to comment.