mirror of
https://github.com/velopack/velopack.git
synced 2025-10-25 15:19:22 +00:00
Make ProcessInfo mockable, to help unusual setups and Unity
This commit is contained in:
@@ -46,6 +46,18 @@ namespace Velopack.Locators
|
||||
/// home directory.
|
||||
/// </summary>
|
||||
public bool IsPortable { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The process for which the Velopack Locator has been constructed. This should usually be the current process path.
|
||||
/// </summary>
|
||||
public string ProcessExePath { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The process ID for which the Velopack Locator has been constructed. This should usually be the current process ID.
|
||||
/// Setting this to zero will disable some features of Velopack (like the ability to wait for the process to exit
|
||||
/// before installing updates).
|
||||
/// </summary>
|
||||
public uint ProcessId { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Finds .nupkg files in the PackagesDir and returns a list of ReleaseEntryName objects.
|
||||
|
||||
@@ -32,7 +32,7 @@ namespace Velopack.Locators
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string? Channel { get; }
|
||||
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string? AppTempDir => CreateSubDirIfDoesNotExist(TempUtil.GetDefaultTempBaseDirectory(), AppId);
|
||||
|
||||
@@ -52,23 +52,25 @@ namespace Velopack.Locators
|
||||
/// Creates a new <see cref="OsxVelopackLocator"/> and auto-detects the
|
||||
/// app information from metadata embedded in the .app.
|
||||
/// </summary>
|
||||
public LinuxVelopackLocator(ILogger logger)
|
||||
public LinuxVelopackLocator(string currentProcessPath, uint currentProcessId, ILogger logger)
|
||||
: base(logger)
|
||||
{
|
||||
if (!VelopackRuntimeInfo.IsLinux)
|
||||
throw new NotSupportedException("Cannot instantiate LinuxVelopackLocator on a non-linux system.");
|
||||
|
||||
ProcessId = currentProcessId;
|
||||
ProcessExePath = currentProcessPath;
|
||||
|
||||
Log.Info($"Initialising {nameof(LinuxVelopackLocator)}");
|
||||
|
||||
// are we inside a mounted .AppImage?
|
||||
var ourPath = VelopackRuntimeInfo.EntryExePath;
|
||||
var ix = ourPath.IndexOf("/usr/bin/", StringComparison.InvariantCultureIgnoreCase);
|
||||
var ix = ProcessExePath.IndexOf("/usr/bin/", StringComparison.InvariantCultureIgnoreCase);
|
||||
if (ix <= 0) {
|
||||
Log.Warn($"Unable to locate .AppImage root from '{ourPath}'. This warning indicates that the application is not running from a mounted .AppImage, for example during development.");
|
||||
Log.Warn($"Unable to locate .AppImage root from '{ProcessExePath}'. This warning indicates that the application is not running from a mounted .AppImage, for example during development.");
|
||||
return;
|
||||
}
|
||||
|
||||
var rootDir = ourPath.Substring(0, ix);
|
||||
var rootDir = ProcessExePath.Substring(0, ix);
|
||||
var contentsDir = Path.Combine(rootDir, "usr", "bin");
|
||||
var updateExe = Path.Combine(contentsDir, "UpdateNix");
|
||||
var metadataPath = Path.Combine(contentsDir, CoreUtil.SpecVersionFileName);
|
||||
|
||||
@@ -49,23 +49,25 @@ namespace Velopack.Locators
|
||||
/// Creates a new <see cref="OsxVelopackLocator"/> and auto-detects the
|
||||
/// app information from metadata embedded in the .app.
|
||||
/// </summary>
|
||||
public OsxVelopackLocator(ILogger logger)
|
||||
public OsxVelopackLocator(string currentProcessPath, uint currentProcessId, ILogger logger)
|
||||
: base(logger)
|
||||
{
|
||||
if (!VelopackRuntimeInfo.IsOSX)
|
||||
throw new NotSupportedException("Cannot instantiate OsxLocator on a non-osx system.");
|
||||
|
||||
ProcessId = currentProcessId;
|
||||
ProcessExePath = currentProcessPath;
|
||||
|
||||
Log.Info($"Initialising {nameof(OsxVelopackLocator)}");
|
||||
|
||||
// are we inside a .app?
|
||||
var ourPath = VelopackRuntimeInfo.EntryExePath;
|
||||
var ix = ourPath.IndexOf(".app/", StringComparison.InvariantCultureIgnoreCase);
|
||||
var ix = ProcessExePath.IndexOf(".app/", StringComparison.InvariantCultureIgnoreCase);
|
||||
if (ix <= 0) {
|
||||
Log.Warn($"Unable to locate .app root from '{ourPath}'");
|
||||
Log.Warn($"Unable to locate .app root from '{ProcessExePath}'");
|
||||
return;
|
||||
}
|
||||
|
||||
var appPath = ourPath.Substring(0, ix + 4);
|
||||
var appPath = ProcessExePath.Substring(0, ix + 4);
|
||||
var contentsDir = Path.Combine(appPath, "Contents");
|
||||
var macosDir = Path.Combine(contentsDir, "MacOS");
|
||||
var updateExe = Path.Combine(macosDir, "UpdateMac");
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
@@ -27,14 +28,18 @@ namespace Velopack.Locators
|
||||
if (_current != null)
|
||||
return _current;
|
||||
|
||||
var process = Process.GetCurrentProcess();
|
||||
var processExePath = process.MainModule?.FileName ?? throw new InvalidOperationException("Could not determine process path.");
|
||||
var processId = (uint)process.Id;
|
||||
|
||||
if (VelopackRuntimeInfo.IsWindows)
|
||||
return _current = new WindowsVelopackLocator(log);
|
||||
return _current = new WindowsVelopackLocator(processExePath, processId, log);
|
||||
|
||||
if (VelopackRuntimeInfo.IsOSX)
|
||||
return _current = new OsxVelopackLocator(log);
|
||||
return _current = new OsxVelopackLocator(processExePath, processId, log);
|
||||
|
||||
if (VelopackRuntimeInfo.IsLinux)
|
||||
return _current = new LinuxVelopackLocator(log);
|
||||
return _current = new LinuxVelopackLocator(processExePath, processId, log);
|
||||
|
||||
throw new PlatformNotSupportedException($"OS platform '{VelopackRuntimeInfo.SystemOs.GetOsLongName()}' is not supported.");
|
||||
}
|
||||
@@ -62,12 +67,18 @@ namespace Velopack.Locators
|
||||
|
||||
/// <inheritdoc/>
|
||||
public virtual bool IsPortable => false;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public uint ProcessId { get; protected set; }
|
||||
|
||||
/// <inheritdoc/>
|
||||
public string ProcessExePath { get; protected set; }
|
||||
|
||||
/// <inheritdoc/>
|
||||
public virtual string? ThisExeRelativePath {
|
||||
get {
|
||||
if (AppContentDir == null) return null;
|
||||
var path = VelopackRuntimeInfo.EntryExePath;
|
||||
var path = ProcessExePath;
|
||||
if (path.StartsWith(AppContentDir, StringComparison.OrdinalIgnoreCase)) {
|
||||
return path.Substring(AppContentDir.Length + 1);
|
||||
} else {
|
||||
|
||||
@@ -33,37 +33,32 @@ namespace Velopack.Locators
|
||||
public override string? PackagesDir => CreateSubDirIfDoesNotExist(RootAppDir, "packages");
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool IsPortable =>
|
||||
RootAppDir != null ? File.Exists(Path.Combine(RootAppDir, ".portable")) : false;
|
||||
public override bool IsPortable => RootAppDir != null && File.Exists(Path.Combine(RootAppDir, ".portable"));
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string? Channel { get; }
|
||||
|
||||
/// <inheritdoc cref="WindowsVelopackLocator" />
|
||||
public WindowsVelopackLocator(ILogger logger) : this(VelopackRuntimeInfo.EntryExePath, logger)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Internal use only. Auto detect app details from the specified EXE path.
|
||||
/// </summary>
|
||||
internal WindowsVelopackLocator(string ourExePath, ILogger logger)
|
||||
public WindowsVelopackLocator(string currentProcessPath, uint currentProcessId, ILogger logger)
|
||||
: base(logger)
|
||||
{
|
||||
if (!VelopackRuntimeInfo.IsWindows)
|
||||
throw new NotSupportedException("Cannot instantiate WindowsLocator on a non-Windows system.");
|
||||
|
||||
ProcessId = currentProcessId;
|
||||
ProcessExePath = currentProcessPath;
|
||||
|
||||
// We try various approaches here. Firstly, if Update.exe is in the parent directory,
|
||||
// we use that. If it's not present, we search for a parent "current" or "app-{ver}" directory,
|
||||
// which could designate that this executable is running in a nested sub-directory.
|
||||
// There is some legacy code here, because it's possible that we're running in an "app-{ver}"
|
||||
// directory which is NOT containing a sq.version, in which case we need to infer a lot of info.
|
||||
|
||||
ourExePath = Path.GetFullPath(ourExePath);
|
||||
string myDirPath = Path.GetDirectoryName(ourExePath)!;
|
||||
ProcessExePath = Path.GetFullPath(ProcessExePath);
|
||||
string myDirPath = Path.GetDirectoryName(ProcessExePath)!;
|
||||
var myDirName = Path.GetFileName(myDirPath);
|
||||
var possibleUpdateExe = Path.GetFullPath(Path.Combine(myDirPath, "..", "Update.exe"));
|
||||
var ixCurrent = ourExePath.LastIndexOf("/current/", StringComparison.InvariantCultureIgnoreCase);
|
||||
var ixCurrent = ProcessExePath.LastIndexOf("/current/", StringComparison.InvariantCultureIgnoreCase);
|
||||
|
||||
Log.Info($"Initializing {nameof(WindowsVelopackLocator)}");
|
||||
|
||||
@@ -91,7 +86,7 @@ namespace Velopack.Locators
|
||||
}
|
||||
} else if (ixCurrent > 0) {
|
||||
// this is an attempt to handle the case where we are running in a nested current directory.
|
||||
var rootDir = ourExePath.Substring(0, ixCurrent);
|
||||
var rootDir = ProcessExePath.Substring(0, ixCurrent);
|
||||
var currentDir = Path.Combine(rootDir, "current");
|
||||
var manifestFile = Path.Combine(currentDir, CoreUtil.SpecVersionFileName);
|
||||
possibleUpdateExe = Path.GetFullPath(Path.Combine(rootDir, "Update.exe"));
|
||||
|
||||
@@ -45,13 +45,13 @@ namespace Velopack
|
||||
/// <param name="restartArgs">The arguments to pass to the application when it is restarted.</param>
|
||||
public void WaitExitThenApplyUpdates(VelopackAsset? toApply, bool silent = false, bool restart = true, string[]? restartArgs = null)
|
||||
{
|
||||
UpdateExe.Apply(Locator, toApply, silent, VelopackRuntimeInfo.ProcessId, restart, restartArgs, Log);
|
||||
UpdateExe.Apply(Locator, toApply, silent, Locator.ProcessId, restart, restartArgs, Log);
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="WaitExitThenApplyUpdates"/>
|
||||
public async Task WaitExitThenApplyUpdatesAsync(VelopackAsset? toApply, bool silent = false, bool restart = true, string[]? restartArgs = null)
|
||||
{
|
||||
await UpdateExe.ApplyAsync(Locator, toApply, silent, VelopackRuntimeInfo.ProcessId, restart, restartArgs, Log).ConfigureAwait(false);
|
||||
await UpdateExe.ApplyAsync(Locator, toApply, silent, Locator.ProcessId, restart, restartArgs, Log).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Security.Cryptography;
|
||||
@@ -77,9 +78,16 @@ namespace Velopack.Util
|
||||
logger ??= NullLogger.Instance;
|
||||
logger.Debug($"Starting to delete: {path}");
|
||||
|
||||
string? currentExePath = null;
|
||||
try {
|
||||
currentExePath = Process.GetCurrentProcess().MainModule?.FileName;
|
||||
} catch {
|
||||
// ... ignore
|
||||
}
|
||||
|
||||
try {
|
||||
if (File.Exists(path)) {
|
||||
DeleteFsiVeryHard(new FileInfo(path), logger);
|
||||
DeleteFsiVeryHard(new FileInfo(path), currentExePath, logger);
|
||||
} else if (Directory.Exists(path)) {
|
||||
if (renameFirst) {
|
||||
// if there are locked files in a directory, we will not attempt to delte it
|
||||
@@ -88,7 +96,7 @@ namespace Velopack.Util
|
||||
path = oldPath;
|
||||
}
|
||||
|
||||
DeleteFsiTree(new DirectoryInfo(path), logger);
|
||||
DeleteFsiTree(new DirectoryInfo(path), currentExePath, logger);
|
||||
} else {
|
||||
if (throwOnFailure)
|
||||
logger?.Warn($"Cannot delete '{path}' if it does not exist.");
|
||||
@@ -103,11 +111,11 @@ namespace Velopack.Util
|
||||
}
|
||||
}
|
||||
|
||||
private static void DeleteFsiTree(FileSystemInfo fileSystemInfo, ILogger logger)
|
||||
private static void DeleteFsiTree(FileSystemInfo fileSystemInfo, string? currentExePath, ILogger logger)
|
||||
{
|
||||
// if junction / symlink, don't iterate, just delete it.
|
||||
if (fileSystemInfo.Attributes.HasFlag(FileAttributes.ReparsePoint)) {
|
||||
DeleteFsiVeryHard(fileSystemInfo, logger);
|
||||
DeleteFsiVeryHard(fileSystemInfo, currentExePath, logger);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -115,7 +123,7 @@ namespace Velopack.Util
|
||||
try {
|
||||
if (fileSystemInfo is DirectoryInfo directoryInfo) {
|
||||
foreach (FileSystemInfo childInfo in directoryInfo.GetFileSystemInfos()) {
|
||||
DeleteFsiTree(childInfo, logger);
|
||||
DeleteFsiTree(childInfo, currentExePath, logger);
|
||||
}
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
@@ -124,14 +132,15 @@ namespace Velopack.Util
|
||||
|
||||
// finally, delete myself, we should try this even if deleting children failed
|
||||
// because Directory.Delete can also be recursive
|
||||
DeleteFsiVeryHard(fileSystemInfo, logger);
|
||||
DeleteFsiVeryHard(fileSystemInfo, currentExePath, logger);
|
||||
}
|
||||
|
||||
private static void DeleteFsiVeryHard(FileSystemInfo fileSystemInfo, ILogger logger)
|
||||
private static void DeleteFsiVeryHard(FileSystemInfo fileSystemInfo, string? currentExePath, ILogger logger)
|
||||
{
|
||||
// don't try to delete the running process
|
||||
if (PathUtil.FullPathEquals(fileSystemInfo.FullName, VelopackRuntimeInfo.EntryExePath))
|
||||
if (currentExePath != null && PathUtil.FullPathEquals(fileSystemInfo.FullName, currentExePath)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// try to remove "ReadOnly" attributes
|
||||
try { fileSystemInfo.Attributes = FileAttributes.Normal; } catch { }
|
||||
|
||||
@@ -224,7 +224,7 @@ namespace Velopack
|
||||
log.Info($"Launching app is out-dated. Current: {myVersion}, Newest Local Available: {latestLocal.Version}");
|
||||
if (!restarted && _autoApply) {
|
||||
log.Info("Auto apply is true, so restarting to apply update...");
|
||||
UpdateExe.Apply(locator, latestLocal, false, VelopackRuntimeInfo.ProcessId, true, args, log);
|
||||
UpdateExe.Apply(locator, latestLocal, false, locator.ProcessId, true, args, log);
|
||||
Exit(0);
|
||||
} else {
|
||||
log.Info("Pre-condition failed, we will not restart to apply updates. (restarted: " + restarted + ", autoApply: " + _autoApply + ")");
|
||||
|
||||
@@ -34,7 +34,9 @@ namespace System.Runtime.Versioning
|
||||
|
||||
namespace System.Runtime.CompilerServices
|
||||
{
|
||||
internal static class IsExternalInit { }
|
||||
internal static class IsExternalInit
|
||||
{
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -88,12 +90,6 @@ namespace Velopack
|
||||
/// <summary> The current compiled Velopack ProductVersion. </summary>
|
||||
public static NuGetVersion VelopackProductVersion { get; }
|
||||
|
||||
/// <summary> The path on disk of the entry assembly. </summary>
|
||||
public static string EntryExePath { get; }
|
||||
|
||||
/// <summary> The current executing process ID. </summary>
|
||||
public static uint ProcessId { get; }
|
||||
|
||||
/// <summary> The current machine architecture, ignoring the current process / pe architecture. </summary>
|
||||
public static RuntimeCpu SystemArch { get; private set; }
|
||||
|
||||
@@ -125,10 +121,6 @@ namespace Velopack
|
||||
|
||||
static VelopackRuntimeInfo()
|
||||
{
|
||||
var currentProcess = System.Diagnostics.Process.GetCurrentProcess();
|
||||
EntryExePath = currentProcess.MainModule.FileName;
|
||||
ProcessId = (uint)currentProcess.Id;
|
||||
|
||||
#if DEBUG
|
||||
InUnitTestRunner = CheckForUnitTestRunner();
|
||||
#endif
|
||||
@@ -145,6 +137,7 @@ namespace Velopack
|
||||
VelopackNugetVersion = NuGetVersion.Parse(VelopackNugetVersion.ToNormalizedString() + "-g" + VelopackNugetVersion.Metadata);
|
||||
}
|
||||
}
|
||||
|
||||
VelopackDisplayVersion = VelopackNugetVersion.ToNormalizedString() + (VelopackNugetVersion.IsPrerelease ? " (prerelease)" : "");
|
||||
#pragma warning restore CS0612
|
||||
|
||||
|
||||
Reference in New Issue
Block a user