snarkos_cli/helpers/
updater.rs1use colored::Colorize;
17use self_update::{UpdateConfig, VersionStatus, 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<VersionStatus, 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.release_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().to_string();
73 let latest_releases = updater.get_latest_release()?;
74 let latest_release = latest_releases
75 .latest()
76 .ok_or_else(|| UpdaterError::Crate("self_update", "no releases found".to_string()))?;
77
78 if bump_is_greater(¤t_version, latest_release.version())? {
79 Ok(latest_release.version().to_string())
80 } else {
81 Err(UpdaterError::OldReleaseVersion(current_version, latest_release.version().to_string()))
82 }
83 }
84
85 pub fn print_cli() -> Option<String> {
87 if let Ok(latest_version) = Self::update_available() {
88 let mut output = "🟢 A new version is available! Run".bold().green().to_string();
89 output += &" `snarkos update` ".bold().white();
90 output += &format!("to update to v{latest_version}.").bold().green();
91 Some(output)
92 } else {
93 None
94 }
95 }
96}
97
98#[cfg(test)]
99mod tests {
100 use std::{fs, path::Path};
101
102 #[test]
103 fn snarkos_version_matches_release_version() {
104 let release_version = Path::new(env!("CARGO_MANIFEST_DIR"))
105 .ancestors()
106 .map(|dir| dir.join(".cargo").join("release-version"))
107 .find(|path| path.is_file())
108 .map(|path| fs::read_to_string(path).unwrap())
109 .unwrap();
110
111 assert_eq!(env!("SNARKOS_VERSION"), release_version.trim().trim_start_matches('v'));
112 }
113}
114
115#[derive(Debug, Error)]
116pub enum UpdaterError {
117 #[error("{}: {}", _0, _1)]
118 Crate(&'static str, String),
119
120 #[error("The current version {} is more recent than the release version {}", _0, _1)]
121 OldReleaseVersion(String, String),
122}
123
124impl From<self_update::errors::Error> for UpdaterError {
125 fn from(error: self_update::errors::Error) -> Self {
126 UpdaterError::Crate("self_update", error.to_string())
127 }
128}