diff options
| author | Ellie Huxtable <ellie@elliehuxtable.com> | 2022-10-14 10:59:21 +0100 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2022-10-14 10:59:21 +0100 |
| commit | f03f6e9ad74d8e1cf1fa33dc2c0c7c5dd7ae5c94 (patch) | |
| tree | aa03f60230b3bc96485806724383bfd6e4cc6b1d /atuin-client/src/settings.rs | |
| parent | Fix ZSH import print (diff) | |
| download | atuin-f03f6e9ad74d8e1cf1fa33dc2c0c7c5dd7ae5c94.zip | |
Add automatic update checking (#555)
* Add automatic update checking
* Add setting to opt out of update checks
* Document options
* no
* no
* also no
* Make clippy happy
* Update atuin-client/src/settings.rs
Co-authored-by: Conrad Ludgate <conradludgate@gmail.com>
* fix features
Co-authored-by: Conrad Ludgate <conradludgate@gmail.com>
Co-authored-by: Conrad Ludgate <conrad.ludgate@truelayer.com>
Diffstat (limited to 'atuin-client/src/settings.rs')
| -rw-r--r-- | atuin-client/src/settings.rs | 121 |
1 files changed, 111 insertions, 10 deletions
diff --git a/atuin-client/src/settings.rs b/atuin-client/src/settings.rs index f836ce02..b743a154 100644 --- a/atuin-client/src/settings.rs +++ b/atuin-client/src/settings.rs @@ -8,9 +8,13 @@ use config::{Config, Environment, File as ConfigFile, FileFormat}; use eyre::{eyre, Context, Result}; use fs_err::{create_dir_all, File}; use parse_duration::parse; +use semver::Version; use serde::Deserialize; pub const HISTORY_PAGE_SIZE: i64 = 100; +pub const LAST_SYNC_FILENAME: &str = "last_sync_time"; +pub const LAST_VERSION_CHECK_FILENAME: &str = "last_version_check_time"; +pub const LATEST_VERSION_FILENAME: &str = "latest_version"; #[derive(Clone, Debug, Deserialize, Copy)] pub enum SearchMode { @@ -86,6 +90,7 @@ pub struct Settings { pub dialect: Dialect, pub style: Style, pub auto_sync: bool, + pub update_check: bool, pub sync_address: String, pub sync_frequency: String, pub db_path: String, @@ -99,31 +104,65 @@ pub struct Settings { } impl Settings { - pub fn save_sync_time() -> Result<()> { + fn save_to_data_dir(filename: &str, value: &str) -> Result<()> { let data_dir = atuin_common::utils::data_dir(); let data_dir = data_dir.as_path(); - let sync_time_path = data_dir.join("last_sync_time"); + let path = data_dir.join(filename); - fs_err::write(sync_time_path, Utc::now().to_rfc3339())?; + fs_err::write(path, value)?; Ok(()) } - pub fn last_sync() -> Result<chrono::DateTime<Utc>> { + fn read_from_data_dir(filename: &str) -> Option<String> { let data_dir = atuin_common::utils::data_dir(); let data_dir = data_dir.as_path(); - let sync_time_path = data_dir.join("last_sync_time"); + let path = data_dir.join(filename); - if !sync_time_path.exists() { - return Ok(Utc.ymd(1970, 1, 1).and_hms(0, 0, 0)); + if !path.exists() { + return None; } - let time = fs_err::read_to_string(sync_time_path)?; - let time = chrono::DateTime::parse_from_rfc3339(time.as_str())?; + let value = fs_err::read_to_string(path); + + value.ok() + } + + fn save_current_time(filename: &str) -> Result<()> { + Settings::save_to_data_dir(filename, Utc::now().to_rfc3339().as_str())?; + + Ok(()) + } + + fn load_time_from_file(filename: &str) -> Result<chrono::DateTime<Utc>> { + let value = Settings::read_from_data_dir(filename); - Ok(time.with_timezone(&Utc)) + match value { + Some(v) => { + let time = chrono::DateTime::parse_from_rfc3339(v.as_str())?; + + Ok(time.with_timezone(&Utc)) + } + None => Ok(Utc.ymd(1970, 1, 1).and_hms(0, 0, 0)), + } + } + + pub fn save_sync_time() -> Result<()> { + Settings::save_current_time(LAST_SYNC_FILENAME) + } + + pub fn save_version_check_time() -> Result<()> { + Settings::save_current_time(LAST_VERSION_CHECK_FILENAME) + } + + pub fn last_sync() -> Result<chrono::DateTime<Utc>> { + Settings::load_time_from_file(LAST_SYNC_FILENAME) + } + + pub fn last_version_check() -> Result<chrono::DateTime<Utc>> { + Settings::load_time_from_file(LAST_VERSION_CHECK_FILENAME) } pub fn should_sync(&self) -> Result<bool> { @@ -142,6 +181,67 @@ impl Settings { } } + fn needs_update_check(&self) -> Result<bool> { + let last_check = Settings::last_version_check()?; + let diff = Utc::now() - last_check; + + // Check a max of once per hour + Ok(diff.num_hours() >= 1) + } + + async fn latest_version(&self) -> Result<Version> { + // Default to the current version, and if that doesn't parse, a version so high it's unlikely to ever + // suggest upgrading. + let current = + Version::parse(env!("CARGO_PKG_VERSION")).unwrap_or(Version::new(100000, 0, 0)); + + if !self.needs_update_check()? { + // Worst case, we don't want Atuin to fail to start because something funky is going on with + // version checking. + let version = match Settings::read_from_data_dir(LATEST_VERSION_FILENAME) { + Some(v) => Version::parse(&v).unwrap_or(current), + None => current, + }; + + return Ok(version); + } + + #[cfg(feature = "sync")] + let latest = crate::api_client::latest_version().await.unwrap_or(current); + + #[cfg(not(feature = "sync"))] + let latest = current; + + Settings::save_version_check_time()?; + Settings::save_to_data_dir(LATEST_VERSION_FILENAME, latest.to_string().as_str())?; + + Ok(latest) + } + + // Return Some(latest version) if an update is needed. Otherwise, none. + pub async fn needs_update(&self) -> Option<Version> { + if !self.update_check { + return None; + } + + let current = + Version::parse(env!("CARGO_PKG_VERSION")).unwrap_or(Version::new(100000, 0, 0)); + + let latest = self.latest_version().await; + + if latest.is_err() { + return None; + } + + let latest = latest.unwrap(); + + if latest > current { + return Some(latest); + } + + None + } + pub fn new() -> Result<Self> { let config_dir = atuin_common::utils::config_dir(); @@ -172,6 +272,7 @@ impl Settings { .set_default("session_path", session_path.to_str())? .set_default("dialect", "us")? .set_default("auto_sync", true)? + .set_default("update_check", true)? .set_default("sync_frequency", "1h")? .set_default("sync_address", "https://api.atuin.sh")? .set_default("search_mode", "prefix")? |
