generated from nhcarrigan/template
feat: add auto-update checker
Implements issue #17 - the app now checks for updates on startup and shows a notification when a newer version is available. Users can disable this in settings. Uses Gitea releases API with semver comparison. Closes #17
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
use tauri::{AppHandle, State};
|
||||
use tauri_plugin_store::StoreExt;
|
||||
use tauri_plugin_http::reqwest;
|
||||
|
||||
use crate::config::{ClaudeStartOptions, HikariConfig};
|
||||
use crate::stats::UsageStats;
|
||||
@@ -222,3 +223,74 @@ pub async fn list_skills() -> Result<Vec<String>, String> {
|
||||
|
||||
Ok(skills)
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
|
||||
pub struct UpdateInfo {
|
||||
pub current_version: String,
|
||||
pub latest_version: String,
|
||||
pub has_update: bool,
|
||||
pub release_url: String,
|
||||
pub release_notes: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, serde::Deserialize)]
|
||||
struct GiteaRelease {
|
||||
tag_name: String,
|
||||
html_url: String,
|
||||
body: Option<String>,
|
||||
prerelease: bool,
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn check_for_updates() -> Result<UpdateInfo, String> {
|
||||
const CURRENT_VERSION: &str = env!("CARGO_PKG_VERSION");
|
||||
const RELEASES_API: &str = "https://git.nhcarrigan.com/api/v1/repos/nhcarrigan/hikari-desktop/releases";
|
||||
|
||||
// Fetch releases from Gitea API
|
||||
let client = reqwest::Client::new();
|
||||
let response = client
|
||||
.get(RELEASES_API)
|
||||
.header("Accept", "application/json")
|
||||
.send()
|
||||
.await
|
||||
.map_err(|e| format!("Failed to fetch releases: {}", e))?;
|
||||
|
||||
if !response.status().is_success() {
|
||||
return Err(format!("API returned status: {}", response.status()));
|
||||
}
|
||||
|
||||
let text = response
|
||||
.text()
|
||||
.await
|
||||
.map_err(|e| format!("Failed to read response: {}", e))?;
|
||||
|
||||
let releases: Vec<GiteaRelease> = serde_json::from_str(&text)
|
||||
.map_err(|e| format!("Failed to parse releases: {}", e))?;
|
||||
|
||||
// Find the latest non-prerelease, or fall back to latest prerelease
|
||||
let latest = releases
|
||||
.iter()
|
||||
.find(|r| !r.prerelease)
|
||||
.or_else(|| releases.first());
|
||||
|
||||
let latest = match latest {
|
||||
Some(r) => r,
|
||||
None => return Err("No releases found".to_string()),
|
||||
};
|
||||
|
||||
// Parse version strings (remove 'v' prefix if present)
|
||||
let current = semver::Version::parse(CURRENT_VERSION)
|
||||
.map_err(|e| format!("Failed to parse current version: {}", e))?;
|
||||
|
||||
let latest_tag = latest.tag_name.trim_start_matches('v');
|
||||
let latest_ver = semver::Version::parse(latest_tag)
|
||||
.map_err(|e| format!("Failed to parse latest version: {}", e))?;
|
||||
|
||||
Ok(UpdateInfo {
|
||||
current_version: CURRENT_VERSION.to_string(),
|
||||
latest_version: latest.tag_name.clone(),
|
||||
has_update: latest_ver > current,
|
||||
release_url: latest.html_url.clone(),
|
||||
release_notes: latest.body.clone(),
|
||||
})
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user