From d87caa06f629d40cb4e9f4e4e26daefbc673c2f6 Mon Sep 17 00:00:00 2001 From: Carson McManus Date: Sun, 25 Jun 2023 09:24:53 -0400 Subject: [PATCH] add an update checker (#222) --- Cargo.lock | 59 ++++++++++++++++++++++++++++++++++++++++++++----- Cargo.toml | 8 +++---- src/commands.rs | 8 +++++++ src/main.rs | 51 +++++++++++++++++++++++++++++++----------- src/updater.rs | 46 ++++++++++++++++++++++++++++++++++++++ 5 files changed, 150 insertions(+), 22 deletions(-) create mode 100644 src/updater.rs diff --git a/Cargo.lock b/Cargo.lock index 8f595f5..55da01e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -416,13 +416,22 @@ dependencies = [ "generic-array", ] +[[package]] +name = "directories" +version = "5.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a49173b84e034382284f27f1af4dcbbd231ffa358c0fe316541a7337f376a35" +dependencies = [ + "dirs-sys 0.4.1", +] + [[package]] name = "dirs" version = "3.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "30baa043103c9d0c2a57cf537cc2f35623889dc0d405e6c3cccfadbc81c71309" dependencies = [ - "dirs-sys", + "dirs-sys 0.3.7", ] [[package]] @@ -436,6 +445,18 @@ dependencies = [ "winapi", ] +[[package]] +name = "dirs-sys" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "520f05a5cbd335fae5a99ff7a6ab8627577660ee5cfd6a94a6a929b52ff0321c" +dependencies = [ + "libc", + "option-ext", + "redox_users", + "windows-sys 0.48.0", +] + [[package]] name = "discard" version = "1.0.4" @@ -966,7 +987,7 @@ dependencies = [ "libc", "log", "wasi 0.11.0+wasi-snapshot-preview1", - "windows-sys", + "windows-sys 0.36.1", ] [[package]] @@ -1069,6 +1090,12 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" +[[package]] +name = "option-ext" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" + [[package]] name = "os_str_bytes" version = "6.1.0" @@ -1095,7 +1122,7 @@ dependencies = [ "libc", "redox_syscall", "smallvec", - "windows-sys", + "windows-sys 0.36.1", ] [[package]] @@ -1837,9 +1864,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.81" +version = "1.0.99" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b7ce2b32a1aed03c558dc61a5cd328f15aff2dbc17daad8fb8af04d2100e15c" +checksum = "46266871c240a00b8f503b877622fe33430b3c7d963bdc0f2adc511e54a1eae3" dependencies = [ "itoa 1.0.2", "ryu", @@ -2112,6 +2139,7 @@ dependencies = [ "tempdir", "text_io", "thiserror", + "update-informer", "uuid", "zeroize", ] @@ -2495,6 +2523,18 @@ version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" +[[package]] +name = "update-informer" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19d952f24d9cd8b7564d22c7b2cde3429c48b50c6f4e72b07b382ae4ee5f21b3" +dependencies = [ + "directories", + "semver 1.0.9", + "serde", + "serde_json", +] + [[package]] name = "url" version = "2.2.2" @@ -2721,6 +2761,15 @@ dependencies = [ "windows_x86_64_msvc 0.36.1", ] +[[package]] +name = "windows-sys" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets", +] + [[package]] name = "windows-targets" version = "0.48.0" diff --git a/Cargo.toml b/Cargo.toml index 0d5a65f..d2b0bc8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,8 +1,6 @@ [workspace] -members = [ - "steamguard" -] +members = ["steamguard"] [package] name = "steamguard-cli" @@ -16,8 +14,9 @@ repository = "https://github.com/dyc3/steamguard-cli" license = "GPL-3.0-or-later" [features] -default = ["qr"] +default = ["qr", "updater"] qr = ["qrcode"] +updater = ["update-informer"] # [[bin]] # name = "steamguard-cli" @@ -61,6 +60,7 @@ gethostname = "0.4.3" secrecy = { version = "0.8", features = ["serde"] } zeroize = "^1.4.3" serde_path_to_error = "0.1.11" +update-informer = { version = "1.0.0", optional = true, default-features = false, features = ["github"] } [dev-dependencies] tempdir = "0.3" diff --git a/src/commands.rs b/src/commands.rs index 73d04dc..cf39f8b 100644 --- a/src/commands.rs +++ b/src/commands.rs @@ -104,6 +104,14 @@ pub(crate) struct GlobalArgs { pub passkey: Option, #[clap(short, long, arg_enum, default_value_t=Verbosity::Info, help = "Set the log level. Be warned, trace is capable of printing sensitive data.")] pub verbosity: Verbosity, + + #[cfg(feature = "updater")] + #[clap( + long, + help = "Disable checking for updates.", + long_help = "Disable checking for updates. By default, steamguard-cli will check for updates every now and then. This can be disabled with this flag." + )] + pub no_update_check: bool, } #[derive(Debug, Clone, Parser)] diff --git a/src/main.rs b/src/main.rs index c8fd9f5..9d2727a 100644 --- a/src/main.rs +++ b/src/main.rs @@ -33,30 +33,55 @@ mod encryption; mod errors; mod secret_string; pub(crate) mod tui; +#[cfg(feature = "updater")] +mod updater; fn main() { - std::process::exit(match run() { + let args = commands::Args::parse(); + + stderrlog::new() + .verbosity(args.global.verbosity as usize) + .module(module_path!()) + .module("steamguard") + .init() + .unwrap(); + debug!("{:?}", args); + #[cfg(feature = "updater")] + let should_do_update_check = !args.global.no_update_check; + + let exit_code = match run(args) { Ok(_) => 0, Err(e) => { error!("{:?}", e); 255 } - }); + }; + + #[cfg(feature = "updater")] + if should_do_update_check { + match updater::check_for_update() { + Ok(Some(version)) => { + eprintln!(); + info!( + "steamguard-cli v{} is available. Download it here: https://github.com/dyc3/steamguard-cli/releases", + version + ); + } + Ok(None) => { + debug!("No update available"); + } + Err(e) => { + warn!("Failed to check for updates: {}", e); + } + } + } + + std::process::exit(exit_code); } -fn run() -> anyhow::Result<()> { - let args = commands::Args::parse(); - info!("{:?}", args); - +fn run(args: commands::Args) -> anyhow::Result<()> { let globalargs = args.global; - stderrlog::new() - .verbosity(globalargs.verbosity as usize) - .module(module_path!()) - .module("steamguard") - .init() - .unwrap(); - let cmd: CommandType = match args.sub.unwrap_or(Subcommands::Code(args.code)) { Subcommands::Debug(args) => CommandType::Const(Box::new(args)), Subcommands::Completion(args) => CommandType::Const(Box::new(args)), diff --git a/src/updater.rs b/src/updater.rs new file mode 100644 index 0000000..709f2ac --- /dev/null +++ b/src/updater.rs @@ -0,0 +1,46 @@ +use std::time::Duration; + +use serde::de::DeserializeOwned; +use update_informer::{ + http_client::{HeaderMap, HttpClient}, + registry, Check, Version, +}; + +use crate::debug; + +pub fn check_for_update() -> update_informer::Result> { + let name = "dyc3/steamguard-cli"; + let version = env!("CARGO_PKG_VERSION"); + debug!("Checking for updates to {} v{}", name, version); + let informer = update_informer::new(registry::GitHub, name, version) + .http_client(ReqwestHttpClient) + .interval(Duration::from_secs(60 * 60 * 24 * 2)); + + informer.check_version() +} + +struct ReqwestHttpClient; + +impl HttpClient for ReqwestHttpClient { + fn get( + url: &str, + timeout: Duration, + headers: HeaderMap, + ) -> update_informer::Result { + let mut req = reqwest::blocking::Client::builder() + .timeout(timeout) + .build()? + .get(url) + .header(reqwest::header::USER_AGENT, "steamguard-cli"); + + for (key, value) in headers { + req = req.header(key, value); + } + + let resp = req.send()?; + debug!("Update check response status: {:?}", resp.status()); + let json = resp.json()?; + + Ok(json) + } +}