Skip to main content

arborist_cli/
update.rs

1use std::process::ExitCode;
2
3const CURRENT_VERSION: &str = env!("CARGO_PKG_VERSION");
4const REPO_OWNER: &str = "StrangeDaysTech";
5const REPO_NAME: &str = "arborist-cli";
6
7pub fn run(check_only: bool) -> ExitCode {
8    println!("arborist-cli v{CURRENT_VERSION}");
9
10    if is_cargo_installed() {
11        eprintln!(
12            "hint: this binary appears to be installed via cargo. \
13             To update, run: cargo install arborist-cli"
14        );
15        if check_only {
16            return check_latest_version();
17        }
18        return ExitCode::SUCCESS;
19    }
20
21    if check_only {
22        return check_latest_version();
23    }
24
25    perform_update()
26}
27
28fn check_latest_version() -> ExitCode {
29    print!("Checking for updates... ");
30
31    match self_update::backends::github::Update::configure()
32        .repo_owner(REPO_OWNER)
33        .repo_name(REPO_NAME)
34        .bin_name("arborist")
35        .current_version(CURRENT_VERSION)
36        .build()
37    {
38        Ok(updater) => match updater.get_latest_release() {
39            Ok(release) => {
40                let latest = &release.version;
41                if latest == CURRENT_VERSION {
42                    println!("already up to date.");
43                    ExitCode::SUCCESS
44                } else {
45                    println!("v{latest} available (current: v{CURRENT_VERSION})");
46                    println!("Run `arborist update` to install it.");
47                    ExitCode::SUCCESS
48                }
49            }
50            Err(e) => {
51                eprintln!("failed to check for updates: {e}");
52                ExitCode::from(2)
53            }
54        },
55        Err(e) => {
56            eprintln!("failed to configure updater: {e}");
57            ExitCode::from(2)
58        }
59    }
60}
61
62fn perform_update() -> ExitCode {
63    println!("Checking for updates...");
64
65    let result = self_update::backends::github::Update::configure()
66        .repo_owner(REPO_OWNER)
67        .repo_name(REPO_NAME)
68        .bin_name("arborist")
69        .current_version(CURRENT_VERSION)
70        .show_download_progress(true)
71        .show_output(false)
72        .no_confirm(true)
73        .build();
74
75    match result {
76        Ok(updater) => match updater.update() {
77            Ok(status) => {
78                if status.updated() {
79                    println!("Updated to v{}.", status.version());
80                } else {
81                    println!("Already up to date (v{CURRENT_VERSION}).");
82                }
83                ExitCode::SUCCESS
84            }
85            Err(e) => {
86                eprintln!("error: update failed: {e}");
87                ExitCode::from(2)
88            }
89        },
90        Err(e) => {
91            eprintln!("error: failed to configure updater: {e}");
92            ExitCode::from(2)
93        }
94    }
95}
96
97/// Heuristic: if the binary is inside a cargo bin directory, it was likely
98/// installed via `cargo install`.
99fn is_cargo_installed() -> bool {
100    let Ok(exe) = std::env::current_exe() else {
101        return false;
102    };
103    let path = exe.to_string_lossy();
104    path.contains(".cargo/bin") || path.contains(".cargo\\bin")
105}