diff --git a/swap/src/bin/asb.rs b/swap/src/bin/asb.rs index a0e9b5e3..fd2da1f9 100644 --- a/swap/src/bin/asb.rs +++ b/swap/src/bin/asb.rs @@ -29,6 +29,7 @@ use swap::asb::config::{ initial_setup, query_user_for_initial_config, read_config, Config, ConfigNotInitialized, }; use swap::asb::{cancel, punish, redeem, refund, safely_abort, EventLoop, Finality, KrakenRate}; +use swap::common::check_latest_version; use swap::database::open_db; use swap::monero::Amount; use swap::network::rendezvous::XmrBtcNamespace; @@ -68,6 +69,10 @@ async fn main() -> Result<()> { } }; + if let Err(e) = check_latest_version(env!("CARGO_PKG_VERSION")).await { + eprintln!("{}", e); + } + asb::tracing::init(LevelFilter::DEBUG, json, !disable_timestamp).expect("initialize tracing"); let config = match read_config(config_path.clone())? { diff --git a/swap/src/bin/swap.rs b/swap/src/bin/swap.rs index 580b7d23..be5a92cc 100644 --- a/swap/src/bin/swap.rs +++ b/swap/src/bin/swap.rs @@ -26,6 +26,7 @@ use std::time::Duration; use swap::bitcoin::TxLock; use swap::cli::command::{parse_args_and_apply_defaults, Arguments, Command, ParseResult}; use swap::cli::{list_sellers, EventLoop, SellerStatus}; +use swap::common::check_latest_version; use swap::database::open_db; use swap::env::Config; use swap::libp2p_ext::MultiAddrExt; @@ -54,6 +55,10 @@ async fn main() -> Result<()> { } }; + if let Err(e) = check_latest_version(env!("CARGO_PKG_VERSION")).await { + eprintln!("{}", e); + } + match cmd { Command::BuyXmr { seller, @@ -69,11 +74,6 @@ async fn main() -> Result<()> { cli::tracing::init(debug, json, data_dir.join("logs"), Some(swap_id))?; - match check_latest_version().await { - Ok(()) => (), - Err(error) => tracing::error!("{}", error), - }; - let db = open_db(data_dir.join("sqlite")).await?; let seed = Seed::from_file_or_generate(data_dir.as_path()) .context("Failed to read in seed file")?; @@ -164,11 +164,6 @@ async fn main() -> Result<()> { Command::History => { cli::tracing::init(debug, json, data_dir.join("logs"), None)?; - match check_latest_version().await { - Ok(()) => (), - Err(error) => tracing::error!("{}", error), - }; - let db = open_db(data_dir.join("sqlite")).await?; let swaps = db.all().await?; @@ -193,11 +188,6 @@ async fn main() -> Result<()> { Command::Config => { cli::tracing::init(debug, json, data_dir.join("logs"), None)?; - match check_latest_version().await { - Ok(()) => (), - Err(error) => tracing::error!("{}", error), - }; - tracing::info!(path=%data_dir.display(), "Data directory"); tracing::info!(path=%format!("{}/logs", data_dir.display()), "Log files directory"); tracing::info!(path=%format!("{}/sqlite", data_dir.display()), "Sqlite file location"); @@ -213,11 +203,6 @@ async fn main() -> Result<()> { } => { cli::tracing::init(debug, json, data_dir.join("logs"), None)?; - match check_latest_version().await { - Ok(()) => (), - Err(error) => tracing::error!("{}", error), - }; - let seed = Seed::from_file_or_generate(data_dir.as_path()) .context("Failed to read in seed file")?; let bitcoin_wallet = init_bitcoin_wallet( @@ -252,11 +237,6 @@ async fn main() -> Result<()> { } => { cli::tracing::init(debug, json, data_dir.join("logs"), None)?; - match check_latest_version().await { - Ok(()) => (), - Err(error) => tracing::error!("{}", error), - }; - let seed = Seed::from_file_or_generate(data_dir.as_path()) .context("Failed to read in seed file")?; let bitcoin_wallet = init_bitcoin_wallet( @@ -284,11 +264,6 @@ async fn main() -> Result<()> { } => { cli::tracing::init(debug, json, data_dir.join("logs"), Some(swap_id))?; - match check_latest_version().await { - Ok(()) => (), - Err(error) => tracing::error!("{}", error), - }; - let db = open_db(data_dir.join("sqlite")).await?; let seed = Seed::from_file_or_generate(data_dir.as_path()) .context("Failed to read in seed file")?; @@ -356,11 +331,6 @@ async fn main() -> Result<()> { } => { cli::tracing::init(debug, json, data_dir.join("logs"), Some(swap_id))?; - match check_latest_version().await { - Ok(()) => (), - Err(error) => tracing::error!("{}", error), - }; - let db = open_db(data_dir.join("sqlite")).await?; let seed = Seed::from_file_or_generate(data_dir.as_path()) .context("Failed to read in seed file")?; @@ -384,11 +354,6 @@ async fn main() -> Result<()> { } => { cli::tracing::init(debug, json, data_dir.join("logs"), Some(swap_id))?; - match check_latest_version().await { - Ok(()) => (), - Err(error) => tracing::error!("{}", error), - }; - let db = open_db(data_dir.join("sqlite")).await?; let seed = Seed::from_file_or_generate(data_dir.as_path()) .context("Failed to read in seed file")?; @@ -414,10 +379,6 @@ async fn main() -> Result<()> { .context("Rendezvous node address must contain peer ID")?; cli::tracing::init(debug, json, data_dir.join("logs"), None)?; - match check_latest_version().await { - Ok(()) => (), - Err(error) => tracing::error!("{}", error), - }; let seed = Seed::from_file_or_generate(data_dir.as_path()) .context("Failed to read in seed file")?; @@ -499,11 +460,6 @@ async fn main() -> Result<()> { } => { cli::tracing::init(debug, json, data_dir.join("logs"), None)?; - match check_latest_version().await { - Ok(()) => (), - Err(error) => tracing::error!("{}", error), - }; - let seed = Seed::from_file_or_generate(data_dir.as_path()) .context("Failed to read in seed file")?; let bitcoin_wallet = init_bitcoin_wallet( @@ -520,11 +476,6 @@ async fn main() -> Result<()> { Command::MoneroRecovery { swap_id } => { cli::tracing::init(debug, json, data_dir.join("logs"), Some(swap_id))?; - match check_latest_version().await { - Ok(()) => (), - Err(error) => tracing::error!("{}", error), - }; - let db = open_db(data_dir.join("sqlite")).await?; let swap_state: BobState = db.get_state(swap_id).await?.try_into()?; @@ -717,43 +668,10 @@ where Ok((btc_swap_amount, fees)) } -pub async fn check_latest_version() -> Result<()> { - const GITHUB_LATEST_VERSION_URL: &str = - "https://github.com/comit-network/xmr-btc-swap/releases/latest"; - - let response = match reqwest::get(GITHUB_LATEST_VERSION_URL).await { - Ok(res) => res, - Err(_) => bail!( - "could not request the website {}", - GITHUB_LATEST_VERSION_URL - ), - }; - - let last_version_from_url: &str = match response.url().path_segments() { - Some(split_segments) => match split_segments.last() { - Some(seg) => seg, - None => bail!("could not check the latest version"), - }, - None => bail!("could not check the latest version"), - }; - - let version_from_binary: &str = env!("CARGO_PKG_VERSION"); - - if last_version_from_url != version_from_binary { - tracing::warn!( - "You are not on the lastest version {}, it's available on Github: {}", - last_version_from_url, - GITHUB_LATEST_VERSION_URL - ); - }; - - Ok(()) -} - #[cfg(test)] mod tests { use super::*; - use crate::{check_latest_version, determine_btc_to_swap}; + use crate::determine_btc_to_swap; use ::bitcoin::Amount; use std::sync::Mutex; use swap::tracing_ext::capture_logs; @@ -1130,13 +1048,4 @@ mod tests { async fn get_dummy_address() -> Result { Ok("1PdfytjS7C8wwd9Lq5o4x9aXA2YRqaCpH6".parse()?) } - - #[tokio::test] - async fn check_correct_latest_version() { - let writer = capture_logs(LevelFilter::INFO); - - check_latest_version().await.unwrap(); - - assert_eq!(writer.captured(), r""); - } } diff --git a/swap/src/common.rs b/swap/src/common.rs new file mode 100644 index 00000000..66adca83 --- /dev/null +++ b/swap/src/common.rs @@ -0,0 +1,57 @@ +use anyhow::anyhow; + +const LATEST_RELEASE_URL: &str = "https://github.com/comit-network/xmr-btc-swap/releases/latest"; + +#[derive(Clone, Debug, PartialEq)] +pub enum Version { + Current, + Available, +} + +/// Check the latest release from GitHub API. +pub async fn check_latest_version(current: &str) -> anyhow::Result { + let response = reqwest::get(LATEST_RELEASE_URL).await?; + let e = "Failed to get latest release."; + let url = response.url(); + let segments = url.path_segments().ok_or_else(|| anyhow!(e))?; + let latest = segments.last().ok_or_else(|| anyhow!(e))?; + + let result = if is_latest_version(current, latest) { + Version::Current + } else { + println!( + "You are not on the latest version: {} is available. \n{}", + latest, url + ); + Version::Available + }; + + Ok(result) +} + +// todo: naive implementation can be improved using semver +fn is_latest_version(current: &str, latest: &str) -> bool { + current == latest +} + +#[cfg(test)] +mod test { + use super::*; + + #[test] + fn it_compares_versions() { + assert!(is_latest_version("0.10.2", "0.10.2")); + assert!(!is_latest_version("0.10.2", "0.10.3")); + assert!(!is_latest_version("0.10.2", "0.11.0")); + } + + #[tokio::test] + #[ignore = "For local testing, makes http requests to github."] + async fn it_compares_with_github() { + let result = check_latest_version("0.10.1").await.unwrap(); + assert_eq!(result, Version::Available); + + let result = check_latest_version("0.10.2").await.unwrap(); + assert_eq!(result, Version::Current); + } +} diff --git a/swap/src/lib.rs b/swap/src/lib.rs index 1b050ae2..4865187b 100644 --- a/swap/src/lib.rs +++ b/swap/src/lib.rs @@ -19,6 +19,7 @@ pub mod asb; pub mod bitcoin; pub mod cli; +pub mod common; pub mod database; pub mod env; pub mod fs;