Skip to main content

sparrow/
update.rs

1// ─── Self-update ────────────────────────────────────────────────────────────────
2
3pub fn self_update() -> anyhow::Result<String> {
4    let current = env!("CARGO_PKG_VERSION");
5    let bin_path = std::env::current_exe()?;
6
7    // Check latest version from GitHub releases
8    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    // Download new binary
28    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    // Replace current binary
50    #[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        // Make executable
61        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
73/// Check if an update is available
74pub 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}