From 2791f86fedb9af87ad2ef4a37d2ee729c2e28e7d Mon Sep 17 00:00:00 2001 From: Brandon Shippy Date: Fri, 26 Jun 2026 18:48:06 -0700 Subject: [PATCH 1/3] version cli top level command --- Cargo.toml | 1 + rust/crates/sift_cli/Cargo.toml | 4 +- rust/crates/sift_cli/src/cli/mod.rs | 3 + rust/crates/sift_cli/src/cmd/mod.rs | 1 + rust/crates/sift_cli/src/cmd/version.rs | 75 +++++++++++++++++++++++++ rust/crates/sift_cli/src/main.rs | 1 + 6 files changed, 84 insertions(+), 1 deletion(-) create mode 100644 rust/crates/sift_cli/src/cmd/version.rs diff --git a/Cargo.toml b/Cargo.toml index 1efff87a2..fb8bca79f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -65,6 +65,7 @@ pyo3-stub-gen = "0.10" rand = "0.10" reqwest = "0.13" rmcp = "1.7.0" +semver = "1.0" serde = "^1.0" serde_json = "^1.0" tdms = "0.3.0" diff --git a/rust/crates/sift_cli/Cargo.toml b/rust/crates/sift_cli/Cargo.toml index b28014861..99d6acf7a 100644 --- a/rust/crates/sift_cli/Cargo.toml +++ b/rust/crates/sift_cli/Cargo.toml @@ -30,7 +30,9 @@ indicatif = { workspace = true } parquet = { workspace = true } pbjson-types = { workspace = true } percent-encoding = { workspace = true } -reqwest = { workspace = true } +reqwest = { workspace = true, features = ["json"] } +semver = { workspace = true } +serde = { workspace = true } serde_json = { workspace = true } sift_mcp.workspace = true sift_pbfs = { workspace = true } diff --git a/rust/crates/sift_cli/src/cli/mod.rs b/rust/crates/sift_cli/src/cli/mod.rs index 34a354aa1..b3bbfec8e 100644 --- a/rust/crates/sift_cli/src/cli/mod.rs +++ b/rust/crates/sift_cli/src/cli/mod.rs @@ -59,6 +59,9 @@ pub enum Cmd { /// Ping the Sift API to verify credentials and connectivity Ping, + + /// Print the installed CLI version and check for a newer release on GitHub + Version, } /// Serve the bundled Sift CLI user documentation over HTTP. diff --git a/rust/crates/sift_cli/src/cmd/mod.rs b/rust/crates/sift_cli/src/cmd/mod.rs index 64d7704d2..7716e378b 100644 --- a/rust/crates/sift_cli/src/cmd/mod.rs +++ b/rust/crates/sift_cli/src/cmd/mod.rs @@ -11,6 +11,7 @@ pub mod import; pub mod install; pub mod mcp; pub mod ping; +pub mod version; pub struct Context { pub grpc_uri: String, diff --git a/rust/crates/sift_cli/src/cmd/version.rs b/rust/crates/sift_cli/src/cmd/version.rs new file mode 100644 index 000000000..a1f3478ed --- /dev/null +++ b/rust/crates/sift_cli/src/cmd/version.rs @@ -0,0 +1,75 @@ +use std::{process::ExitCode, time::Duration}; + +use anyhow::Result; +use crossterm::style::Stylize; +use reqwest::ClientBuilder; +use semver::Version; +use serde::Deserialize; + +use crate::util::tty::Output; + +const RELEASES_URL: &str = + "https://api.github.com/repos/sift-stack/sift/releases?per_page=100"; +const TAG_PREFIX: &str = "sift_cli-v"; +const USER_AGENT: &str = concat!("sift-cli/", env!("CARGO_PKG_VERSION")); + +#[derive(Deserialize)] +struct GithubRelease { + tag_name: String, +} + +pub async fn run() -> Result { + let current_str = env!("CARGO_PKG_VERSION"); + let current = Version::parse(current_str)?; + + let mut out = Output::new(); + out.line(format!("sift-cli {}", current_str.bold())); + + match fetch_latest().await { + Ok(Some(latest)) => { + out.line(format!("Latest release: {}", latest.to_string().bold())); + if latest > current { + out.line(format!( + "{}: {} → {}", + "Update available".green(), + current_str.yellow(), + latest.to_string().green(), + )); + out.tip("run `sift-cli update` to install the latest release"); + } else { + out.line("You're on the latest release.".to_string()); + } + } + Ok(None) => { + out.line(format!( + "{}: no `{TAG_PREFIX}*` releases found on GitHub", + "warning".yellow(), + )); + } + Err(err) => { + out.line(format!( + "{}: unable to check for updates ({err})", + "warning".yellow(), + )); + } + } + + out.print(); + Ok(ExitCode::SUCCESS) +} + +async fn fetch_latest() -> Result> { + let client = ClientBuilder::new() + .user_agent(USER_AGENT) + .timeout(Duration::from_secs(5)) + .build()?; + + let releases: Vec = + client.get(RELEASES_URL).send().await?.error_for_status()?.json().await?; + + Ok(releases + .into_iter() + .filter_map(|r| r.tag_name.strip_prefix(TAG_PREFIX).map(str::to_string)) + .filter_map(|v| Version::parse(&v).ok()) + .max()) +} diff --git a/rust/crates/sift_cli/src/main.rs b/rust/crates/sift_cli/src/main.rs index 086f8eb31..00df8a0d1 100644 --- a/rust/crates/sift_cli/src/main.rs +++ b/rust/crates/sift_cli/src/main.rs @@ -65,6 +65,7 @@ fn run(clargs: cli::Args) -> Result { ConfigCmd::Update(args) => return cmd::config::update(clargs.profile, args), }, Cmd::Doc(args) => return run_future(cmd::doc::serve(args)), + Cmd::Version => return run_future(cmd::version::run()), Cmd::Install(cmd) => match cmd { InstallCmd::Completions(cmd) => match cmd { cli::CompletionsCmd::Print(args) => return cmd::install::completions::print(args), From 6930571ffe446be016ecf7bf2c7019f649d9aee3 Mon Sep 17 00:00:00 2001 From: Brandon Shippy Date: Fri, 26 Jun 2026 18:53:38 -0700 Subject: [PATCH 2/3] cargo fmt --- rust/crates/sift_cli/src/cmd/version.rs | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/rust/crates/sift_cli/src/cmd/version.rs b/rust/crates/sift_cli/src/cmd/version.rs index a1f3478ed..cffa46268 100644 --- a/rust/crates/sift_cli/src/cmd/version.rs +++ b/rust/crates/sift_cli/src/cmd/version.rs @@ -8,8 +8,7 @@ use serde::Deserialize; use crate::util::tty::Output; -const RELEASES_URL: &str = - "https://api.github.com/repos/sift-stack/sift/releases?per_page=100"; +const RELEASES_URL: &str = "https://api.github.com/repos/sift-stack/sift/releases?per_page=100"; const TAG_PREFIX: &str = "sift_cli-v"; const USER_AGENT: &str = concat!("sift-cli/", env!("CARGO_PKG_VERSION")); @@ -64,8 +63,13 @@ async fn fetch_latest() -> Result> { .timeout(Duration::from_secs(5)) .build()?; - let releases: Vec = - client.get(RELEASES_URL).send().await?.error_for_status()?.json().await?; + let releases: Vec = client + .get(RELEASES_URL) + .send() + .await? + .error_for_status()? + .json() + .await?; Ok(releases .into_iter() From 4ec2bdc6a04d9f8fa4ef38ddf99d055e3543e56b Mon Sep 17 00:00:00 2001 From: Brandon Shippy Date: Fri, 26 Jun 2026 18:55:40 -0700 Subject: [PATCH 3/3] moving sift-cli update wording to different pr --- rust/crates/sift_cli/src/cmd/version.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/rust/crates/sift_cli/src/cmd/version.rs b/rust/crates/sift_cli/src/cmd/version.rs index cffa46268..96b070bd4 100644 --- a/rust/crates/sift_cli/src/cmd/version.rs +++ b/rust/crates/sift_cli/src/cmd/version.rs @@ -34,7 +34,6 @@ pub async fn run() -> Result { current_str.yellow(), latest.to_string().green(), )); - out.tip("run `sift-cli update` to install the latest release"); } else { out.line("You're on the latest release.".to_string()); }