use std::path::PathBuf;
use serde::{Deserialize, Serialize};
const CURRENT_VERSION: &str = env!("CARGO_PKG_VERSION");
#[derive(Debug, Serialize, Deserialize, Default)]
struct UpdateCache {
last_check: String,
latest_version: Option<String>,
}
fn get_cache_path() -> PathBuf {
dirs::cache_dir()
.unwrap_or_else(|| dirs::home_dir().unwrap_or_else(|| PathBuf::from(".")))
.join("git-worktree-manager")
.join("update_check.json")
}
fn load_cache() -> UpdateCache {
let path = get_cache_path();
if !path.exists() {
return UpdateCache::default();
}
std::fs::read_to_string(&path)
.ok()
.and_then(|c| serde_json::from_str(&c).ok())
.unwrap_or_default()
}
fn save_cache(cache: &UpdateCache) {
let path = get_cache_path();
if let Some(parent) = path.parent() {
let _ = std::fs::create_dir_all(parent);
}
if let Ok(content) = serde_json::to_string_pretty(cache) {
let _ = std::fs::write(&path, content);
}
}
fn today_str() -> String {
crate::session::chrono_now_iso_pub()
.split('T')
.next()
.unwrap_or("")
.to_string()
}
fn should_check() -> bool {
let config = crate::config::load_config().unwrap_or_default();
if !config.update.auto_check {
return false;
}
let cache = load_cache();
cache.last_check != today_str()
}
pub fn check_for_update_if_needed() {
if !should_check() {
return;
}
if let Some(latest) = fetch_latest_version() {
let cache = UpdateCache {
last_check: today_str(),
latest_version: Some(latest.clone()),
};
save_cache(&cache);
if is_newer(&latest, CURRENT_VERSION) {
eprintln!(
"\ngit-worktree-manager {} is available (current: {})",
latest, CURRENT_VERSION
);
eprintln!("Run 'cw upgrade' to update.\n");
}
} else {
let cache = UpdateCache {
last_check: today_str(),
latest_version: None,
};
save_cache(&cache);
}
}
fn fetch_latest_version() -> Option<String> {
let output = std::process::Command::new("curl")
.args([
"-s",
"-H",
"Accept: application/vnd.github+json",
"https://api.github.com/repos/DaveDev42/git-worktree-manager/releases/latest",
])
.output()
.ok()?;
if !output.status.success() {
return None;
}
let body = String::from_utf8_lossy(&output.stdout);
let json: serde_json::Value = serde_json::from_str(&body).ok()?;
let tag = json.get("tag_name")?.as_str()?;
Some(tag.strip_prefix('v').unwrap_or(tag).to_string())
}
fn is_newer(latest: &str, current: &str) -> bool {
let parse = |s: &str| -> Vec<u32> { s.split('.').filter_map(|p| p.parse().ok()).collect() };
let l = parse(latest);
let c = parse(current);
l > c
}
pub fn upgrade() {
println!("git-worktree-manager v{}", CURRENT_VERSION);
if let Some(latest) = fetch_latest_version() {
if is_newer(&latest, CURRENT_VERSION) {
println!("New version available: v{}", latest);
println!("\nTo upgrade:");
println!(
" Download from: https://github.com/DaveDev42/git-worktree-manager/releases/latest"
);
#[cfg(target_os = "macos")]
println!(" Or: brew upgrade git-worktree-manager");
} else {
println!("Already up to date.");
}
} else {
println!("Could not check for updates. Check your internet connection.");
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_is_newer() {
assert!(is_newer("0.2.0", "0.1.0"));
assert!(is_newer("1.0.0", "0.10.0"));
assert!(!is_newer("0.1.0", "0.1.0"));
assert!(!is_newer("0.1.0", "0.2.0"));
}
}