1pub fn self_update() -> anyhow::Result<String> {
4 let current = env!("CARGO_PKG_VERSION");
5 let bin_path = std::env::current_exe()?;
6
7 let client = reqwest::blocking::Client::builder()
9 .user_agent("sparrow-updater")
10 .build()?;
11
12 let resp: serde_json::Value = client
13 .get("https://api.github.com/repos/sparrow-dev/sparrow/releases/latest")
14 .send()
15 .map_err(|_| anyhow::anyhow!("Cannot reach GitHub. Check your connection."))?
16 .json()?;
17
18 let latest = resp["tag_name"]
19 .as_str()
20 .unwrap_or("v0.0.0")
21 .trim_start_matches('v');
22
23 if latest <= current {
24 return Ok(format!("Already up to date (v{}).", current));
25 }
26
27 let platform = if cfg!(target_os = "linux") {
29 "linux-x86_64"
30 } else if cfg!(target_os = "macos") {
31 "macos-arm64"
32 } else if cfg!(target_os = "windows") {
33 "windows-x86_64.exe"
34 } else {
35 anyhow::bail!("Unsupported platform for auto-update");
36 };
37
38 let download_url = format!(
39 "https://github.com/sparrow-dev/sparrow/releases/download/v{}/sparrow-{}",
40 latest, platform
41 );
42
43 let new_bin = bin_path.with_extension("new");
44
45 let response = reqwest::blocking::get(&download_url)?;
46 let bytes = response.bytes()?;
47 std::fs::write(&new_bin, bytes)?;
48
49 #[cfg(windows)]
51 {
52 let old_bin = bin_path.with_extension("old");
53 std::fs::rename(&bin_path, &old_bin)?;
54 std::fs::rename(&new_bin, &bin_path)?;
55 let _ = std::fs::remove_file(&old_bin);
56 }
57 #[cfg(not(windows))]
58 {
59 std::fs::rename(&new_bin, &bin_path)?;
60 use std::os::unix::fs::PermissionsExt;
62 let mut perms = std::fs::metadata(&bin_path)?.permissions();
63 perms.set_mode(0o755);
64 std::fs::set_permissions(&bin_path, perms)?;
65 }
66
67 Ok(format!(
68 "Updated from v{} → v{}. Restart sparrow.",
69 current, latest
70 ))
71}
72
73pub fn check_update() -> Option<String> {
75 let current = env!("CARGO_PKG_VERSION");
76 let client = reqwest::blocking::Client::builder()
77 .user_agent("sparrow-check")
78 .timeout(std::time::Duration::from_secs(5))
79 .build()
80 .ok()?;
81
82 let resp: serde_json::Value = client
83 .get("https://api.github.com/repos/sparrow-dev/sparrow/releases/latest")
84 .send()
85 .ok()?
86 .json()
87 .ok()?;
88
89 let latest = resp["tag_name"]
90 .as_str()
91 .unwrap_or("v0.0.0")
92 .trim_start_matches('v');
93
94 if latest > current {
95 Some(format!("v{} available (current: v{})", latest, current))
96 } else {
97 None
98 }
99}