From ac13d0f707d4014dc85ef911d7f07e37e2d082bc Mon Sep 17 00:00:00 2001 From: fmoletta Date: Thu, 13 Nov 2025 13:06:29 -0300 Subject: [PATCH 1/5] feat(l1): periodically check if there is a newer release available --- Cargo.lock | 1 + cmd/ethrex/Cargo.toml | 1 + cmd/ethrex/ethrex.rs | 60 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 62 insertions(+) diff --git a/Cargo.lock b/Cargo.lock index 6d7132b0139..818e5f1734c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3308,6 +3308,7 @@ dependencies = [ "rand 0.8.5", "reqwest 0.12.24", "secp256k1", + "semver 1.0.27", "serde", "serde_json", "spawned-concurrency", diff --git a/cmd/ethrex/Cargo.toml b/cmd/ethrex/Cargo.toml index 5092c49cc48..1c966e500e3 100644 --- a/cmd/ethrex/Cargo.toml +++ b/cmd/ethrex/Cargo.toml @@ -51,6 +51,7 @@ reqwest.workspace = true thiserror.workspace = true itertools = "0.14.0" url.workspace = true +semver = "1.0.27" spawned-rt.workspace = true spawned-concurrency.workspace = true diff --git a/cmd/ethrex/ethrex.rs b/cmd/ethrex/ethrex.rs index aeeb16a20ea..75d65733e58 100644 --- a/cmd/ethrex/ethrex.rs +++ b/cmd/ethrex/ethrex.rs @@ -5,6 +5,8 @@ use ethrex::{ utils::{NodeConfigFile, get_client_version, store_node_config_file}, }; use ethrex_p2p::{discv4::peer_table::PeerTable, types::NodeRecord}; +use semver::Version; +use serde::Deserialize; use std::{path::Path, time::Duration}; use tokio::signal::unix::{SignalKind, signal}; use tokio_util::sync::CancellationToken; @@ -45,6 +47,63 @@ async fn server_shutdown( info!("Server shutting down!"); } +/// Fetches the latest release version on github +/// Returns None if there was an error when requesting the latest version +async fn latest_release_version() -> Option { + #[derive(Deserialize)] + struct Release { + tag_name: String, + } + let client = reqwest::Client::new(); + let response = client + .get("https://api.github.com/repos/lambdaclass/ethrex/releases/latest") + .header("User-Agent", "ethrex") + .send() + .await + .ok()?; + if !response.status().is_success() { + None + } else { + Version::parse( + response + .json::() + .await + .ok()? + .tag_name + .trim_start_matches("v"), + ) + .ok() + } +} + +/// Reads current crate version +fn current_version() -> Option { + Version::parse(env!("CARGO_PKG_VERSION")).ok() +} + +/// Checks if the latest released version is higher than the current version and emits an info log +/// Won't emit a log line if the current version is newer or equal, or if there was a problem reading either version +async fn check_version_update() { + if let (Some(current_version), Some(latest_version)) = + (current_version(), latest_release_version().await) + { + if current_version < latest_version { + info!( + "There is a newer ethrex version available, current version: {current_version} vs latest version: {latest_version}" + ); + } + } +} + +/// Checks if there is a newer ethrex verison available every hour +async fn periodically_check_version_update() { + let mut interval = tokio::time::interval(Duration::from_secs(60 * 60)); + loop { + interval.tick().await; + check_version_update().await; + } +} + #[tokio::main] async fn main() -> eyre::Result<()> { let CLI { opts, command } = CLI::parse(); @@ -56,6 +115,7 @@ async fn main() -> eyre::Result<()> { let log_filter_handler = init_tracing(&opts); info!("ethrex version: {}", get_client_version()); + tokio::spawn(periodically_check_version_update()); let (datadir, cancel_token, peer_table, local_node_record) = init_l1(opts, Some(log_filter_handler)).await?; From ed2d554c29f5e5157a7b82debfaa01e20395eaf9 Mon Sep 17 00:00:00 2001 From: fmoletta Date: Thu, 13 Nov 2025 13:12:30 -0300 Subject: [PATCH 2/5] clippy --- cmd/ethrex/ethrex.rs | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/cmd/ethrex/ethrex.rs b/cmd/ethrex/ethrex.rs index 75d65733e58..e78793cf2de 100644 --- a/cmd/ethrex/ethrex.rs +++ b/cmd/ethrex/ethrex.rs @@ -86,12 +86,11 @@ fn current_version() -> Option { async fn check_version_update() { if let (Some(current_version), Some(latest_version)) = (current_version(), latest_release_version().await) + && current_version < latest_version { - if current_version < latest_version { - info!( - "There is a newer ethrex version available, current version: {current_version} vs latest version: {latest_version}" - ); - } + info!( + "There is a newer ethrex version available, current version: {current_version} vs latest version: {latest_version}" + ); } } From f637f4e8f14f6a3b389a8da936ce75d8d4ecae3a Mon Sep 17 00:00:00 2001 From: fmoletta Date: Tue, 2 Dec 2025 18:55:24 -0300 Subject: [PATCH 3/5] Implement manual version comparison --- Cargo.lock | 1 - cmd/ethrex/Cargo.toml | 1 - cmd/ethrex/ethrex.rs | 39 ++++++++++++++++++++++++++++----------- 3 files changed, 28 insertions(+), 13 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 818e5f1734c..6d7132b0139 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3308,7 +3308,6 @@ dependencies = [ "rand 0.8.5", "reqwest 0.12.24", "secp256k1", - "semver 1.0.27", "serde", "serde_json", "spawned-concurrency", diff --git a/cmd/ethrex/Cargo.toml b/cmd/ethrex/Cargo.toml index 1c966e500e3..5092c49cc48 100644 --- a/cmd/ethrex/Cargo.toml +++ b/cmd/ethrex/Cargo.toml @@ -51,7 +51,6 @@ reqwest.workspace = true thiserror.workspace = true itertools = "0.14.0" url.workspace = true -semver = "1.0.27" spawned-rt.workspace = true spawned-concurrency.workspace = true diff --git a/cmd/ethrex/ethrex.rs b/cmd/ethrex/ethrex.rs index e78793cf2de..b394443c8e3 100644 --- a/cmd/ethrex/ethrex.rs +++ b/cmd/ethrex/ethrex.rs @@ -5,7 +5,6 @@ use ethrex::{ utils::{NodeConfigFile, get_client_version, store_node_config_file}, }; use ethrex_p2p::{discv4::peer_table::PeerTable, types::NodeRecord}; -use semver::Version; use serde::Deserialize; use std::{path::Path, time::Duration}; use tokio::signal::unix::{SignalKind, signal}; @@ -49,7 +48,7 @@ async fn server_shutdown( /// Fetches the latest release version on github /// Returns None if there was an error when requesting the latest version -async fn latest_release_version() -> Option { +async fn latest_release_version() -> Option { #[derive(Deserialize)] struct Release { tag_name: String, @@ -64,32 +63,50 @@ async fn latest_release_version() -> Option { if !response.status().is_success() { None } else { - Version::parse( + Some( response .json::() .await .ok()? .tag_name - .trim_start_matches("v"), + .trim_start_matches("v") + .to_string(), ) - .ok() } } /// Reads current crate version -fn current_version() -> Option { - Version::parse(env!("CARGO_PKG_VERSION")).ok() +fn current_version() -> &'static str { + env!("CARGO_PKG_VERSION") +} + +fn is_higher_than_current(latest_version: &str) -> bool { + // ethrex.x.y.z + let current_version_numbers = current_version() + .split(".") + .map(|c| c.parse::().unwrap_or_default()); + let latest_version_numbers = latest_version + .split(".") + .map(|c| c.parse::().unwrap_or_default()); + for (current, latest) in current_version_numbers.zip(latest_version_numbers) { + match current.cmp(&latest) { + std::cmp::Ordering::Less => return true, + std::cmp::Ordering::Equal => {} + std::cmp::Ordering::Greater => return false, + }; + } + false } /// Checks if the latest released version is higher than the current version and emits an info log /// Won't emit a log line if the current version is newer or equal, or if there was a problem reading either version async fn check_version_update() { - if let (Some(current_version), Some(latest_version)) = - (current_version(), latest_release_version().await) - && current_version < latest_version + if let Some(latest_version) = latest_release_version().await + && is_higher_than_current(&latest_version) { info!( - "There is a newer ethrex version available, current version: {current_version} vs latest version: {latest_version}" + "There is a newer ethrex version available, current version: {} vs latest version: {latest_version}", + current_version() ); } } From 18c2960a2652b67c4ab718ec1b9cb5e0d74999c4 Mon Sep 17 00:00:00 2001 From: fmoletta Date: Wed, 3 Dec 2025 11:17:14 -0300 Subject: [PATCH 4/5] docs --- cmd/ethrex/ethrex.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/ethrex/ethrex.rs b/cmd/ethrex/ethrex.rs index b394443c8e3..c6702e38a36 100644 --- a/cmd/ethrex/ethrex.rs +++ b/cmd/ethrex/ethrex.rs @@ -80,8 +80,8 @@ fn current_version() -> &'static str { env!("CARGO_PKG_VERSION") } +/// Returns true if the received latest version is higher than the current ethrex version fn is_higher_than_current(latest_version: &str) -> bool { - // ethrex.x.y.z let current_version_numbers = current_version() .split(".") .map(|c| c.parse::().unwrap_or_default()); From b3734d33c3fcad5136f7838d368dbe7486785dc8 Mon Sep 17 00:00:00 2001 From: fmoletta Date: Wed, 3 Dec 2025 15:37:27 -0300 Subject: [PATCH 5/5] Move url to constant --- cmd/ethrex/ethrex.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/cmd/ethrex/ethrex.rs b/cmd/ethrex/ethrex.rs index 18c0e122798..96851dbe26c 100644 --- a/cmd/ethrex/ethrex.rs +++ b/cmd/ethrex/ethrex.rs @@ -11,6 +11,8 @@ use tokio::signal::unix::{SignalKind, signal}; use tokio_util::sync::CancellationToken; use tracing::info; +const LATEST_VERSION_URL: &str = "https://api.github.com/repos/lambdaclass/ethrex/releases/latest"; + #[cfg(all(feature = "jemalloc", not(target_env = "msvc")))] #[global_allocator] static ALLOC: tikv_jemallocator::Jemalloc = tikv_jemallocator::Jemalloc; @@ -55,7 +57,7 @@ async fn latest_release_version() -> Option { } let client = reqwest::Client::new(); let response = client - .get("https://api.github.com/repos/lambdaclass/ethrex/releases/latest") + .get(LATEST_VERSION_URL) .header("User-Agent", "ethrex") .send() .await