mirror of
https://github.com/velopack/velopack.git
synced 2025-10-25 15:19:22 +00:00
Add native check/download commands
This commit is contained in:
19
src/Rust/Cargo.lock
generated
19
src/Rust/Cargo.lock
generated
@@ -1208,6 +1208,12 @@ version = "1.0.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7ffc183a10b4478d04cbbbfc96d0873219d962dd5accaff2ffbd4ceb7df837f4"
|
||||
|
||||
[[package]]
|
||||
name = "ryu"
|
||||
version = "1.0.16"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f98d2aa92eebf49b69786be48e4477826b256916e84a57ff2a4f21923b48eb4c"
|
||||
|
||||
[[package]]
|
||||
name = "same-file"
|
||||
version = "1.0.6"
|
||||
@@ -1275,6 +1281,17 @@ dependencies = [
|
||||
"syn 2.0.48",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_json"
|
||||
version = "1.0.113"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "69801b70b1c3dac963ecb03a364ba0ceda9cf60c71cfe475e99864759c8b8a79"
|
||||
dependencies = [
|
||||
"itoa",
|
||||
"ryu",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sha1_smol"
|
||||
version = "1.0.0"
|
||||
@@ -1575,6 +1592,8 @@ dependencies = [
|
||||
"regex",
|
||||
"remove_dir_all",
|
||||
"semver",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"sha1_smol",
|
||||
"simple-stopwatch",
|
||||
"simplelog",
|
||||
|
||||
@@ -75,6 +75,8 @@ remove_dir_all = { git = "https://github.com/caesay/remove_dir_all.git", feature
|
||||
zstd = "0.13"
|
||||
sha1_smol = "1.0"
|
||||
url = "2.5"
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
serde_json = "1.0"
|
||||
|
||||
[target.'cfg(unix)'.dependencies]
|
||||
native-dialog = "0.7"
|
||||
|
||||
@@ -1,38 +1,146 @@
|
||||
use crate::bundle::Manifest;
|
||||
use crate::{bundle::Manifest, shared};
|
||||
use anyhow::{bail, Result};
|
||||
use std::path::PathBuf;
|
||||
use url::{ParseError, Url};
|
||||
use semver::Version;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::{fs, path::PathBuf};
|
||||
|
||||
pub fn check(root_path: &PathBuf, app: &Manifest, path: String, allow_downgrade: bool, channel: Option<String>) -> Result<()> {
|
||||
match Url::parse(&path) {
|
||||
Ok(url) => check_url(root_path, app, url, allow_downgrade, channel),
|
||||
_ => {
|
||||
let buf = PathBuf::from(&path);
|
||||
if !buf.exists() {
|
||||
bail!("Path must be a valid HTTP Url or a path to an existing directory: {}", path);
|
||||
}
|
||||
check_dir(root_path, app, buf, allow_downgrade, channel)
|
||||
}
|
||||
}
|
||||
#[allow(non_snake_case)]
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, Default)]
|
||||
#[serde(default)]
|
||||
pub struct VelopackAssetFeed {
|
||||
pub Assets: Vec<VelopackAsset>,
|
||||
}
|
||||
|
||||
fn check_url(root_path: &PathBuf, app: &Manifest, path: Url, allow_downgrade: bool, channel: Option<String>) -> Result<()> {
|
||||
let channel = channel.unwrap_or(app.channel.clone());
|
||||
#[allow(non_snake_case)]
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, Default)]
|
||||
#[serde(default)]
|
||||
pub struct VelopackAsset {
|
||||
pub PackageId: String,
|
||||
pub Version: String,
|
||||
pub Type: String,
|
||||
pub FileName: String,
|
||||
pub SHA1: String,
|
||||
pub Size: u64,
|
||||
pub NotesMarkdown: String,
|
||||
pub NotesHtml: String,
|
||||
}
|
||||
|
||||
#[allow(non_snake_case)]
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, Default)]
|
||||
#[serde(default)]
|
||||
pub struct UpdateInfo {
|
||||
pub TargetFullRelease: VelopackAsset,
|
||||
pub IsDowngrade: bool,
|
||||
}
|
||||
|
||||
pub fn check(app: &Manifest, path: &str, allow_downgrade: bool, channel: Option<&str>) -> Result<Option<UpdateInfo>> {
|
||||
let result = if shared::is_http_url(&path) {
|
||||
info!("Checking for updates from URL: {}", path);
|
||||
check_url(app, path, allow_downgrade, channel)
|
||||
} else {
|
||||
let buf = PathBuf::from(&path);
|
||||
info!("Checking for updates from Local Path: {}", buf.to_string_lossy());
|
||||
if !buf.exists() {
|
||||
bail!("Path must be a valid HTTP Url or a path to an existing directory: {}", path);
|
||||
}
|
||||
check_dir(app, buf, allow_downgrade, channel)
|
||||
};
|
||||
result
|
||||
}
|
||||
|
||||
fn get_default_channel() -> String {
|
||||
#[cfg(target_os = "windows")]
|
||||
return "win".to_owned();
|
||||
#[cfg(target_os = "linux")]
|
||||
return "linux".to_owned();
|
||||
#[cfg(target_os = "macos")]
|
||||
return "osx".to_owned();
|
||||
}
|
||||
|
||||
fn check_url(app: &Manifest, path: &str, allow_downgrade: bool, channel: Option<&str>) -> Result<Option<UpdateInfo>> {
|
||||
let mut channel = channel.unwrap_or(&app.channel).to_string();
|
||||
if channel.is_empty() {
|
||||
channel = get_default_channel();
|
||||
}
|
||||
|
||||
let non_default_channel = channel != app.channel;
|
||||
let releases_name = format!("releases.{}.json", channel);
|
||||
|
||||
todo!();
|
||||
let path = path.trim_end_matches('/').to_owned() + "/";
|
||||
let url = url::Url::parse(&path)?;
|
||||
let mut releases_url = url.join(&releases_name)?;
|
||||
releases_url.set_query(Some(format!("localVersion={}&id={}", app.version, app.id).as_str()));
|
||||
|
||||
info!("Downloading releases for channel {} from: {}", channel, releases_url.to_string());
|
||||
|
||||
let json = shared::download::download_url_as_string(releases_url.as_str())?;
|
||||
let feed: VelopackAssetFeed = serde_json::from_str(&json)?;
|
||||
process_feed(app, feed, allow_downgrade, non_default_channel)
|
||||
}
|
||||
|
||||
fn check_dir(root_path: &PathBuf, app: &Manifest, path: PathBuf, allow_downgrade: bool, channel: Option<String>) -> Result<()> {
|
||||
let channel = channel.unwrap_or(app.channel.clone());
|
||||
fn check_dir(app: &Manifest, path: PathBuf, allow_downgrade: bool, channel: Option<&str>) -> Result<Option<UpdateInfo>> {
|
||||
let mut channel = channel.unwrap_or(&app.channel).to_string();
|
||||
if channel.is_empty() {
|
||||
channel = get_default_channel();
|
||||
}
|
||||
|
||||
let non_default_channel = channel != app.channel;
|
||||
let releases_name = format!("releases.{}.json", channel);
|
||||
let releases_path = path.join(&releases_name);
|
||||
|
||||
info!("Reading releases file for channel {} from: {}", channel, releases_path.to_string_lossy());
|
||||
|
||||
if !releases_path.exists() {
|
||||
bail!("Could not find releases file: {}", path.to_string_lossy());
|
||||
bail!("Could not find releases file: {}", path.to_string_lossy());
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
todo!();
|
||||
let json = fs::read_to_string(&releases_path)?;
|
||||
let feed: VelopackAssetFeed = serde_json::from_str(&json)?;
|
||||
process_feed(app, feed, allow_downgrade, non_default_channel)
|
||||
}
|
||||
|
||||
fn process_feed(app: &Manifest, feed: VelopackAssetFeed, allow_downgrade: bool, is_non_default_channel: bool) -> Result<Option<UpdateInfo>> {
|
||||
let assets = feed.Assets;
|
||||
|
||||
if assets.is_empty() {
|
||||
bail!("Zero assets found in releases feed.");
|
||||
}
|
||||
|
||||
let mut latest: Option<VelopackAsset> = None;
|
||||
let mut latest_version: Version = Version::parse("0.0.0")?;
|
||||
for asset in assets {
|
||||
if let Ok(sv) = Version::parse(&asset.Version) {
|
||||
debug!("Found asset: {} ({}).", asset.FileName, sv.to_string());
|
||||
if latest.is_none() || (sv > latest_version && asset.Type.eq_ignore_ascii_case("Full")) {
|
||||
latest = Some(asset);
|
||||
latest_version = sv;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if latest.is_none() {
|
||||
bail!("No valid full releases found in feed.");
|
||||
}
|
||||
|
||||
let remote_version = latest_version;
|
||||
let remote_asset = latest.unwrap();
|
||||
|
||||
debug!("Latest remote release: {} ({}).", remote_asset.FileName, remote_version.to_string());
|
||||
|
||||
let mut result: Option<UpdateInfo> = None;
|
||||
|
||||
if remote_version > app.version {
|
||||
info!("Found newer remote release available ({} -> {}).", app.version, remote_version);
|
||||
result = Some(UpdateInfo { TargetFullRelease: remote_asset, IsDowngrade: false });
|
||||
} else if remote_version < app.version && allow_downgrade {
|
||||
info!("Found older remote release available and downgrade is enabled ({} -> {}).", app.version, remote_version);
|
||||
result = Some(UpdateInfo { TargetFullRelease: remote_asset, IsDowngrade: true });
|
||||
} else if remote_version == app.version && allow_downgrade && is_non_default_channel {
|
||||
info!("Latest remote release is the same version of a different channel, and downgrade is enabled ({} -> {}).", app.version, remote_version);
|
||||
result = Some(UpdateInfo { TargetFullRelease: remote_asset, IsDowngrade: true });
|
||||
} else {
|
||||
info!("No update available.");
|
||||
}
|
||||
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
64
src/Rust/src/commands/download.rs
Normal file
64
src/Rust/src/commands/download.rs
Normal file
@@ -0,0 +1,64 @@
|
||||
use crate::{bundle::Manifest, shared};
|
||||
use anyhow::{bail, Result};
|
||||
use std::{
|
||||
fs,
|
||||
path::{Path, PathBuf},
|
||||
};
|
||||
|
||||
pub fn download<A>(root_path: &PathBuf, app: &Manifest, path: &str, clean: bool, name: &str, mut progress: A) -> Result<PathBuf>
|
||||
where
|
||||
A: FnMut(i16),
|
||||
{
|
||||
if !name.ends_with(".nupkg") {
|
||||
bail!("Asset name must end with .nupkg");
|
||||
}
|
||||
|
||||
let packages_dir_str = app.get_packages_path(root_path);
|
||||
let packages_dir = Path::new(&packages_dir_str);
|
||||
let target_file = packages_dir.join(name);
|
||||
|
||||
let mut to_delete = Vec::new();
|
||||
|
||||
if clean {
|
||||
let g = format!("{}/*.nupkg", packages_dir_str);
|
||||
info!("Searching for packages to clean in: '{}'", g);
|
||||
match glob::glob(&g) {
|
||||
Ok(paths) => {
|
||||
for path in paths {
|
||||
if let Ok(path) = path {
|
||||
to_delete.push(path);
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
error!("Error while searching for packages to clean: {}", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if shared::is_http_url(path) {
|
||||
info!("About to download from URL '{}' to file '{}'", path, target_file.to_string_lossy());
|
||||
shared::download::download_url_to_file(path, &target_file.to_string_lossy(), &mut progress)?;
|
||||
} else {
|
||||
let source_path = Path::new(path);
|
||||
let source_file = source_path.join(name);
|
||||
info!("About to copy local file from '{}' to '{}'", source_file.to_string_lossy(), target_file.to_string_lossy());
|
||||
|
||||
if !source_file.exists() {
|
||||
bail!("Local file does not exist: {}", source_file.to_string_lossy());
|
||||
}
|
||||
|
||||
fs::copy(&source_file, &target_file)?;
|
||||
}
|
||||
|
||||
info!("Successfully placed file: '{}'", target_file.to_string_lossy());
|
||||
|
||||
if clean {
|
||||
for path in to_delete {
|
||||
info!("Cleaning up old package: '{}'", path.to_string_lossy());
|
||||
fs::remove_file(&path)?;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(target_file)
|
||||
}
|
||||
@@ -7,6 +7,9 @@ pub use patch::*;
|
||||
mod check;
|
||||
pub use check::*;
|
||||
|
||||
mod download;
|
||||
pub use download::*;
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
mod apply_linux_impl;
|
||||
#[cfg(target_os = "macos")]
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
pub mod bundle;
|
||||
pub mod download;
|
||||
|
||||
mod dialogs_const;
|
||||
mod dialogs_common;
|
||||
|
||||
@@ -3,6 +3,13 @@ use rand::distributions::{Alphanumeric, DistString};
|
||||
use regex::Regex;
|
||||
use std::{path::Path, thread, time::Duration};
|
||||
|
||||
pub fn is_http_url(url: &str) -> bool {
|
||||
match url::Url::parse(url) {
|
||||
Ok(url) => url.scheme().eq_ignore_ascii_case("http") || url.scheme().eq_ignore_ascii_case("https"),
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn retry_io<F, T, E>(op: F) -> Result<T, E>
|
||||
where
|
||||
F: Fn() -> Result<T, E>,
|
||||
|
||||
@@ -29,14 +29,20 @@ fn root_command() -> Command {
|
||||
)
|
||||
.subcommand(Command::new("check")
|
||||
.about("Checks for available updates")
|
||||
.arg(arg!([PATH] "HTTP URL or path to local folder containing an update source.").required(true))
|
||||
.arg(arg!(--url <URL> "URL or local folder containing an update source").required(true))
|
||||
.arg(arg!(--downgrade "Allow version downgrade"))
|
||||
.arg(arg!(--channel <NAME> "Explicitly switch to a specific channel"))
|
||||
.arg(arg!(--format <FORMAT> "The format of the program output (json|text)").default_value("json"))
|
||||
)
|
||||
.subcommand(Command::new("download")
|
||||
.about("Download/copies an available remote file into the packages directory.")
|
||||
.arg(arg!([PATH] "HTTP URL or path to local folder containing an update source.").required(true))
|
||||
.arg(arg!([NAME] "The remote package file to download.").required(true))
|
||||
.about("Download/copies an available remote file into the packages directory")
|
||||
.arg(arg!(--url <URL> "URL or local folder containing an update source").required(true))
|
||||
.arg(arg!(--name <NAME> "The name of the asset to download").required(true))
|
||||
.arg(arg!(--clean "Delete all other packages if download is successful"))
|
||||
.arg(arg!(--format <FORMAT> "The format of the program output (json|text)").default_value("json"))
|
||||
)
|
||||
.subcommand(Command::new("get-version")
|
||||
.about("Prints the current version of the application")
|
||||
)
|
||||
.arg(arg!(--verbose "Print debug messages to console / log").global(true))
|
||||
.arg(arg!(--nocolor "Disable colored output").hide(true).global(true))
|
||||
@@ -82,17 +88,24 @@ fn main() -> Result<()> {
|
||||
#[cfg(unix)]
|
||||
let matches = root_command().get_matches();
|
||||
|
||||
let (subcommand, subcommand_matches) = matches.subcommand().ok_or_else(|| anyhow!("No subcommand was used. Try `--help` for more information."))?;
|
||||
|
||||
let verbose = matches.get_flag("verbose");
|
||||
let silent = matches.get_flag("silent");
|
||||
let mut silent = matches.get_flag("silent");
|
||||
let nocolor = matches.get_flag("nocolor");
|
||||
let log_file = matches.get_one("log");
|
||||
|
||||
dialogs::set_silent(silent);
|
||||
// these commands output machine-readable data, so we don't want to show dialogs or logs to stdout
|
||||
let no_console = subcommand.eq_ignore_ascii_case("check") || subcommand.eq_ignore_ascii_case("download") || subcommand.eq_ignore_ascii_case("get-version");
|
||||
if no_console {
|
||||
silent = true;
|
||||
}
|
||||
|
||||
dialogs::set_silent(silent);
|
||||
if let Some(log_file) = log_file {
|
||||
logging::setup_logging(Some(&log_file), true, verbose, nocolor)?;
|
||||
logging::setup_logging(Some(&log_file), !no_console, verbose, nocolor)?;
|
||||
} else {
|
||||
default_logging(verbose, nocolor)?;
|
||||
default_logging(verbose, nocolor, !no_console)?;
|
||||
}
|
||||
|
||||
info!("Starting Velopack Updater ({})", env!("NGBV_VERSION"));
|
||||
@@ -106,14 +119,16 @@ fn main() -> Result<()> {
|
||||
containing_dir.pop();
|
||||
env::set_current_dir(containing_dir)?;
|
||||
|
||||
let (subcommand, subcommand_matches) = matches.subcommand().ok_or_else(|| anyhow!("No subcommand was used. Try `--help` for more information."))?;
|
||||
let result = match subcommand {
|
||||
#[cfg(target_os = "windows")]
|
||||
"uninstall" => uninstall(subcommand_matches).map_err(|e| anyhow!("Uninstall error: {}", e)),
|
||||
#[cfg(target_os = "windows")]
|
||||
"start" => start(&subcommand_matches).map_err(|e| anyhow!("Start error: {}", e)),
|
||||
"start" => start(subcommand_matches).map_err(|e| anyhow!("Start error: {}", e)),
|
||||
"apply" => apply(subcommand_matches).map_err(|e| anyhow!("Apply error: {}", e)),
|
||||
"patch" => patch(subcommand_matches).map_err(|e| anyhow!("Patch error: {}", e)),
|
||||
"check" => check(subcommand_matches).map_err(|e| anyhow!("Check error: {}", e)),
|
||||
"download" => download(subcommand_matches).map_err(|e| anyhow!("Download error: {}", e)),
|
||||
"get-version" => get_version(subcommand_matches).map_err(|e| anyhow!("Get-version error: {}", e)),
|
||||
_ => bail!("Unknown subcommand. Try `--help` for more information."),
|
||||
};
|
||||
|
||||
@@ -125,6 +140,94 @@ fn main() -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn get_version(_matches: &ArgMatches) -> Result<()> {
|
||||
let (_, app) = shared::detect_current_manifest()?;
|
||||
println!("{}", app.version);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn check(matches: &ArgMatches) -> Result<()> {
|
||||
let url = matches.get_one::<String>("url").unwrap();
|
||||
let format = matches.get_one::<String>("format").unwrap();
|
||||
let allow_downgrade = matches.get_flag("downgrade");
|
||||
let channel = matches.get_one::<String>("channel").map(|x| x.as_str());
|
||||
let is_json = format.eq_ignore_ascii_case("json");
|
||||
|
||||
info!("Command: Check");
|
||||
info!(" URL: {:?}", url);
|
||||
info!(" Allow Downgrade: {:?}", allow_downgrade);
|
||||
info!(" Channel: {:?}", channel);
|
||||
info!(" Format: {:?}", format);
|
||||
|
||||
// this is a machine readable command, so we write program output to stdout in the desired format
|
||||
let (_, app) = shared::detect_current_manifest()?;
|
||||
match commands::check(&app, url, allow_downgrade, channel) {
|
||||
Ok(opt) => match opt {
|
||||
Some(info) => {
|
||||
if is_json {
|
||||
println!("{}", serde_json::to_string(&info)?);
|
||||
} else {
|
||||
let asset = info.TargetFullRelease;
|
||||
println!("{} {} {} {}", asset.Version, asset.SHA1, asset.FileName, asset.Size);
|
||||
}
|
||||
}
|
||||
_ => println!("null"),
|
||||
},
|
||||
Err(e) => {
|
||||
if is_json {
|
||||
println!("{{ \"error\": \"{}\" }}", e);
|
||||
} else {
|
||||
println!("err: {}", e);
|
||||
}
|
||||
return Err(e);
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn download(matches: &ArgMatches) -> Result<()> {
|
||||
let url = matches.get_one::<String>("url").unwrap();
|
||||
let name = matches.get_one::<String>("name").unwrap();
|
||||
let format = matches.get_one::<String>("format").unwrap();
|
||||
let clean = matches.get_flag("clean");
|
||||
let is_json = format.eq_ignore_ascii_case("json");
|
||||
|
||||
info!("Command: Download");
|
||||
info!(" URL: {:?}", url);
|
||||
info!(" Asset Name: {:?}", name);
|
||||
info!(" Format: {:?}", format);
|
||||
info!(" Clean: {:?}", clean);
|
||||
|
||||
// this is a machine readable command, so we write program output to stdout in the desired format
|
||||
let (root_path, app) = shared::detect_current_manifest()?;
|
||||
#[cfg(target_os = "windows")]
|
||||
let _mutex = shared::retry_io(|| windows::create_global_mutex(&app))?;
|
||||
match commands::download(&root_path, &app, url, clean, name, |p| {
|
||||
if is_json {
|
||||
println!("{{ \"progress\": {} }}", p);
|
||||
} else {
|
||||
println!("{}", p);
|
||||
}
|
||||
}) {
|
||||
Ok(path) => {
|
||||
if is_json {
|
||||
println!("{{ \"complete\": true, \"progress\": 100, \"file\": \"{}\" }}", path.to_string_lossy());
|
||||
} else {
|
||||
println!("complete: {}", path.to_string_lossy());
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
if is_json {
|
||||
println!("{{ \"error\": \"{}\" }}", e);
|
||||
} else {
|
||||
println!("err: {}", e);
|
||||
}
|
||||
return Err(e);
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn patch(matches: &ArgMatches) -> Result<()> {
|
||||
let old_file = matches.get_one::<PathBuf>("old").unwrap();
|
||||
let patch_file = matches.get_one::<PathBuf>("patch").unwrap();
|
||||
@@ -184,7 +287,7 @@ fn uninstall(_matches: &ArgMatches) -> Result<()> {
|
||||
commands::uninstall(&root_path, &app, true)
|
||||
}
|
||||
|
||||
pub fn default_logging(verbose: bool, nocolor: bool) -> Result<()> {
|
||||
pub fn default_logging(verbose: bool, nocolor: bool, console: bool) -> Result<()> {
|
||||
#[cfg(windows)]
|
||||
let default_log_file = {
|
||||
let mut my_dir = env::current_exe().unwrap();
|
||||
@@ -195,7 +298,7 @@ pub fn default_logging(verbose: bool, nocolor: bool) -> Result<()> {
|
||||
#[cfg(unix)]
|
||||
let default_log_file = std::path::Path::new("/tmp/velopack.log").to_path_buf();
|
||||
|
||||
logging::setup_logging(Some(&default_log_file), true, verbose, nocolor)
|
||||
logging::setup_logging(Some(&default_log_file), console, verbose, nocolor)
|
||||
}
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
pub mod download;
|
||||
pub mod prerequisite;
|
||||
pub mod runtimes;
|
||||
pub mod splash;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
use super::{download, runtimes, splash};
|
||||
use crate::shared::{bundle, dialogs};
|
||||
use super::{runtimes, splash};
|
||||
use crate::shared::{bundle, dialogs, download};
|
||||
|
||||
use anyhow::Result;
|
||||
use std::path::Path;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
use super::download;
|
||||
use crate::shared as util;
|
||||
use crate::shared::download;
|
||||
use anyhow::{anyhow, bail, Result};
|
||||
use regex::Regex;
|
||||
use std::process::Command as Process;
|
||||
|
||||
@@ -9,6 +9,41 @@ use velopack::*;
|
||||
#[cfg(target_os = "windows")]
|
||||
use winsafe::{self as w, co};
|
||||
|
||||
#[test]
|
||||
pub fn test_check_updates() {
|
||||
let fixtures = find_fixtures();
|
||||
let feedjson = fixtures.join("testfeed.json");
|
||||
|
||||
let tmp = tempdir().unwrap();
|
||||
|
||||
// verify that we can't check for updates without a manifest
|
||||
let mut manifest = bundle::Manifest::default();
|
||||
manifest.version = semver::Version::parse("1.0.5").unwrap();
|
||||
let result = commands::check(&manifest, &tmp.path().to_string_lossy(), false, Some("stable"));
|
||||
assert!(result.is_err());
|
||||
|
||||
// we should find a version greater than ours
|
||||
fs::copy(feedjson, tmp.path().join("releases.stable.json")).unwrap();
|
||||
let result = commands::check(&manifest, &tmp.path().to_string_lossy(), false, Some("stable")).unwrap();
|
||||
assert!(result.is_some());
|
||||
assert!(semver::Version::parse(&result.unwrap().TargetFullRelease.Version).unwrap() == semver::Version::parse("1.0.11").unwrap());
|
||||
|
||||
// we should not find a version equal to ours
|
||||
manifest.version = semver::Version::parse("1.0.11").unwrap();
|
||||
let result = commands::check(&manifest, &tmp.path().to_string_lossy(), false, Some("stable")).unwrap();
|
||||
assert!(result.is_none());
|
||||
|
||||
// we should not find a version less than ours
|
||||
manifest.version = semver::Version::parse("1.0.20").unwrap();
|
||||
let result = commands::check(&manifest, &tmp.path().to_string_lossy(), false, Some("stable")).unwrap();
|
||||
assert!(result.is_none());
|
||||
|
||||
// if downgrade is allowed, we should find a version less than ours
|
||||
let result = commands::check(&manifest, &tmp.path().to_string_lossy(), true, Some("stable")).unwrap();
|
||||
assert!(result.is_some());
|
||||
assert!(semver::Version::parse(&result.unwrap().TargetFullRelease.Version).unwrap() == semver::Version::parse("1.0.11").unwrap());
|
||||
}
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
#[test]
|
||||
pub fn test_install_apply_uninstall() {
|
||||
|
||||
1
test/fixtures/testfeed.json
vendored
1
test/fixtures/testfeed.json
vendored
@@ -4,6 +4,7 @@
|
||||
"PackageId": "AvaloniaCrossPlat",
|
||||
"Version": "1.0.11",
|
||||
"Type": "Full",
|
||||
"UnknownProperty": "To test that serializers will ignore this",
|
||||
"FileName": "AvaloniaCrossPlat-1.0.11-full.nupkg",
|
||||
"SHA1": "D9F1CE7DE35D9544DF65AE6A5674D1A2D7EE5EAC",
|
||||
"Size": 14763516,
|
||||
|
||||
Reference in New Issue
Block a user