snarkos_cli/helpers/
updater.rs1use 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 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 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!("SNARKOS_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 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!("SNARKOS_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(¤t_version, &latest_release.version)? {
76 Ok(latest_release.version)
77 } else {
78 Err(UpdaterError::OldReleaseVersion(current_version, latest_release.version))
79 }
80 }
81
82 pub fn print_cli() -> Option<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 Some(output)
89 } else {
90 None
91 }
92 }
93}
94
95#[cfg(test)]
96mod tests {
97 use std::{fs, path::Path};
98
99 #[test]
100 fn snarkos_version_matches_release_version() {
101 let release_version = Path::new(env!("CARGO_MANIFEST_DIR"))
102 .ancestors()
103 .map(|dir| dir.join(".cargo").join("release-version"))
104 .find(|path| path.is_file())
105 .map(|path| fs::read_to_string(path).unwrap())
106 .unwrap();
107
108 assert_eq!(env!("SNARKOS_VERSION"), release_version.trim().trim_start_matches('v'));
109 }
110}
111
112#[derive(Debug, Error)]
113pub enum UpdaterError {
114 #[error("{}: {}", _0, _1)]
115 Crate(&'static str, String),
116
117 #[error("The current version {} is more recent than the release version {}", _0, _1)]
118 OldReleaseVersion(String, String),
119}
120
121impl From<self_update::errors::Error> for UpdaterError {
122 fn from(error: self_update::errors::Error) -> Self {
123 UpdaterError::Crate("self_update", error.to_string())
124 }
125}