snarkos_cli/helpers/
updater.rs

1// Copyright 2024 Aleo Network Foundation
2// This file is part of the snarkOS library.
3
4// Licensed under the Apache License, Version 2.0 (the "License");
5// you may not use this file except in compliance with the License.
6// You may obtain a copy of the License at:
7
8// http://www.apache.org/licenses/LICENSE-2.0
9
10// Unless required by applicable law or agreed to in writing, software
11// distributed under the License is distributed on an "AS IS" BASIS,
12// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13// See the License for the specific language governing permissions and
14// limitations under the License.
15
16use colored::Colorize;
17use self_update::{Status, backends::github, version::bump_is_greater};
18use std::fmt::Write;
19
20pub struct Updater;
21
22impl Updater {
23    const SNARKOS_BIN_NAME: &'static str = "snarkos";
24    const SNARKOS_REPO_NAME: &'static str = "snarkOS";
25    const SNARKOS_REPO_OWNER: &'static str = "ProvableHQ";
26
27    /// Show all available releases for `snarkos`.
28    pub fn show_available_releases() -> Result<String, UpdaterError> {
29        let releases = github::ReleaseList::configure()
30            .repo_owner(Self::SNARKOS_REPO_OWNER)
31            .repo_name(Self::SNARKOS_REPO_NAME)
32            .build()?
33            .fetch()?;
34
35        let mut output = "List of available versions\n".to_string();
36        for release in releases {
37            let _ = writeln!(output, "  * {}", release.version);
38        }
39        Ok(output)
40    }
41
42    /// Update `snarkOS` to the specified release.
43    pub fn update_to_release(show_output: bool, version: Option<String>) -> Result<Status, UpdaterError> {
44        let mut update_builder = github::Update::configure();
45
46        update_builder
47            .repo_owner(Self::SNARKOS_REPO_OWNER)
48            .repo_name(Self::SNARKOS_REPO_NAME)
49            .bin_name(Self::SNARKOS_BIN_NAME)
50            .current_version(env!("CARGO_PKG_VERSION"))
51            .show_download_progress(show_output)
52            .no_confirm(true)
53            .show_output(show_output);
54
55        let status = match version {
56            None => update_builder.build()?.update()?,
57            Some(v) => update_builder.target_version_tag(&v).build()?.update()?,
58        };
59
60        Ok(status)
61    }
62
63    /// Check if there is an available update for `snarkos` and return the newest release.
64    pub fn update_available() -> Result<String, UpdaterError> {
65        let updater = github::Update::configure()
66            .repo_owner(Self::SNARKOS_REPO_OWNER)
67            .repo_name(Self::SNARKOS_REPO_NAME)
68            .bin_name(Self::SNARKOS_BIN_NAME)
69            .current_version(env!("CARGO_PKG_VERSION"))
70            .build()?;
71
72        let current_version = updater.current_version();
73        let latest_release = updater.get_latest_release()?;
74
75        if bump_is_greater(&current_version, &latest_release.version)? {
76            Ok(latest_release.version)
77        } else {
78            Err(UpdaterError::OldReleaseVersion(current_version, latest_release.version))
79        }
80    }
81
82    /// Display the CLI message.
83    pub fn print_cli() -> String {
84        if let Ok(latest_version) = Self::update_available() {
85            let mut output = "🟢 A new version is available! Run".bold().green().to_string();
86            output += &" `snarkos update` ".bold().white();
87            output += &format!("to update to v{latest_version}.").bold().green();
88            output
89        } else {
90            String::new()
91        }
92    }
93}
94
95#[derive(Debug, Error)]
96pub enum UpdaterError {
97    #[error("{}: {}", _0, _1)]
98    Crate(&'static str, String),
99
100    #[error("The current version {} is more recent than the release version {}", _0, _1)]
101    OldReleaseVersion(String, String),
102}
103
104impl From<self_update::errors::Error> for UpdaterError {
105    fn from(error: self_update::errors::Error) -> Self {
106        UpdaterError::Crate("self_update", error.to_string())
107    }
108}