mirror of
https://github.com/velopack/velopack.git
synced 2025-10-25 15:19:22 +00:00
Add legacy migration code & tests
This commit is contained in:
@@ -12,6 +12,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "SolutionLevel", "SolutionLe
|
|||||||
.editorconfig = .editorconfig
|
.editorconfig = .editorconfig
|
||||||
.github\workflows\build.yml = .github\workflows\build.yml
|
.github\workflows\build.yml = .github\workflows\build.yml
|
||||||
src\Directory.Build.props = src\Directory.Build.props
|
src\Directory.Build.props = src\Directory.Build.props
|
||||||
|
test\Directory.Build.props = test\Directory.Build.props
|
||||||
README.md = README.md
|
README.md = README.md
|
||||||
Velopack.entitlements = Velopack.entitlements
|
Velopack.entitlements = Velopack.entitlements
|
||||||
version.json = version.json
|
version.json = version.json
|
||||||
|
|||||||
@@ -18,10 +18,12 @@ pub fn apply<'a>(
|
|||||||
noelevate: bool,
|
noelevate: bool,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
if wait_for_parent {
|
if wait_for_parent {
|
||||||
let _ = shared::wait_for_parent_to_exit(60_000); // 1 minute
|
if let Err(e) = shared::wait_for_parent_to_exit(60_000) {
|
||||||
|
warn!("Failed to wait for parent process to exit ({}).", e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Err(e) = apply_package(&root_path, &app, restart, package, exe_args.clone(), noelevate) {
|
if let Err(e) = apply_package_impl(&root_path, &app, restart, package, exe_args.clone(), noelevate) {
|
||||||
error!("Error applying package: {}", e);
|
error!("Error applying package: {}", e);
|
||||||
if !restart {
|
if !restart {
|
||||||
return Err(e);
|
return Err(e);
|
||||||
@@ -36,7 +38,7 @@ pub fn apply<'a>(
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn apply_package<'a>(
|
fn apply_package_impl<'a>(
|
||||||
root_path: &PathBuf,
|
root_path: &PathBuf,
|
||||||
app: &Manifest,
|
app: &Manifest,
|
||||||
restart: bool,
|
restart: bool,
|
||||||
@@ -84,7 +86,11 @@ fn apply_package<'a>(
|
|||||||
|
|
||||||
let found_version = (&package_manifest.version).to_owned();
|
let found_version = (&package_manifest.version).to_owned();
|
||||||
if found_version <= app.version {
|
if found_version <= app.version {
|
||||||
bail!("Latest package found is {}, which is not newer than current version {}.", found_version, app.version);
|
if package.is_none() {
|
||||||
|
bail!("Latest package found is {}, which is not newer than current version {}.", found_version, app.version);
|
||||||
|
} else {
|
||||||
|
warn!("Provided package is {}, which is not newer than current version {}.", found_version, app.version);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(target_os = "windows")]
|
#[cfg(target_os = "windows")]
|
||||||
|
|||||||
@@ -1,6 +1,14 @@
|
|||||||
use crate::shared;
|
use crate::{
|
||||||
use anyhow::{bail, Result};
|
dialogs,
|
||||||
use std::path::Path;
|
shared::{self, bundle},
|
||||||
|
windows,
|
||||||
|
};
|
||||||
|
use anyhow::{anyhow, bail, Result};
|
||||||
|
use std::{
|
||||||
|
fs,
|
||||||
|
path::{Path, PathBuf},
|
||||||
|
};
|
||||||
|
use winsafe::{self as w, co};
|
||||||
|
|
||||||
pub fn start(wait_for_parent: bool, exe_name: Option<&String>, exe_args: Option<Vec<&str>>, legacy_args: Option<&String>) -> Result<()> {
|
pub fn start(wait_for_parent: bool, exe_name: Option<&String>, exe_args: Option<Vec<&str>>, legacy_args: Option<&String>) -> Result<()> {
|
||||||
if legacy_args.is_some() && exe_args.is_some() {
|
if legacy_args.is_some() && exe_args.is_some() {
|
||||||
@@ -8,16 +16,47 @@ pub fn start(wait_for_parent: bool, exe_name: Option<&String>, exe_args: Option<
|
|||||||
}
|
}
|
||||||
|
|
||||||
if wait_for_parent {
|
if wait_for_parent {
|
||||||
shared::wait_for_parent_to_exit(60_000)?; // 1 minute
|
if let Err(e) = shared::wait_for_parent_to_exit(60_000) {
|
||||||
|
warn!("Failed to wait for parent process to exit ({}).", e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let (root_path, app) = shared::detect_current_manifest()?;
|
let (root_dir, app) = shared::detect_current_manifest()?;
|
||||||
|
|
||||||
let current = app.get_current_path(&root_path);
|
match shared::has_app_prefixed_folder(&root_dir) {
|
||||||
|
Ok(has_prefix) => {
|
||||||
|
if has_prefix {
|
||||||
|
info!("This is a legacy app. Will try and upgrade it now.");
|
||||||
|
|
||||||
|
// if started by legacy Squirrel, the working dir of Update.exe may be inside the app-* folder,
|
||||||
|
// meaning we can not clean up properly.
|
||||||
|
std::env::set_current_dir(&root_dir)?;
|
||||||
|
|
||||||
|
if let Err(e) = try_legacy_migration(&root_dir, &app) {
|
||||||
|
warn!("Failed to migrate legacy app ({}).", e);
|
||||||
|
dialogs::show_error(
|
||||||
|
&app.title,
|
||||||
|
Some("Unable to start app"),
|
||||||
|
"This app installation has been corrupted and cannot be started. Please reinstall the app.",
|
||||||
|
);
|
||||||
|
return Err(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
// we can't run the normal start command, because legacy squirrel might provide an "exe name" to restart
|
||||||
|
// which no longer exists in the package
|
||||||
|
let (root_dir, app) = shared::detect_current_manifest()?;
|
||||||
|
shared::start_package(&app, &root_dir, exe_args, Some("VELOPACK_RESTART"))?;
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(e) => warn!("Failed legacy check ({}).", e),
|
||||||
|
}
|
||||||
|
|
||||||
|
let current = app.get_current_path(&root_dir);
|
||||||
let exe_to_execute = if let Some(exe) = exe_name {
|
let exe_to_execute = if let Some(exe) = exe_name {
|
||||||
Path::new(¤t).join(exe)
|
Path::new(¤t).join(exe)
|
||||||
} else {
|
} else {
|
||||||
let exe = app.get_main_exe_path(&root_path);
|
let exe = app.get_main_exe_path(&root_dir);
|
||||||
Path::new(&exe).to_path_buf()
|
Path::new(&exe).to_path_buf()
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -39,3 +78,42 @@ pub fn start(wait_for_parent: bool, exe_name: Option<&String>, exe_args: Option<
|
|||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn try_legacy_migration(root_dir: &PathBuf, app: &bundle::Manifest) -> Result<()> {
|
||||||
|
let package = shared::find_latest_full_package(&root_dir).ok_or_else(|| anyhow!("Unable to find latest full package."))?;
|
||||||
|
let bundle = bundle::load_bundle_from_file(&package.file_path)?;
|
||||||
|
let _bundle_manifest = bundle.read_manifest()?; // this verifies it's a bundle we support
|
||||||
|
warn!("This application is installed in a folder prefixed with 'app-'. Attempting to migrate...");
|
||||||
|
let _ = shared::force_stop_package(&root_dir);
|
||||||
|
let current_dir = app.get_current_path(&root_dir);
|
||||||
|
let main_exe_path = app.get_main_exe_path(&root_dir);
|
||||||
|
|
||||||
|
if !Path::new(¤t_dir).exists() {
|
||||||
|
info!("Renaming latest app-* folder to current.");
|
||||||
|
if let Some((latest_app_dir, _latest_ver)) = shared::get_latest_app_version_folder(&root_dir)? {
|
||||||
|
fs::rename(&latest_app_dir, ¤t_dir)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
info!("Applying latest full package...");
|
||||||
|
let buf = Path::new(&package.file_path).to_path_buf();
|
||||||
|
super::apply(&root_dir, &app, false, false, Some(&buf), None, true)?;
|
||||||
|
|
||||||
|
info!("Removing old app-* folders...");
|
||||||
|
shared::delete_app_prefixed_folders(&root_dir)?;
|
||||||
|
let _ = remove_dir_all::remove_dir_all(root_dir.join("staging"));
|
||||||
|
|
||||||
|
info!("Removing old shortcuts...");
|
||||||
|
if let Err(e) = windows::remove_all_shortcuts_for_root_dir(&root_dir) {
|
||||||
|
warn!("Failed to remove shortcuts ({}).", e);
|
||||||
|
}
|
||||||
|
|
||||||
|
info!("Creating start menu shortcut...");
|
||||||
|
let startmenu = w::SHGetKnownFolderPath(&co::KNOWNFOLDERID::StartMenu, co::KF::DONT_UNEXPAND, None)?;
|
||||||
|
let lnk_path = Path::new(&startmenu).join("Programs").join(format!("{}.lnk", &app.title));
|
||||||
|
if let Err(e) = windows::create_lnk(&lnk_path.to_string_lossy(), &main_exe_path, ¤t_dir, None) {
|
||||||
|
warn!("Failed to create start menu shortcut: {}", e);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|||||||
@@ -78,8 +78,9 @@ pub struct BundleInfo<'a> {
|
|||||||
file_path: Option<PathBuf>,
|
file_path: Option<PathBuf>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn load_bundle_from_file<'a>(file_name: &PathBuf) -> Result<BundleInfo<'a>> {
|
pub fn load_bundle_from_file<'a, P: AsRef<Path>>(file_name: P) -> Result<BundleInfo<'a>> {
|
||||||
debug!("Loading bundle from file '{}'...", file_name.display());
|
let file_name = file_name.as_ref();
|
||||||
|
debug!("Loading bundle from file '{}'...", file_name.to_string_lossy());
|
||||||
let file = super::retry_io(|| File::open(&file_name))?;
|
let file = super::retry_io(|| File::open(&file_name))?;
|
||||||
let cursor: Box<dyn ReadSeek> = Box::new(file);
|
let cursor: Box<dyn ReadSeek> = Box::new(file);
|
||||||
let zip = ZipArchive::new(cursor)?;
|
let zip = ZipArchive::new(cursor)?;
|
||||||
|
|||||||
@@ -1,12 +1,15 @@
|
|||||||
use anyhow::{anyhow, bail, Result};
|
use anyhow::{anyhow, bail, Result};
|
||||||
|
use regex::Regex;
|
||||||
|
use semver::Version;
|
||||||
use std::{
|
use std::{
|
||||||
collections::HashMap,
|
collections::HashMap,
|
||||||
|
fs,
|
||||||
path::{Path, PathBuf},
|
path::{Path, PathBuf},
|
||||||
process::Command as Process,
|
process::Command as Process,
|
||||||
};
|
};
|
||||||
use winsafe::{self as w, co, prelude::*};
|
use winsafe::{self as w, co, prelude::*};
|
||||||
|
|
||||||
use super::bundle::{self, Manifest, EntryNameInfo};
|
use super::bundle::{self, EntryNameInfo, Manifest};
|
||||||
|
|
||||||
pub fn wait_for_parent_to_exit(ms_to_wait: u32) -> Result<()> {
|
pub fn wait_for_parent_to_exit(ms_to_wait: u32) -> Result<()> {
|
||||||
info!("Reading parent process information.");
|
info!("Reading parent process information.");
|
||||||
@@ -105,9 +108,7 @@ fn kill_pid(pid: u32) -> Result<()> {
|
|||||||
|
|
||||||
pub fn force_stop_package<P: AsRef<Path>>(root_dir: P) -> Result<()> {
|
pub fn force_stop_package<P: AsRef<Path>>(root_dir: P) -> Result<()> {
|
||||||
let root_dir = root_dir.as_ref();
|
let root_dir = root_dir.as_ref();
|
||||||
super::retry_io(|| {
|
super::retry_io(|| _force_stop_package(root_dir))?;
|
||||||
_force_stop_package(root_dir)
|
|
||||||
})?;
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -170,6 +171,72 @@ pub fn detect_current_manifest() -> Result<(PathBuf, Manifest)> {
|
|||||||
detect_manifest_from_update_path(&me)
|
detect_manifest_from_update_path(&me)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn get_app_prefixed_folders<P: AsRef<Path>>(parent_path: P) -> Result<Vec<PathBuf>> {
|
||||||
|
let parent_path = parent_path.as_ref();
|
||||||
|
let re = Regex::new(r"(?i)^app-")?;
|
||||||
|
let mut folders = Vec::new();
|
||||||
|
// Squirrel.Windows and Clowd.Squirrel V2
|
||||||
|
for entry in fs::read_dir(parent_path)? {
|
||||||
|
let entry = entry?;
|
||||||
|
let path = entry.path();
|
||||||
|
if path.is_dir() {
|
||||||
|
if let Some(name) = path.file_name().and_then(|n| n.to_str()) {
|
||||||
|
if re.is_match(name) {
|
||||||
|
folders.push(path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Clowd.Squirrel V3
|
||||||
|
let staging_dir = parent_path.join("staging");
|
||||||
|
if staging_dir.exists() {
|
||||||
|
for entry in fs::read_dir(&staging_dir)? {
|
||||||
|
let entry = entry?;
|
||||||
|
let path = entry.path();
|
||||||
|
if path.is_dir() {
|
||||||
|
if let Some(name) = path.file_name().and_then(|n| n.to_str()) {
|
||||||
|
if re.is_match(name) {
|
||||||
|
folders.push(path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(folders)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_latest_app_version_folder<P: AsRef<Path>>(parent_path: P) -> Result<Option<(PathBuf, Version)>> {
|
||||||
|
let mut latest_version: Option<Version> = None;
|
||||||
|
let mut latest_folder: Option<PathBuf> = None;
|
||||||
|
for entry in get_app_prefixed_folders(&parent_path)? {
|
||||||
|
if let Some(name) = entry.file_name().and_then(|n| n.to_str()) {
|
||||||
|
if let Some(version) = parse_version_from_folder_name(name) {
|
||||||
|
if latest_version.is_none() || version > latest_version.clone().unwrap() {
|
||||||
|
latest_version = Some(version);
|
||||||
|
latest_folder = Some(entry);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(latest_folder.zip(latest_version))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn has_app_prefixed_folder<P: AsRef<Path>>(parent_path: P) -> Result<bool> {
|
||||||
|
Ok(!get_app_prefixed_folders(parent_path)?.is_empty())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn delete_app_prefixed_folders<P: AsRef<Path>>(parent_path: P) -> Result<()> {
|
||||||
|
let folders = get_app_prefixed_folders(parent_path)?;
|
||||||
|
for folder in folders {
|
||||||
|
super::retry_io(|| remove_dir_all::remove_dir_all(&folder))?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_version_from_folder_name(folder_name: &str) -> Option<Version> {
|
||||||
|
folder_name.strip_prefix("app-").and_then(|v| Version::parse(v).ok())
|
||||||
|
}
|
||||||
|
|
||||||
fn find_manifest_from_root_dir(root_path: &PathBuf) -> Result<Manifest> {
|
fn find_manifest_from_root_dir(root_path: &PathBuf) -> Result<Manifest> {
|
||||||
// default to checking current/sq.version
|
// default to checking current/sq.version
|
||||||
let cm = find_current_manifest(root_path);
|
let cm = find_current_manifest(root_path);
|
||||||
@@ -178,6 +245,7 @@ fn find_manifest_from_root_dir(root_path: &PathBuf) -> Result<Manifest> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// if that fails, check for latest full package
|
// if that fails, check for latest full package
|
||||||
|
warn!("Unable to find current manifest, checking for latest full package. (LEGACY MODE)");
|
||||||
let latest = find_latest_full_package(root_path);
|
let latest = find_latest_full_package(root_path);
|
||||||
if let Some(latest) = latest {
|
if let Some(latest) = latest {
|
||||||
let mani = latest.load_manifest()?;
|
let mani = latest.load_manifest()?;
|
||||||
@@ -198,7 +266,7 @@ fn find_current_manifest(root_path: &PathBuf) -> Result<Manifest> {
|
|||||||
bail!("Unable to read nuspec file in current directory.")
|
bail!("Unable to read nuspec file in current directory.")
|
||||||
}
|
}
|
||||||
|
|
||||||
fn find_latest_full_package(root_path: &PathBuf) -> Option<EntryNameInfo> {
|
pub fn find_latest_full_package(root_path: &PathBuf) -> Option<EntryNameInfo> {
|
||||||
let packages = get_all_packages(root_path);
|
let packages = get_all_packages(root_path);
|
||||||
let mut latest: Option<EntryNameInfo> = None;
|
let mut latest: Option<EntryNameInfo> = None;
|
||||||
for pkg in packages {
|
for pkg in packages {
|
||||||
|
|||||||
@@ -31,6 +31,7 @@ fn root_command() -> Command {
|
|||||||
.arg(arg!(--nocolor "Disable colored output").hide(true).global(true))
|
.arg(arg!(--nocolor "Disable colored output").hide(true).global(true))
|
||||||
.arg(arg!(-s --silent "Don't show any prompts / dialogs").global(true))
|
.arg(arg!(-s --silent "Don't show any prompts / dialogs").global(true))
|
||||||
.arg(arg!(-l --log <PATH> "Override the default log file location").global(true).value_parser(value_parser!(PathBuf)))
|
.arg(arg!(-l --log <PATH> "Override the default log file location").global(true).value_parser(value_parser!(PathBuf)))
|
||||||
|
.arg(arg!(--forceLatest "Legacy / not used").hide(true))
|
||||||
.disable_help_subcommand(true)
|
.disable_help_subcommand(true)
|
||||||
.flatten_help(true);
|
.flatten_help(true);
|
||||||
|
|
||||||
|
|||||||
@@ -166,9 +166,13 @@ namespace Velopack
|
|||||||
VelopackHook defaultBlock = ((v) => { });
|
VelopackHook defaultBlock = ((v) => { });
|
||||||
var fastExitlookup = new[] {
|
var fastExitlookup = new[] {
|
||||||
new { Key = "--veloapp-install", Value = _install ?? defaultBlock },
|
new { Key = "--veloapp-install", Value = _install ?? defaultBlock },
|
||||||
|
new { Key = "--squirrel-install", Value = _install ?? defaultBlock },
|
||||||
new { Key = "--veloapp-updated", Value = _update ?? defaultBlock },
|
new { Key = "--veloapp-updated", Value = _update ?? defaultBlock },
|
||||||
|
new { Key = "--squirrel-updated", Value = _update ?? defaultBlock },
|
||||||
new { Key = "--veloapp-obsolete", Value = _obsolete ?? defaultBlock },
|
new { Key = "--veloapp-obsolete", Value = _obsolete ?? defaultBlock },
|
||||||
|
new { Key = "--squirrel-obsolete", Value = _obsolete ?? defaultBlock },
|
||||||
new { Key = "--veloapp-uninstall", Value = _uninstall ?? defaultBlock },
|
new { Key = "--veloapp-uninstall", Value = _uninstall ?? defaultBlock },
|
||||||
|
new { Key = "--squirrel-uninstall", Value = _uninstall ?? defaultBlock },
|
||||||
}.ToDictionary(k => k.Key, v => v.Value, StringComparer.OrdinalIgnoreCase);
|
}.ToDictionary(k => k.Key, v => v.Value, StringComparer.OrdinalIgnoreCase);
|
||||||
if (args.Length >= 2 && fastExitlookup.ContainsKey(args[0])) {
|
if (args.Length >= 2 && fastExitlookup.ContainsKey(args[0])) {
|
||||||
try {
|
try {
|
||||||
|
|||||||
@@ -16,7 +16,7 @@
|
|||||||
<IsPackable>false</IsPackable>
|
<IsPackable>false</IsPackable>
|
||||||
<LangVersion>latest</LangVersion>
|
<LangVersion>latest</LangVersion>
|
||||||
<IsTest>true</IsTest>
|
<IsTest>true</IsTest>
|
||||||
<NoWarn>$(NoWarn);CS1998,xUnit2015,xUnit2017,xUnit2005,xUnit2009,xUnit2013,xUnit2004;CA2007;CS8002</NoWarn>
|
<NoWarn>$(NoWarn);CS1998,xUnit2015,xUnit2017,xUnit2005,xUnit2009,xUnit2013,xUnit1013,xUnit2004;CA2007;CS8002</NoWarn>
|
||||||
<SignAssembly>True</SignAssembly>
|
<SignAssembly>True</SignAssembly>
|
||||||
<AssemblyOriginatorKeyFile>..\..\Velopack.snk</AssemblyOriginatorKeyFile>
|
<AssemblyOriginatorKeyFile>..\..\Velopack.snk</AssemblyOriginatorKeyFile>
|
||||||
<IsPackable>false</IsPackable>
|
<IsPackable>false</IsPackable>
|
||||||
|
|||||||
@@ -372,7 +372,7 @@ public class WindowsPackTests
|
|||||||
|
|
||||||
RunCoveredDotnet(appPath, new string[] { "--autoupdate" }, installDir, logger, exitCode: null);
|
RunCoveredDotnet(appPath, new string[] { "--autoupdate" }, installDir, logger, exitCode: null);
|
||||||
|
|
||||||
Thread.Sleep(2000);
|
Thread.Sleep(3000); // update.exe runs in separate process
|
||||||
|
|
||||||
var chk1version = RunCoveredDotnet(appPath, new string[] { "version" }, installDir, logger);
|
var chk1version = RunCoveredDotnet(appPath, new string[] { "version" }, installDir, logger);
|
||||||
Assert.EndsWith(Environment.NewLine + "2.0.0", chk1version);
|
Assert.EndsWith(Environment.NewLine + "2.0.0", chk1version);
|
||||||
@@ -551,6 +551,53 @@ public class WindowsPackTests
|
|||||||
logger.Info("TEST: uninstalled / complete");
|
logger.Info("TEST: uninstalled / complete");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[SkippableTheory]
|
||||||
|
[InlineData("LegacyTestApp-ClowdV2-Setup.exe", "app-1.0.0")]
|
||||||
|
[InlineData("LegacyTestApp-ClowdV3-Setup.exe", "current")]
|
||||||
|
[InlineData("LegacyTestApp-SquirrelWinV2-Setup.exe", "app-1.0.0")]
|
||||||
|
public void LegacyAppCanSuccessfullyMigrate(string fixture, string origDirName)
|
||||||
|
{
|
||||||
|
Skip.IfNot(VelopackRuntimeInfo.IsWindows);
|
||||||
|
using var logger = _output.BuildLoggerFor<WindowsPackTests>();
|
||||||
|
|
||||||
|
var rootDir = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "LegacyTestApp");
|
||||||
|
if (Directory.Exists(rootDir))
|
||||||
|
Utility.DeleteFileOrDirectoryHard(rootDir);
|
||||||
|
|
||||||
|
var setup = PathHelper.GetFixture(fixture);
|
||||||
|
var p = Process.Start(setup);
|
||||||
|
p.WaitForExit();
|
||||||
|
|
||||||
|
var currentDir = Path.Combine(rootDir, origDirName);
|
||||||
|
var appExe = Path.Combine(currentDir, "LegacyTestApp.exe");
|
||||||
|
var updateExe = Path.Combine(rootDir, "Update.exe");
|
||||||
|
Assert.True(File.Exists(appExe));
|
||||||
|
Assert.True(File.Exists(updateExe));
|
||||||
|
|
||||||
|
using var _1 = Utility.GetTempDirectory(out var releaseDir);
|
||||||
|
PackTestApp("LegacyTestApp", "2.0.0", "hello!", releaseDir, logger);
|
||||||
|
|
||||||
|
RunNoCoverage(appExe, new string[] { "download", releaseDir }, currentDir, logger, exitCode: 0);
|
||||||
|
RunNoCoverage(appExe, new string[] { "apply", releaseDir }, currentDir, logger, exitCode: null);
|
||||||
|
|
||||||
|
Thread.Sleep(3000); // update.exe runs in a separate process here
|
||||||
|
|
||||||
|
if (origDirName != "current") {
|
||||||
|
Assert.True(!Directory.Exists(currentDir));
|
||||||
|
currentDir = Path.Combine(rootDir, "current");
|
||||||
|
}
|
||||||
|
|
||||||
|
Assert.True(Directory.Exists(currentDir));
|
||||||
|
appExe = Path.Combine(currentDir, "TestApp.exe");
|
||||||
|
Assert.True(File.Exists(appExe));
|
||||||
|
|
||||||
|
Assert.False(Directory.EnumerateDirectories(rootDir, "app-*").Any());
|
||||||
|
Assert.False(Directory.Exists(Path.Combine(rootDir, "staging")));
|
||||||
|
|
||||||
|
// this is the file written by TestApp when it's detected the squirrel restart. if this is here, everything went smoothly.
|
||||||
|
Assert.True(File.Exists(Path.Combine(rootDir, "restarted")));
|
||||||
|
}
|
||||||
|
|
||||||
//private string RunCoveredRust(string binName, string[] args, string workingDir, ILogger logger, int? exitCode = 0)
|
//private string RunCoveredRust(string binName, string[] args, string workingDir, ILogger logger, int? exitCode = 0)
|
||||||
//{
|
//{
|
||||||
// var outputfile = GetPath($"coverage.runrust.{RandomString(8)}.xml");
|
// var outputfile = GetPath($"coverage.runrust.{RandomString(8)}.xml");
|
||||||
|
|||||||
Reference in New Issue
Block a user