Auto update (w/ cli)

This commit is contained in:
veeso
2021-09-10 22:22:15 +02:00
parent 8bb05406c8
commit 06a67de14f
21 changed files with 1093 additions and 215 deletions

View File

@@ -53,13 +53,13 @@ extern crate path_slash;
extern crate rand;
extern crate regex;
extern crate s3;
extern crate self_update;
extern crate ssh2;
extern crate suppaftp;
extern crate tempfile;
extern crate textwrap;
extern crate tui_realm_stdlib;
extern crate tuirealm;
extern crate ureq;
#[cfg(target_family = "unix")]
extern crate users;
extern crate whoami;

View File

@@ -62,6 +62,7 @@ use system::logging;
enum Task {
Activity(NextActivity),
ImportTheme(PathBuf),
InstallUpdate,
}
#[derive(FromArgs)]
@@ -84,6 +85,12 @@ struct Args {
quiet: bool,
#[argh(option, short = 't', description = "import specified theme")]
theme: Option<String>,
#[argh(
switch,
short = 'u',
description = "update termscp to the latest version"
)]
update: bool,
#[argh(
option,
short = 'T',
@@ -177,6 +184,9 @@ fn parse_args(args: Args) -> Result<RunOpts, String> {
if let Some(theme) = args.theme {
run_opts.task = Task::ImportTheme(PathBuf::from(theme));
}
if args.update {
run_opts.task = Task::InstallUpdate;
}
// @! Ordinary mode
// Remote argument
if let Some(remote) = args.positional.get(0) {
@@ -256,6 +266,16 @@ fn run(mut run_opts: RunOpts) -> i32 {
1
}
},
Task::InstallUpdate => match support::install_update() {
Ok(msg) => {
println!("{}", msg);
0
}
Err(err) => {
eprintln!("Could not install update: {}", err);
1
}
},
Task::Activity(activity) => {
// Get working directory
let wrkdir: PathBuf = match env::current_dir() {

View File

@@ -26,7 +26,11 @@
* SOFTWARE.
*/
// mod
use crate::system::{environment, theme_provider::ThemeProvider};
use crate::system::{
auto_update::{Update, UpdateStatus},
environment,
theme_provider::ThemeProvider,
};
use std::fs;
use std::path::{Path, PathBuf};
@@ -51,6 +55,23 @@ pub fn import_theme(p: &Path) -> Result<(), String> {
.map_err(|e| format!("Could not import theme: {}", e))
}
/// ### install_update
///
/// Install latest version of termscp if an update is available
pub fn install_update() -> Result<String, String> {
match Update::default()
.show_progress(true)
.ask_confirm(true)
.upgrade()
{
Ok(UpdateStatus::AlreadyUptodate) => Ok("termscp is already up to date".to_string()),
Ok(UpdateStatus::UpdateInstalled(v)) => {
Ok(format!("termscp has been updated to version {}", v))
}
Err(err) => Err(err.to_string()),
}
}
/// ### get_config_dir
///
/// Get configuration directory

233
src/system/auto_update.rs Normal file
View File

@@ -0,0 +1,233 @@
//! ## Auto update
//!
//! Automatic update module. This module is used to upgrade the current version of termscp to the latest available on Github
/**
* MIT License
*
* termscp - Copyright (c) 2021 Christian Visintin
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
use crate::utils::parser::parse_semver;
pub use self_update::errors::Error as UpdateError;
use self_update::{
backends::github::Update as GithubUpdater, cargo_crate_version, update::Release as UpdRelease,
Status,
};
/// ### UpdateStatus
///
/// The status of the update in case of success
#[derive(Debug, Eq, PartialEq)]
pub enum UpdateStatus {
/// Termscp is already up to date
AlreadyUptodate,
/// The update has been correctly installed
UpdateInstalled(String),
}
/// ## Release
///
/// Info related to a github release
#[derive(Debug)]
pub struct Release {
pub version: String,
pub body: String,
}
/// ## Update
///
/// The update structure defines the options used to install the update.
/// Once you're fine with the options, just call the `upgrade()` method to upgrade termscp.
#[derive(Debug)]
pub struct Update {
ask_confirm: bool,
progress: bool,
}
impl Update {
/// ### show_progress
///
/// Set whether to show or not the progress bar
pub fn show_progress(mut self, opt: bool) -> Self {
self.progress = opt;
self
}
/// ### ask_confirm
///
/// Set whether to ask for confirm when updating
pub fn ask_confirm(mut self, opt: bool) -> Self {
self.ask_confirm = opt;
self
}
pub fn upgrade(self) -> Result<UpdateStatus, UpdateError> {
info!("Updating termscp...");
GithubUpdater::configure()
// Set default options
.repo_owner("veeso")
.repo_name("termscp")
.bin_name("termscp")
.current_version(cargo_crate_version!())
.no_confirm(!self.ask_confirm)
.show_download_progress(self.progress)
.show_output(self.progress)
.build()?
.update()
.map(UpdateStatus::from)
}
/// ### is_new_version_available
///
/// Returns whether a new version of termscp is available
/// In case of success returns Ok(Option<Release>), where the Option is Some(new_version);
/// otherwise if no version is available, return None
/// In case of error returns Error with the error description
pub fn is_new_version_available() -> Result<Option<Release>, UpdateError> {
info!("Checking whether a new version is available...");
GithubUpdater::configure()
// Set default options
.repo_owner("veeso")
.repo_name("termscp")
.bin_name("termscp")
.current_version(cargo_crate_version!())
.no_confirm(true)
.show_download_progress(false)
.show_output(false)
.build()?
.get_latest_release()
.map(Release::from)
.map(Self::check_version)
}
/// ### check_version
///
/// In case received version is newer than current one, version as Some is returned; otherwise None
fn check_version(r: Release) -> Option<Release> {
match parse_semver(r.version.as_str()) {
Some(new_version) => {
// Check if version is different
debug!(
"New version: {}; current version: {}",
new_version,
cargo_crate_version!()
);
if new_version.as_str() > "cargo_crate_version!()" {
Some(r) // New version is available
} else {
None // No new version
}
}
None => None,
}
}
}
impl Default for Update {
fn default() -> Self {
Self {
progress: false,
ask_confirm: false,
}
}
}
impl From<Status> for UpdateStatus {
fn from(s: Status) -> Self {
match s {
Status::UpToDate(_) => Self::AlreadyUptodate,
Status::Updated(v) => Self::UpdateInstalled(v),
}
}
}
impl From<UpdRelease> for Release {
fn from(r: UpdRelease) -> Self {
Self {
version: r.version,
body: r.body.unwrap_or_default(),
}
}
}
#[cfg(test)]
mod test {
use super::*;
use pretty_assertions::assert_eq;
#[test]
fn auto_update_default() {
let upd: Update = Update::default();
assert_eq!(upd.ask_confirm, false);
assert_eq!(upd.progress, false);
let upd = upd.ask_confirm(true).show_progress(true);
assert_eq!(upd.ask_confirm, true);
assert_eq!(upd.progress, true);
}
#[test]
fn auto_update() {
// Wno version
assert_eq!(
Update::default()
.show_progress(true)
.upgrade()
.ok()
.unwrap(),
UpdateStatus::AlreadyUptodate,
);
}
#[test]
fn check_for_updates() {
println!("{:?}", Update::is_new_version_available());
assert!(Update::is_new_version_available().is_ok());
}
#[test]
fn update_status() {
assert_eq!(
UpdateStatus::from(Status::Updated(String::from("0.6.0"))),
UpdateStatus::UpdateInstalled(String::from("0.6.0"))
);
assert_eq!(
UpdateStatus::from(Status::UpToDate(String::from("0.6.0"))),
UpdateStatus::AlreadyUptodate
);
}
#[test]
fn release() {
let release: UpdRelease = UpdRelease {
name: String::from("termscp 0.7.0"),
version: String::from("0.7.0"),
date: String::from("2021-09-12T00:00:00Z"),
body: Some(String::from("fixed everything")),
assets: vec![],
};
let release: Release = Release::from(release);
assert_eq!(release.body.as_str(), "fixed everything");
assert_eq!(release.version.as_str(), "0.7.0");
}
}

View File

@@ -26,6 +26,7 @@
* SOFTWARE.
*/
// modules
pub mod auto_update;
pub mod bookmarks_client;
pub mod config_client;
pub mod environment;

View File

@@ -27,6 +27,9 @@
*/
use super::{AuthActivity, FileTransferParams, FileTransferProtocol};
use crate::filetransfer::params::{AwsS3Params, GenericProtocolParams, ProtocolParams};
use crate::system::auto_update::{Update, UpdateStatus};
use tuirealm::tui::style::Color;
impl AuthActivity {
/// ### protocol_opt_to_enum
@@ -151,4 +154,31 @@ impl AuthActivity {
entry_directory: None,
})
}
// -- update install
/// ### install_update
///
/// Install latest termscp version via GUI
pub(super) fn install_update(&mut self) {
// Umount release notes
self.umount_release_notes();
// Mount wait box
self.mount_wait("Installing update. Please wait…");
// Install update
let result = Update::default().show_progress(false).upgrade();
// Umount wait
self.umount_wait();
// Show outcome
match result {
Ok(UpdateStatus::AlreadyUptodate) => {
self.mount_info("termscp is already up to date!", Color::Cyan)
}
Ok(UpdateStatus::UpdateInstalled(ver)) => self.mount_info(
format!("termscp has been updated to version {}!", ver),
Color::Green,
),
Err(err) => self.mount_error(format!("Could not install update: {}", err)),
}
}
}

View File

@@ -35,8 +35,8 @@ mod view;
use super::{Activity, Context, ExitReason};
use crate::config::themes::Theme;
use crate::filetransfer::{FileTransferParams, FileTransferProtocol};
use crate::system::auto_update::{Release, Update as TermscpUpdate};
use crate::system::bookmarks_client::BookmarksClient;
use crate::utils::git;
// Includes
use crossterm::event::Event;
@@ -51,6 +51,8 @@ const COMPONENT_TEXT_NEW_VERSION_NOTES: &str = "TEXTAREA_NEW_VERSION";
const COMPONENT_TEXT_FOOTER: &str = "TEXT_FOOTER";
const COMPONENT_TEXT_HELP: &str = "TEXT_HELP";
const COMPONENT_TEXT_ERROR: &str = "TEXT_ERROR";
const COMPONENT_TEXT_INFO: &str = "TEXT_INFO";
const COMPONENT_TEXT_WAIT: &str = "TEXT_WAIT";
const COMPONENT_TEXT_SIZE_ERR: &str = "TEXT_SIZE_ERR";
const COMPONENT_INPUT_ADDR: &str = "INPUT_ADDRESS";
const COMPONENT_INPUT_PORT: &str = "INPUT_PORT";
@@ -65,6 +67,7 @@ const COMPONENT_RADIO_QUIT: &str = "RADIO_QUIT";
const COMPONENT_RADIO_BOOKMARK_DEL_BOOKMARK: &str = "RADIO_DELETE_BOOKMARK";
const COMPONENT_RADIO_BOOKMARK_DEL_RECENT: &str = "RADIO_DELETE_RECENT";
const COMPONENT_RADIO_BOOKMARK_SAVE_PWD: &str = "RADIO_SAVE_PASSWORD";
const COMPONENT_RADIO_INSTALL_UPDATE: &str = "RADIO_INSTALL_UPDATE";
const COMPONENT_BOOKMARKS_LIST: &str = "BOOKMARKS_LIST";
const COMPONENT_RECENTS_LIST: &str = "RECENTS_LIST";
@@ -119,12 +122,12 @@ impl AuthActivity {
if ctx.config().get_check_for_updates() {
debug!("Check for updates is enabled");
// Send request
match git::check_for_updates(env!("CARGO_PKG_VERSION")) {
Ok(Some(git::GithubTag { tag_name, body })) => {
match TermscpUpdate::is_new_version_available() {
Ok(Some(Release { version, body })) => {
// If some, store version and release notes
info!("Latest version is: {}", tag_name);
info!("Latest version is: {}", version);
ctx.store_mut()
.set_string(STORE_KEY_LATEST_VERSION, tag_name);
.set_string(STORE_KEY_LATEST_VERSION, version);
ctx.store_mut().set_string(STORE_KEY_RELEASE_NOTES, body);
}
Ok(None) => {

View File

@@ -32,8 +32,9 @@ use super::{
COMPONENT_INPUT_S3_BUCKET, COMPONENT_INPUT_S3_PROFILE, COMPONENT_INPUT_S3_REGION,
COMPONENT_INPUT_USERNAME, COMPONENT_RADIO_BOOKMARK_DEL_BOOKMARK,
COMPONENT_RADIO_BOOKMARK_DEL_RECENT, COMPONENT_RADIO_BOOKMARK_SAVE_PWD,
COMPONENT_RADIO_PROTOCOL, COMPONENT_RADIO_QUIT, COMPONENT_RECENTS_LIST, COMPONENT_TEXT_ERROR,
COMPONENT_TEXT_HELP, COMPONENT_TEXT_NEW_VERSION_NOTES, COMPONENT_TEXT_SIZE_ERR,
COMPONENT_RADIO_INSTALL_UPDATE, COMPONENT_RADIO_PROTOCOL, COMPONENT_RADIO_QUIT,
COMPONENT_RECENTS_LIST, COMPONENT_TEXT_ERROR, COMPONENT_TEXT_HELP, COMPONENT_TEXT_INFO,
COMPONENT_TEXT_NEW_VERSION_NOTES, COMPONENT_TEXT_SIZE_ERR, COMPONENT_TEXT_WAIT,
};
use crate::ui::keymap::*;
use tui_realm_stdlib::InputPropsBuilder;
@@ -252,15 +253,44 @@ impl Update for AuthActivity {
self.umount_error();
None
}
(COMPONENT_TEXT_ERROR, _) => None,
(COMPONENT_TEXT_NEW_VERSION_NOTES, key)
if key == &MSG_KEY_ESC || key == &MSG_KEY_ENTER =>
{
// -- Text info
(COMPONENT_TEXT_INFO, key) if key == &MSG_KEY_ESC || key == &MSG_KEY_ENTER => {
// Umount text info
self.umount_info();
None
}
(COMPONENT_TEXT_ERROR, _) | (COMPONENT_TEXT_INFO, _) => None,
// -- Text wait
(COMPONENT_TEXT_WAIT, _) => None,
// -- Release notes
(COMPONENT_TEXT_NEW_VERSION_NOTES, key) if key == &MSG_KEY_ESC => {
// Umount release notes
self.umount_release_notes();
None
}
(COMPONENT_TEXT_NEW_VERSION_NOTES, key) if key == &MSG_KEY_TAB => {
// Focus to radio update
self.view.active(COMPONENT_RADIO_INSTALL_UPDATE);
None
}
(COMPONENT_TEXT_NEW_VERSION_NOTES, _) => None,
// -- Install update radio
(COMPONENT_RADIO_INSTALL_UPDATE, Msg::OnSubmit(Payload::One(Value::Usize(0)))) => {
// Install update
self.install_update();
None
}
(COMPONENT_RADIO_INSTALL_UPDATE, Msg::OnSubmit(Payload::One(Value::Usize(1)))) => {
// Umount
self.umount_release_notes();
None
}
(COMPONENT_RADIO_INSTALL_UPDATE, key) if key == &MSG_KEY_TAB => {
// Focus to changelog
self.view.active(COMPONENT_TEXT_NEW_VERSION_NOTES);
None
}
(COMPONENT_RADIO_INSTALL_UPDATE, _) => None,
// Help
(_, key) if key == &MSG_KEY_CTRL_H => {
// Show help

View File

@@ -209,7 +209,7 @@ impl AuthActivity {
.with_spans(vec![
TextSpan::from("termscp "),
TextSpan::new(version.as_str()).underlined().bold(),
TextSpan::from(" is NOW available! Get it from <https://veeso.github.io/termscp/>; view release notes with <CTRL+R>"),
TextSpan::from(" is NOW available! Install update and view release notes with <CTRL+R>"),
])
.build(),
)),
@@ -360,6 +360,22 @@ impl AuthActivity {
self.view.render(super::COMPONENT_TEXT_ERROR, f, popup);
}
}
if let Some(props) = self.view.get_props(super::COMPONENT_TEXT_INFO) {
if props.visible {
let popup = draw_area_in(f.size(), 50, 10);
f.render_widget(Clear, popup);
// make popup
self.view.render(super::COMPONENT_TEXT_INFO, f, popup);
}
}
if let Some(props) = self.view.get_props(super::COMPONENT_TEXT_WAIT) {
if props.visible {
let popup = draw_area_in(f.size(), 50, 10);
f.render_widget(Clear, popup);
// make popup
self.view.render(super::COMPONENT_TEXT_WAIT, f, popup);
}
}
if let Some(props) = self.view.get_props(super::COMPONENT_TEXT_SIZE_ERR) {
if props.visible {
let popup = draw_area_in(f.size(), 80, 20);
@@ -403,10 +419,22 @@ impl AuthActivity {
if let Some(props) = self.view.get_props(super::COMPONENT_TEXT_NEW_VERSION_NOTES) {
if props.visible {
// make popup
let popup = draw_area_in(f.size(), 90, 90);
let popup = draw_area_in(f.size(), 90, 85);
f.render_widget(Clear, popup);
let popup_chunks = Layout::default()
.direction(Direction::Vertical)
.constraints(
[
Constraint::Percentage(90), // Notes
Constraint::Length(3), // Install radio
]
.as_ref(),
)
.split(popup);
self.view
.render(super::COMPONENT_TEXT_NEW_VERSION_NOTES, f, popup);
.render(super::COMPONENT_TEXT_NEW_VERSION_NOTES, f, popup_chunks[0]);
self.view
.render(super::COMPONENT_RADIO_INSTALL_UPDATE, f, popup_chunks[1]);
}
}
if let Some(props) = self.view.get_props(super::COMPONENT_TEXT_HELP) {
@@ -515,7 +543,7 @@ impl AuthActivity {
/// ### mount_error
///
/// Mount error box
pub(super) fn mount_error(&mut self, text: &str) {
pub(super) fn mount_error<S: AsRef<str>>(&mut self, text: S) {
// Mount
let err_color = self.theme().misc_error_dialog;
self.view.mount(
@@ -526,7 +554,7 @@ impl AuthActivity {
.with_borders(Borders::ALL, BorderType::Thick, err_color)
.bold()
.with_text_alignment(Alignment::Center)
.with_texts(vec![TextSpan::from(text)])
.with_texts(vec![TextSpan::from(text.as_ref().to_string())])
.build(),
)),
);
@@ -541,6 +569,61 @@ impl AuthActivity {
self.view.umount(super::COMPONENT_TEXT_ERROR);
}
/// ### mount_info
///
/// Mount info box
pub(super) fn mount_info<S: AsRef<str>>(&mut self, text: S, color: Color) {
// Mount
self.view.mount(
super::COMPONENT_TEXT_INFO,
Box::new(Paragraph::new(
ParagraphPropsBuilder::default()
.with_borders(Borders::ALL, BorderType::Thick, color)
.bold()
.with_text_alignment(Alignment::Center)
.with_texts(vec![TextSpan::from(text.as_ref().to_string())])
.with_foreground(color)
.build(),
)),
);
// Give focus to error
self.view.active(super::COMPONENT_TEXT_INFO);
}
/// ### umount_info
///
/// Umount info message
pub(super) fn umount_info(&mut self) {
self.view.umount(super::COMPONENT_TEXT_INFO);
}
/// ### mount_error
///
/// Mount wait box
pub(super) fn mount_wait(&mut self, text: &str) {
// Mount
self.view.mount(
super::COMPONENT_TEXT_WAIT,
Box::new(Paragraph::new(
ParagraphPropsBuilder::default()
.with_borders(Borders::ALL, BorderType::Thick, Color::Reset)
.bold()
.with_text_alignment(Alignment::Center)
.with_texts(vec![TextSpan::from(text)])
.build(),
)),
);
// Give focus to error
self.view.active(super::COMPONENT_TEXT_WAIT);
}
/// ### umount_wait
///
/// Umount wait message
pub(super) fn umount_wait(&mut self) {
self.view.umount(super::COMPONENT_TEXT_WAIT);
}
/// ### mount_size_err
///
/// Mount size error
@@ -785,7 +868,22 @@ impl AuthActivity {
.build(),
)),
);
self.view.active(super::COMPONENT_TEXT_NEW_VERSION_NOTES);
// Mount install popup
self.view.mount(
super::COMPONENT_RADIO_INSTALL_UPDATE,
Box::new(Radio::new(
RadioPropsBuilder::default()
.with_color(Color::LightYellow)
.with_inverted_color(Color::Black)
.with_borders(Borders::ALL, BorderType::Rounded, Color::LightYellow)
.with_title("Install new version?", Alignment::Left)
.with_options(&["Yes", "No"])
.with_value(0)
.rewind(true)
.build(),
)),
);
self.view.active(super::COMPONENT_RADIO_INSTALL_UPDATE);
}
}
}
@@ -795,6 +893,7 @@ impl AuthActivity {
/// Umount release notes text area
pub(super) fn umount_release_notes(&mut self) {
self.view.umount(super::COMPONENT_TEXT_NEW_VERSION_NOTES);
self.view.umount(super::COMPONENT_RADIO_INSTALL_UPDATE);
}
/// ### get_protocol

View File

@@ -1,95 +0,0 @@
//! ## git
//!
//! `git` is the module which provides utilities to interact through the GIT API and to perform some stuff at git level
/**
* MIT License
*
* termscp - Copyright (c) 2021 Christian Visintin
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
// Locals
use super::parser::parse_semver;
// Others
use serde::Deserialize;
#[derive(Debug, Deserialize)]
/// ## GithubTag
///
/// Info related to a github tag
pub struct GithubTag {
pub tag_name: String,
pub body: String,
}
/// ### check_for_updates
///
/// Check if there is a new version available for termscp.
/// This is performed through the Github API
/// In case of success returns Ok(Option<GithubTag>), where the Option is Some(new_version); otherwise if no version is available, return None
/// In case of error returns Error with the error description
pub fn check_for_updates(current_version: &str) -> Result<Option<GithubTag>, String> {
// Send request
let github_tag: Result<GithubTag, String> =
match ureq::get("https://api.github.com/repos/veeso/termscp/releases/latest").call() {
Ok(response) => response.into_json::<GithubTag>().map_err(|x| x.to_string()),
Err(err) => Err(err.to_string()),
};
// Check version
match github_tag {
Err(err) => Err(err),
Ok(tag) => {
// Parse version
match parse_semver(tag.tag_name.as_str()) {
Some(new_version) => {
// Check if version is different
if new_version.as_str() > current_version {
Ok(Some(tag)) // New version is available
} else {
Ok(None) // No new version
}
}
None => Err(String::from("Got bad response from Github")),
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
#[cfg(not(all(
any(
target_os = "macos",
target_os = "freebsd",
target_os = "netbsd",
target_os = "netbsd"
),
feature = "github-actions"
)))]
fn test_utils_git_check_for_updates() {
assert!(check_for_updates("100.0.0").ok().unwrap().is_none());
assert!(check_for_updates("0.0.1").ok().unwrap().is_some());
}
}

View File

@@ -29,7 +29,6 @@
pub mod crypto;
pub mod file;
pub mod fmt;
pub mod git;
pub mod parser;
pub mod path;
pub mod random;