mirror of
https://github.com/velopack/velopack.git
synced 2025-10-25 15:19:22 +00:00
Remove extended cli code, this is going to another binary
This commit is contained in:
@@ -5,7 +5,6 @@ edition = "2021"
|
||||
|
||||
[features]
|
||||
windows = []
|
||||
extendedcli = []
|
||||
|
||||
[lib]
|
||||
name = "velopack"
|
||||
|
||||
@@ -1,146 +0,0 @@
|
||||
use crate::{bundle::Manifest, shared};
|
||||
use anyhow::{bail, Result};
|
||||
use semver::Version;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::{fs, path::PathBuf};
|
||||
|
||||
#[allow(non_snake_case)]
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, Default)]
|
||||
#[serde(default)]
|
||||
pub struct VelopackAssetFeed {
|
||||
pub Assets: Vec<VelopackAsset>,
|
||||
}
|
||||
|
||||
#[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);
|
||||
|
||||
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(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());
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
@@ -1,64 +0,0 @@
|
||||
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)
|
||||
}
|
||||
@@ -4,16 +4,6 @@ pub use apply::*;
|
||||
mod patch;
|
||||
pub use patch::*;
|
||||
|
||||
#[cfg(feature = "extendedcli")]
|
||||
mod check;
|
||||
#[cfg(feature = "extendedcli")]
|
||||
pub use check::*;
|
||||
|
||||
#[cfg(feature = "extendedcli")]
|
||||
mod download;
|
||||
#[cfg(feature = "extendedcli")]
|
||||
pub use download::*;
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
mod apply_linux_impl;
|
||||
#[cfg(target_os = "macos")]
|
||||
|
||||
@@ -8,7 +8,6 @@ use anyhow::{anyhow, bail, Result};
|
||||
use clap::{arg, value_parser, ArgMatches, Command};
|
||||
use std::{env, path::PathBuf};
|
||||
use velopack::*;
|
||||
use serde_json::json;
|
||||
|
||||
#[rustfmt::skip]
|
||||
fn root_command() -> Command {
|
||||
@@ -39,24 +38,6 @@ fn root_command() -> Command {
|
||||
.disable_help_subcommand(true)
|
||||
.flatten_help(true);
|
||||
|
||||
#[cfg(feature = "extendedcli")]
|
||||
let cmd = cmd.subcommand(Command::new("download")
|
||||
.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"))
|
||||
);
|
||||
|
||||
#[cfg(feature = "extendedcli")]
|
||||
let cmd = cmd.subcommand(Command::new("check")
|
||||
.about("Checks for available updates")
|
||||
.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"))
|
||||
);
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
let cmd = cmd.subcommand(Command::new("start")
|
||||
.about("Starts the currently installed version of the application")
|
||||
@@ -131,11 +112,6 @@ fn main() -> Result<()> {
|
||||
"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)),
|
||||
#[cfg(feature = "extendedcli")]
|
||||
"check" => check(subcommand_matches).map_err(|e| anyhow!("Check error: {}", e)),
|
||||
#[cfg(feature = "extendedcli")]
|
||||
"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."),
|
||||
};
|
||||
|
||||
@@ -147,96 +123,6 @@ fn main() -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn get_version(_matches: &ArgMatches) -> Result<()> {
|
||||
let (_, app) = shared::detect_current_manifest()?;
|
||||
println!("{}", app.version);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(feature = "extendedcli")]
|
||||
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\": {} }}", json!(format!("{}", e)));
|
||||
} else {
|
||||
println!("err: {}", e);
|
||||
}
|
||||
return Err(e);
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(feature = "extendedcli")]
|
||||
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\": {} }}", json!(path.to_string_lossy()));
|
||||
} else {
|
||||
println!("complete: {}", path.to_string_lossy());
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
if is_json {
|
||||
println!("{{ \"error\": {} }}", json!(format!("{}", 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();
|
||||
|
||||
@@ -9,42 +9,6 @@ use velopack::*;
|
||||
#[cfg(target_os = "windows")]
|
||||
use winsafe::{self as w, co};
|
||||
|
||||
#[cfg(feature = "extendedcli")]
|
||||
#[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() {
|
||||
|
||||
Reference in New Issue
Block a user