From 563519ca1778a3205042343eabbd1c936af826ea Mon Sep 17 00:00:00 2001 From: Ben Lovell Date: Wed, 7 Jan 2026 17:17:14 +0100 Subject: [PATCH 1/3] fix: don't tell people to upgrade to old versions --- crates/tower-cmd/src/lib.rs | 4 ++-- crates/tower-cmd/src/output.rs | 2 +- crates/tower-version/src/lib.rs | 9 +++++++++ 3 files changed, 12 insertions(+), 3 deletions(-) diff --git a/crates/tower-cmd/src/lib.rs b/crates/tower-cmd/src/lib.rs index 483f7c48..0a3f581f 100644 --- a/crates/tower-cmd/src/lib.rs +++ b/crates/tower-cmd/src/lib.rs @@ -85,8 +85,8 @@ impl App { if let Some(latest) = Self::check_latest_version().await { let current = tower_version::current_version(); - if current != latest { - output::write_update_message(&latest, ¤t); + if tower_version::is_older_version(current, &latest) { + output::write_update_available_message(&latest, current); } } diff --git a/crates/tower-cmd/src/output.rs b/crates/tower-cmd/src/output.rs index fc3b7bb2..d3f3dc75 100644 --- a/crates/tower-cmd/src/output.rs +++ b/crates/tower-cmd/src/output.rs @@ -501,7 +501,7 @@ pub fn spinner(msg: &str) -> Spinner { Spinner::new(msg.into()) } -pub fn write_update_message(latest: &str, current: &str) { +pub fn write_update_available_message(latest: &str, current: &str) { let line = format!( "{}\n{}\n", format!( diff --git a/crates/tower-version/src/lib.rs b/crates/tower-version/src/lib.rs index 3deb1449..f3f4d45f 100644 --- a/crates/tower-version/src/lib.rs +++ b/crates/tower-version/src/lib.rs @@ -42,3 +42,12 @@ pub async fn check_latest_version() -> Result> { } Ok(None) } + +pub fn is_older_version(current: &str, latest: &str) -> bool { + let parse = |v: &str| { + let parts: Vec<_> = v.split('.').filter_map(|p| p.parse::().ok()).collect(); + (parts.len() == 3).then(|| (parts[0], parts[1], parts[2])) + }; + + matches!((parse(current), parse(latest)), (Some(c), Some(l)) if c < l) +} From 7ce7c1fd990f02c94018619c13f2b129584d9c4c Mon Sep 17 00:00:00 2001 From: Ben Lovell Date: Thu, 8 Jan 2026 21:09:14 +0100 Subject: [PATCH 2/3] feat: warn people when they're ahead of latest release --- crates/tower-cmd/src/lib.rs | 2 ++ crates/tower-cmd/src/output.rs | 14 ++++++++++++++ crates/tower-version/src/lib.rs | 14 +++++++++----- 3 files changed, 25 insertions(+), 5 deletions(-) diff --git a/crates/tower-cmd/src/lib.rs b/crates/tower-cmd/src/lib.rs index 0a3f581f..cd4fcbda 100644 --- a/crates/tower-cmd/src/lib.rs +++ b/crates/tower-cmd/src/lib.rs @@ -87,6 +87,8 @@ impl App { if tower_version::is_older_version(current, &latest) { output::write_update_available_message(&latest, current); + } else if tower_version::is_newer_version(current, &latest) { + output::write_dev_version_message(current, &latest); } } diff --git a/crates/tower-cmd/src/output.rs b/crates/tower-cmd/src/output.rs index d3f3dc75..272a31da 100644 --- a/crates/tower-cmd/src/output.rs +++ b/crates/tower-cmd/src/output.rs @@ -517,6 +517,20 @@ pub fn write_update_available_message(latest: &str, current: &str) { io::stderr().write_all(line.as_bytes()).unwrap(); } +pub fn write_dev_version_message(current: &str, latest: &str) { + let line = format!( + "{}\n", + format!( + "Running dev version {} (latest published: {})", + current, latest + ) + .dimmed() + ); + + use std::io::{self, Write}; + io::stderr().write_all(line.as_bytes()).unwrap(); +} + /// newline just outputs a newline. This is useful when you have a very specific formatting you /// want to maintain and you don't want to use println!. pub fn newline() { diff --git a/crates/tower-version/src/lib.rs b/crates/tower-version/src/lib.rs index f3f4d45f..98f90af7 100644 --- a/crates/tower-version/src/lib.rs +++ b/crates/tower-version/src/lib.rs @@ -43,11 +43,15 @@ pub async fn check_latest_version() -> Result> { Ok(None) } +fn parse_version(v: &str) -> Option<(u32, u32, u32)> { + let parts: Vec<_> = v.split('.').filter_map(|p| p.parse::().ok()).collect(); + (parts.len() == 3).then(|| (parts[0], parts[1], parts[2])) +} + pub fn is_older_version(current: &str, latest: &str) -> bool { - let parse = |v: &str| { - let parts: Vec<_> = v.split('.').filter_map(|p| p.parse::().ok()).collect(); - (parts.len() == 3).then(|| (parts[0], parts[1], parts[2])) - }; + matches!((parse_version(current), parse_version(latest)), (Some(c), Some(l)) if c < l) +} - matches!((parse(current), parse(latest)), (Some(c), Some(l)) if c < l) +pub fn is_newer_version(current: &str, latest: &str) -> bool { + matches!((parse_version(current), parse_version(latest)), (Some(c), Some(l)) if c > l) } From 81af51f6068e1b4b515fd21faa534ecd0c951fbd Mon Sep 17 00:00:00 2001 From: Ben Lovell Date: Thu, 8 Jan 2026 22:39:48 +0100 Subject: [PATCH 3/3] test: MVP tests for version comparison --- crates/tower-version/src/lib.rs | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/crates/tower-version/src/lib.rs b/crates/tower-version/src/lib.rs index 98f90af7..4d8202c6 100644 --- a/crates/tower-version/src/lib.rs +++ b/crates/tower-version/src/lib.rs @@ -55,3 +55,26 @@ pub fn is_older_version(current: &str, latest: &str) -> bool { pub fn is_newer_version(current: &str, latest: &str) -> bool { matches!((parse_version(current), parse_version(latest)), (Some(c), Some(l)) if c > l) } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_current_version_equal() { + assert!(!is_older_version("0.3.39", "0.3.39")); + assert!(!is_newer_version("0.3.39", "0.3.39")); + } + + #[test] + fn test_older_version() { + assert!(is_older_version("0.3.38", "0.3.39")); + assert!(!is_newer_version("0.3.38", "0.3.39")); + } + + #[test] + fn test_newer_version() { + assert!(!is_older_version("0.3.40", "0.3.39")); + assert!(is_newer_version("0.3.40", "0.3.39")); + } +}