wraith/src-tauri/src/commands/updater.rs
Vantz Stockwell c1f8d2a14d
Some checks failed
Build & Sign Wraith / Build Windows + Sign (push) Has been cancelled
fix: read version from tauri.conf.json, not CARGO_PKG_VERSION
CARGO_PKG_VERSION is always 0.1.0 (hardcoded in Cargo.toml). CI
patches tauri.conf.json from the git tag. Now reads app_handle.config()
for the real version so the update checker compares correctly.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-25 01:42:20 -04:00

97 lines
2.9 KiB
Rust

//! Version check against Gitea releases API.
use serde::Serialize;
#[derive(Debug, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct UpdateInfo {
pub current_version: String,
pub latest_version: String,
pub update_available: bool,
pub download_url: String,
pub release_notes: String,
}
/// Check Gitea for the latest release and compare with current version.
#[tauri::command]
pub async fn check_for_updates(app_handle: tauri::AppHandle) -> Result<UpdateInfo, String> {
// Read version from tauri.conf.json (patched by CI from git tag)
// rather than CARGO_PKG_VERSION which is always 0.1.0
let current = app_handle.config().version.clone().unwrap_or_else(|| "0.0.0".to_string());
let client = reqwest::Client::builder()
.timeout(std::time::Duration::from_secs(10))
.build()
.map_err(|e| format!("HTTP client error: {}", e))?;
let resp = client
.get("https://git.command.vigilcyber.com/api/v1/repos/vstockwell/wraith/releases?limit=1")
.header("Accept", "application/json")
.send()
.await
.map_err(|e| format!("Failed to check for updates: {}", e))?;
let releases: Vec<serde_json::Value> = resp.json().await
.map_err(|e| format!("Failed to parse releases: {}", e))?;
let latest = releases.first()
.ok_or_else(|| "No releases found".to_string())?;
let tag = latest.get("tag_name")
.and_then(|v| v.as_str())
.unwrap_or("v0.0.0")
.trim_start_matches('v')
.to_string();
let notes = latest.get("body")
.and_then(|v| v.as_str())
.unwrap_or("")
.to_string();
let html_url = latest.get("html_url")
.and_then(|v| v.as_str())
.unwrap_or("https://git.command.vigilcyber.com/vstockwell/wraith/releases")
.to_string();
let update_available = version_is_newer(&tag, &current);
Ok(UpdateInfo {
current_version: current,
latest_version: tag,
update_available,
download_url: html_url,
release_notes: notes,
})
}
/// Compare semver strings. Returns true if `latest` is newer than `current`.
fn version_is_newer(latest: &str, current: &str) -> bool {
let parse = |v: &str| -> Vec<u32> {
v.split('.').filter_map(|s| s.parse().ok()).collect()
};
let l = parse(latest);
let c = parse(current);
for i in 0..3 {
let lv = l.get(i).copied().unwrap_or(0);
let cv = c.get(i).copied().unwrap_or(0);
if lv > cv { return true; }
if lv < cv { return false; }
}
false
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn version_comparison() {
assert!(version_is_newer("1.5.7", "1.5.6"));
assert!(version_is_newer("1.6.0", "1.5.9"));
assert!(version_is_newer("2.0.0", "1.9.9"));
assert!(!version_is_newer("1.5.6", "1.5.6"));
assert!(!version_is_newer("1.5.5", "1.5.6"));
assert!(!version_is_newer("1.4.0", "1.5.0"));
}
}