From 8347f293098fdfe8ee28926c7dfcdf813adeb5ec Mon Sep 17 00:00:00 2001 From: Caelan Date: Sat, 21 Sep 2024 12:48:05 -0600 Subject: [PATCH] Refactor util into separate classes --- src/lib-csharp/Compression/DeltaPackage.cs | 7 +- src/lib-csharp/Compression/DeltaUpdateExe.cs | 1 + src/lib-csharp/Compression/EasyZip.cs | 9 +- src/lib-csharp/Internal/Utility.cs | 711 ------------------ .../Locators/LinuxVelopackLocator.cs | 5 +- src/lib-csharp/Locators/OsxVelopackLocator.cs | 5 +- src/lib-csharp/Locators/VelopackLocator.cs | 3 +- .../Locators/VelopackLocatorExtensions.cs | 3 +- .../Locators/WindowsVelopackLocator.cs | 7 +- src/lib-csharp/ReleaseEntry.cs | 11 +- src/lib-csharp/Sources/GitBase.cs | 7 +- src/lib-csharp/Sources/GiteaSource.cs | 2 +- src/lib-csharp/Sources/GithubSource.cs | 2 +- src/lib-csharp/Sources/GitlabSource.cs | 2 +- src/lib-csharp/Sources/SimpleFileSource.cs | 3 +- src/lib-csharp/Sources/SimpleWebSource.cs | 15 +- .../Sources/VelopackFlowUpdateSource.cs | 10 +- src/lib-csharp/UpdateExe.cs | 1 + src/lib-csharp/UpdateManager.cs | 31 +- .../{Internal => Util}/CompiledJson.cs | 2 +- src/lib-csharp/Util/CoreUtil.cs | 142 ++++ .../{Internal => Util}/Disposable.cs | 2 +- .../EnumerableExtensions.cs | 35 +- src/lib-csharp/Util/GuidUtil.cs | 88 +++ src/lib-csharp/Util/HttpUtil.cs | 52 ++ src/lib-csharp/Util/IoUtil.cs | 218 ++++++ .../{Internal => Util}/LoggerExtensions.cs | 2 +- src/lib-csharp/Util/PathUtil.cs | 154 ++++ .../{Internal => Util}/ProcessExtensions.cs | 28 +- .../{Internal => Util}/SymbolicLink.cs | 80 +- src/lib-csharp/Util/TempUtil.cs | 83 ++ src/lib-csharp/VelopackApp.cs | 3 +- src/lib-csharp/VelopackAsset.cs | 6 +- src/lib-csharp/VelopackRuntimeInfo.cs | 3 +- src/lib-csharp/Windows/RuntimeInfo.cs | 7 +- src/lib-csharp/Windows/Shortcuts.cs | 5 +- .../Velopack.Deployment/AzureRepository.cs | 1 + .../Velopack.Deployment/GitHubRepository.cs | 3 +- .../Velopack.Deployment/GiteaRepository.cs | 3 +- .../Velopack.Deployment/LocalRepository.cs | 3 +- src/vpk/Velopack.Deployment/S3Repository.cs | 1 + .../Velopack.Deployment/_ObjectRepository.cs | 11 +- src/vpk/Velopack.Deployment/_Repository.cs | 9 +- .../Velopack.Packaging.Unix/AppImageTool.cs | 7 +- .../Commands/LinuxPackCommandRunner.cs | 1 + .../Commands/OsxBundleCommandRunner.cs | 3 +- .../Commands/OsxPackCommandRunner.cs | 5 +- .../Velopack.Packaging.Unix/OsxBuildTools.cs | 12 +- .../Velopack.Packaging.Unix/PlistWriter.cs | 1 + .../Velopack.Packaging.Windows/CodeSign.cs | 5 +- .../Commands/WindowsPackCommandRunner.cs | 15 +- .../Velopack.Packaging.Windows/CompatUtil.cs | 1 + .../ResourceEdit.cs | 3 +- .../Velopack.Packaging.Windows/SetupBundle.cs | 7 +- src/vpk/Velopack.Packaging/BuildAssets.cs | 3 +- .../Commands/DeltaPatchCommandRunner.cs | 5 +- .../Velopack.Packaging/DeltaPackageBuilder.cs | 15 +- src/vpk/Velopack.Packaging/Exe.cs | 7 +- src/vpk/Velopack.Packaging/PackageBuilder.cs | 9 +- .../Velopack.Packaging/ReleaseEntryHelper.cs | 12 +- src/vpk/Velopack.Vpk/Logging/BasicConsole.cs | 1 + .../Velopack.Vpk/Logging/SpectreConsole.cs | 1 + src/vpk/Velopack.Vpk/Program.cs | 3 +- src/vpk/Velopack.Vpk/Updates/UpdateChecker.cs | 1 + .../Commands/LocalDownloadCommandTests.cs | 5 +- .../AzureDeploymentTests.cs | 3 +- .../CompatUtilTests.cs | 11 +- test/Velopack.Packaging.Tests/CrossCompile.cs | 7 +- .../GithubDeploymentTests.cs | 21 +- test/Velopack.Packaging.Tests/OsxPackTests.cs | 7 +- .../ResourceEditTests.cs | 15 +- .../S3DeploymentTests.cs | 3 +- .../SimpleJsonTests.cs | 1 - test/Velopack.Packaging.Tests/TestApp.cs | 1 + .../WindowsPackTests.cs | 43 +- test/Velopack.Tests/RuntimeInfoTests.cs | 1 + test/Velopack.Tests/ShortcutTests.cs | 3 +- test/Velopack.Tests/SymbolicLinkTests.cs | 35 +- .../TestHelpers/FakeFixtureRepository.cs | 5 +- .../TestHelpers/StaticHttpServer.cs | 1 + test/Velopack.Tests/UpdateManagerTests.cs | 39 +- test/Velopack.Tests/UtilityTests.cs | 49 +- test/Velopack.Tests/ZipPackageTests.cs | 9 +- 83 files changed, 1104 insertions(+), 1048 deletions(-) delete mode 100644 src/lib-csharp/Internal/Utility.cs rename src/lib-csharp/{Internal => Util}/CompiledJson.cs (99%) create mode 100644 src/lib-csharp/Util/CoreUtil.cs rename src/lib-csharp/{Internal => Util}/Disposable.cs (96%) rename src/lib-csharp/{Internal => Util}/EnumerableExtensions.cs (81%) create mode 100644 src/lib-csharp/Util/GuidUtil.cs create mode 100644 src/lib-csharp/Util/HttpUtil.cs create mode 100644 src/lib-csharp/Util/IoUtil.cs rename src/lib-csharp/{Internal => Util}/LoggerExtensions.cs (99%) create mode 100644 src/lib-csharp/Util/PathUtil.cs rename src/lib-csharp/{Internal => Util}/ProcessExtensions.cs (90%) rename src/lib-csharp/{Internal => Util}/SymbolicLink.cs (78%) create mode 100644 src/lib-csharp/Util/TempUtil.cs diff --git a/src/lib-csharp/Compression/DeltaPackage.cs b/src/lib-csharp/Compression/DeltaPackage.cs index 94fa382e..a730d90f 100644 --- a/src/lib-csharp/Compression/DeltaPackage.cs +++ b/src/lib-csharp/Compression/DeltaPackage.cs @@ -5,6 +5,7 @@ using System.Linq; using System.Text; using System.Text.RegularExpressions; using Microsoft.Extensions.Logging; +using Velopack.Util; namespace Velopack.Compression { @@ -29,7 +30,7 @@ namespace Velopack.Compression Log.Info($"Applying delta package from {deltaPackageZip} to delta staging directory."); - using var _1 = Utility.GetTempDirectory(out var deltaPath, BaseTempDir); + using var _1 = TempUtil.GetTempDirectory(out var deltaPath, BaseTempDir); EasyZip.ExtractZipToDirectory(Log, deltaPackageZip, deltaPath); progress(10); @@ -51,7 +52,7 @@ namespace Velopack.Compression pathsVisited.Add(DIFF_SUFFIX.Replace(file, "").ToLowerInvariant()); applyDiffToFile(deltaPath, file, workingPath); var perc = (index + 1) / (double) files.Length * 100; - progress(Utility.CalculateProgress((int) perc, 10, 90)); + progress(CoreUtil.CalculateProgress((int) perc, 10, 90)); } progress(80); @@ -118,7 +119,7 @@ namespace Velopack.Compression var inputFile = Path.Combine(deltaPath, relativeFilePath); var finalTarget = Path.Combine(workingDirectory, DIFF_SUFFIX.Replace(relativeFilePath, "")); - using var _d = Utility.GetTempFileName(out var tempTargetFile, BaseTempDir); + using var _d = TempUtil.GetTempFileName(out var tempTargetFile, BaseTempDir); // NB: Zero-length diffs indicate the file hasn't actually changed if (new FileInfo(inputFile).Length == 0) { diff --git a/src/lib-csharp/Compression/DeltaUpdateExe.cs b/src/lib-csharp/Compression/DeltaUpdateExe.cs index f9a8e08e..224bfbc4 100644 --- a/src/lib-csharp/Compression/DeltaUpdateExe.cs +++ b/src/lib-csharp/Compression/DeltaUpdateExe.cs @@ -1,6 +1,7 @@ using System; using System.Diagnostics; using Microsoft.Extensions.Logging; +using Velopack.Util; namespace Velopack.Compression { diff --git a/src/lib-csharp/Compression/EasyZip.cs b/src/lib-csharp/Compression/EasyZip.cs index 1da14aca..8dc7a217 100644 --- a/src/lib-csharp/Compression/EasyZip.cs +++ b/src/lib-csharp/Compression/EasyZip.cs @@ -7,6 +7,7 @@ using System.Text; using System.Threading; using System.Threading.Tasks; using Microsoft.Extensions.Logging; +using Velopack.Util; namespace Velopack.Compression { @@ -17,7 +18,7 @@ namespace Velopack.Compression public static void ExtractZipToDirectory(ILogger logger, string inputFile, string outputDirectory, bool expandSymlinks = false) { logger.Debug($"Extracting '{inputFile}' to '{outputDirectory}' using System.IO.Compression..."); - Utility.DeleteFileOrDirectoryHard(outputDirectory); + IoUtil.DeleteFileOrDirectoryHard(outputDirectory); List symlinks = new(); using (ZipArchive archive = ZipFile.Open(inputFile, ZipArchiveMode.Read)) { @@ -58,7 +59,7 @@ namespace Velopack.Compression using (var reader = new StreamReader(source.Open())) { var targetPath = reader.ReadToEnd(); var absolute = Path.GetFullPath(Path.Combine(Path.GetDirectoryName(fileDestinationPath)!, targetPath)); - if (!Utility.IsFileInDirectory(absolute, destinationDirectoryName)) { + if (!PathUtil.IsFileInDirectory(absolute, destinationDirectoryName)) { throw new IOException("IO_SymlinkTargetNotInDirectory"); } @@ -127,7 +128,7 @@ namespace Velopack.Compression // if dir is a symlink, write it as a file containing path to target if (SymbolicLink.Exists(dir.FullName)) { - if (!Utility.IsFileInDirectory(SymbolicLink.GetTarget(dir.FullName, relative: false), sourceDirectoryName)) { + if (!PathUtil.IsFileInDirectory(SymbolicLink.GetTarget(dir.FullName, relative: false), sourceDirectoryName)) { throw new IOException("IO_SymlinkTargetNotInDirectory"); } @@ -169,7 +170,7 @@ namespace Velopack.Compression if (SymbolicLink.Exists(fileInfo.FullName)) { // Handle symlink: Store the symlink target instead of its content - if (!Utility.IsFileInDirectory(SymbolicLink.GetTarget(fileInfo.FullName, relative: false), sourceDirectoryName)) { + if (!PathUtil.IsFileInDirectory(SymbolicLink.GetTarget(fileInfo.FullName, relative: false), sourceDirectoryName)) { throw new IOException("IO_SymlinkTargetNotInDirectory"); } diff --git a/src/lib-csharp/Internal/Utility.cs b/src/lib-csharp/Internal/Utility.cs deleted file mode 100644 index 1429cca8..00000000 --- a/src/lib-csharp/Internal/Utility.cs +++ /dev/null @@ -1,711 +0,0 @@ -using System; -using System.Collections.Concurrent; -using System.Collections.Generic; -using System.Diagnostics; -using System.Diagnostics.Contracts; -using System.IO; -using System.Linq; -using System.Security.Cryptography; -using System.Text; -using System.Threading; -using System.Threading.Tasks; -using Microsoft.Extensions.Logging; -using Microsoft.Extensions.Logging.Abstractions; - -namespace Velopack -{ - internal static class Utility - { - public const string SpecVersionFileName = "sq.version"; - - /// - /// Calculates the total percentage of a specific step that should report within a specific range. - /// - /// If a step needs to report between 50 -> 75 %, this method should be used as CalculateProgress(percentage, 50, 75). - /// - /// The percentage of the current step, a value between 0 and 100. - /// The start percentage of the range the current step represents. - /// The end percentage of the range the current step represents. - /// The calculated percentage that can be reported about the total progress. - public static int CalculateProgress(int percentageOfCurrentStep, int stepStartPercentage, int stepEndPercentage) - { - // Ensure we are between 0 and 100 - percentageOfCurrentStep = Math.Max(Math.Min(percentageOfCurrentStep, 100), 0); - - var range = stepEndPercentage - stepStartPercentage; - var singleValue = range / 100d; - var totalPercentage = (singleValue * percentageOfCurrentStep) + stepStartPercentage; - - return (int) totalPercentage; - } - - public static Action CreateProgressDelegate(Action rootProgress, int stepStartPercentage, int stepEndPercentage) - { - return percentage => { - rootProgress(CalculateProgress(percentage, stepStartPercentage, stepEndPercentage)); - }; - } - - public static string RemoveByteOrderMarkerIfPresent(string content) - { - return string.IsNullOrEmpty(content) - ? string.Empty - : RemoveByteOrderMarkerIfPresent(Encoding.UTF8.GetBytes(content)); - } - - public static string RemoveByteOrderMarkerIfPresent(byte[] content) - { - byte[] output = { }; - - Func matches = (bom, src) => { - if (src.Length < bom.Length) return false; - - return !bom.Where((chr, index) => src[index] != chr).Any(); - }; - - var utf32Be = new byte[] { 0x00, 0x00, 0xFE, 0xFF }; - var utf32Le = new byte[] { 0xFF, 0xFE, 0x00, 0x00 }; - var utf16Be = new byte[] { 0xFE, 0xFF }; - var utf16Le = new byte[] { 0xFF, 0xFE }; - var utf8 = new byte[] { 0xEF, 0xBB, 0xBF }; - - if (matches(utf32Be, content)) { - output = new byte[content.Length - utf32Be.Length]; - } else if (matches(utf32Le, content)) { - output = new byte[content.Length - utf32Le.Length]; - } else if (matches(utf16Be, content)) { - output = new byte[content.Length - utf16Be.Length]; - } else if (matches(utf16Le, content)) { - output = new byte[content.Length - utf16Le.Length]; - } else if (matches(utf8, content)) { - output = new byte[content.Length - utf8.Length]; - } else { - output = content; - } - - if (output.Length > 0) { - Buffer.BlockCopy(content, content.Length - output.Length, output, 0, output.Length); - } - - return Encoding.UTF8.GetString(output); - } - - public static bool TryParseEnumU16(ushort enumValue, out TEnum? retVal) - { - retVal = default; - bool success = Enum.IsDefined(typeof(TEnum), enumValue); - if (success) { - retVal = (TEnum) Enum.ToObject(typeof(TEnum), enumValue); - } - - return success; - } - - public static bool FullPathEquals(string path1, string path2) - { - return NormalizePath(path1).Equals(NormalizePath(path2), VelopackRuntimeInfo.PathStringComparison); - } - - public static bool PathPartEquals(string part1, string part2) - { - return part1.Equals(part2, VelopackRuntimeInfo.PathStringComparison); - } - - public static bool PathPartStartsWith(string part1, string startsWith) - { - return part1.StartsWith(startsWith, VelopackRuntimeInfo.PathStringComparison); - } - - public static bool PathPartEndsWith(string part1, string endsWith) - { - return part1.EndsWith(endsWith, VelopackRuntimeInfo.PathStringComparison); - } - - public static bool FileHasExtension(string filePath, string extension) - { - var ext = Path.GetExtension(filePath); - if (!extension.StartsWith(".")) extension = "." + extension; - return PathPartEquals(ext, extension); - } - - public static string NormalizePath(string path) - { - var fullPath = Path.GetFullPath(path); - var normalized = new Uri(fullPath, UriKind.Absolute).LocalPath; - return normalized.TrimEnd(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar); - } - - public static bool IsFileInDirectory(string file, string directory) - { - var normalizedDir = NormalizePath(directory) + Path.DirectorySeparatorChar; - var normalizedFile = NormalizePath(file); - return normalizedFile.StartsWith(normalizedDir, VelopackRuntimeInfo.PathStringComparison); - } - - public static IEnumerable GetAllFilesRecursively(this DirectoryInfo rootPath) - { - if (rootPath == null) return Enumerable.Empty(); - return rootPath.EnumerateFiles("*", SearchOption.AllDirectories); - } - - public static string CalculateFileSHA1(string filePath) - { - var bufferSize = 1000000; // 1mb - using (var stream = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.Read, bufferSize)) { - return CalculateStreamSHA1(stream); - } - } - - public static string CalculateStreamSHA1(Stream file) - { - using (var sha1 = SHA1.Create()) { - return BitConverter.ToString(sha1.ComputeHash(file)).Replace("-", String.Empty); - } - } - - /// - public static string CalculateFileSHA256(string filePath) - { - var bufferSize = 1000000; // 1mb - using (var stream = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.Read, bufferSize)) { - return CalculateStreamSHA256(stream); - } - } - - /// - /// Get SHA256 hash of the specified file and returns the result as a base64 encoded string (with length 44) - /// - public static string CalculateStreamSHA256(Stream file) - { - using (var sha256 = SHA256.Create()) { - return BitConverter.ToString(sha256.ComputeHash(file)).Replace("-", String.Empty); - } - } - - public static Sources.IFileDownloader CreateDefaultDownloader() - { - return new Sources.HttpClientFileDownloader(); - } - - public static string GetVeloReleaseIndexName(string channel) - { - return $"releases.{channel ?? VelopackRuntimeInfo.SystemOs.GetOsShortName()}.json"; - } - - [Obsolete] - public static string GetReleasesFileName(string channel) - { - if (channel == null) { - // default RELEASES file name for each platform. - if (VelopackRuntimeInfo.IsOSX) return "RELEASES-osx"; - if (VelopackRuntimeInfo.IsLinux) return "RELEASES-linux"; - return "RELEASES"; - } else { - // if the channel is an empty string or "win", we use the default RELEASES file name. - if (String.IsNullOrWhiteSpace(channel) || channel.ToLower() == "win") { - return "RELEASES"; - } - - // all other cases the RELEASES file includes the channel name. - return $"RELEASES-{channel.ToLower()}"; - } - } - - public static void Retry(this Action block, int retries = 4, int retryDelay = 250, ILogger? logger = null) - { - Retry( - () => { - block(); - return true; - }, - retries, - retryDelay, - logger); - } - - public static T Retry(this Func block, int retries = 4, int retryDelay = 250, ILogger? logger = null) - { - Contract.Requires(retries > 0); - - while (true) { - try { - T ret = block(); - return ret; - } catch (Exception ex) { - if (retries == 0) throw; - logger?.Warn($"Operation failed ({ex.Message}). Retrying {retries} more times..."); - retries--; - Thread.Sleep(retryDelay); - } - } - } - - public static Task RetryAsync(this Func block, int retries = 4, int retryDelay = 250, ILogger? logger = null) - { - return RetryAsync( - async () => { - await block().ConfigureAwait(false); - return true; - }, - retries, - retryDelay, - logger); - } - - public static async Task RetryAsync(this Func> block, int retries = 4, int retryDelay = 250, ILogger? logger = null) - { - while (true) { - try { - return await block().ConfigureAwait(false); - } catch (Exception ex) { - if (retries == 0) throw; - logger?.Warn($"Operation failed ({ex.Message}). Retrying {retries} more times..."); - retries--; - await Task.Delay(retryDelay).ConfigureAwait(false); - } - } - } - - public static T GetAwaiterResult(this Task task) - { - return task.ConfigureAwait(false).GetAwaiter().GetResult(); - } - - public static void GetAwaiterResult(this Task task) - { - task.ConfigureAwait(false).GetAwaiter().GetResult(); - } - - public static Task ForEachAsync(this IEnumerable source, Action body, int degreeOfParallelism = 4) - { - return ForEachAsync(source, x => Task.Run(() => body(x)), degreeOfParallelism); - } - - public static Task ForEachAsync(this IEnumerable source, Func body, int degreeOfParallelism = 4) - { - return Task.WhenAll( - from partition in Partitioner.Create(source).GetPartitions(degreeOfParallelism) - select Task.Run( - async () => { - using (partition) - while (partition.MoveNext()) - await body(partition.Current).ConfigureAwait(false); - })); - } - - /// - /// Escapes file name such that the file name is safe for writing to disk in the packages folder - /// - public static string GetSafeFilename(string fileName) - { - string safeFileName = Path.GetFileName(fileName); - char[] invalidFileNameChars = Path.GetInvalidFileNameChars(); - - if (safeFileName.IndexOfAny(invalidFileNameChars) != -1) { - StringBuilder safeName = new(); - foreach (char ch in safeFileName) { - if (Array.IndexOf(invalidFileNameChars, ch) == -1) - safeName.Append(ch); - else - safeName.Append('_'); - } - - safeFileName = safeName.ToString(); - } - - return safeFileName; - } - - public static string GetDefaultTempBaseDirectory() - { - string tempDir; - - if (VelopackRuntimeInfo.IsOSX || VelopackRuntimeInfo.IsLinux) { - tempDir = "/tmp/velopack"; - } else if (VelopackRuntimeInfo.IsWindows) { - tempDir = Path.Combine(Path.GetTempPath(), "Velopack"); - } else { - throw new PlatformNotSupportedException(); - } - - if (Environment.GetEnvironmentVariable("VELOPACK_TEMP") is var squirrlTmp - && !string.IsNullOrWhiteSpace(squirrlTmp)) - tempDir = squirrlTmp; - - var di = new DirectoryInfo(tempDir); - if (!di.Exists) di.Create(); - - return di.FullName; - } - - private static string GetNextTempName(string tempDir) - { - for (int i = 1; i < 1000; i++) { - string name = "temp." + i; - var target = Path.Combine(tempDir, name); - - FileSystemInfo? info = null; - if (Directory.Exists(target)) info = new DirectoryInfo(target); - else if (File.Exists(target)) info = new FileInfo(target); - - // this dir/file does not exist, lets use it. - if (info == null) { - return target; - } - - // this dir/file exists, but it is old, let's re-use it. - // this shouldn't generally happen, but crashes do exist. - if (DateTime.UtcNow - info.LastWriteTimeUtc > TimeSpan.FromDays(1)) { - if (DeleteFileOrDirectoryHard(target, false, true)) { - // the dir/file was deleted successfully. - return target; - } - } - } - - throw new Exception( - "Unable to find free temp path. Has the temp directory exceeded it's maximum number of items? (1000)"); - } - - public static IDisposable GetTempDirectory(out string newTempDirectory) - { - return GetTempDirectory(out newTempDirectory, GetDefaultTempBaseDirectory()); - } - - public static IDisposable GetTempDirectory(out string newTempDirectory, string rootTempDir) - { - var disp = GetTempFileName(out newTempDirectory, rootTempDir); - Directory.CreateDirectory(newTempDirectory); - return disp; - } - - public static IDisposable GetTempFileName(out string newTempFile) - { - return GetTempFileName(out newTempFile, GetDefaultTempBaseDirectory()); - } - - public static IDisposable GetTempFileName(out string newTempFile, string rootTempDir) - { - var path = GetNextTempName(rootTempDir); - newTempFile = path; - return Disposable.Create(() => DeleteFileOrDirectoryHard(path, throwOnFailure: false)); - } - - /// - /// Repeatedly tries various methods to delete a file system object. Optionally renames the directory first. - /// Optionally ignores errors. - /// - /// The path of the file system entity to delete. - /// Whether this function should throw if the delete fails. - /// Try to rename this object first before deleting. Can help prevent partial delete of folders. - /// Logger for diagnostic messages. - /// True if the file system object was deleted, false otherwise. - public static bool DeleteFileOrDirectoryHard(string path, bool throwOnFailure = true, bool renameFirst = false, ILogger? logger = null) - { - logger ??= NullLogger.Instance; - Contract.Requires(!String.IsNullOrEmpty(path)); - logger.Debug($"Starting to delete: {path}"); - - try { - if (File.Exists(path)) { - DeleteFsiVeryHard(new FileInfo(path), logger); - } else if (Directory.Exists(path)) { - if (renameFirst) { - // if there are locked files in a directory, we will not attempt to delte it - var oldPath = path + ".old"; - Directory.Move(path, oldPath); - path = oldPath; - } - - DeleteFsiTree(new DirectoryInfo(path), logger); - } else { - if (throwOnFailure) - logger?.Warn($"Cannot delete '{path}' if it does not exist."); - } - - return true; - } catch (Exception ex) { - logger.Error(ex, $"Unable to delete '{path}'"); - if (throwOnFailure) - throw; - return false; - } - } - - private static void DeleteFsiTree(FileSystemInfo fileSystemInfo, ILogger logger) - { - // if junction / symlink, don't iterate, just delete it. - if (fileSystemInfo.Attributes.HasFlag(FileAttributes.ReparsePoint)) { - DeleteFsiVeryHard(fileSystemInfo, logger); - return; - } - - // recursively delete children - try { - var directoryInfo = fileSystemInfo as DirectoryInfo; - if (directoryInfo != null) { - foreach (FileSystemInfo childInfo in directoryInfo.GetFileSystemInfos()) { - DeleteFsiTree(childInfo, logger); - } - } - } catch (Exception ex) { - logger.Warn(ex, $"Unable to traverse children of '{fileSystemInfo.FullName}'"); - } - - // finally, delete myself, we should try this even if deleting children failed - // because Directory.Delete can also be recursive - DeleteFsiVeryHard(fileSystemInfo, logger); - } - - private static void DeleteFsiVeryHard(FileSystemInfo fileSystemInfo, ILogger logger) - { - // don't try to delete the running process - if (FullPathEquals(fileSystemInfo.FullName, VelopackRuntimeInfo.EntryExePath)) - return; - - // try to remove "ReadOnly" attributes - try { fileSystemInfo.Attributes = FileAttributes.Normal; } catch { } - - try { fileSystemInfo.Refresh(); } catch { } - - // use this instead of fsi.Delete() because it is more resilient/aggressive - Action deleteMe = fileSystemInfo is DirectoryInfo - ? () => Directory.Delete(fileSystemInfo.FullName, true) - : () => File.Delete(fileSystemInfo.FullName); - - // retry a few times. if a directory in this tree is open in Windows Explorer, - // it might be locked for a little while WE cleans up handles - try { - Retry( - () => { - try { - deleteMe(); - } catch (DirectoryNotFoundException) { - return; // good! - } - }, - retries: 4, - retryDelay: 50); - } catch (Exception ex) { - logger?.Warn(ex, $"Unable to delete child '{fileSystemInfo.FullName}'"); - throw; - } - } - - //public static string PackageDirectoryForAppDir(string rootAppDirectory) - //{ - // return Path.Combine(rootAppDirectory, "packages"); - //} - - //public static string LocalReleaseFileForAppDir(string rootAppDirectory) - //{ - // return Path.Combine(PackageDirectoryForAppDir(rootAppDirectory), "RELEASES"); - //} - - //public static IEnumerable LoadLocalReleases(string localReleaseFile) - //{ - // var file = File.OpenRead(localReleaseFile); - - // // NB: sr disposes file - // using (var sr = new StreamReader(file, Encoding.UTF8)) { - // return ReleaseEntry.ParseReleaseFile(sr.ReadToEnd()); - // } - //} - - //public static ReleaseEntry FindLatestFullVersion(IEnumerable localReleases, RID compatibleRid) - //{ - // return FindCompatibleVersions(localReleases, compatibleRid).FirstOrDefault(f => !f.IsDelta); - //} - - //public static IEnumerable FindCompatibleVersions(IEnumerable localReleases, RID compatibleRid) - //{ - // if (!localReleases.Any()) { - // return null; - // } - - // if (compatibleRid == null || !compatibleRid.IsValid) { - // return localReleases.OrderByDescending(x => x.Version); - // } - - // return localReleases - // .Where(r => r.Rid.BaseRID == compatibleRid.BaseRID) - // .Where(r => r.Rid.Architecture == compatibleRid.Architecture) - // .OrderByDescending(x => x.Version); - //} - - public static string GetAppUserModelId(string packageId) - { - return $"velopack.{packageId}"; - } - - public static bool IsHttpUrl(string urlOrPath) - { - if (!Uri.TryCreate(urlOrPath, UriKind.Absolute, out Uri? uri)) { - return false; - } - - return uri.Scheme == Uri.UriSchemeHttp || uri.Scheme == Uri.UriSchemeHttps; - } - - public static Uri AppendPathToUri(Uri uri, string path) - { - var builder = new UriBuilder(uri); - if (!builder.Path.EndsWith("/")) { - builder.Path += "/"; - } - - builder.Path += path; - return builder.Uri; - } - - public static Uri EnsureTrailingSlash(Uri uri) - { - return AppendPathToUri(uri, ""); - } - - public static async Task GetExitCodeAsync(this Process p) - { -#if NET5_0_OR_GREATER - await p.WaitForExitAsync().ConfigureAwait(false); - return p.ExitCode; -#else - var tcs = new TaskCompletionSource(); - var thread = new Thread(() => { - try { - p.WaitForExit(); - tcs.SetResult(p.ExitCode); - } catch (Exception ex) { - tcs.SetException(ex); - } - }); - thread.IsBackground = true; - thread.Start(); - await tcs.Task.ConfigureAwait(false); - return p.ExitCode; -#endif - } - - public static Uri AddQueryParamsToUri(Uri uri, IEnumerable> newQuery) - { - var query = System.Web.HttpUtility.ParseQueryString(uri.Query); - - foreach (var entry in newQuery) { - query[entry.Key] = entry.Value; - } - - var builder = new UriBuilder(uri); - builder.Query = query.ToString(); - - return builder.Uri; - } - - readonly static string[] peExtensions = new[] { ".exe", ".dll", ".node" }; - - public static bool FileIsLikelyPEImage(string name) - { - var ext = Path.GetExtension(name); - return peExtensions.Any(x => ext.Equals(x, StringComparison.OrdinalIgnoreCase)); - } - - public static Guid CreateGuidFromHash(string text) - { - return CreateGuidFromHash(text, Utility.IsoOidNamespace); - } - - public static Guid CreateGuidFromHash(byte[] data) - { - return CreateGuidFromHash(data, Utility.IsoOidNamespace); - } - - public static Guid CreateGuidFromHash(string text, Guid namespaceId) - { - return CreateGuidFromHash(Encoding.UTF8.GetBytes(text), namespaceId); - } - - public static Guid CreateGuidFromHash(byte[] nameBytes, Guid namespaceId) - { - // convert the namespace UUID to network order (step 3) - byte[] namespaceBytes = namespaceId.ToByteArray(); - SwapByteOrder(namespaceBytes); - - // comput the hash of the name space ID concatenated with the - // name (step 4) - byte[] hash; - using (var algorithm = SHA1.Create()) { - algorithm.TransformBlock(namespaceBytes, 0, namespaceBytes.Length, null, 0); - algorithm.TransformFinalBlock(nameBytes, 0, nameBytes.Length); - hash = algorithm.Hash!; - } - - // most bytes from the hash are copied straight to the bytes of - // the new GUID (steps 5-7, 9, 11-12) - var newGuid = new byte[16]; - Array.Copy(hash, 0, newGuid, 0, 16); - - // set the four most significant bits (bits 12 through 15) of - // the time_hi_and_version field to the appropriate 4-bit - // version number from Section 4.1.3 (step 8) - newGuid[6] = (byte) ((newGuid[6] & 0x0F) | (5 << 4)); - - // set the two most significant bits (bits 6 and 7) of the - // clock_seq_hi_and_reserved to zero and one, respectively - // (step 10) - newGuid[8] = (byte) ((newGuid[8] & 0x3F) | 0x80); - - // convert the resulting UUID to local byte order (step 13) - SwapByteOrder(newGuid); - return new Guid(newGuid); - } - - /// - /// The namespace for fully-qualified domain names (from RFC 4122, Appendix C). - /// - public static readonly Guid DnsNamespace = new Guid("6ba7b810-9dad-11d1-80b4-00c04fd430c8"); - - /// - /// The namespace for URLs (from RFC 4122, Appendix C). - /// - public static readonly Guid UrlNamespace = new Guid("6ba7b811-9dad-11d1-80b4-00c04fd430c8"); - - /// - /// The namespace for ISO OIDs (from RFC 4122, Appendix C). - /// - public static readonly Guid IsoOidNamespace = new Guid("6ba7b812-9dad-11d1-80b4-00c04fd430c8"); - - // Converts a GUID (expressed as a byte array) to/from network order (MSB-first). - static void SwapByteOrder(byte[] guid) - { - SwapBytes(guid, 0, 3); - SwapBytes(guid, 1, 2); - SwapBytes(guid, 4, 5); - SwapBytes(guid, 6, 7); - } - - static void SwapBytes(byte[] guid, int left, int right) - { - byte temp = guid[left]; - guid[left] = guid[right]; - guid[right] = temp; - } - - public static void MoveFile(string source, string dest, bool overwrite) - { -#if NET6_0_OR_GREATER - File.Move(source, dest, overwrite); -#else - if (!File.Exists(source)) throw new FileNotFoundException("File not found", source); - if (overwrite) File.Delete(dest); - File.Move(source, dest); -#endif - } - - public static TEnum[] GetEnumValues() where TEnum : struct, Enum - { -#if NET6_0_OR_GREATER - return Enum.GetValues(); -#else - return Enum.GetValues(typeof(TEnum)).Cast().ToArray(); -#endif - } - } -} \ No newline at end of file diff --git a/src/lib-csharp/Locators/LinuxVelopackLocator.cs b/src/lib-csharp/Locators/LinuxVelopackLocator.cs index 43592f98..91aa0999 100644 --- a/src/lib-csharp/Locators/LinuxVelopackLocator.cs +++ b/src/lib-csharp/Locators/LinuxVelopackLocator.cs @@ -4,6 +4,7 @@ using System.Runtime.Versioning; using Microsoft.Extensions.Logging; using NuGet.Versioning; using Velopack.NuGet; +using Velopack.Util; namespace Velopack.Locators { @@ -33,7 +34,7 @@ namespace Velopack.Locators public override string? Channel { get; } /// - public override string? AppTempDir => CreateSubDirIfDoesNotExist(Utility.GetDefaultTempBaseDirectory(), AppId); + public override string? AppTempDir => CreateSubDirIfDoesNotExist(TempUtil.GetDefaultTempBaseDirectory(), AppId); /// public override string? PackagesDir => CreateSubDirIfDoesNotExist(PersistentTempDir, "packages"); @@ -70,7 +71,7 @@ namespace Velopack.Locators var rootDir = ourPath.Substring(0, ix); var contentsDir = Path.Combine(rootDir, "usr", "bin"); var updateExe = Path.Combine(contentsDir, "UpdateNix"); - var metadataPath = Path.Combine(contentsDir, Utility.SpecVersionFileName); + var metadataPath = Path.Combine(contentsDir, CoreUtil.SpecVersionFileName); if (!String.IsNullOrEmpty(AppImagePath) && File.Exists(AppImagePath)) { if (File.Exists(updateExe) && PackageManifest.TryParseFromFile(metadataPath, out var manifest)) { diff --git a/src/lib-csharp/Locators/OsxVelopackLocator.cs b/src/lib-csharp/Locators/OsxVelopackLocator.cs index a7466417..dbf9d8e6 100644 --- a/src/lib-csharp/Locators/OsxVelopackLocator.cs +++ b/src/lib-csharp/Locators/OsxVelopackLocator.cs @@ -4,6 +4,7 @@ using System.Runtime.Versioning; using Microsoft.Extensions.Logging; using NuGet.Versioning; using Velopack.NuGet; +using Velopack.Util; namespace Velopack.Locators { @@ -30,7 +31,7 @@ namespace Velopack.Locators public override string? AppContentDir => RootAppDir; /// - public override string? AppTempDir => CreateSubDirIfDoesNotExist(Utility.GetDefaultTempBaseDirectory(), AppId); + public override string? AppTempDir => CreateSubDirIfDoesNotExist(TempUtil.GetDefaultTempBaseDirectory(), AppId); /// public override string? PackagesDir => CreateSubDirIfDoesNotExist(CachesAppDir, "packages"); @@ -68,7 +69,7 @@ namespace Velopack.Locators var contentsDir = Path.Combine(appPath, "Contents"); var macosDir = Path.Combine(contentsDir, "MacOS"); var updateExe = Path.Combine(macosDir, "UpdateMac"); - var metadataPath = Path.Combine(macosDir, Utility.SpecVersionFileName); + var metadataPath = Path.Combine(macosDir, CoreUtil.SpecVersionFileName); if (File.Exists(updateExe) && PackageManifest.TryParseFromFile(metadataPath, out var manifest)) { Log.Info("Located valid manifest file at: " + metadataPath); diff --git a/src/lib-csharp/Locators/VelopackLocator.cs b/src/lib-csharp/Locators/VelopackLocator.cs index a28604ac..cc95b199 100644 --- a/src/lib-csharp/Locators/VelopackLocator.cs +++ b/src/lib-csharp/Locators/VelopackLocator.cs @@ -6,6 +6,7 @@ using System.Text; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging.Abstractions; using NuGet.Versioning; +using Velopack.Util; namespace Velopack.Locators { @@ -162,7 +163,7 @@ namespace Velopack.Locators var buf = new byte[4096]; prng.NextBytes(buf); - ret = Utility.CreateGuidFromHash(buf); + ret = GuidUtil.CreateGuidFromHash(buf); try { File.WriteAllText(stagedUserIdFile, ret.ToString(), Encoding.UTF8); Log.Info($"Generated new staging userId: {ret}"); diff --git a/src/lib-csharp/Locators/VelopackLocatorExtensions.cs b/src/lib-csharp/Locators/VelopackLocatorExtensions.cs index 8b8b8045..6f27732c 100644 --- a/src/lib-csharp/Locators/VelopackLocatorExtensions.cs +++ b/src/lib-csharp/Locators/VelopackLocatorExtensions.cs @@ -1,4 +1,5 @@ using System.IO; +using Velopack.Util; namespace Velopack.Locators { @@ -13,7 +14,7 @@ namespace Velopack.Locators /// public static string GetLocalPackagePath(this IVelopackLocator locator, VelopackAsset velopackAsset) { - return Path.Combine(locator.PackagesDir!, Utility.GetSafeFilename(velopackAsset.FileName)); + return Path.Combine(locator.PackagesDir!, PathUtil.GetSafeFilename(velopackAsset.FileName)); } } diff --git a/src/lib-csharp/Locators/WindowsVelopackLocator.cs b/src/lib-csharp/Locators/WindowsVelopackLocator.cs index f65e6ecf..9f171f7b 100644 --- a/src/lib-csharp/Locators/WindowsVelopackLocator.cs +++ b/src/lib-csharp/Locators/WindowsVelopackLocator.cs @@ -4,6 +4,7 @@ using System.Runtime.Versioning; using Microsoft.Extensions.Logging; using NuGet.Versioning; using Velopack.NuGet; +using Velopack.Util; namespace Velopack.Locators { @@ -69,7 +70,7 @@ namespace Velopack.Locators if (File.Exists(possibleUpdateExe)) { Log.Info("Update.exe found in parent directory"); // we're running in a directory with an Update.exe in the parent directory - var manifestFile = Path.Combine(myDirPath, Utility.SpecVersionFileName); + var manifestFile = Path.Combine(myDirPath, CoreUtil.SpecVersionFileName); if (PackageManifest.TryParseFromFile(manifestFile, out var manifest)) { // ideal, the info we need is in a manifest file. Log.Info("Located valid manifest file at: " + manifestFile); @@ -79,7 +80,7 @@ namespace Velopack.Locators UpdateExePath = possibleUpdateExe; AppContentDir = myDirPath; Channel = manifest.Channel; - } else if (Utility.PathPartStartsWith(myDirName, "app-") && NuGetVersion.TryParse(myDirName.Substring(4), out var version)) { + } else if (PathUtil.PathPartStartsWith(myDirName, "app-") && NuGetVersion.TryParse(myDirName.Substring(4), out var version)) { // this is a legacy case, where we're running in an 'root/app-*/' directory, and there is no manifest. Log.Warn("Legacy app-* directory detected, sq.version not found. Using directory name for AppId and Version."); AppId = Path.GetFileName(Path.GetDirectoryName(possibleUpdateExe)); @@ -92,7 +93,7 @@ namespace Velopack.Locators // this is an attempt to handle the case where we are running in a nested current directory. var rootDir = ourExePath.Substring(0, ixCurrent); var currentDir = Path.Combine(rootDir, "current"); - var manifestFile = Path.Combine(currentDir, Utility.SpecVersionFileName); + var manifestFile = Path.Combine(currentDir, CoreUtil.SpecVersionFileName); possibleUpdateExe = Path.GetFullPath(Path.Combine(rootDir, "Update.exe")); // we only support parsing a manifest when we're in a nested current directory. no legacy fallback. if (File.Exists(possibleUpdateExe) && PackageManifest.TryParseFromFile(manifestFile, out var manifest)) { diff --git a/src/lib-csharp/ReleaseEntry.cs b/src/lib-csharp/ReleaseEntry.cs index 05b6ef27..57974635 100644 --- a/src/lib-csharp/ReleaseEntry.cs +++ b/src/lib-csharp/ReleaseEntry.cs @@ -11,6 +11,7 @@ using System.Text; using System.Text.RegularExpressions; using System.Threading.Tasks; using NuGet.Versioning; +using Velopack.Util; namespace Velopack { @@ -225,7 +226,7 @@ namespace Velopack string baseUrl = null; string query = null; - if (Utility.IsHttpUrl(filename)) { + if (HttpUtil.IsHttpUrl(filename)) { var uri = new Uri(filename); var path = uri.LocalPath; var authority = uri.GetLeftPart(UriPartial.Authority); @@ -281,7 +282,7 @@ namespace Velopack return new ReleaseEntry[0]; } - fileContents = Utility.RemoveByteOrderMarkerIfPresent(fileContents); + fileContents = CoreUtil.RemoveByteOrderMarkerIfPresent(fileContents); var ret = fileContents.Split('\n') .Where(x => !String.IsNullOrWhiteSpace(x)) @@ -302,7 +303,7 @@ namespace Velopack return new ReleaseEntry[0]; } - fileContents = Utility.RemoveByteOrderMarkerIfPresent(fileContents); + fileContents = CoreUtil.RemoveByteOrderMarkerIfPresent(fileContents); var ret = fileContents.Split('\n') .Where(x => !String.IsNullOrWhiteSpace(x)) @@ -350,7 +351,7 @@ namespace Velopack Contract.Requires(file != null && file.CanRead); Contract.Requires(!String.IsNullOrEmpty(filename)); - var hash = Utility.CalculateStreamSHA1(file); + var hash = IoUtil.CalculateStreamSHA1(file); return new ReleaseEntry(hash, filename, file.Length, baseUrl); } @@ -387,7 +388,7 @@ namespace Velopack var entries = entriesQueue.ToList(); if (writeToDisk) { - using var _ = Utility.GetTempFileName(out var tempFile); + using var _ = TempUtil.GetTempFileName(out var tempFile); using (var of = File.OpenWrite(tempFile)) { if (entries.Count > 0) WriteReleaseFile(entries, of); } diff --git a/src/lib-csharp/Sources/GitBase.cs b/src/lib-csharp/Sources/GitBase.cs index b1f31ec6..c2e39b46 100644 --- a/src/lib-csharp/Sources/GitBase.cs +++ b/src/lib-csharp/Sources/GitBase.cs @@ -4,6 +4,7 @@ using System.Linq; using System.Threading; using System.Threading.Tasks; using Microsoft.Extensions.Logging; +using Velopack.Util; namespace Velopack.Sources { @@ -44,7 +45,7 @@ namespace Velopack.Sources RepoUri = new Uri(repoUrl.TrimEnd('/')); AccessToken = accessToken; Prerelease = prerelease; - Downloader = downloader ?? Utility.CreateDefaultDownloader(); + Downloader = downloader ?? HttpUtil.CreateDefaultDownloader(); } /// @@ -69,7 +70,7 @@ namespace Velopack.Sources return new VelopackAssetFeed(); } - var releasesFileName = Utility.GetVeloReleaseIndexName(channel); + var releasesFileName = CoreUtil.GetVeloReleaseIndexName(channel); List entries = new List(); foreach (var r in releases) { @@ -83,7 +84,7 @@ namespace Velopack.Sources continue; } var releaseBytes = await Downloader.DownloadBytes(assetUrl, Authorization, "application/octet-stream").ConfigureAwait(false); - var txt = Utility.RemoveByteOrderMarkerIfPresent(releaseBytes); + var txt = CoreUtil.RemoveByteOrderMarkerIfPresent(releaseBytes); var feed = VelopackAssetFeed.FromJson(txt); foreach (var f in feed.Assets) { entries.Add(new GitBaseAsset(f, r)); diff --git a/src/lib-csharp/Sources/GiteaSource.cs b/src/lib-csharp/Sources/GiteaSource.cs index 7bdd629e..1cf14abc 100644 --- a/src/lib-csharp/Sources/GiteaSource.cs +++ b/src/lib-csharp/Sources/GiteaSource.cs @@ -3,7 +3,7 @@ using System.Collections.Generic; using System.Linq; using System.Text.Json.Serialization; using System.Threading.Tasks; -using Velopack.Json; +using Velopack.Util; namespace Velopack.Sources { /// Describes a Gitea release, including attached assets. diff --git a/src/lib-csharp/Sources/GithubSource.cs b/src/lib-csharp/Sources/GithubSource.cs index c7e1e90d..dac9ad56 100644 --- a/src/lib-csharp/Sources/GithubSource.cs +++ b/src/lib-csharp/Sources/GithubSource.cs @@ -3,7 +3,7 @@ using System.Collections.Generic; using System.Linq; using System.Text.Json.Serialization; using System.Threading.Tasks; -using Velopack.Json; +using Velopack.Util; namespace Velopack.Sources { diff --git a/src/lib-csharp/Sources/GitlabSource.cs b/src/lib-csharp/Sources/GitlabSource.cs index dde0af98..d7312725 100644 --- a/src/lib-csharp/Sources/GitlabSource.cs +++ b/src/lib-csharp/Sources/GitlabSource.cs @@ -2,7 +2,7 @@ using System.Linq; using System.Text.Json.Serialization; using System.Threading.Tasks; -using Velopack.Json; +using Velopack.Util; namespace Velopack.Sources { diff --git a/src/lib-csharp/Sources/SimpleFileSource.cs b/src/lib-csharp/Sources/SimpleFileSource.cs index a7aaf029..b95ba104 100644 --- a/src/lib-csharp/Sources/SimpleFileSource.cs +++ b/src/lib-csharp/Sources/SimpleFileSource.cs @@ -5,6 +5,7 @@ using System.Threading; using System.Threading.Tasks; using Microsoft.Extensions.Logging; using Velopack.NuGet; +using Velopack.Util; namespace Velopack.Sources { @@ -32,7 +33,7 @@ namespace Velopack.Sources } // if a feed exists in the folder, let's use that. - var feedLoc = Path.Combine(BaseDirectory.FullName, Utility.GetVeloReleaseIndexName(channel)); + var feedLoc = Path.Combine(BaseDirectory.FullName, CoreUtil.GetVeloReleaseIndexName(channel)); if (File.Exists(feedLoc)) { logger.Debug($"Found local file feed at '{feedLoc}'."); return Task.FromResult(VelopackAssetFeed.FromJson(File.ReadAllText(feedLoc))); diff --git a/src/lib-csharp/Sources/SimpleWebSource.cs b/src/lib-csharp/Sources/SimpleWebSource.cs index 85419d6b..99c12f2c 100644 --- a/src/lib-csharp/Sources/SimpleWebSource.cs +++ b/src/lib-csharp/Sources/SimpleWebSource.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; using Microsoft.Extensions.Logging; +using Velopack.Util; namespace Velopack.Sources { @@ -28,14 +29,14 @@ namespace Velopack.Sources public SimpleWebSource(Uri baseUri, IFileDownloader? downloader = null) { BaseUri = baseUri; - Downloader = downloader ?? Utility.CreateDefaultDownloader(); + Downloader = downloader ?? HttpUtil.CreateDefaultDownloader(); } /// public async virtual Task GetReleaseFeed(ILogger logger, string channel, Guid? stagingId = null, VelopackAsset? latestLocalRelease = null) { - var releaseFilename = Utility.GetVeloReleaseIndexName(channel); - var uri = Utility.AppendPathToUri(BaseUri, releaseFilename); + var releaseFilename = CoreUtil.GetVeloReleaseIndexName(channel); + var uri = HttpUtil.AppendPathToUri(BaseUri, releaseFilename); var args = new Dictionary(); if (VelopackRuntimeInfo.SystemArch != RuntimeCpu.Unknown) { @@ -52,7 +53,7 @@ namespace Velopack.Sources args.Add("localVersion", latestLocalRelease.Version.ToString()); } - var uriAndQuery = Utility.AddQueryParamsToUri(uri, args); + var uriAndQuery = HttpUtil.AddQueryParamsToUri(uri, args); logger.Info($"Downloading release file '{releaseFilename}' from '{uriAndQuery}'."); @@ -68,11 +69,11 @@ namespace Velopack.Sources // releaseUri can be a relative url (eg. "MyPackage.nupkg") or it can be an // absolute url (eg. "https://example.com/MyPackage.nupkg"). In the former case - var sourceBaseUri = Utility.EnsureTrailingSlash(BaseUri); + var sourceBaseUri = HttpUtil.EnsureTrailingSlash(BaseUri); - var source = Utility.IsHttpUrl(releaseEntry.FileName) + var source = HttpUtil.IsHttpUrl(releaseEntry.FileName) ? releaseEntry.FileName - : Utility.AppendPathToUri(sourceBaseUri, releaseEntry.FileName).ToString(); + : HttpUtil.AppendPathToUri(sourceBaseUri, releaseEntry.FileName).ToString(); logger.Info($"Downloading '{releaseEntry.FileName}' from '{source}'."); await Downloader.DownloadFile(source, localFile, progress, cancelToken: cancelToken).ConfigureAwait(false); diff --git a/src/lib-csharp/Sources/VelopackFlowUpdateSource.cs b/src/lib-csharp/Sources/VelopackFlowUpdateSource.cs index 64605ee8..161e684e 100644 --- a/src/lib-csharp/Sources/VelopackFlowUpdateSource.cs +++ b/src/lib-csharp/Sources/VelopackFlowUpdateSource.cs @@ -3,7 +3,7 @@ using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; using Microsoft.Extensions.Logging; -using Velopack.Json; +using Velopack.Util; using Velopack.Locators; namespace Velopack.Sources @@ -19,7 +19,7 @@ namespace Velopack.Sources IFileDownloader? downloader = null) { BaseUri = new Uri(baseUri); - Downloader = downloader ?? Utility.CreateDefaultDownloader(); + Downloader = downloader ?? HttpUtil.CreateDefaultDownloader(); } /// The URL of the server hosting packages to update to. @@ -33,7 +33,7 @@ namespace Velopack.Sources VelopackAsset? latestLocalRelease = null) { Uri baseUri = new(BaseUri, $"v1.0/manifest/"); - var uri = Utility.AppendPathToUri(baseUri, Utility.GetVeloReleaseIndexName(channel)); + var uri = HttpUtil.AppendPathToUri(baseUri, CoreUtil.GetVeloReleaseIndexName(channel)); var args = new Dictionary(); if (VelopackRuntimeInfo.SystemArch != RuntimeCpu.Unknown) { @@ -52,7 +52,7 @@ namespace Velopack.Sources args.Add("id", VelopackLocator.GetDefault(logger).AppId ?? ""); } - var uriAndQuery = Utility.AddQueryParamsToUri(uri, args); + var uriAndQuery = HttpUtil.AddQueryParamsToUri(uri, args); logger.LogInformation("Downloading releases from '{Uri}'.", uriAndQuery); @@ -73,7 +73,7 @@ namespace Velopack.Sources } if (localFile is null) throw new ArgumentNullException(nameof(localFile)); - Uri sourceBaseUri = Utility.EnsureTrailingSlash(BaseUri); + Uri sourceBaseUri = HttpUtil.EnsureTrailingSlash(BaseUri); Uri downloadUri = new(sourceBaseUri, $"v1.0/download/{velopackRelease.Id}"); diff --git a/src/lib-csharp/UpdateExe.cs b/src/lib-csharp/UpdateExe.cs index cf24a202..b690d0fc 100644 --- a/src/lib-csharp/UpdateExe.cs +++ b/src/lib-csharp/UpdateExe.cs @@ -9,6 +9,7 @@ using System.Threading.Tasks; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging.Abstractions; using Velopack.Locators; +using Velopack.Util; namespace Velopack { diff --git a/src/lib-csharp/UpdateManager.cs b/src/lib-csharp/UpdateManager.cs index 769ed0b6..020500b4 100644 --- a/src/lib-csharp/UpdateManager.cs +++ b/src/lib-csharp/UpdateManager.cs @@ -11,6 +11,7 @@ using Velopack.Compression; using Velopack.Locators; using Velopack.NuGet; using Velopack.Sources; +using Velopack.Util; namespace Velopack { @@ -126,7 +127,7 @@ namespace Velopack var feedObj = await Source.GetReleaseFeed(Log, Channel, betaId, latestLocalFull).ConfigureAwait(false); var feed = feedObj.Assets; - var latestRemoteFull = feed.Where(r => r.Type == VelopackAssetType.Full).MaxBy(x => x.Version).FirstOrDefault(); + var latestRemoteFull = feed.Where(r => r.Type == VelopackAssetType.Full).MaxByPolyfill(x => x.Version).FirstOrDefault(); if (latestRemoteFull == null) { Log.Info("No remote full releases found."); return null; @@ -264,20 +265,20 @@ namespace Velopack Log.Info($"There are too many delta's ({deltasCount} > 10) or the sum of their size ({deltasSize} > {targetRelease.Size}) is too large. " + $"Only full update will be available."); } else { - using var _1 = Utility.GetTempDirectory(out var deltaStagingDir, appTempDir); + using var _1 = TempUtil.GetTempDirectory(out var deltaStagingDir, appTempDir); string basePackagePath = Locator.GetLocalPackagePath(updates.BaseRelease); if (!File.Exists(basePackagePath)) throw new Exception($"Unable to find base package {basePackagePath} for delta update."); EasyZip.ExtractZipToDirectory(Log, basePackagePath, deltaStagingDir); reportProgress(10); - await DownloadAndApplyDeltaUpdates(deltaStagingDir, updates, x => reportProgress(Utility.CalculateProgress(x, 10, 80)), cancelToken) + await DownloadAndApplyDeltaUpdates(deltaStagingDir, updates, x => reportProgress(CoreUtil.CalculateProgress(x, 10, 80)), cancelToken) .ConfigureAwait(false); reportProgress(80); Log.Info("Delta updates completed, creating final update package."); File.Delete(incompleteFile); - await EasyZip.CreateZipFromDirectoryAsync(Log, incompleteFile, deltaStagingDir, x => reportProgress(Utility.CalculateProgress(x, 80, 100)), + await EasyZip.CreateZipFromDirectoryAsync(Log, incompleteFile, deltaStagingDir, x => reportProgress(CoreUtil.CalculateProgress(x, 80, 100)), cancelToken: cancelToken).ConfigureAwait(false); File.Delete(completeFile); File.Move(incompleteFile, completeFile); @@ -297,7 +298,7 @@ namespace Velopack Log.Info("Verifying package checksum..."); VerifyPackageChecksum(targetRelease, incompleteFile); - Utility.MoveFile(incompleteFile, completeFile, true); + IoUtil.MoveFile(incompleteFile, completeFile, true); Log.Info("Full release download complete. Package moved to: " + completeFile); reportProgress(100); } finally { @@ -310,7 +311,7 @@ namespace Velopack if (zip.UpdateExeBytes == null) { Log.Error("Update.exe not found in package, skipping extraction."); } else { - await Utility.RetryAsync(async () => { + await IoUtil.RetryAsync(async () => { using var ms = new MemoryStream(zip.UpdateExeBytes); using var fs = File.Create(updateExe); await ms.CopyToAsync(fs).ConfigureAwait(false); @@ -352,7 +353,7 @@ namespace Velopack current -= component; component = toIncrement / 100.0 * p; var progressOfStep = (int) Math.Round(current += component); - progress(Utility.CalculateProgress(progressOfStep, 0, 50)); + progress(CoreUtil.CalculateProgress(progressOfStep, 0, 50)); } }, cancelToken).ConfigureAwait(false); VerifyPackageChecksum(x, targetFile); @@ -372,7 +373,7 @@ namespace Velopack var packageFile = Locator.GetLocalPackagePath(rel); builder.ApplyDeltaPackageFast(extractedBasePackage, packageFile, x => { var progressOfStep = (int) (baseProgress + (progressStepSize * (x / 100d))); - progress(Utility.CalculateProgress(progressOfStep, 50, 100)); + progress(CoreUtil.CalculateProgress(progressOfStep, 50, 100)); }); } @@ -391,11 +392,11 @@ namespace Velopack var appPackageDir = Locator.PackagesDir!; foreach (var l in Directory.EnumerateFiles(appPackageDir, "*.nupkg").ToArray()) { try { - if (assetToKeep != null && Utility.FullPathEquals(l, assetToKeep)) { + if (assetToKeep != null && PathUtil.FullPathEquals(l, assetToKeep)) { continue; } - Utility.DeleteFileOrDirectoryHard(l); + IoUtil.DeleteFileOrDirectoryHard(l); Log.Trace(l + " deleted."); } catch (Exception ex) { Log.Warn(ex, "Failed to delete partial package: " + l); @@ -404,7 +405,7 @@ namespace Velopack foreach (var l in Directory.EnumerateFiles(appPackageDir, "*.partial").ToArray()) { try { - Utility.DeleteFileOrDirectoryHard(l); + IoUtil.DeleteFileOrDirectoryHard(l); Log.Trace(l + " deleted."); } catch (Exception ex) { Log.Warn(ex, "Failed to delete partial package: " + l); @@ -433,12 +434,12 @@ namespace Velopack } if (!string.IsNullOrEmpty(release.SHA256)) { - var hash = Utility.CalculateFileSHA256(targetPackage.FullName); + var hash = IoUtil.CalculateFileSHA256(targetPackage.FullName); if (!hash.Equals(release.SHA256, StringComparison.Ordinal)) { throw new ChecksumFailedException(targetPackage.FullName, $"SHA256 doesn't match ({release.SHA256} != {hash})."); } } else { - var hash = Utility.CalculateFileSHA1(targetPackage.FullName); + var hash = IoUtil.CalculateFileSHA1(targetPackage.FullName); if (!hash.Equals(release.SHA1, StringComparison.OrdinalIgnoreCase)) { throw new ChecksumFailedException(targetPackage.FullName, $"SHA1 doesn't match ({release.SHA1} != {hash})."); } @@ -479,8 +480,8 @@ namespace Velopack if (String.IsNullOrWhiteSpace(urlOrPath)) { throw new ArgumentException("Must pass a valid URL or file path to UpdateManager", nameof(urlOrPath)); } - if (Utility.IsHttpUrl(urlOrPath)) { - return new SimpleWebSource(urlOrPath, Utility.CreateDefaultDownloader()); + if (HttpUtil.IsHttpUrl(urlOrPath)) { + return new SimpleWebSource(urlOrPath, HttpUtil.CreateDefaultDownloader()); } else { return new SimpleFileSource(new DirectoryInfo(urlOrPath)); } diff --git a/src/lib-csharp/Internal/CompiledJson.cs b/src/lib-csharp/Util/CompiledJson.cs similarity index 99% rename from src/lib-csharp/Internal/CompiledJson.cs rename to src/lib-csharp/Util/CompiledJson.cs index f5028d3d..bafa87d5 100644 --- a/src/lib-csharp/Internal/CompiledJson.cs +++ b/src/lib-csharp/Util/CompiledJson.cs @@ -22,7 +22,7 @@ namespace System.Text.Json.Serialization } #endif -namespace Velopack.Json +namespace Velopack.Util { #if NET6_0_OR_GREATER diff --git a/src/lib-csharp/Util/CoreUtil.cs b/src/lib-csharp/Util/CoreUtil.cs new file mode 100644 index 00000000..61c7ce8f --- /dev/null +++ b/src/lib-csharp/Util/CoreUtil.cs @@ -0,0 +1,142 @@ +using System; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Velopack.Util +{ + internal static class CoreUtil + { + public const string SpecVersionFileName = "sq.version"; + + public static string GetVeloReleaseIndexName(string channel) + { + return $"releases.{channel ?? VelopackRuntimeInfo.SystemOs.GetOsShortName()}.json"; + } + + public static string GetReleasesFileName(string? channel) + { + if (channel == null) { + // default RELEASES file name for each platform. + if (VelopackRuntimeInfo.IsOSX) return "RELEASES-osx"; + if (VelopackRuntimeInfo.IsLinux) return "RELEASES-linux"; + return "RELEASES"; + } else { + // if the channel is an empty string or "win", we use the default RELEASES file name. + if (String.IsNullOrWhiteSpace(channel) || channel.ToLower() == "win") { + return "RELEASES"; + } + + // all other cases the RELEASES file includes the channel name. + return $"RELEASES-{channel.ToLower()}"; + } + } + + public static string GetAppUserModelId(string packageId) + { + return $"velopack.{packageId}"; + } + + /// + /// Calculates the total percentage of a specific step that should report within a specific range. + /// + /// If a step needs to report between 50 -> 75 %, this method should be used as CalculateProgress(percentage, 50, 75). + /// + /// The percentage of the current step, a value between 0 and 100. + /// The start percentage of the range the current step represents. + /// The end percentage of the range the current step represents. + /// The calculated percentage that can be reported about the total progress. + public static int CalculateProgress(int percentageOfCurrentStep, int stepStartPercentage, int stepEndPercentage) + { + // Ensure we are between 0 and 100 + percentageOfCurrentStep = Math.Max(Math.Min(percentageOfCurrentStep, 100), 0); + + var range = stepEndPercentage - stepStartPercentage; + var singleValue = range / 100d; + var totalPercentage = (singleValue * percentageOfCurrentStep) + stepStartPercentage; + + return (int) totalPercentage; + } + + public static Action CreateProgressDelegate(Action rootProgress, int stepStartPercentage, int stepEndPercentage) + { + return percentage => { + rootProgress(CalculateProgress(percentage, stepStartPercentage, stepEndPercentage)); + }; + } + + public static string RemoveByteOrderMarkerIfPresent(string content) + { + return string.IsNullOrEmpty(content) + ? string.Empty + : RemoveByteOrderMarkerIfPresent(Encoding.UTF8.GetBytes(content)); + } + + public static string RemoveByteOrderMarkerIfPresent(byte[] content) + { + byte[] output = { }; + + Func matches = (bom, src) => { + if (src.Length < bom.Length) return false; + + return !bom.Where((chr, index) => src[index] != chr).Any(); + }; + + var utf32Be = new byte[] { 0x00, 0x00, 0xFE, 0xFF }; + var utf32Le = new byte[] { 0xFF, 0xFE, 0x00, 0x00 }; + var utf16Be = new byte[] { 0xFE, 0xFF }; + var utf16Le = new byte[] { 0xFF, 0xFE }; + var utf8 = new byte[] { 0xEF, 0xBB, 0xBF }; + + if (matches(utf32Be, content)) { + output = new byte[content.Length - utf32Be.Length]; + } else if (matches(utf32Le, content)) { + output = new byte[content.Length - utf32Le.Length]; + } else if (matches(utf16Be, content)) { + output = new byte[content.Length - utf16Be.Length]; + } else if (matches(utf16Le, content)) { + output = new byte[content.Length - utf16Le.Length]; + } else if (matches(utf8, content)) { + output = new byte[content.Length - utf8.Length]; + } else { + output = content; + } + + if (output.Length > 0) { + Buffer.BlockCopy(content, content.Length - output.Length, output, 0, output.Length); + } + + return Encoding.UTF8.GetString(output); + } + + public static T GetAwaiterResult(this Task task) + { + return task.ConfigureAwait(false).GetAwaiter().GetResult(); + } + + public static void GetAwaiterResult(this Task task) + { + task.ConfigureAwait(false).GetAwaiter().GetResult(); + } + + public static bool TryParseEnumU16(ushort enumValue, out TEnum? retVal) + { + retVal = default; + bool success = Enum.IsDefined(typeof(TEnum), enumValue); + if (success) { + retVal = (TEnum) Enum.ToObject(typeof(TEnum), enumValue); + } + + return success; + } + + public static TEnum[] GetEnumValues() where TEnum : struct, Enum + { +#if NET6_0_OR_GREATER + return Enum.GetValues(); +#else + return Enum.GetValues(typeof(TEnum)).Cast().ToArray(); +#endif + } + } +} \ No newline at end of file diff --git a/src/lib-csharp/Internal/Disposable.cs b/src/lib-csharp/Util/Disposable.cs similarity index 96% rename from src/lib-csharp/Internal/Disposable.cs rename to src/lib-csharp/Util/Disposable.cs index 24051429..aee48693 100644 --- a/src/lib-csharp/Internal/Disposable.cs +++ b/src/lib-csharp/Util/Disposable.cs @@ -2,7 +2,7 @@ using System.Diagnostics.CodeAnalysis; using System.Threading; -namespace Velopack +namespace Velopack.Util { [ExcludeFromCodeCoverage] internal static class Disposable diff --git a/src/lib-csharp/Internal/EnumerableExtensions.cs b/src/lib-csharp/Util/EnumerableExtensions.cs similarity index 81% rename from src/lib-csharp/Internal/EnumerableExtensions.cs rename to src/lib-csharp/Util/EnumerableExtensions.cs index 8439d15b..764f792e 100644 --- a/src/lib-csharp/Internal/EnumerableExtensions.cs +++ b/src/lib-csharp/Util/EnumerableExtensions.cs @@ -1,10 +1,13 @@ // Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt using System; +using System.Collections.Concurrent; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; +using System.Linq; +using System.Threading.Tasks; -namespace Velopack +namespace Velopack.Util { [ExcludeFromCodeCoverage] internal static class EnumerableExtensions @@ -45,6 +48,7 @@ namespace Velopack $" There were 2 or more."); } } + return result; } @@ -72,14 +76,14 @@ namespace Velopack /// Source sequence. /// Key selector used to extract the key for each element in the sequence. /// List with the elements that share the same maximum key value. - public static IList MaxBy(this IEnumerable source, Func keySelector) + public static IList MaxByPolyfill(this IEnumerable source, Func keySelector) { if (source == null) throw new ArgumentNullException("source"); if (keySelector == null) throw new ArgumentNullException("keySelector"); - - return MaxBy(source, keySelector, Comparer.Default); + + return MaxByPolyfill(source, keySelector, Comparer.Default); } /// @@ -91,12 +95,12 @@ namespace Velopack /// Key selector used to extract the key for each element in the sequence. /// Comparer used to determine the maximum key value. /// List with the elements that share the same maximum key value. - public static IList MaxBy(this IEnumerable source, Func keySelector, IComparer comparer) + public static IList MaxByPolyfill(this IEnumerable source, Func keySelector, IComparer comparer) { if (source == null) throw new ArgumentNullException("source"); if (keySelector == null) throw new ArgumentNullException("keySelector"); if (comparer == null) throw new ArgumentNullException("comparer"); - + return ExtremaBy(source, keySelector, (key, minValue) => comparer.Compare(key, minValue)); } @@ -127,5 +131,22 @@ namespace Velopack return result; } + + public static Task ForEachAsync(this IEnumerable source, Action body, int degreeOfParallelism = 4) + { + return ForEachAsync(source, x => Task.Run(() => body(x)), degreeOfParallelism); + } + + public static Task ForEachAsync(this IEnumerable source, Func body, int degreeOfParallelism = 4) + { + return Task.WhenAll( + from partition in Partitioner.Create(source).GetPartitions(degreeOfParallelism) + select Task.Run( + async () => { + using (partition) + while (partition.MoveNext()) + await body(partition.Current).ConfigureAwait(false); + })); + } } -} +} \ No newline at end of file diff --git a/src/lib-csharp/Util/GuidUtil.cs b/src/lib-csharp/Util/GuidUtil.cs new file mode 100644 index 00000000..bd800187 --- /dev/null +++ b/src/lib-csharp/Util/GuidUtil.cs @@ -0,0 +1,88 @@ +using System; +using System.Security.Cryptography; +using System.Text; + +namespace Velopack.Util +{ + internal static class GuidUtil + { + public static Guid CreateGuidFromHash(string text) + { + return CreateGuidFromHash(text, IsoOidNamespace); + } + + public static Guid CreateGuidFromHash(byte[] data) + { + return CreateGuidFromHash(data, IsoOidNamespace); + } + + public static Guid CreateGuidFromHash(string text, Guid namespaceId) + { + return CreateGuidFromHash(Encoding.UTF8.GetBytes(text), namespaceId); + } + + public static Guid CreateGuidFromHash(byte[] nameBytes, Guid namespaceId) + { + // convert the namespace UUID to network order (step 3) + byte[] namespaceBytes = namespaceId.ToByteArray(); + SwapByteOrder(namespaceBytes); + + // comput the hash of the name space ID concatenated with the + // name (step 4) + byte[] hash; + using (var algorithm = SHA1.Create()) { + algorithm.TransformBlock(namespaceBytes, 0, namespaceBytes.Length, null, 0); + algorithm.TransformFinalBlock(nameBytes, 0, nameBytes.Length); + hash = algorithm.Hash!; + } + + // most bytes from the hash are copied straight to the bytes of + // the new GUID (steps 5-7, 9, 11-12) + var newGuid = new byte[16]; + Array.Copy(hash, 0, newGuid, 0, 16); + + // set the four most significant bits (bits 12 through 15) of + // the time_hi_and_version field to the appropriate 4-bit + // version number from Section 4.1.3 (step 8) + newGuid[6] = (byte) ((newGuid[6] & 0x0F) | (5 << 4)); + + // set the two most significant bits (bits 6 and 7) of the + // clock_seq_hi_and_reserved to zero and one, respectively + // (step 10) + newGuid[8] = (byte) ((newGuid[8] & 0x3F) | 0x80); + + // convert the resulting UUID to local byte order (step 13) + SwapByteOrder(newGuid); + return new Guid(newGuid); + } + + /// + /// The namespace for fully-qualified domain names (from RFC 4122, Appendix C). + /// + public static readonly Guid DnsNamespace = new Guid("6ba7b810-9dad-11d1-80b4-00c04fd430c8"); + + /// + /// The namespace for URLs (from RFC 4122, Appendix C). + /// + public static readonly Guid UrlNamespace = new Guid("6ba7b811-9dad-11d1-80b4-00c04fd430c8"); + + /// + /// The namespace for ISO OIDs (from RFC 4122, Appendix C). + /// + public static readonly Guid IsoOidNamespace = new Guid("6ba7b812-9dad-11d1-80b4-00c04fd430c8"); + + // Converts a GUID (expressed as a byte array) to/from network order (MSB-first). + static void SwapByteOrder(byte[] guid) + { + SwapBytes(guid, 0, 3); + SwapBytes(guid, 1, 2); + SwapBytes(guid, 4, 5); + SwapBytes(guid, 6, 7); + } + + static void SwapBytes(byte[] guid, int left, int right) + { + (guid[left], guid[right]) = (guid[right], guid[left]); + } + } +} \ No newline at end of file diff --git a/src/lib-csharp/Util/HttpUtil.cs b/src/lib-csharp/Util/HttpUtil.cs new file mode 100644 index 00000000..191db652 --- /dev/null +++ b/src/lib-csharp/Util/HttpUtil.cs @@ -0,0 +1,52 @@ +using System; +using System.Collections.Generic; + +namespace Velopack.Util +{ + internal static class HttpUtil + { + public static Uri AddQueryParamsToUri(Uri uri, IEnumerable> newQuery) + { + var query = System.Web.HttpUtility.ParseQueryString(uri.Query); + + foreach (var entry in newQuery) { + query[entry.Key] = entry.Value; + } + + var builder = new UriBuilder(uri); + builder.Query = query.ToString(); + + return builder.Uri; + } + + public static bool IsHttpUrl(string urlOrPath) + { + if (!Uri.TryCreate(urlOrPath, UriKind.Absolute, out Uri? uri)) { + return false; + } + + return uri.Scheme == Uri.UriSchemeHttp || uri.Scheme == Uri.UriSchemeHttps; + } + + public static Sources.IFileDownloader CreateDefaultDownloader() + { + return new Sources.HttpClientFileDownloader(); + } + + public static Uri AppendPathToUri(Uri uri, string path) + { + var builder = new UriBuilder(uri); + if (!builder.Path.EndsWith("/")) { + builder.Path += "/"; + } + + builder.Path += path; + return builder.Uri; + } + + public static Uri EnsureTrailingSlash(Uri uri) + { + return AppendPathToUri(uri, ""); + } + } +} \ No newline at end of file diff --git a/src/lib-csharp/Util/IoUtil.cs b/src/lib-csharp/Util/IoUtil.cs new file mode 100644 index 00000000..b2bae288 --- /dev/null +++ b/src/lib-csharp/Util/IoUtil.cs @@ -0,0 +1,218 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Security.Cryptography; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Logging.Abstractions; + +namespace Velopack.Util +{ + internal static class IoUtil + { + public static IEnumerable GetAllFilesRecursively(this DirectoryInfo? rootPath) + { + if (rootPath == null) return Enumerable.Empty(); + return rootPath.EnumerateFiles("*", SearchOption.AllDirectories); + } + + public static string CalculateFileSHA1(string filePath) + { + var bufferSize = 1000000; // 1mb + using (var stream = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.Read, bufferSize)) { + return CalculateStreamSHA1(stream); + } + } + + public static string CalculateStreamSHA1(Stream file) + { + using (var sha1 = SHA1.Create()) { + return BitConverter.ToString(sha1.ComputeHash(file)).Replace("-", String.Empty); + } + } + + /// + public static string CalculateFileSHA256(string filePath) + { + var bufferSize = 1000000; // 1mb + using (var stream = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.Read, bufferSize)) { + return CalculateStreamSHA256(stream); + } + } + + /// + /// Get SHA256 hash of the specified file and returns the result as a base64 encoded string (with length 44) + /// + public static string CalculateStreamSHA256(Stream file) + { + using (var sha256 = SHA256.Create()) { + return BitConverter.ToString(sha256.ComputeHash(file)).Replace("-", String.Empty); + } + } + + public static void MoveFile(string source, string dest, bool overwrite) + { +#if NET6_0_OR_GREATER + File.Move(source, dest, overwrite); +#else + if (!File.Exists(source)) throw new FileNotFoundException("File not found", source); + if (overwrite) File.Delete(dest); + File.Move(source, dest); +#endif + } + + /// + /// Repeatedly tries various methods to delete a file system object. Optionally renames the directory first. + /// Optionally ignores errors. + /// + /// The path of the file system entity to delete. + /// Whether this function should throw if the delete fails. + /// Try to rename this object first before deleting. Can help prevent partial delete of folders. + /// Logger for diagnostic messages. + /// True if the file system object was deleted, false otherwise. + public static bool DeleteFileOrDirectoryHard(string path, bool throwOnFailure = true, bool renameFirst = false, ILogger? logger = null) + { + logger ??= NullLogger.Instance; + logger.Debug($"Starting to delete: {path}"); + + try { + if (File.Exists(path)) { + DeleteFsiVeryHard(new FileInfo(path), logger); + } else if (Directory.Exists(path)) { + if (renameFirst) { + // if there are locked files in a directory, we will not attempt to delte it + var oldPath = path + ".old"; + Directory.Move(path, oldPath); + path = oldPath; + } + + DeleteFsiTree(new DirectoryInfo(path), logger); + } else { + if (throwOnFailure) + logger?.Warn($"Cannot delete '{path}' if it does not exist."); + } + + return true; + } catch (Exception ex) { + logger.Error(ex, $"Unable to delete '{path}'"); + if (throwOnFailure) + throw; + return false; + } + } + + private static void DeleteFsiTree(FileSystemInfo fileSystemInfo, ILogger logger) + { + // if junction / symlink, don't iterate, just delete it. + if (fileSystemInfo.Attributes.HasFlag(FileAttributes.ReparsePoint)) { + DeleteFsiVeryHard(fileSystemInfo, logger); + return; + } + + // recursively delete children + try { + if (fileSystemInfo is DirectoryInfo directoryInfo) { + foreach (FileSystemInfo childInfo in directoryInfo.GetFileSystemInfos()) { + DeleteFsiTree(childInfo, logger); + } + } + } catch (Exception ex) { + logger.Warn(ex, $"Unable to traverse children of '{fileSystemInfo.FullName}'"); + } + + // finally, delete myself, we should try this even if deleting children failed + // because Directory.Delete can also be recursive + DeleteFsiVeryHard(fileSystemInfo, logger); + } + + private static void DeleteFsiVeryHard(FileSystemInfo fileSystemInfo, ILogger logger) + { + // don't try to delete the running process + if (PathUtil.FullPathEquals(fileSystemInfo.FullName, VelopackRuntimeInfo.EntryExePath)) + return; + + // try to remove "ReadOnly" attributes + try { fileSystemInfo.Attributes = FileAttributes.Normal; } catch { } + + try { fileSystemInfo.Refresh(); } catch { } + + // use this instead of fsi.Delete() because it is more resilient/aggressive + Action deleteMe = fileSystemInfo is DirectoryInfo + ? () => Directory.Delete(fileSystemInfo.FullName, true) + : () => File.Delete(fileSystemInfo.FullName); + + // retry a few times. if a directory in this tree is open in Windows Explorer, + // it might be locked for a little while WE cleans up handles + try { + Retry( + () => { + try { + deleteMe(); + } catch (DirectoryNotFoundException) { + return; // good! + } + }, + retries: 4, + retryDelay: 50); + } catch (Exception ex) { + logger?.Warn(ex, $"Unable to delete child '{fileSystemInfo.FullName}'"); + throw; + } + } + + public static void Retry(this Action block, int retries = 4, int retryDelay = 250, ILogger? logger = null) + { + Retry( + () => { + block(); + return true; + }, + retries, + retryDelay, + logger); + } + + public static T Retry(this Func block, int retries = 4, int retryDelay = 250, ILogger? logger = null) + { + while (true) { + try { + T ret = block(); + return ret; + } catch (Exception ex) { + if (retries == 0) throw; + logger?.Warn($"Operation failed ({ex.Message}). Retrying {retries} more times..."); + retries--; + Thread.Sleep(retryDelay); + } + } + } + + public static Task RetryAsync(this Func block, int retries = 4, int retryDelay = 250, ILogger? logger = null) + { + return RetryAsync( + async () => { + await block().ConfigureAwait(false); + return true; + }, + retries, + retryDelay, + logger); + } + + public static async Task RetryAsync(this Func> block, int retries = 4, int retryDelay = 250, ILogger? logger = null) + { + while (true) { + try { + return await block().ConfigureAwait(false); + } catch (Exception ex) { + if (retries == 0) throw; + logger?.Warn($"Operation failed ({ex.Message}). Retrying {retries} more times..."); + retries--; + await Task.Delay(retryDelay).ConfigureAwait(false); + } + } + } + } +} \ No newline at end of file diff --git a/src/lib-csharp/Internal/LoggerExtensions.cs b/src/lib-csharp/Util/LoggerExtensions.cs similarity index 99% rename from src/lib-csharp/Internal/LoggerExtensions.cs rename to src/lib-csharp/Util/LoggerExtensions.cs index 528e2c20..79ef65b5 100644 --- a/src/lib-csharp/Internal/LoggerExtensions.cs +++ b/src/lib-csharp/Util/LoggerExtensions.cs @@ -2,7 +2,7 @@ using System.Diagnostics.CodeAnalysis; using Microsoft.Extensions.Logging; -namespace Velopack +namespace Velopack.Util { [ExcludeFromCodeCoverage] internal static class LoggerExtensions diff --git a/src/lib-csharp/Util/PathUtil.cs b/src/lib-csharp/Util/PathUtil.cs new file mode 100644 index 00000000..d0627999 --- /dev/null +++ b/src/lib-csharp/Util/PathUtil.cs @@ -0,0 +1,154 @@ +using System; +using System.IO; +using System.Linq; +using System.Text; + +namespace Velopack.Util +{ + internal static class PathUtil + { + public static string MakePathRelativeTo(string relativeTo, string thePath) + { +#if NETFRAMEWORK || NETSTANDARD + relativeTo = Path.GetFullPath(relativeTo); + thePath = Path.GetFullPath(thePath); + return ToggleRelative(relativeTo, thePath); +#else + return Path.GetRelativePath(relativeTo, thePath); +#endif + } + + public static bool FullPathEquals(string path1, string path2) + { + return NormalizePath(path1).Equals(NormalizePath(path2), VelopackRuntimeInfo.PathStringComparison); + } + + public static bool PathPartEquals(string part1, string part2) + { + return part1.Equals(part2, VelopackRuntimeInfo.PathStringComparison); + } + + public static bool PathPartStartsWith(string part1, string startsWith) + { + return part1.StartsWith(startsWith, VelopackRuntimeInfo.PathStringComparison); + } + + public static bool PathPartEndsWith(string part1, string endsWith) + { + return part1.EndsWith(endsWith, VelopackRuntimeInfo.PathStringComparison); + } + + public static bool FileHasExtension(string filePath, string extension) + { + var ext = Path.GetExtension(filePath); + if (!extension.StartsWith(".")) extension = "." + extension; + return PathPartEquals(ext, extension); + } + + public static string NormalizePath(string path) + { + var fullPath = Path.GetFullPath(path); + var normalized = new Uri(fullPath, UriKind.Absolute).LocalPath; + return normalized.TrimEnd(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar); + } + + public static bool IsFileInDirectory(string file, string directory) + { + var normalizedDir = NormalizePath(directory) + Path.DirectorySeparatorChar; + var normalizedFile = NormalizePath(file); + return normalizedFile.StartsWith(normalizedDir, VelopackRuntimeInfo.PathStringComparison); + } + + /// + /// Escapes file name such that the file name is safe for writing to disk in the packages folder + /// + public static string GetSafeFilename(string fileName) + { + string safeFileName = Path.GetFileName(fileName); + char[] invalidFileNameChars = Path.GetInvalidFileNameChars(); + + if (safeFileName.IndexOfAny(invalidFileNameChars) != -1) { + StringBuilder safeName = new(); + foreach (char ch in safeFileName) { + if (Array.IndexOf(invalidFileNameChars, ch) == -1) + safeName.Append(ch); + else + safeName.Append('_'); + } + + safeFileName = safeName.ToString(); + } + + return safeFileName; + } + + readonly static string[] peExtensions = new[] { ".exe", ".dll", ".node" }; + + public static bool FileIsLikelyPEImage(string name) + { + var ext = Path.GetExtension(name); + return peExtensions.Any(x => ext.Equals(x, StringComparison.OrdinalIgnoreCase)); + } + + private static string ToggleRelative(string basePath, string toggledPath) + { + // from https://github.com/RT-Projects/RT.Util/blob/master/RT.Util.Core/Paths/PathUtil.cs#L297 + if (basePath.Length == 0) + throw new Exception("InvalidBasePath"); + if (toggledPath.Length == 0) + throw new Exception("InvalidToggledPath"); + if (!Path.IsPathRooted(basePath)) + throw new Exception("BasePathNotAbsolute"); + + try { basePath = Path.GetFullPath(basePath + "\\"); } catch { throw new Exception("InvalidBasePath"); } + + if (!Path.IsPathRooted(toggledPath)) { + try { + return StripTrailingSeparator(Path.GetFullPath(Path.Combine(basePath, toggledPath))); + } catch { + throw new Exception("InvalidToggledPath"); + } + } + + // Both basePath and toggledPath are absolute. Need to relativize toggledPath. + try { toggledPath = Path.GetFullPath(toggledPath + "\\"); } catch { throw new Exception("InvalidToggledPath"); } + + int prevPos = -1; + int pos = toggledPath.IndexOf(Path.DirectorySeparatorChar); + while (pos != -1 && pos < basePath.Length && + basePath.Substring(0, pos + 1).Equals(toggledPath.Substring(0, pos + 1), StringComparison.OrdinalIgnoreCase)) { + prevPos = pos; + pos = toggledPath.IndexOf(Path.DirectorySeparatorChar, pos + 1); + } + + if (prevPos == -1) + throw new Exception("PathsOnDifferentDrives"); + var piece = basePath.Substring(prevPos + 1); + var result = StripTrailingSeparator( + (".." + Path.DirectorySeparatorChar).Repeat(piece.Count(ch => ch == Path.DirectorySeparatorChar)) + + toggledPath.Substring(prevPos + 1)); + return result.Length == 0 ? "." : result; + } + + private static string Repeat(this string input, int numTimes) + { + if (numTimes == 0) return ""; + if (numTimes == 1) return input; + if (numTimes == 2) return input + input; + var sb = new StringBuilder(); + for (int i = 0; i < numTimes; i++) + sb.Append(input); + return sb.ToString(); + } + + private static string StripTrailingSeparator(string path) + { + if (path.Length < 1) + return path; + if (path[path.Length - 1] == '/' || path[path.Length - 1] == '\\') + return (path.Length == 3 && path[1] == ':') ? path : path.Substring(0, path.Length - 1); + else + return path; + } + } +} \ No newline at end of file diff --git a/src/lib-csharp/Internal/ProcessExtensions.cs b/src/lib-csharp/Util/ProcessExtensions.cs similarity index 90% rename from src/lib-csharp/Internal/ProcessExtensions.cs rename to src/lib-csharp/Util/ProcessExtensions.cs index 46b4e02c..2fff267a 100644 --- a/src/lib-csharp/Internal/ProcessExtensions.cs +++ b/src/lib-csharp/Util/ProcessExtensions.cs @@ -4,13 +4,37 @@ using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Text; +using System.Threading; +using System.Threading.Tasks; using Microsoft.Extensions.Logging; -namespace Velopack +namespace Velopack.Util { [ExcludeFromCodeCoverage] internal static class ProcessStartExtensions { + public static async Task GetExitCodeAsync(this Process p) + { +#if NET5_0_OR_GREATER + await p.WaitForExitAsync().ConfigureAwait(false); + return p.ExitCode; +#else + var tcs = new TaskCompletionSource(); + var thread = new Thread( + () => { + try { + p.WaitForExit(); + tcs.SetResult(p.ExitCode); + } catch (Exception ex) { + tcs.SetException(ex); + } + }); + thread.IsBackground = true; + thread.Start(); + await tcs.Task.ConfigureAwait(false); + return p.ExitCode; +#endif + } #if NET5_0_OR_GREATER public static void AppendArgumentListSafe(this ProcessStartInfo psi, IEnumerable args, out string debug) @@ -210,4 +234,4 @@ namespace Velopack private const char Quote = '\"'; private const char Backslash = '\\'; } -} +} \ No newline at end of file diff --git a/src/lib-csharp/Internal/SymbolicLink.cs b/src/lib-csharp/Util/SymbolicLink.cs similarity index 78% rename from src/lib-csharp/Internal/SymbolicLink.cs rename to src/lib-csharp/Util/SymbolicLink.cs index 4aa80d8a..4ad5cd8d 100644 --- a/src/lib-csharp/Internal/SymbolicLink.cs +++ b/src/lib-csharp/Util/SymbolicLink.cs @@ -1,12 +1,11 @@ using System; using System.ComponentModel; using System.IO; -using System.Linq; using System.Runtime.InteropServices; using System.Text; using Microsoft.Win32.SafeHandles; -namespace Velopack +namespace Velopack.Util { internal static class SymbolicLink { @@ -28,14 +27,14 @@ namespace Velopack if (Directory.Exists(linkPath) || File.Exists(linkPath)) { if (overwrite) { - Utility.DeleteFileOrDirectoryHard(linkPath); + IoUtil.DeleteFileOrDirectoryHard(linkPath); } else { throw new IOException("Junction / symlink path already exists and overwrite parameter is false."); } } var finalTarget = relative - ? GetRelativePath(Path.GetDirectoryName(linkPath)!, targetPath) + ? PathUtil.MakePathRelativeTo(Path.GetDirectoryName(linkPath)!, targetPath) : targetPath; if (Directory.Exists(targetPath)) { @@ -106,7 +105,7 @@ namespace Velopack var target = GetUnresolvedTarget(linkPath); if (relative) { if (Path.IsPathRooted(target)) { - return GetRelativePath(Path.GetDirectoryName(linkPath)!, target); + return PathUtil.MakePathRelativeTo(Path.GetDirectoryName(linkPath)!, target); } else { return target; } @@ -150,79 +149,8 @@ namespace Velopack return fsi != null && (fsi.Attributes & FileAttributes.ReparsePoint) != 0; } - private static string GetRelativePath(string relativeTo, string path) - { -#if NETFRAMEWORK || NETSTANDARD - relativeTo = Path.GetFullPath(relativeTo); - path = Path.GetFullPath(path); - return ToggleRelative(relativeTo, path); -#else - return Path.GetRelativePath(relativeTo, path); -#endif - } #if NETFRAMEWORK || NETSTANDARD - private static string ToggleRelative(string basePath, string toggledPath) - { - // from https://github.com/RT-Projects/RT.Util/blob/master/RT.Util.Core/Paths/PathUtil.cs#L297 - if (basePath.Length == 0) - throw new Exception("InvalidBasePath"); - if (toggledPath.Length == 0) - throw new Exception("InvalidToggledPath"); - if (!Path.IsPathRooted(basePath)) - throw new Exception("BasePathNotAbsolute"); - - try { basePath = Path.GetFullPath(basePath + "\\"); } catch { throw new Exception("InvalidBasePath"); } - - if (!Path.IsPathRooted(toggledPath)) { - try { - return StripTrailingSeparator(Path.GetFullPath(Path.Combine(basePath, toggledPath))); - } catch { - throw new Exception("InvalidToggledPath"); - } - } - - // Both basePath and toggledPath are absolute. Need to relativize toggledPath. - try { toggledPath = Path.GetFullPath(toggledPath + "\\"); } catch { throw new Exception("InvalidToggledPath"); } - - int prevPos = -1; - int pos = toggledPath.IndexOf(Path.DirectorySeparatorChar); - while (pos != -1 && pos < basePath.Length && - basePath.Substring(0, pos + 1).Equals(toggledPath.Substring(0, pos + 1), StringComparison.OrdinalIgnoreCase)) { - prevPos = pos; - pos = toggledPath.IndexOf(Path.DirectorySeparatorChar, pos + 1); - } - - if (prevPos == -1) - throw new Exception("PathsOnDifferentDrives"); - var piece = basePath.Substring(prevPos + 1); - var result = StripTrailingSeparator( - (".." + Path.DirectorySeparatorChar).Repeat(piece.Count(ch => ch == Path.DirectorySeparatorChar)) - + toggledPath.Substring(prevPos + 1)); - return result.Length == 0 ? "." : result; - } - - private static string Repeat(this string input, int numTimes) - { - if (numTimes == 0) return ""; - if (numTimes == 1) return input; - if (numTimes == 2) return input + input; - var sb = new StringBuilder(); - for (int i = 0; i < numTimes; i++) - sb.Append(input); - return sb.ToString(); - } - - private static string StripTrailingSeparator(string path) - { - if (path.Length < 1) - return path; - if (path[path.Length - 1] == '/' || path[path.Length - 1] == '\\') - return (path.Length == 3 && path[1] == ':') ? path : path.Substring(0, path.Length - 1); - else - return path; - } - [Flags] private enum EFileAttributes : uint { diff --git a/src/lib-csharp/Util/TempUtil.cs b/src/lib-csharp/Util/TempUtil.cs new file mode 100644 index 00000000..307f123d --- /dev/null +++ b/src/lib-csharp/Util/TempUtil.cs @@ -0,0 +1,83 @@ +using System; +using System.IO; + +namespace Velopack.Util +{ + internal static class TempUtil + { + public static string GetDefaultTempBaseDirectory() + { + string tempDir; + + if (VelopackRuntimeInfo.IsOSX || VelopackRuntimeInfo.IsLinux) { + tempDir = "/tmp/velopack"; + } else if (VelopackRuntimeInfo.IsWindows) { + tempDir = Path.Combine(Path.GetTempPath(), "Velopack"); + } else { + throw new PlatformNotSupportedException(); + } + + if (Environment.GetEnvironmentVariable("VELOPACK_TEMP") is var squirrlTmp + && !string.IsNullOrWhiteSpace(squirrlTmp)) + tempDir = squirrlTmp; + + var di = new DirectoryInfo(tempDir); + if (!di.Exists) di.Create(); + + return di.FullName; + } + + private static string GetNextTempName(string tempDir) + { + for (int i = 1; i < 1000; i++) { + string name = "temp." + i; + var target = Path.Combine(tempDir, name); + + FileSystemInfo? info = null; + if (Directory.Exists(target)) info = new DirectoryInfo(target); + else if (File.Exists(target)) info = new FileInfo(target); + + // this dir/file does not exist, lets use it. + if (info == null) { + return target; + } + + // this dir/file exists, but it is old, let's re-use it. + // this shouldn't generally happen, but crashes do exist. + if (DateTime.UtcNow - info.LastWriteTimeUtc > TimeSpan.FromDays(1)) { + if (IoUtil.DeleteFileOrDirectoryHard(target, false, true)) { + // the dir/file was deleted successfully. + return target; + } + } + } + + throw new Exception( + "Unable to find free temp path. Has the temp directory exceeded it's maximum number of items? (1000)"); + } + + public static IDisposable GetTempDirectory(out string newTempDirectory) + { + return GetTempDirectory(out newTempDirectory, GetDefaultTempBaseDirectory()); + } + + public static IDisposable GetTempDirectory(out string newTempDirectory, string rootTempDir) + { + var disp = GetTempFileName(out newTempDirectory, rootTempDir); + Directory.CreateDirectory(newTempDirectory); + return disp; + } + + public static IDisposable GetTempFileName(out string newTempFile) + { + return GetTempFileName(out newTempFile, GetDefaultTempBaseDirectory()); + } + + public static IDisposable GetTempFileName(out string newTempFile, string rootTempDir) + { + var path = GetNextTempName(rootTempDir); + newTempFile = path; + return Disposable.Create(() => IoUtil.DeleteFileOrDirectoryHard(path, throwOnFailure: false)); + } + } +} \ No newline at end of file diff --git a/src/lib-csharp/VelopackApp.cs b/src/lib-csharp/VelopackApp.cs index deccd042..228c45bf 100644 --- a/src/lib-csharp/VelopackApp.cs +++ b/src/lib-csharp/VelopackApp.cs @@ -7,6 +7,7 @@ using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging.Abstractions; using NuGet.Versioning; using Velopack.Locators; +using Velopack.Util; namespace Velopack { @@ -170,7 +171,7 @@ namespace Velopack log.Info("Starting Velopack App (Run)."); if (VelopackRuntimeInfo.IsWindows && locator.AppId != null) { - var appUserModelId = Utility.GetAppUserModelId(locator.AppId); + var appUserModelId = CoreUtil.GetAppUserModelId(locator.AppId); log.Info($"Setting current process explicit AppUserModelID to '{appUserModelId}'"); SetCurrentProcessExplicitAppUserModelID(appUserModelId); } diff --git a/src/lib-csharp/VelopackAsset.cs b/src/lib-csharp/VelopackAsset.cs index f2db03a9..51f7a0c5 100644 --- a/src/lib-csharp/VelopackAsset.cs +++ b/src/lib-csharp/VelopackAsset.cs @@ -2,7 +2,7 @@ using System; using System.IO; using NuGet.Versioning; -using Velopack.Json; +using Velopack.Util; using Velopack.NuGet; namespace Velopack @@ -81,8 +81,8 @@ namespace Velopack NotesMarkdown = zip.ReleaseNotes, NotesHTML = zip.ReleaseNotesHtml, Size = new FileInfo(filePath).Length, - SHA1 = Utility.CalculateFileSHA1(filePath), - SHA256 = Utility.CalculateFileSHA256(filePath), + SHA1 = IoUtil.CalculateFileSHA1(filePath), + SHA256 = IoUtil.CalculateFileSHA256(filePath), FileName = Path.GetFileName(filePath), Type = IsDeltaFile(filePath) ? VelopackAssetType.Delta : VelopackAssetType.Full, }; diff --git a/src/lib-csharp/VelopackRuntimeInfo.cs b/src/lib-csharp/VelopackRuntimeInfo.cs index dfd64d8f..1054ce13 100644 --- a/src/lib-csharp/VelopackRuntimeInfo.cs +++ b/src/lib-csharp/VelopackRuntimeInfo.cs @@ -5,6 +5,7 @@ using System.Linq; using System.Runtime.InteropServices; using System.Runtime.Versioning; using NuGet.Versioning; +using Velopack.Util; #if !NETFRAMEWORK using InteropArchitecture = System.Runtime.InteropServices.Architecture; @@ -221,7 +222,7 @@ namespace Velopack try { if (IsWow64Process2(GetCurrentProcess(), out var _, out var nativeMachine)) { - if (Utility.TryParseEnumU16(nativeMachine, out var val)) { + if (CoreUtil.TryParseEnumU16(nativeMachine, out var val)) { SystemArch = val; } } diff --git a/src/lib-csharp/Windows/RuntimeInfo.cs b/src/lib-csharp/Windows/RuntimeInfo.cs index a1e3a5b8..6b9a8dac 100644 --- a/src/lib-csharp/Windows/RuntimeInfo.cs +++ b/src/lib-csharp/Windows/RuntimeInfo.cs @@ -11,6 +11,7 @@ using Microsoft.Extensions.Logging; using Microsoft.Win32; using NuGet.Versioning; using Velopack.Sources; +using Velopack.Util; namespace Velopack.Windows { @@ -77,7 +78,7 @@ namespace Velopack.Windows { var url = await GetDownloadUrl().ConfigureAwait(false); log?.Info($"Downloading {Id} from {url} to {localPath}"); - downloader = downloader ?? Utility.CreateDefaultDownloader(); + downloader = downloader ?? HttpUtil.CreateDefaultDownloader(); await downloader.DownloadFile(url, localPath, progress).ConfigureAwait(false); } @@ -319,7 +320,7 @@ namespace Velopack.Windows var archValid = Enum.TryParse(String.IsNullOrWhiteSpace(archstr) ? "x64" : archstr, true, out var cpu); if (!archValid) { throw new ArgumentException($"Invalid machine architecture '{archstr}'. " + - $"Valid values: {String.Join(", ", Utility.GetEnumValues())}"); + $"Valid values: {String.Join(", ", CoreUtil.GetEnumValues())}"); } var type = DotnetRuntimeType.WindowsDesktop; @@ -404,7 +405,7 @@ namespace Velopack.Windows _ => throw new NotImplementedException(), }; - downloader = downloader ?? Utility.CreateDefaultDownloader(); + downloader = downloader ?? HttpUtil.CreateDefaultDownloader(); try { return await downloader.DownloadString($"{UncachedDotNetFeed}/{runtime}/{channel}/latest.version").ConfigureAwait(false); diff --git a/src/lib-csharp/Windows/Shortcuts.cs b/src/lib-csharp/Windows/Shortcuts.cs index 6abe80df..3ed25606 100644 --- a/src/lib-csharp/Windows/Shortcuts.cs +++ b/src/lib-csharp/Windows/Shortcuts.cs @@ -9,6 +9,7 @@ using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging.Abstractions; using Velopack.Locators; using Velopack.NuGet; +using Velopack.Util; namespace Velopack.Windows { @@ -165,7 +166,7 @@ namespace Velopack.Windows Log.Info($"Creating shortcut for {relativeExeName} => {file}"); ShellLink sl; - Utility.Retry(() => { + IoUtil.Retry(() => { File.Delete(file); var target = Path.Combine(currentDir, relativeExeName); @@ -280,7 +281,7 @@ namespace Velopack.Windows private ShortcutLocation[] GetLocations(ShortcutLocation flag) { - var locations = Utility.GetEnumValues(); + var locations = CoreUtil.GetEnumValues(); return locations .Where(x => x != ShortcutLocation.None) .Where(x => flag.HasFlag(x)) diff --git a/src/vpk/Velopack.Deployment/AzureRepository.cs b/src/vpk/Velopack.Deployment/AzureRepository.cs index dfef0a27..cfbcb5e1 100644 --- a/src/vpk/Velopack.Deployment/AzureRepository.cs +++ b/src/vpk/Velopack.Deployment/AzureRepository.cs @@ -3,6 +3,7 @@ using Azure.Storage; using Azure.Storage.Blobs; using Azure.Storage.Blobs.Models; using Microsoft.Extensions.Logging; +using Velopack.Util; namespace Velopack.Deployment; diff --git a/src/vpk/Velopack.Deployment/GitHubRepository.cs b/src/vpk/Velopack.Deployment/GitHubRepository.cs index 096f80fc..c58d989f 100644 --- a/src/vpk/Velopack.Deployment/GitHubRepository.cs +++ b/src/vpk/Velopack.Deployment/GitHubRepository.cs @@ -5,6 +5,7 @@ using Velopack.NuGet; using Velopack.Packaging; using Velopack.Packaging.Exceptions; using Velopack.Sources; +using Velopack.Util; namespace Velopack.Deployment; @@ -103,7 +104,7 @@ public class GitHubRepository(ILogger logger) : SourceRepository a.Name == releasesFileName); if (releaseAsset != null) { throw new UserInfoException($"There is already a remote asset named '{releasesFileName}', and merging release files on GitHub is not supported."); diff --git a/src/vpk/Velopack.Deployment/GiteaRepository.cs b/src/vpk/Velopack.Deployment/GiteaRepository.cs index cde3a812..4b17ac1d 100644 --- a/src/vpk/Velopack.Deployment/GiteaRepository.cs +++ b/src/vpk/Velopack.Deployment/GiteaRepository.cs @@ -8,6 +8,7 @@ using Velopack.NuGet; using Velopack.Packaging; using Velopack.Packaging.Exceptions; using Velopack.Sources; +using Velopack.Util; namespace Velopack.Deployment; @@ -132,7 +133,7 @@ public class GiteaRepository : SourceRepository a.Name == releasesFileName); if (releaseAsset != null) { throw new UserInfoException($"There is already a remote asset named '{releasesFileName}', and merging release files on Gitea is not supported."); diff --git a/src/vpk/Velopack.Deployment/LocalRepository.cs b/src/vpk/Velopack.Deployment/LocalRepository.cs index 20c46dae..8289ced3 100644 --- a/src/vpk/Velopack.Deployment/LocalRepository.cs +++ b/src/vpk/Velopack.Deployment/LocalRepository.cs @@ -1,6 +1,7 @@ using Microsoft.Extensions.Logging; using Velopack.Packaging; using Velopack.Sources; +using Velopack.Util; namespace Velopack.Deployment; @@ -26,7 +27,7 @@ public class LocalRepository(ILogger logger) : ObjectRepository : DownRepository GetReleasesAsync(TDown options) { - var releasesName = Utility.GetVeloReleaseIndexName(options.Channel); + var releasesName = CoreUtil.GetVeloReleaseIndexName(options.Channel); var client = CreateClient(options); var bytes = await GetObjectBytes(client, releasesName); if (bytes == null || bytes.Length == 0) { @@ -87,15 +88,15 @@ public abstract class ObjectRepository : DownRepository : IRepositoryCanDownload Log.Warn($"File '{path}' already exists on disk. Verifying checksum..."); bool hashMatch = (latest.SHA256 != null) - ? latest.SHA256 == Utility.CalculateFileSHA256(path) - : latest.SHA1 == Utility.CalculateFileSHA1(path); + ? latest.SHA256 == IoUtil.CalculateFileSHA256(path) + : latest.SHA1 == IoUtil.CalculateFileSHA1(path); if (hashMatch) { Log.Info("Checksum matches. Finished."); @@ -99,12 +100,12 @@ public abstract class DownRepository : IRepositoryCanDownload Log.Info("Verifying checksum..."); string newHash; if (!string.IsNullOrEmpty(latest.SHA256)) { - if (latest.SHA256 != (newHash = Utility.CalculateFileSHA256(incomplete))) { + if (latest.SHA256 != (newHash = IoUtil.CalculateFileSHA256(incomplete))) { Log.Error($"Checksum mismatch, expected {latest.SHA256}, got {newHash}"); return; } } - else if (latest.SHA1 != (newHash = Utility.CalculateFileSHA1(incomplete))) { + else if (latest.SHA1 != (newHash = IoUtil.CalculateFileSHA1(incomplete))) { Log.Error($"Checksum mismatch, expected {latest.SHA1}, got {newHash}"); return; } diff --git a/src/vpk/Velopack.Packaging.Unix/AppImageTool.cs b/src/vpk/Velopack.Packaging.Unix/AppImageTool.cs index add13364..73bed17b 100644 --- a/src/vpk/Velopack.Packaging.Unix/AppImageTool.cs +++ b/src/vpk/Velopack.Packaging.Unix/AppImageTool.cs @@ -1,6 +1,7 @@ -using ICSharpCode.SharpZipLib.Tar; +using ICSharpCode.SharpZipLib.Tar; using Microsoft.Extensions.Logging; using Velopack.Compression; +using Velopack.Util; namespace Velopack.Packaging.Unix; @@ -81,8 +82,8 @@ public class AppImageTool Chmod.ChmodFileAsExecutable(outputFile); } finally { - Utility.DeleteFileOrDirectoryHard(tmpSquashFile); - Utility.DeleteFileOrDirectoryHard(tmpTarFile); + IoUtil.DeleteFileOrDirectoryHard(tmpSquashFile); + IoUtil.DeleteFileOrDirectoryHard(tmpTarFile); } } } diff --git a/src/vpk/Velopack.Packaging.Unix/Commands/LinuxPackCommandRunner.cs b/src/vpk/Velopack.Packaging.Unix/Commands/LinuxPackCommandRunner.cs index 5ee9c1d0..1b3dc369 100644 --- a/src/vpk/Velopack.Packaging.Unix/Commands/LinuxPackCommandRunner.cs +++ b/src/vpk/Velopack.Packaging.Unix/Commands/LinuxPackCommandRunner.cs @@ -1,6 +1,7 @@ using ELFSharp.ELF; using Microsoft.Extensions.Logging; using Velopack.Packaging.Abstractions; +using Velopack.Util; namespace Velopack.Packaging.Unix.Commands; diff --git a/src/vpk/Velopack.Packaging.Unix/Commands/OsxBundleCommandRunner.cs b/src/vpk/Velopack.Packaging.Unix/Commands/OsxBundleCommandRunner.cs index 3fba6abc..96aa350a 100644 --- a/src/vpk/Velopack.Packaging.Unix/Commands/OsxBundleCommandRunner.cs +++ b/src/vpk/Velopack.Packaging.Unix/Commands/OsxBundleCommandRunner.cs @@ -4,6 +4,7 @@ using Microsoft.Extensions.Logging; using NuGet.Versioning; using Velopack.Packaging.Abstractions; using Velopack.Packaging.Exceptions; +using Velopack.Util; namespace Velopack.Packaging.Unix.Commands; @@ -69,7 +70,7 @@ public class OsxBundleCommandRunner : ICommand var builder = new OsxStructureBuilder(packId, releaseDir.FullName); if (Directory.Exists(builder.AppDirectory)) { _logger.Warn(builder.AppDirectory + " already exists, deleting..."); - Utility.DeleteFileOrDirectoryHard(builder.AppDirectory); + IoUtil.DeleteFileOrDirectoryHard(builder.AppDirectory); } builder.Build(); diff --git a/src/vpk/Velopack.Packaging.Unix/Commands/OsxPackCommandRunner.cs b/src/vpk/Velopack.Packaging.Unix/Commands/OsxPackCommandRunner.cs index 57b36c8f..543f28ad 100644 --- a/src/vpk/Velopack.Packaging.Unix/Commands/OsxPackCommandRunner.cs +++ b/src/vpk/Velopack.Packaging.Unix/Commands/OsxPackCommandRunner.cs @@ -1,6 +1,7 @@ using System.Runtime.Versioning; using Microsoft.Extensions.Logging; using Velopack.Packaging.Abstractions; +using Velopack.Util; namespace Velopack.Packaging.Unix.Commands; @@ -27,7 +28,7 @@ public class OsxPackCommandRunner : PackageBuilder if (deleteAppBundle) { Log.Debug("Removing temporary .app bundle."); - Utility.DeleteFileOrDirectoryHard(appBundlePath); + IoUtil.DeleteFileOrDirectoryHard(appBundlePath); } var structure = new OsxStructureBuilder(dir.FullName); @@ -98,7 +99,7 @@ public class OsxPackCommandRunner : PackageBuilder var packId = Options.PackId; if (!string.IsNullOrEmpty(Options.SignInstallIdentity) && !string.IsNullOrEmpty(Options.NotaryProfile)) { - helper.CreateInstallerPkg(packDir, packTitle, packId, pkgContent, pkgPath, Options.SignInstallIdentity, Utility.CreateProgressDelegate(progress, 0, 60)); + helper.CreateInstallerPkg(packDir, packTitle, packId, pkgContent, pkgPath, Options.SignInstallIdentity, CoreUtil.CreateProgressDelegate(progress, 0, 60)); progress(-1); // indeterminate helper.Notarize(pkgPath, Options.NotaryProfile, Options.Keychain); progress(80); diff --git a/src/vpk/Velopack.Packaging.Unix/OsxBuildTools.cs b/src/vpk/Velopack.Packaging.Unix/OsxBuildTools.cs index ba7247bd..a115c420 100644 --- a/src/vpk/Velopack.Packaging.Unix/OsxBuildTools.cs +++ b/src/vpk/Velopack.Packaging.Unix/OsxBuildTools.cs @@ -1,7 +1,7 @@ using System.Runtime.Versioning; using System.Security; using Microsoft.Extensions.Logging; -using Velopack.Json; +using Velopack.Util; namespace Velopack.Packaging.Unix; @@ -97,11 +97,11 @@ public class OsxBuildTools if (File.Exists(pkgOutputPath)) File.Delete(pkgOutputPath); - using var _1 = Utility.GetTempDirectory(out var tmp); - using var _2 = Utility.GetTempDirectory(out var tmpPayload1); - using var _3 = Utility.GetTempDirectory(out var tmpPayload2); - using var _4 = Utility.GetTempDirectory(out var tmpScripts); - using var _5 = Utility.GetTempDirectory(out var tmpResources); + using var _1 = TempUtil.GetTempDirectory(out var tmp); + using var _2 = TempUtil.GetTempDirectory(out var tmpPayload1); + using var _3 = TempUtil.GetTempDirectory(out var tmpPayload2); + using var _4 = TempUtil.GetTempDirectory(out var tmpScripts); + using var _5 = TempUtil.GetTempDirectory(out var tmpResources); // copy .app to tmp folder var bundleName = Path.GetFileName(appBundlePath); diff --git a/src/vpk/Velopack.Packaging.Unix/PlistWriter.cs b/src/vpk/Velopack.Packaging.Unix/PlistWriter.cs index 32d34eb3..e3b5f4ef 100644 --- a/src/vpk/Velopack.Packaging.Unix/PlistWriter.cs +++ b/src/vpk/Velopack.Packaging.Unix/PlistWriter.cs @@ -1,6 +1,7 @@ // https://raw.githubusercontent.com/egramtel/dotnet-bundle/master/DotNet.Bundle/PlistWriter.cs using System.Xml; using Microsoft.Extensions.Logging; +using Velopack.Util; namespace Velopack.Packaging.Unix; diff --git a/src/vpk/Velopack.Packaging.Windows/CodeSign.cs b/src/vpk/Velopack.Packaging.Windows/CodeSign.cs index 515c8b2d..09edff24 100644 --- a/src/vpk/Velopack.Packaging.Windows/CodeSign.cs +++ b/src/vpk/Velopack.Packaging.Windows/CodeSign.cs @@ -2,6 +2,7 @@ using System.Text.RegularExpressions; using Microsoft.Extensions.Logging; using Velopack.Packaging.Exceptions; +using Velopack.Util; namespace Velopack.Packaging.Windows; @@ -45,13 +46,13 @@ public class CodeSign if (String.IsNullOrEmpty(rootDir)) { pendingSign.Enqueue(f); } else { - var partialPath = Utility.NormalizePath(f).Substring(Utility.NormalizePath(rootDir).Length).Trim('/', '\\'); + var partialPath = PathUtil.NormalizePath(f).Substring(PathUtil.NormalizePath(rootDir).Length).Trim('/', '\\'); pendingSign.Enqueue(partialPath); } } } - using var _1 = Utility.GetTempFileName(out var signLogFile); + using var _1 = TempUtil.GetTempFileName(out var signLogFile); var totalToSign = pendingSign.Count; if (signAsTemplate) { diff --git a/src/vpk/Velopack.Packaging.Windows/Commands/WindowsPackCommandRunner.cs b/src/vpk/Velopack.Packaging.Windows/Commands/WindowsPackCommandRunner.cs index 927a9466..142cd7d8 100644 --- a/src/vpk/Velopack.Packaging.Windows/Commands/WindowsPackCommandRunner.cs +++ b/src/vpk/Velopack.Packaging.Windows/Commands/WindowsPackCommandRunner.cs @@ -3,6 +3,7 @@ using Velopack.Compression; using Velopack.NuGet; using Velopack.Packaging.Abstractions; using Velopack.Packaging.Exceptions; +using Velopack.Util; using Velopack.Windows; namespace Velopack.Packaging.Windows.Commands; @@ -17,7 +18,7 @@ public class WindowsPackCommandRunner : PackageBuilder protected override Task CodeSign(Action progress, string packDir) { var filesToSign = new DirectoryInfo(packDir).GetAllFilesRecursively() - .Where(x => Options.SignSkipDll ? Utility.PathPartEndsWith(x.Name, ".exe") : Utility.FileIsLikelyPEImage(x.Name)) + .Where(x => Options.SignSkipDll ? PathUtil.PathPartEndsWith(x.Name, ".exe") : PathUtil.FileIsLikelyPEImage(x.Name)) .Select(x => x.FullName) .ToArray(); @@ -37,7 +38,7 @@ public class WindowsPackCommandRunner : PackageBuilder // add nuspec metadata ExtraNuspecMetadata["runtimeDependencies"] = GetRuntimeDependencies(); ExtraNuspecMetadata["shortcutLocations"] = GetShortcutLocations(); - ExtraNuspecMetadata["shortcutAmuid"] = Utility.GetAppUserModelId(Options.PackId); + ExtraNuspecMetadata["shortcutAmuid"] = CoreUtil.GetAppUserModelId(Options.PackId); // copy files to temp dir, so we can modify them var dir = TempDir.CreateSubdirectory("PreprocessPackDirWin"); @@ -173,7 +174,7 @@ public class WindowsPackCommandRunner : PackageBuilder protected override Task CreateSetupPackage(Action progress, string releasePkg, string packDir, string targetSetupExe) { var bundledZip = new ZipPackage(releasePkg); - Utility.Retry(() => File.Copy(HelperFile.SetupPath, targetSetupExe, true)); + IoUtil.Retry(() => File.Copy(HelperFile.SetupPath, targetSetupExe, true)); progress(10); var editor = new ResourceEdit(targetSetupExe, Log); @@ -189,7 +190,7 @@ public class WindowsPackCommandRunner : PackageBuilder progress(50); Log.Debug("Signing Setup bundle"); var targetDir = Path.GetDirectoryName(targetSetupExe); - SignFilesImpl(Options, targetDir, Utility.CreateProgressDelegate(progress, 50, 100), targetSetupExe); + SignFilesImpl(Options, targetDir, CoreUtil.CreateProgressDelegate(progress, 50, 100), targetSetupExe); Log.Debug($"Setup bundle created '{Path.GetFileName(targetSetupExe)}'."); progress(100); return Task.CompletedTask; @@ -201,7 +202,7 @@ public class WindowsPackCommandRunner : PackageBuilder File.Copy(Path.Combine(packDir, "Squirrel.exe"), Path.Combine(dir.FullName, "Update.exe"), true); var current = dir.CreateSubdirectory("current"); - CopyFiles(new DirectoryInfo(packDir), current, Utility.CreateProgressDelegate(progress, 0, 30)); + CopyFiles(new DirectoryInfo(packDir), current, CoreUtil.CreateProgressDelegate(progress, 0, 30)); File.Delete(Path.Combine(current.FullName, "Squirrel.exe")); @@ -214,7 +215,7 @@ public class WindowsPackCommandRunner : PackageBuilder // create a .portable file to indicate this is a portable package File.Create(Path.Combine(dir.FullName, ".portable")).Close(); - await EasyZip.CreateZipFromDirectoryAsync(Log, outputPath, dir.FullName, Utility.CreateProgressDelegate(progress, 40, 100)); + await EasyZip.CreateZipFromDirectoryAsync(Log, outputPath, dir.FullName, CoreUtil.CreateProgressDelegate(progress, 40, 100)); progress(100); } @@ -233,7 +234,7 @@ public class WindowsPackCommandRunner : PackageBuilder } try { - Utility.Retry(() => File.Copy(HelperFile.StubExecutablePath, targetStubPath, true)); + IoUtil.Retry(() => File.Copy(HelperFile.StubExecutablePath, targetStubPath, true)); var edit = new ResourceEdit(targetStubPath, Log); edit.CopyResourcesFrom(exeToCopy); edit.Commit(); diff --git a/src/vpk/Velopack.Packaging.Windows/CompatUtil.cs b/src/vpk/Velopack.Packaging.Windows/CompatUtil.cs index f2365ae1..585a1ca0 100644 --- a/src/vpk/Velopack.Packaging.Windows/CompatUtil.cs +++ b/src/vpk/Velopack.Packaging.Windows/CompatUtil.cs @@ -8,6 +8,7 @@ using Microsoft.Extensions.Logging; using NuGet.Versioning; using Velopack.Packaging.Abstractions; using Velopack.Packaging.Exceptions; +using Velopack.Util; namespace Velopack.Packaging.Windows; diff --git a/src/vpk/Velopack.Packaging.Windows/ResourceEdit.cs b/src/vpk/Velopack.Packaging.Windows/ResourceEdit.cs index b5fb1be0..9f5eda44 100644 --- a/src/vpk/Velopack.Packaging.Windows/ResourceEdit.cs +++ b/src/vpk/Velopack.Packaging.Windows/ResourceEdit.cs @@ -7,6 +7,7 @@ using AsmResolver.PE.Win32Resources.Icon; using AsmResolver.PE.Win32Resources.Version; using Microsoft.Extensions.Logging; using Velopack.NuGet; +using Velopack.Util; namespace Velopack.Packaging.Windows; @@ -181,7 +182,7 @@ public class ResourceEdit _file.OptionalHeader.SetDataDirectory(DataDirectoryIndex.ResourceDirectory, new DataDirectory(resourceBuffer.Rva, resourceBuffer.GetPhysicalSize())); - Utility.Retry(() => { + IoUtil.Retry(() => { using var fs = File.Create(_exePath); _file.Write(fs); }); diff --git a/src/vpk/Velopack.Packaging.Windows/SetupBundle.cs b/src/vpk/Velopack.Packaging.Windows/SetupBundle.cs index 015f02ee..d32f4fef 100644 --- a/src/vpk/Velopack.Packaging.Windows/SetupBundle.cs +++ b/src/vpk/Velopack.Packaging.Windows/SetupBundle.cs @@ -1,4 +1,5 @@ using System.IO.MemoryMappedFiles; +using Velopack.Util; namespace Velopack.Packaging.Windows; @@ -31,7 +32,7 @@ public static class SetupBundle length = accessor.ReadInt64(position - 8); } - Utility.Retry(FindBundleHeader); + IoUtil.Retry(FindBundleHeader); bundleOffset = offset; bundleLength = length; @@ -45,8 +46,8 @@ public static class SetupBundle Stream pkgStream = null, setupStream = null; try { - pkgStream = Utility.Retry(() => File.OpenRead(packagePath), retries: 10); - setupStream = Utility.Retry(() => File.Open(setupPath, FileMode.Append, FileAccess.Write), retries: 10); + pkgStream = IoUtil.Retry(() => File.OpenRead(packagePath), retries: 10); + setupStream = IoUtil.Retry(() => File.Open(setupPath, FileMode.Append, FileAccess.Write), retries: 10); bundleOffset = setupStream.Position; bundleLength = pkgStream.Length; pkgStream.CopyTo(setupStream); diff --git a/src/vpk/Velopack.Packaging/BuildAssets.cs b/src/vpk/Velopack.Packaging/BuildAssets.cs index 0bb8470b..97e0d280 100644 --- a/src/vpk/Velopack.Packaging/BuildAssets.cs +++ b/src/vpk/Velopack.Packaging/BuildAssets.cs @@ -1,5 +1,4 @@ -using Velopack.Json; -using Velopack.Packaging.Exceptions; +using Velopack.Packaging.Exceptions; namespace Velopack.Packaging; diff --git a/src/vpk/Velopack.Packaging/Commands/DeltaPatchCommandRunner.cs b/src/vpk/Velopack.Packaging/Commands/DeltaPatchCommandRunner.cs index 5dc98037..d24e7f2d 100644 --- a/src/vpk/Velopack.Packaging/Commands/DeltaPatchCommandRunner.cs +++ b/src/vpk/Velopack.Packaging/Commands/DeltaPatchCommandRunner.cs @@ -2,6 +2,7 @@ using Velopack.Compression; using Velopack.Packaging.Exceptions; using Velopack.Packaging.Abstractions; +using Velopack.Util; namespace Velopack.Packaging.Commands; @@ -28,8 +29,8 @@ public class DeltaPatchCommandRunner : ICommand } } - var tmp = Utility.GetDefaultTempBaseDirectory(); - using var _1 = Utility.GetTempDirectory(out var workDir); + var tmp = TempUtil.GetDefaultTempBaseDirectory(); + using var _1 = TempUtil.GetTempDirectory(out var workDir); var delta = new DeltaEmbedded(HelperFile.GetZstdPath(), _logger, tmp); EasyZip.ExtractZipToDirectory(_logger, options.BasePackage, workDir); diff --git a/src/vpk/Velopack.Packaging/DeltaPackageBuilder.cs b/src/vpk/Velopack.Packaging/DeltaPackageBuilder.cs index 04f223f6..d15a0b64 100644 --- a/src/vpk/Velopack.Packaging/DeltaPackageBuilder.cs +++ b/src/vpk/Velopack.Packaging/DeltaPackageBuilder.cs @@ -3,6 +3,7 @@ using System.Text; using Microsoft.Extensions.Logging; using Velopack.Compression; using Velopack.Packaging.Exceptions; +using Velopack.Util; namespace Velopack.Packaging; @@ -61,8 +62,8 @@ public class DeltaPackageBuilder int fNew = 0, fSame = 0, fChanged = 0, fWarnings = 0, fProcessed = 0, fRemoved = 0; - using (Utility.GetTempDirectory(out var baseTempPath)) - using (Utility.GetTempDirectory(out var tempPath)) { + using (TempUtil.GetTempDirectory(out var baseTempPath)) + using (TempUtil.GetTempDirectory(out var tempPath)) { var baseTempInfo = new DirectoryInfo(baseTempPath); var tempInfo = new DirectoryInfo(tempPath); @@ -139,12 +140,12 @@ public class DeltaPackageBuilder targetFile.Delete(); baseLibFiles.Remove(relativePath); var p = Interlocked.Increment(ref fProcessed); - progress(Utility.CalculateProgress((int) ((double) p / numNewFiles * 100), 0, 70)); + progress(CoreUtil.CalculateProgress((int) ((double) p / numNewFiles * 100), 0, 70)); } catch (Exception ex) { _logger.Debug(ex, String.Format("Failed to create a delta for {0}", targetFile.Name)); - Utility.DeleteFileOrDirectoryHard(targetFile.FullName + ".bsdiff", throwOnFailure: false); - Utility.DeleteFileOrDirectoryHard(targetFile.FullName + ".diff", throwOnFailure: false); - Utility.DeleteFileOrDirectoryHard(targetFile.FullName + ".shasum", throwOnFailure: false); + IoUtil.DeleteFileOrDirectoryHard(targetFile.FullName + ".bsdiff", throwOnFailure: false); + IoUtil.DeleteFileOrDirectoryHard(targetFile.FullName + ".diff", throwOnFailure: false); + IoUtil.DeleteFileOrDirectoryHard(targetFile.FullName + ".shasum", throwOnFailure: false); Interlocked.Increment(ref fWarnings); throw; } @@ -179,7 +180,7 @@ public class DeltaPackageBuilder throw new UserInfoException("Delta creation failed for one or more files. See log for details. To skip delta generation, use the '--delta none' argument."); } - EasyZip.CreateZipFromDirectoryAsync(_logger, outputFile, tempInfo.FullName, Utility.CreateProgressDelegate(progress, 70, 100)).GetAwaiterResult(); + EasyZip.CreateZipFromDirectoryAsync(_logger, outputFile, tempInfo.FullName, CoreUtil.CreateProgressDelegate(progress, 70, 100)).GetAwaiterResult(); progress(100); fRemoved = baseLibFiles.Count; diff --git a/src/vpk/Velopack.Packaging/Exe.cs b/src/vpk/Velopack.Packaging/Exe.cs index bb89a95b..a4cc6cfb 100644 --- a/src/vpk/Velopack.Packaging/Exe.cs +++ b/src/vpk/Velopack.Packaging/Exe.cs @@ -1,6 +1,7 @@ using System.Diagnostics; using System.Text; using Velopack.Packaging.Exceptions; +using Velopack.Util; namespace Velopack.Packaging; @@ -38,7 +39,7 @@ public static class Exe public static string RunHostedCommand(string command, string workDir = null) { - using var _1 = Utility.GetTempFileName(out var outputFile); + using var _1 = TempUtil.GetTempFileName(out var outputFile); File.Create(outputFile).Close(); var fileName = "cmd.exe"; @@ -64,7 +65,7 @@ public static class Exe process.WaitForExit(); - var stdout = Utility.Retry(() => File.ReadAllText(outputFile).Trim(), 10, 1000); + var stdout = IoUtil.Retry(() => File.ReadAllText(outputFile).Trim(), 10, 1000); var result = (process.ExitCode, stdout, "", command); ProcessFailedException.ThrowIfNonZero(result); return result.Item2; @@ -72,7 +73,7 @@ public static class Exe public static void RunHostedCommandNoWait(string command, string workDir = null) { - using var _1 = Utility.GetTempFileName(out var outputFile); + using var _1 = TempUtil.GetTempFileName(out var outputFile); File.Create(outputFile).Close(); var fileName = "cmd.exe"; diff --git a/src/vpk/Velopack.Packaging/PackageBuilder.cs b/src/vpk/Velopack.Packaging/PackageBuilder.cs index d30fdb6c..758a8f9f 100644 --- a/src/vpk/Velopack.Packaging/PackageBuilder.cs +++ b/src/vpk/Velopack.Packaging/PackageBuilder.cs @@ -8,6 +8,7 @@ using Velopack.Compression; using Velopack.NuGet; using Velopack.Packaging.Abstractions; using Velopack.Packaging.Exceptions; +using Velopack.Util; namespace Velopack.Packaging; @@ -90,7 +91,7 @@ public abstract class PackageBuilder : ICommand MainExePath = mainExePath; options.EntryExecutableName = Path.GetFileName(mainExePath); - using var _1 = Utility.GetTempDirectory(out var pkgTempDir); + using var _1 = TempUtil.GetTempDirectory(out var pkgTempDir); TempDir = new DirectoryInfo(pkgTempDir); Options = options; @@ -158,7 +159,7 @@ public abstract class PackageBuilder : ICommand await ctx.RunTask("Post-process steps", (progress) => { foreach (var f in filesToCopy) { - Utility.MoveFile(f.from, f.to, true); + IoUtil.MoveFile(f.from, f.to, true); } ReleaseEntryHelper.UpdateReleaseFiles(releaseDir.FullName, Log); @@ -261,7 +262,7 @@ public abstract class PackageBuilder : ICommand File.WriteAllText(nuspecPath, GenerateNuspecContent()); var appDir = stagingDir.CreateSubdirectory("lib").CreateSubdirectory("app"); - CopyFiles(new DirectoryInfo(packDir), appDir, Utility.CreateProgressDelegate(progress, 0, 30)); + CopyFiles(new DirectoryInfo(packDir), appDir, CoreUtil.CreateProgressDelegate(progress, 0, 30)); var metadataFiles = GetReleaseMetadataFiles(); foreach (var kvp in metadataFiles) { @@ -270,7 +271,7 @@ public abstract class PackageBuilder : ICommand AddContentTypesAndRel(nuspecPath); - await EasyZip.CreateZipFromDirectoryAsync(Log, outputPath, stagingDir.FullName, Utility.CreateProgressDelegate(progress, 30, 100)); + await EasyZip.CreateZipFromDirectoryAsync(Log, outputPath, stagingDir.FullName, CoreUtil.CreateProgressDelegate(progress, 30, 100)); progress(100); } diff --git a/src/vpk/Velopack.Packaging/ReleaseEntryHelper.cs b/src/vpk/Velopack.Packaging/ReleaseEntryHelper.cs index 0e49d74a..b81a07f8 100644 --- a/src/vpk/Velopack.Packaging/ReleaseEntryHelper.cs +++ b/src/vpk/Velopack.Packaging/ReleaseEntryHelper.cs @@ -1,8 +1,8 @@ using System.Text; using Microsoft.Extensions.Logging; using NuGet.Versioning; -using Velopack.Json; using Velopack.NuGet; +using Velopack.Util; namespace Velopack.Packaging; @@ -64,7 +64,7 @@ public class ReleaseEntryHelper { var releases = _releases.ContainsKey(_channel) ? _releases[_channel] : null; if (releases == null || !releases.Any()) return null; - return releases.Where(z => z.Type == VelopackAssetType.Full).MaxBy(z => z.Version).First(); + return releases.Where(z => z.Type == VelopackAssetType.Full).MaxByPolyfill(z => z.Version).First(); } public IEnumerable GetLatestAssets() @@ -72,7 +72,7 @@ public class ReleaseEntryHelper if (!_releases.ContainsKey(_channel) || !_releases[_channel].Any()) return Enumerable.Empty(); - var latest = _releases[_channel].MaxBy(x => x.Version).First(); + var latest = _releases[_channel].MaxByPolyfill(x => x.Version).First(); _logger.Info($"Latest release: {latest.FileName}"); var assets = _releases[_channel] @@ -102,9 +102,8 @@ public class ReleaseEntryHelper } // We write a legacy RELEASES file to allow older applications to update to velopack -#pragma warning disable CS0612 // Type or member is obsolete #pragma warning disable CS0618 // Type or member is obsolete - var name = Utility.GetReleasesFileName(kvp.Key); + var name = CoreUtil.GetReleasesFileName(kvp.Key); var path = Path.Combine(outputDir, name); ReleaseEntry.WriteReleaseFile( @@ -114,9 +113,8 @@ public class ReleaseEntryHelper .Where(entry => !entry.IsDelta), path); #pragma warning restore CS0618 // Type or member is obsolete -#pragma warning restore CS0612 // Type or member is obsolete - var indexPath = Path.Combine(outputDir, Utility.GetVeloReleaseIndexName(kvp.Key)); + var indexPath = Path.Combine(outputDir, CoreUtil.GetVeloReleaseIndexName(kvp.Key)); var feed = new VelopackAssetFeed() { Assets = kvp.Value.OrderByDescending(v => v.Version).ThenBy(v => v.Type).ToArray(), }; diff --git a/src/vpk/Velopack.Vpk/Logging/BasicConsole.cs b/src/vpk/Velopack.Vpk/Logging/BasicConsole.cs index c1102947..8ff8c262 100644 --- a/src/vpk/Velopack.Vpk/Logging/BasicConsole.cs +++ b/src/vpk/Velopack.Vpk/Logging/BasicConsole.cs @@ -1,4 +1,5 @@ using Velopack.Packaging.Abstractions; +using Velopack.Util; namespace Velopack.Vpk.Logging; diff --git a/src/vpk/Velopack.Vpk/Logging/SpectreConsole.cs b/src/vpk/Velopack.Vpk/Logging/SpectreConsole.cs index 63a6321e..68ed47ef 100644 --- a/src/vpk/Velopack.Vpk/Logging/SpectreConsole.cs +++ b/src/vpk/Velopack.Vpk/Logging/SpectreConsole.cs @@ -1,6 +1,7 @@ using System.Threading; using Spectre.Console; using Velopack.Packaging.Abstractions; +using Velopack.Util; namespace Velopack.Vpk.Logging; diff --git a/src/vpk/Velopack.Vpk/Program.cs b/src/vpk/Velopack.Vpk/Program.cs index b50df68f..3690e6e6 100644 --- a/src/vpk/Velopack.Vpk/Program.cs +++ b/src/vpk/Velopack.Vpk/Program.cs @@ -1,4 +1,4 @@ -using System.ComponentModel; +using System.ComponentModel; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; @@ -12,6 +12,7 @@ using Velopack.Packaging.Exceptions; using Velopack.Packaging.Flow; using Velopack.Packaging.Unix.Commands; using Velopack.Packaging.Windows.Commands; +using Velopack.Util; using Velopack.Vpk.Commands; using Velopack.Vpk.Commands.Deployment; using Velopack.Vpk.Commands.Flow; diff --git a/src/vpk/Velopack.Vpk/Updates/UpdateChecker.cs b/src/vpk/Velopack.Vpk/Updates/UpdateChecker.cs index 2b729015..5d9e8519 100644 --- a/src/vpk/Velopack.Vpk/Updates/UpdateChecker.cs +++ b/src/vpk/Velopack.Vpk/Updates/UpdateChecker.cs @@ -1,5 +1,6 @@ using System.Threading; using NuGet.Protocol.Core.Types; +using Velopack.Util; namespace Velopack.Vpk.Updates; diff --git a/test/Velopack.CommandLine.Tests/Commands/LocalDownloadCommandTests.cs b/test/Velopack.CommandLine.Tests/Commands/LocalDownloadCommandTests.cs index ba64588e..f7aece9b 100644 --- a/test/Velopack.CommandLine.Tests/Commands/LocalDownloadCommandTests.cs +++ b/test/Velopack.CommandLine.Tests/Commands/LocalDownloadCommandTests.cs @@ -1,4 +1,5 @@ using System.CommandLine; +using Velopack.Util; using Velopack.Vpk.Commands.Deployment; namespace Velopack.CommandLine.Tests.Commands; @@ -9,7 +10,7 @@ public class LocalDownloadCommandTests : BaseCommandTests { var command = new LocalDownloadCommand(); - using var _1 = Utility.GetTempDirectory(out var releaseDir); + using var _1 = TempUtil.GetTempDirectory(out var releaseDir); File.Create(Path.Combine(releaseDir, "test.txt")).Close(); ParseResult parseResult = command.ParseAndApply($"--path {releaseDir}"); @@ -22,7 +23,7 @@ public class LocalDownloadCommandTests : BaseCommandTests public void Path_WithEmptyPath_ParsesValue() { var command = new LocalDownloadCommand(); - using var _1 = Utility.GetTempDirectory(out var releaseDir); + using var _1 = TempUtil.GetTempDirectory(out var releaseDir); ParseResult parseResult = command.ParseAndApply($"--path {releaseDir}"); diff --git a/test/Velopack.Packaging.Tests/AzureDeploymentTests.cs b/test/Velopack.Packaging.Tests/AzureDeploymentTests.cs index c342c7cf..4d36b54c 100644 --- a/test/Velopack.Packaging.Tests/AzureDeploymentTests.cs +++ b/test/Velopack.Packaging.Tests/AzureDeploymentTests.cs @@ -1,6 +1,7 @@ using NuGet.Versioning; using Velopack.Deployment; using Velopack.Sources; +using Velopack.Util; namespace Velopack.Packaging.Tests; @@ -23,7 +24,7 @@ public class AzureDeploymentTests { Skip.If(String.IsNullOrWhiteSpace(AZ_KEY), "VELOPACK_AZ_TEST_TOKEN is not set."); using var logger = _output.BuildLoggerFor(); - using var _1 = Utility.GetTempDirectory(out var releaseDir); + using var _1 = TempUtil.GetTempDirectory(out var releaseDir); string channel = String.IsNullOrWhiteSpace(Environment.GetEnvironmentVariable("CI")) ? VelopackRuntimeInfo.SystemOs.GetOsShortName() diff --git a/test/Velopack.Packaging.Tests/CompatUtilTests.cs b/test/Velopack.Packaging.Tests/CompatUtilTests.cs index d2c04d41..1d5467e4 100644 --- a/test/Velopack.Packaging.Tests/CompatUtilTests.cs +++ b/test/Velopack.Packaging.Tests/CompatUtilTests.cs @@ -1,6 +1,7 @@ using Neovolve.Logging.Xunit; using Velopack.Packaging.Exceptions; using Velopack.Packaging.Windows; +using Velopack.Util; using Velopack.Vpk; using Velopack.Vpk.Logging; @@ -35,7 +36,7 @@ public class CompatUtilTests { Skip.IfNot(VelopackRuntimeInfo.IsWindows); using var logger = GetCompat(out var compat); - using var _1 = Utility.GetTempDirectory(out var dir); + using var _1 = TempUtil.GetTempDirectory(out var dir); var sample = PathHelper.GetAvaloniaSample(); Exe.InvokeAndThrowIfNonZero( "dotnet", @@ -56,7 +57,7 @@ public class CompatUtilTests { Skip.IfNot(VelopackRuntimeInfo.IsWindows); using var logger = GetCompat(out var compat); - using var _1 = Utility.GetTempDirectory(out var dir); + using var _1 = TempUtil.GetTempDirectory(out var dir); var sample = PathHelper.GetAvaloniaSample(); Exe.InvokeAndThrowIfNonZero( "dotnet", @@ -77,7 +78,7 @@ public class CompatUtilTests { Skip.IfNot(VelopackRuntimeInfo.IsWindows); using var logger = GetCompat(out var compat); - using var _1 = Utility.GetTempDirectory(out var dir); + using var _1 = TempUtil.GetTempDirectory(out var dir); var sample = PathHelper.GetWpfSample(); Exe.InvokeAndThrowIfNonZero( "dotnet", @@ -97,7 +98,7 @@ public class CompatUtilTests { Skip.IfNot(VelopackRuntimeInfo.IsWindows); using var logger = GetCompat(out var compat); - using var _1 = Utility.GetTempDirectory(out var dir); + using var _1 = TempUtil.GetTempDirectory(out var dir); var sample = PathHelper.GetTestRootPath("TestApp"); Exe.InvokeAndThrowIfNonZero( "dotnet", @@ -114,7 +115,7 @@ public class CompatUtilTests { Skip.IfNot(VelopackRuntimeInfo.IsWindows); using var logger = GetCompat(out var compat); - using var _1 = Utility.GetTempDirectory(out var dir); + using var _1 = TempUtil.GetTempDirectory(out var dir); var sample = PathHelper.GetTestRootPath("TestApp"); Exe.InvokeAndThrowIfNonZero( "dotnet", diff --git a/test/Velopack.Packaging.Tests/CrossCompile.cs b/test/Velopack.Packaging.Tests/CrossCompile.cs index 165d79c6..bd41e0b4 100644 --- a/test/Velopack.Packaging.Tests/CrossCompile.cs +++ b/test/Velopack.Packaging.Tests/CrossCompile.cs @@ -1,4 +1,5 @@ using Velopack.Packaging.Unix; +using Velopack.Util; namespace Velopack.Packaging.Tests; @@ -20,7 +21,7 @@ public class CrossCompile var rid = RID.Parse(target); string id = $"from-{VelopackRuntimeInfo.SystemOs.GetOsShortName()}-targets-{rid.BaseRID.GetOsShortName()}"; - using var _1 = Utility.GetTempDirectory(out var tempDir); + using var _1 = TempUtil.GetTempDirectory(out var tempDir); TestApp.PackTestApp(id, "1.0.0", id, tempDir, logger, targetRid: rid); var artifactsDir = PathHelper.GetTestRootPath("artifacts"); @@ -84,7 +85,7 @@ public class CrossCompile var appExe = Path.Combine(appRoot, "current", "TestApp.exe"); var appUpdate = Path.Combine(appRoot, "Update.exe"); - Utility.DeleteFileOrDirectoryHard(appRoot); + IoUtil.DeleteFileOrDirectoryHard(appRoot); Assert.False(File.Exists(appExe)); @@ -102,6 +103,6 @@ public class CrossCompile Assert.False(File.Exists(appExe)); Assert.True(File.Exists(Path.Combine(appRoot, ".dead"))); - Utility.DeleteFileOrDirectoryHard(appRoot); + IoUtil.DeleteFileOrDirectoryHard(appRoot); } } \ No newline at end of file diff --git a/test/Velopack.Packaging.Tests/GithubDeploymentTests.cs b/test/Velopack.Packaging.Tests/GithubDeploymentTests.cs index 5ec8caaa..617a943e 100644 --- a/test/Velopack.Packaging.Tests/GithubDeploymentTests.cs +++ b/test/Velopack.Packaging.Tests/GithubDeploymentTests.cs @@ -2,6 +2,7 @@ using Velopack.Sources; using Octokit; using Velopack.Packaging.Exceptions; +using Velopack.Util; namespace Velopack.Packaging.Tests; @@ -22,8 +23,8 @@ public class GithubDeploymentTests { Skip.If(String.IsNullOrWhiteSpace(GITHUB_TOKEN), "VELOPACK_GITHUB_TEST_TOKEN is not set."); using var logger = _output.BuildLoggerFor(); - using var _1 = Utility.GetTempDirectory(out var releaseDir); - using var _2 = Utility.GetTempDirectory(out var releaseDir2); + using var _1 = TempUtil.GetTempDirectory(out var releaseDir); + using var _2 = TempUtil.GetTempDirectory(out var releaseDir2); using var ghvar = GitHubReleaseTest.Create("nomerge", logger); var id = "GithubUpdateTest"; TestApp.PackTestApp(id, $"0.0.1-{ghvar.UniqueSuffix}", "t1", releaseDir, logger); @@ -51,8 +52,8 @@ public class GithubDeploymentTests { Skip.If(String.IsNullOrWhiteSpace(GITHUB_TOKEN), "VELOPACK_GITHUB_TEST_TOKEN is not set."); using var logger = _output.BuildLoggerFor(); - using var _1 = Utility.GetTempDirectory(out var releaseDir); - using var _2 = Utility.GetTempDirectory(out var releaseDir2); + using var _1 = TempUtil.GetTempDirectory(out var releaseDir); + using var _2 = TempUtil.GetTempDirectory(out var releaseDir2); using var ghvar = GitHubReleaseTest.Create("mixmatched", logger); var id = "GithubUpdateTest"; TestApp.PackTestApp(id, $"0.0.1-{ghvar.UniqueSuffix}", "t1", releaseDir, logger); @@ -81,8 +82,8 @@ public class GithubDeploymentTests { Skip.If(String.IsNullOrWhiteSpace(GITHUB_TOKEN), "VELOPACK_GITHUB_TEST_TOKEN is not set."); using var logger = _output.BuildLoggerFor(); - using var _1 = Utility.GetTempDirectory(out var releaseDir); - using var _2 = Utility.GetTempDirectory(out var releaseDir2); + using var _1 = TempUtil.GetTempDirectory(out var releaseDir); + using var _2 = TempUtil.GetTempDirectory(out var releaseDir2); using var ghvar = GitHubReleaseTest.Create("yesmerge", logger); var id = "GithubUpdateTest"; TestApp.PackTestApp(id, $"0.0.1-{ghvar.UniqueSuffix}", "t1", releaseDir, logger); @@ -114,7 +115,7 @@ public class GithubDeploymentTests Skip.If(String.IsNullOrWhiteSpace(GITHUB_TOKEN), "VELOPACK_GITHUB_TEST_TOKEN is not set."); using var logger = _output.BuildLoggerFor(); var id = "GithubUpdateTest"; - using var _1 = Utility.GetTempDirectory(out var releaseDir); + using var _1 = TempUtil.GetTempDirectory(out var releaseDir); var (repoOwner, repoName) = GitHubRepository.GetOwnerAndRepo(GITHUB_REPOURL); using var ghvar = GitHubReleaseTest.Create("integration", logger); var releaseName = ghvar.ReleaseName; @@ -172,7 +173,7 @@ This is just a _test_! Assert.Equal(newVer, r.Version.ToNormalizedString()); } - using var _2 = Utility.GetTempDirectory(out var releaseDirNew); + using var _2 = TempUtil.GetTempDirectory(out var releaseDirNew); gh.DownloadLatestFullPackageAsync(new GitHubDownloadOptions { Token = GITHUB_TOKEN, RepoUrl = GITHUB_REPOURL, @@ -189,7 +190,7 @@ This is just a _test_! { Skip.If(String.IsNullOrWhiteSpace(GITHUB_TOKEN), "VELOPACK_GITHUB_TEST_TOKEN is not set."); using var logger = _output.BuildLoggerFor(); - using var _1 = Utility.GetTempDirectory(out var releaseDir); + using var _1 = TempUtil.GetTempDirectory(out var releaseDir); using var ghvar = GitHubReleaseTest.Create("targetCommitish", logger, true); var (repoOwner, repoName) = GitHubRepository.GetOwnerAndRepo(GITHUB_REPOURL); var id = "GithubUpdateTest"; @@ -225,7 +226,7 @@ This is just a _test_! { Skip.If(String.IsNullOrWhiteSpace(GITHUB_TOKEN), "VELOPACK_GITHUB_TEST_TOKEN is not set."); using var logger = _output.BuildLoggerFor(); - using var _1 = Utility.GetTempDirectory(out var releaseDir); + using var _1 = TempUtil.GetTempDirectory(out var releaseDir); using var ghvar = GitHubReleaseTest.Create("targetCommitish", logger, true); var (repoOwner, repoName) = GitHubRepository.GetOwnerAndRepo(GITHUB_REPOURL); var id = "GithubUpdateTest"; diff --git a/test/Velopack.Packaging.Tests/OsxPackTests.cs b/test/Velopack.Packaging.Tests/OsxPackTests.cs index f4d47b3a..7e2b80b0 100644 --- a/test/Velopack.Packaging.Tests/OsxPackTests.cs +++ b/test/Velopack.Packaging.Tests/OsxPackTests.cs @@ -1,5 +1,6 @@ using System.Runtime.Versioning; using Velopack.Compression; +using Velopack.Util; namespace Velopack.Packaging.Tests; @@ -20,9 +21,9 @@ public class OsxPackTests using var logger = _output.BuildLoggerFor(); - using var _1 = Utility.GetTempDirectory(out var tmpOutput); - using var _2 = Utility.GetTempDirectory(out var tmpReleaseDir); - using var _3 = Utility.GetTempDirectory(out var unzipDir); + using var _1 = TempUtil.GetTempDirectory(out var tmpOutput); + using var _2 = TempUtil.GetTempDirectory(out var tmpReleaseDir); + using var _3 = TempUtil.GetTempDirectory(out var unzipDir); const string id = "MyAppId"; const string title = "MyAppTitle"; diff --git a/test/Velopack.Packaging.Tests/ResourceEditTests.cs b/test/Velopack.Packaging.Tests/ResourceEditTests.cs index 907fdbfe..893f38f5 100644 --- a/test/Velopack.Packaging.Tests/ResourceEditTests.cs +++ b/test/Velopack.Packaging.Tests/ResourceEditTests.cs @@ -8,6 +8,7 @@ using AsmResolver.PE.Win32Resources.Icon; using AsmResolver.PE.Win32Resources.Version; using Velopack.NuGet; using Velopack.Packaging.Windows; +using Velopack.Util; namespace Velopack.Packaging.Tests; @@ -37,7 +38,7 @@ public class ResourceEditTests public void CommitResourcesInCorrectOrder() { using var logger = _output.BuildLoggerFor(); - using var _1 = Utility.GetTempFileName(out var tempFile); + using var _1 = TempUtil.GetTempFileName(out var tempFile); var exe = PathHelper.GetRustAsset("setup.exe"); File.Copy(exe, tempFile); @@ -65,7 +66,7 @@ public class ResourceEditTests { using var logger = _output.BuildLoggerFor(); - using var _1 = Utility.GetTempFileName(out var tempFile); + using var _1 = TempUtil.GetTempFileName(out var tempFile); CreateTestPEFileWithoutRsrc(tempFile); var edit = new ResourceEdit(tempFile, logger); @@ -81,7 +82,7 @@ public class ResourceEditTests { using var logger = _output.BuildLoggerFor(); - using var _1 = Utility.GetTempFileName(out var tempFile); + using var _1 = TempUtil.GetTempFileName(out var tempFile); var exe = PathHelper.GetFixture("SquirrelAwareTweakedNetCoreApp.exe"); File.Copy(exe, tempFile); @@ -98,7 +99,7 @@ public class ResourceEditTests { using var logger = _output.BuildLoggerFor(); - using var _1 = Utility.GetTempFileName(out var tempFile); + using var _1 = TempUtil.GetTempFileName(out var tempFile); var exe = PathHelper.GetFixture("atom.exe"); File.Copy(exe, tempFile); @@ -125,7 +126,7 @@ public class ResourceEditTests { using var logger = _output.BuildLoggerFor(); - using var _1 = Utility.GetTempFileName(out var tempFile); + using var _1 = TempUtil.GetTempFileName(out var tempFile); CreateTestPEFileWithoutRsrc(tempFile); var beforeRsrc = PEImage.FromFile(PEFile.FromFile(tempFile)).Resources; @@ -146,7 +147,7 @@ public class ResourceEditTests public void SetVersionInfoWithPreExistingRsrc() { using var logger = _output.BuildLoggerFor(); - using var _1 = Utility.GetTempFileName(out var tempFile); + using var _1 = TempUtil.GetTempFileName(out var tempFile); var exe = PathHelper.GetFixture("atom.exe"); File.Copy(exe, tempFile); @@ -165,7 +166,7 @@ public class ResourceEditTests public void SetVersionInfoWithoutRsrc() { using var logger = _output.BuildLoggerFor(); - using var _1 = Utility.GetTempFileName(out var tempFile); + using var _1 = TempUtil.GetTempFileName(out var tempFile); CreateTestPEFileWithoutRsrc(tempFile); var nuspec = PathHelper.GetFixture("FullNuspec.nuspec"); diff --git a/test/Velopack.Packaging.Tests/S3DeploymentTests.cs b/test/Velopack.Packaging.Tests/S3DeploymentTests.cs index cadeae9f..ab2fb873 100644 --- a/test/Velopack.Packaging.Tests/S3DeploymentTests.cs +++ b/test/Velopack.Packaging.Tests/S3DeploymentTests.cs @@ -1,6 +1,7 @@ using NuGet.Versioning; using Velopack.Deployment; using Velopack.Sources; +using Velopack.Util; namespace Velopack.Packaging.Tests; @@ -23,7 +24,7 @@ public class S3DeploymentTests { Skip.If(String.IsNullOrWhiteSpace(B2_SECRET), "VELOPACK_B2_TEST_TOKEN is not set."); using var logger = _output.BuildLoggerFor(); - using var _1 = Utility.GetTempDirectory(out var releaseDir); + using var _1 = TempUtil.GetTempDirectory(out var releaseDir); string channel = String.IsNullOrWhiteSpace(Environment.GetEnvironmentVariable("CI")) ? VelopackRuntimeInfo.SystemOs.GetOsShortName() diff --git a/test/Velopack.Packaging.Tests/SimpleJsonTests.cs b/test/Velopack.Packaging.Tests/SimpleJsonTests.cs index 60f470e5..b33e49fb 100644 --- a/test/Velopack.Packaging.Tests/SimpleJsonTests.cs +++ b/test/Velopack.Packaging.Tests/SimpleJsonTests.cs @@ -1,7 +1,6 @@ using System.Text.Json.Serialization; using System.Text.Json; using NuGet.Versioning; -using Velopack.Json; using Velopack.Packaging; using Velopack.Sources; using JsonPropertyNameAttribute = System.Text.Json.Serialization.JsonPropertyNameAttribute; diff --git a/test/Velopack.Packaging.Tests/TestApp.cs b/test/Velopack.Packaging.Tests/TestApp.cs index 2445ba39..2d9db3ec 100644 --- a/test/Velopack.Packaging.Tests/TestApp.cs +++ b/test/Velopack.Packaging.Tests/TestApp.cs @@ -1,6 +1,7 @@ using System.Diagnostics; using Velopack.Packaging.Unix.Commands; using Velopack.Packaging.Windows.Commands; +using Velopack.Util; using Velopack.Vpk; using Velopack.Vpk.Logging; diff --git a/test/Velopack.Packaging.Tests/WindowsPackTests.cs b/test/Velopack.Packaging.Tests/WindowsPackTests.cs index 71bb32c6..1620f22d 100644 --- a/test/Velopack.Packaging.Tests/WindowsPackTests.cs +++ b/test/Velopack.Packaging.Tests/WindowsPackTests.cs @@ -8,6 +8,7 @@ using Velopack.Compression; using Velopack.Packaging.Commands; using Velopack.Packaging.Exceptions; using Velopack.Packaging.Windows.Commands; +using Velopack.Util; using Velopack.Vpk; using Velopack.Vpk.Logging; using Velopack.Windows; @@ -37,9 +38,9 @@ public class WindowsPackTests using var logger = _output.BuildLoggerFor(); - using var _1 = Utility.GetTempDirectory(out var tmpOutput); - using var _2 = Utility.GetTempDirectory(out var tmpReleaseDir); - using var _3 = Utility.GetTempDirectory(out var unzipDir); + using var _1 = TempUtil.GetTempDirectory(out var tmpOutput); + using var _2 = TempUtil.GetTempDirectory(out var tmpReleaseDir); + using var _3 = TempUtil.GetTempDirectory(out var unzipDir); var exe = "testapp.exe"; var pdb = Path.ChangeExtension(exe, ".pdb"); @@ -106,8 +107,8 @@ public class WindowsPackTests using var logger = _output.BuildLoggerFor(); - using var _1 = Utility.GetTempDirectory(out var tmpOutput); - using var _2 = Utility.GetTempDirectory(out var tmpReleaseDir); + using var _1 = TempUtil.GetTempDirectory(out var tmpOutput); + using var _2 = TempUtil.GetTempDirectory(out var tmpReleaseDir); var exe = "testapp.exe"; var pdb = Path.ChangeExtension(exe, ".pdb"); @@ -139,8 +140,8 @@ public class WindowsPackTests using var logger = _output.BuildLoggerFor(); - using var _1 = Utility.GetTempDirectory(out var tmpOutput); - using var _2 = Utility.GetTempDirectory(out var tmpReleaseDir); + using var _1 = TempUtil.GetTempDirectory(out var tmpOutput); + using var _2 = TempUtil.GetTempDirectory(out var tmpReleaseDir); var exe = "testapp.exe"; var pdb = Path.ChangeExtension(exe, ".pdb"); @@ -177,9 +178,9 @@ public class WindowsPackTests using var logger = _output.BuildLoggerFor(); - using var _1 = Utility.GetTempDirectory(out var tmpOutput); - using var _2 = Utility.GetTempDirectory(out var tmpReleaseDir); - using var _3 = Utility.GetTempDirectory(out var tmpInstallDir); + using var _1 = TempUtil.GetTempDirectory(out var tmpOutput); + using var _2 = TempUtil.GetTempDirectory(out var tmpReleaseDir); + using var _3 = TempUtil.GetTempDirectory(out var tmpInstallDir); var exe = "testapp.exe"; var pdb = Path.ChangeExtension(exe, ".pdb"); @@ -260,8 +261,8 @@ public class WindowsPackTests { Skip.IfNot(VelopackRuntimeInfo.IsWindows); using var logger = _output.BuildLoggerFor(); - using var _1 = Utility.GetTempDirectory(out var releaseDir); - using var _2 = Utility.GetTempDirectory(out var installDir); + using var _1 = TempUtil.GetTempDirectory(out var releaseDir); + using var _2 = TempUtil.GetTempDirectory(out var installDir); string id = "SquirrelAutoUpdateTest"; var appPath = Path.Combine(installDir, "current", "TestApp.exe"); @@ -293,7 +294,7 @@ public class WindowsPackTests [SkippableFact] public void TestPackGeneratesValidDelta() { - using var _1 = Utility.GetTempDirectory(out var releaseDir); + using var _1 = TempUtil.GetTempDirectory(out var releaseDir); Skip.IfNot(VelopackRuntimeInfo.IsWindows); using var logger = _output.BuildLoggerFor(); string id = "SquirrelDeltaTest"; @@ -304,7 +305,7 @@ public class WindowsPackTests // did a zsdiff get created for our v2 update? var deltaPath = Path.Combine(releaseDir, $"{id}-2.0.0-delta.nupkg"); Assert.True(File.Exists(deltaPath)); - using var _2 = Utility.GetTempDirectory(out var extractDir); + using var _2 = TempUtil.GetTempDirectory(out var extractDir); EasyZip.ExtractZipToDirectory(logger, deltaPath, extractDir); var extractDllDiff = Path.Combine(extractDir, "lib", "app", "testapp.dll.zsdiff"); var extractDllShasum = Path.Combine(extractDir, "lib", "app", "testapp.dll.shasum"); @@ -366,8 +367,8 @@ public class WindowsPackTests { Skip.IfNot(VelopackRuntimeInfo.IsWindows); using var logger = _output.BuildLoggerFor(); - using var _1 = Utility.GetTempDirectory(out var releaseDir); - using var _2 = Utility.GetTempDirectory(out var installDir); + using var _1 = TempUtil.GetTempDirectory(out var releaseDir); + using var _2 = TempUtil.GetTempDirectory(out var installDir); string id = "SquirrelHookTest"; var appPath = Path.Combine(installDir, "current", "TestApp.exe"); @@ -415,8 +416,8 @@ public class WindowsPackTests { Skip.IfNot(VelopackRuntimeInfo.IsWindows); using var logger = _output.BuildLoggerFor(); - using var _1 = Utility.GetTempDirectory(out var releaseDir); - using var _2 = Utility.GetTempDirectory(out var installDir); + using var _1 = TempUtil.GetTempDirectory(out var releaseDir); + using var _2 = TempUtil.GetTempDirectory(out var installDir); string id = "SquirrelIntegrationTest"; @@ -507,7 +508,7 @@ public class WindowsPackTests var rootDir = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "LegacyTestApp"); if (Directory.Exists(rootDir)) { - Utility.Retry(() => Utility.DeleteFileOrDirectoryHard(rootDir), 10, 1000); + IoUtil.Retry(() => IoUtil.DeleteFileOrDirectoryHard(rootDir), 10, 1000); } var setup = PathHelper.GetFixture(fixture); @@ -520,7 +521,7 @@ public class WindowsPackTests Assert.True(File.Exists(appExe)); Assert.True(File.Exists(updateExe)); - using var _1 = Utility.GetTempDirectory(out var releaseDir); + using var _1 = TempUtil.GetTempDirectory(out var releaseDir); PackTestApp("LegacyTestApp", "2.0.0", "hello!", releaseDir, logger); RunNoCoverage(appExe, new string[] { "download", releaseDir }, currentDir, logger, exitCode: 0); @@ -612,7 +613,7 @@ public class WindowsPackTests logger.Info($"TEST: Process exited with code {p.ExitCode} in {elapsed.TotalSeconds}s"); - using var fs = Utility.Retry(() => { + using var fs = IoUtil.Retry(() => { return File.Open(outputFile, FileMode.Open, FileAccess.ReadWrite, FileShare.None); }, 10, 1000, logger); diff --git a/test/Velopack.Tests/RuntimeInfoTests.cs b/test/Velopack.Tests/RuntimeInfoTests.cs index 68731e3f..f61ca58d 100644 --- a/test/Velopack.Tests/RuntimeInfoTests.cs +++ b/test/Velopack.Tests/RuntimeInfoTests.cs @@ -1,5 +1,6 @@ using System.Diagnostics; using System.Runtime.InteropServices; +using Velopack.Util; namespace Velopack.Tests; diff --git a/test/Velopack.Tests/ShortcutTests.cs b/test/Velopack.Tests/ShortcutTests.cs index 658167b9..d28eba1e 100644 --- a/test/Velopack.Tests/ShortcutTests.cs +++ b/test/Velopack.Tests/ShortcutTests.cs @@ -1,6 +1,7 @@ #pragma warning disable CS0618 // Type or member is obsolete using System.Runtime.Versioning; using Velopack.Locators; +using Velopack.Util; using Velopack.Windows; namespace Velopack.Tests; @@ -21,7 +22,7 @@ public class ShortcutTests using var logger = _output.BuildLoggerFor(); string exeName = "NotSquirrelAwareApp.exe"; - using var _1 = Utility.GetTempDirectory(out var rootDir); + using var _1 = TempUtil.GetTempDirectory(out var rootDir); var packages = Directory.CreateDirectory(Path.Combine(rootDir, "packages")); var current = Directory.CreateDirectory(Path.Combine(rootDir, "current")); diff --git a/test/Velopack.Tests/SymbolicLinkTests.cs b/test/Velopack.Tests/SymbolicLinkTests.cs index bff4264e..0b0023a6 100644 --- a/test/Velopack.Tests/SymbolicLinkTests.cs +++ b/test/Velopack.Tests/SymbolicLinkTests.cs @@ -1,5 +1,6 @@ using System.IO.Compression; using Velopack.Compression; +using Velopack.Util; namespace Velopack.Tests; @@ -8,14 +9,14 @@ public class SymbolicLinkTests [Fact] public void Exists_NoSuchFile() { - using var _1 = Utility.GetTempDirectory(out var tempFolder); + using var _1 = TempUtil.GetTempDirectory(out var tempFolder); Assert.False(SymbolicLink.Exists(Path.Combine(tempFolder, "$$$NoSuchFolder$$$"))); } [Fact] public void Exists_IsADirectory() { - using var _1 = Utility.GetTempDirectory(out var tempFolder); + using var _1 = TempUtil.GetTempDirectory(out var tempFolder); File.Create(Path.Combine(tempFolder, "AFile")).Close(); Assert.False(SymbolicLink.Exists(Path.Combine(tempFolder, "AFile"))); @@ -24,7 +25,7 @@ public class SymbolicLinkTests [Fact] public void CreateDirectory_VerifyExists_GetTarget_Delete() { - using var _1 = Utility.GetTempDirectory(out var tempFolder); + using var _1 = TempUtil.GetTempDirectory(out var tempFolder); string targetFolder = Path.Combine(tempFolder, "ADirectory"); string junctionPoint = Path.Combine(tempFolder, "SymLink"); @@ -64,7 +65,7 @@ public class SymbolicLinkTests [Fact] public void CreateFile_VerifyExists_GetTarget_Delete() { - using var _1 = Utility.GetTempDirectory(out var tempFolder); + using var _1 = TempUtil.GetTempDirectory(out var tempFolder); var tmpFile = Path.Combine(tempFolder, "AFile"); var symFile = Path.Combine(tempFolder, "SymFile"); File.Create(tmpFile).Close(); @@ -92,7 +93,7 @@ public class SymbolicLinkTests [Fact] public void CreateFile_RelativePath() { - using var _1 = Utility.GetTempDirectory(out var tempFolder); + using var _1 = TempUtil.GetTempDirectory(out var tempFolder); var subDir = Directory.CreateDirectory(Path.Combine(tempFolder, "SubDir")).FullName; var tmpFile = Path.Combine(tempFolder, "AFile"); @@ -122,7 +123,7 @@ public class SymbolicLinkTests [Fact] public void CreateDirectory_RelativePath() { - using var _1 = Utility.GetTempDirectory(out var tempFolder); + using var _1 = TempUtil.GetTempDirectory(out var tempFolder); var subDir = Directory.CreateDirectory(Path.Combine(tempFolder, "SubDir")).FullName; var subSubDir = Directory.CreateDirectory(Path.Combine(subDir, "SubSub")).FullName; var subDir2 = Directory.CreateDirectory(Path.Combine(tempFolder, "SubDir2")).FullName; @@ -145,7 +146,7 @@ public class SymbolicLinkTests [Fact] public void Create_ThrowsIfOverwriteNotSpecifiedAndDirectoryExists() { - using var _1 = Utility.GetTempDirectory(out var tempFolder); + using var _1 = TempUtil.GetTempDirectory(out var tempFolder); string targetFolder = Path.Combine(tempFolder, "ADirectory"); string junctionPoint = Path.Combine(tempFolder, "SymLink"); @@ -156,7 +157,7 @@ public class SymbolicLinkTests [Fact] public void Create_OverwritesIfSpecifiedAndDirectoryExists() { - using var _1 = Utility.GetTempDirectory(out var tempFolder); + using var _1 = TempUtil.GetTempDirectory(out var tempFolder); string targetFolder = Path.Combine(tempFolder, "ADirectory"); string junctionPoint = Path.Combine(tempFolder, "SymLink"); @@ -171,7 +172,7 @@ public class SymbolicLinkTests [Fact] public void Create_ThrowsIfTargetDirectoryDoesNotExist() { - using var _1 = Utility.GetTempDirectory(out var tempFolder); + using var _1 = TempUtil.GetTempDirectory(out var tempFolder); string targetFolder = Path.Combine(tempFolder, "ADirectory"); string junctionPoint = Path.Combine(tempFolder, "SymLink"); Assert.Throws(() => SymbolicLink.Create(junctionPoint, targetFolder, false)); @@ -180,21 +181,21 @@ public class SymbolicLinkTests [Fact] public void GetTarget_NonExistentJunctionPoint() { - using var _1 = Utility.GetTempDirectory(out var tempFolder); + using var _1 = TempUtil.GetTempDirectory(out var tempFolder); Assert.Throws(() => SymbolicLink.GetTarget(Path.Combine(tempFolder, "SymLink"))); } [Fact] public void GetTarget_CalledOnADirectoryThatIsNotAJunctionPoint() { - using var _1 = Utility.GetTempDirectory(out var tempFolder); + using var _1 = TempUtil.GetTempDirectory(out var tempFolder); Assert.Throws(() => SymbolicLink.GetTarget(tempFolder)); } [Fact] public void GetTarget_CalledOnAFile() { - using var _1 = Utility.GetTempDirectory(out var tempFolder); + using var _1 = TempUtil.GetTempDirectory(out var tempFolder); File.Create(Path.Combine(tempFolder, "AFile")).Close(); Assert.Throws(() => SymbolicLink.GetTarget(Path.Combine(tempFolder, "AFile"))); @@ -204,21 +205,21 @@ public class SymbolicLinkTests public void Delete_NonExistentJunctionPoint() { // Should do nothing. - using var _1 = Utility.GetTempDirectory(out var tempFolder); + using var _1 = TempUtil.GetTempDirectory(out var tempFolder); SymbolicLink.Delete(Path.Combine(tempFolder, "SymLink")); } [Fact] public void Delete_CalledOnADirectoryThatIsNotAJunctionPoint() { - using var _1 = Utility.GetTempDirectory(out var tempFolder); + using var _1 = TempUtil.GetTempDirectory(out var tempFolder); Assert.Throws(() => SymbolicLink.Delete(tempFolder)); } [Fact] public void Delete_CalledOnAFile() { - using var _1 = Utility.GetTempDirectory(out var tempFolder); + using var _1 = TempUtil.GetTempDirectory(out var tempFolder); File.Create(Path.Combine(tempFolder, "AFile")).Close(); Assert.Throws(() => SymbolicLink.Delete(Path.Combine(tempFolder, "AFile"))); @@ -227,7 +228,7 @@ public class SymbolicLinkTests [Fact] public async Task ComplexSymlinkDirGetsZippedCorrectly() { - using var _1 = Utility.GetTempDirectory(out var tempFolder); + using var _1 = TempUtil.GetTempDirectory(out var tempFolder); var temp = new DirectoryInfo(tempFolder); var versions = temp.CreateSubdirectory("Versions"); var a = versions.CreateSubdirectory("A"); @@ -238,7 +239,7 @@ public class SymbolicLinkTests SymbolicLink.Create(Path.Combine(temp.FullName, "Resources"), Path.Combine(versions.FullName, "Current", "Resources"), false, true); SymbolicLink.Create(Path.Combine(temp.FullName, "App"), Path.Combine(versions.FullName, "Current", "App"), false, true); - using var _2 = Utility.GetTempDirectory(out var tempOutput); + using var _2 = TempUtil.GetTempDirectory(out var tempOutput); var output = Path.Combine(tempOutput, "output.zip"); await EasyZip.CreateZipFromDirectoryAsync(NullLogger.Instance, output, tempFolder); diff --git a/test/Velopack.Tests/TestHelpers/FakeFixtureRepository.cs b/test/Velopack.Tests/TestHelpers/FakeFixtureRepository.cs index 611d43ee..db674e83 100644 --- a/test/Velopack.Tests/TestHelpers/FakeFixtureRepository.cs +++ b/test/Velopack.Tests/TestHelpers/FakeFixtureRepository.cs @@ -3,6 +3,7 @@ using System.Text; using Velopack.Packaging; using Velopack.Sources; +using Velopack.Util; namespace Velopack.Tests.TestHelpers; @@ -16,8 +17,8 @@ internal class FakeFixtureRepository : Sources.IFileDownloader public FakeFixtureRepository(string pkgId, bool mockLatestFullVer, string channel = null) { - _releasesName = Utility.GetReleasesFileName(channel); - _releasesNameNew = Utility.GetVeloReleaseIndexName(channel); + _releasesName = CoreUtil.GetReleasesFileName(channel); + _releasesNameNew = CoreUtil.GetVeloReleaseIndexName(channel); _pkgId = pkgId; var releases = ReleaseEntry.BuildReleasesFile(PathHelper.GetFixturesDir(), false) .Where(r => r.OriginalFilename.StartsWith(_pkgId)) diff --git a/test/Velopack.Tests/TestHelpers/StaticHttpServer.cs b/test/Velopack.Tests/TestHelpers/StaticHttpServer.cs index d3164cb4..21ebf216 100644 --- a/test/Velopack.Tests/TestHelpers/StaticHttpServer.cs +++ b/test/Velopack.Tests/TestHelpers/StaticHttpServer.cs @@ -1,5 +1,6 @@ using System.Net; using System.Text; +using Velopack.Util; namespace Velopack.Tests; diff --git a/test/Velopack.Tests/UpdateManagerTests.cs b/test/Velopack.Tests/UpdateManagerTests.cs index 9fcfc8e2..d1eca115 100644 --- a/test/Velopack.Tests/UpdateManagerTests.cs +++ b/test/Velopack.Tests/UpdateManagerTests.cs @@ -5,6 +5,7 @@ using Velopack.Packaging; using Velopack.Locators; using Velopack.Sources; using Velopack.Tests.TestHelpers; +using Velopack.Util; namespace Velopack.Tests; @@ -107,7 +108,7 @@ public class UpdateManagerTests var fixture = PathHelper.GetFixture("AvaloniaCrossPlat-1.0.11-win-full.nupkg"); using var logger = _output.BuildLoggerFor(); - using var _1 = Utility.GetTempDirectory(out var tempPath); + using var _1 = TempUtil.GetTempDirectory(out var tempPath); var dl = new FakeDownloader() { MockedResponseBytes = Encoding.UTF8.GetBytes(SimpleJson.SerializeObject( new VelopackAssetFeed { @@ -117,7 +118,7 @@ public class UpdateManagerTests Version = new SemanticVersion(1, 0, 11), Type = VelopackAssetType.Full, FileName = $"https://mysite.com/releases/AvaloniaCrossPlat$-1.1.0.nupkg", - SHA1 = Utility.CalculateFileSHA1(fixture), + SHA1 = IoUtil.CalculateFileSHA1(fixture), Size = new FileInfo(fixture).Length, } } })) @@ -145,7 +146,7 @@ public class UpdateManagerTests public void CheckFromLocal() { using var logger = _output.BuildLoggerFor(); - using var _1 = Utility.GetTempDirectory(out var tempPath); + using var _1 = TempUtil.GetTempDirectory(out var tempPath); var dl = GetMockDownloaderNoDelta(); var source = new SimpleWebSource("http://any.com", dl); var locator = new TestVelopackLocator("MyCoolApp", "1.0.0", tempPath, logger); @@ -162,7 +163,7 @@ public class UpdateManagerTests public void CheckFromLocalWithChannel() { using var logger = _output.BuildLoggerFor(); - using var _1 = Utility.GetTempDirectory(out var tempPath); + using var _1 = TempUtil.GetTempDirectory(out var tempPath); var dl = GetMockDownloaderNoDelta(); var source = new SimpleWebSource("http://any.com", dl); var locator = new TestVelopackLocator("MyCoolApp", "1.0.0", tempPath, logger); @@ -180,7 +181,7 @@ public class UpdateManagerTests public void CheckForSameAsInstalledVersion() { using var logger = _output.BuildLoggerFor(); - using var _1 = Utility.GetTempDirectory(out var tempPath); + using var _1 = TempUtil.GetTempDirectory(out var tempPath); var dl = GetMockDownloaderWith2Delta(); var myVer = new VelopackAsset() { PackageId = "MyCoolApp", @@ -220,7 +221,7 @@ public class UpdateManagerTests public void CheckForLowerThanInstalledVersion() { using var logger = _output.BuildLoggerFor(); - using var _1 = Utility.GetTempDirectory(out var tempPath); + using var _1 = TempUtil.GetTempDirectory(out var tempPath); var dl = GetMockDownloaderWith2Delta(); var myVer = new VelopackAsset() { PackageId = "MyCoolApp", @@ -254,7 +255,7 @@ public class UpdateManagerTests public void CheckFromLocalWithDelta() { using var logger = _output.BuildLoggerFor(); - using var _1 = Utility.GetTempDirectory(out var tempPath); + using var _1 = TempUtil.GetTempDirectory(out var tempPath); var dl = GetMockDownloaderWith2Delta(); var myVer = new VelopackAsset() { PackageId = "MyCoolApp", @@ -282,7 +283,7 @@ public class UpdateManagerTests string version = "3.4.287"; using var logger = _output.BuildLoggerFor(); - using var _1 = Utility.GetTempDirectory(out var packagesDir); + using var _1 = TempUtil.GetTempDirectory(out var packagesDir); var repo = new FakeFixtureRepository(id, false); var source = new SimpleWebSource("http://any.com", repo); var locator = new TestVelopackLocator(id, "1.0.0", packagesDir, logger); @@ -316,7 +317,7 @@ public class UpdateManagerTests string version = "3.4.287"; using var logger = _output.BuildLoggerFor(); - using var _1 = Utility.GetTempDirectory(out var packagesDir); + using var _1 = TempUtil.GetTempDirectory(out var packagesDir); var repo = new FakeFixtureRepository(id, false); var source = new SimpleWebSource("http://any.com", repo); var locator = new TestVelopackLocator(id, "1.0.0", packagesDir, logger); @@ -347,7 +348,7 @@ public class UpdateManagerTests public void NoDeltaIfNoBasePackage() { using var logger = _output.BuildLoggerFor(); - using var _1 = Utility.GetTempDirectory(out var tempPath); + using var _1 = TempUtil.GetTempDirectory(out var tempPath); var dl = GetMockDownloaderWith2Delta(); var source = new SimpleWebSource("http://any.com", dl); var locator = new TestVelopackLocator("MyCoolApp", "1.0.0", tempPath, logger: logger); @@ -363,7 +364,7 @@ public class UpdateManagerTests public void CheckFromLocalWithDeltaNoLocalPackage() { using var logger = _output.BuildLoggerFor(); - using var _1 = Utility.GetTempDirectory(out var tempPath); + using var _1 = TempUtil.GetTempDirectory(out var tempPath); var dl = GetMockDownloaderWith2Delta(); var source = new SimpleWebSource("http://any.com", dl); var locator = new TestVelopackLocator("MyCoolApp", "1.0.0", tempPath, logger: logger); @@ -380,7 +381,7 @@ public class UpdateManagerTests { // https://github.com/caesay/SquirrelCustomLauncherTestApp using var logger = _output.BuildLoggerFor(); - using var _1 = Utility.GetTempDirectory(out var tempPath); + using var _1 = TempUtil.GetTempDirectory(out var tempPath); var locator = new TestVelopackLocator("MyCoolApp", "1.0.0", tempPath, logger); var source = new GithubSource("https://github.com/caesay/SquirrelCustomLauncherTestApp", null, false); var um = new UpdateManager(source, null, logger, locator); @@ -394,7 +395,7 @@ public class UpdateManagerTests { // https://github.com/caesay/SquirrelCustomLauncherTestApp using var logger = _output.BuildLoggerFor(); - using var _1 = Utility.GetTempDirectory(out var tempPath); + using var _1 = TempUtil.GetTempDirectory(out var tempPath); var locator = new TestVelopackLocator("MyCoolApp", "1.0.0", tempPath, logger); var source = new GithubSource("https://github.com/caesay/SquirrelCustomLauncherTestApp", null, false); var opt = new UpdateOptions { ExplicitChannel = "hello" }; @@ -406,7 +407,7 @@ public class UpdateManagerTests { // https://github.com/caesay/SquirrelCustomLauncherTestApp using var logger = _output.BuildLoggerFor(); - using var _1 = Utility.GetTempDirectory(out var tempPath); + using var _1 = TempUtil.GetTempDirectory(out var tempPath); var locator = new TestVelopackLocator("MyCoolApp", "1.0.0", tempPath, logger); var source = new GiteaSource("https://gitea.com/remco1271/VeloPackTest", null, false); var um = new UpdateManager(source, null, logger, locator); @@ -419,7 +420,7 @@ public class UpdateManagerTests public void CheckFromEmptyFileSource() { using var logger = _output.BuildLoggerFor(); - using var _1 = Utility.GetTempDirectory(out var tempPath); + using var _1 = TempUtil.GetTempDirectory(out var tempPath); var source = new SimpleFileSource(new DirectoryInfo(tempPath)); var locator = new TestVelopackLocator("MyCoolApp", "1.0.0", tempPath, logger); var um = new UpdateManager(source, null, logger, locator); @@ -431,7 +432,7 @@ public class UpdateManagerTests public void NoUpdatesIfCurrentEqualsRemoteVersion() { using var logger = _output.BuildLoggerFor(); - using var _1 = Utility.GetTempDirectory(out var tempPath); + using var _1 = TempUtil.GetTempDirectory(out var tempPath); var dl = GetMockDownloaderNoDelta(); var source = new SimpleWebSource("http://any.com", dl); var locator = new TestVelopackLocator("MyCoolApp", "1.1.0", tempPath, logger); @@ -444,7 +445,7 @@ public class UpdateManagerTests public void NoUpdatesIfCurrentGreaterThanRemoteVersion() { using var logger = _output.BuildLoggerFor(); - using var _1 = Utility.GetTempDirectory(out var tempPath); + using var _1 = TempUtil.GetTempDirectory(out var tempPath); var dl = GetMockDownloaderNoDelta(); var source = new SimpleWebSource("http://any.com", dl); var locator = new TestVelopackLocator("MyCoolApp", "1.2.0", tempPath, logger); @@ -459,7 +460,7 @@ public class UpdateManagerTests public void DownloadsLatestFullVersion(string id, string version) { using var logger = _output.BuildLoggerFor(); - using var _1 = Utility.GetTempDirectory(out var packagesDir); + using var _1 = TempUtil.GetTempDirectory(out var packagesDir); var repo = new FakeFixtureRepository(id, false); var source = new SimpleWebSource("http://any.com", repo); var locator = new TestVelopackLocator(id, "1.0.0", packagesDir, logger); @@ -484,7 +485,7 @@ public class UpdateManagerTests { Skip.If(VelopackRuntimeInfo.IsLinux); using var logger = _output.BuildLoggerFor(); - using var _1 = Utility.GetTempDirectory(out var packagesDir); + using var _1 = TempUtil.GetTempDirectory(out var packagesDir); var repo = new FakeFixtureRepository(id, true); var source = new SimpleWebSource("http://any.com", repo); diff --git a/test/Velopack.Tests/UtilityTests.cs b/test/Velopack.Tests/UtilityTests.cs index 98900553..064f894c 100644 --- a/test/Velopack.Tests/UtilityTests.cs +++ b/test/Velopack.Tests/UtilityTests.cs @@ -2,6 +2,7 @@ using System.Runtime.Versioning; using System.Security.Cryptography; using System.Text; +using Velopack.Util; using Velopack.Windows; namespace Velopack.Tests; @@ -26,7 +27,7 @@ public class UtilityTests { Skip.IfNot(VelopackRuntimeInfo.IsWindows); var exp = Path.GetFullPath(expected); - var normal = Utility.NormalizePath(input); + var normal = PathUtil.NormalizePath(input); Assert.Equal(exp, normal); } @@ -43,7 +44,7 @@ public class UtilityTests public void FileIsInDirectory(string directory, string file, bool isIn) { Skip.IfNot(VelopackRuntimeInfo.IsWindows); - var fileInDir = Utility.IsFileInDirectory(file, directory); + var fileInDir = PathUtil.IsFileInDirectory(file, directory); Assert.Equal(isIn, fileInDir); } @@ -84,24 +85,24 @@ public class UtilityTests var emptyString = string.Empty; string nullString = null; byte[] nullByteArray = { }; - Assert.Equal(string.Empty, Utility.RemoveByteOrderMarkerIfPresent(emptyString)); - Assert.Equal(string.Empty, Utility.RemoveByteOrderMarkerIfPresent(nullString)); - Assert.Equal(string.Empty, Utility.RemoveByteOrderMarkerIfPresent(nullByteArray)); + Assert.Equal(string.Empty, CoreUtil.RemoveByteOrderMarkerIfPresent(emptyString)); + Assert.Equal(string.Empty, CoreUtil.RemoveByteOrderMarkerIfPresent(nullString)); + Assert.Equal(string.Empty, CoreUtil.RemoveByteOrderMarkerIfPresent(nullByteArray)); - Assert.Equal(string.Empty, Utility.RemoveByteOrderMarkerIfPresent(utf32Be)); - Assert.Equal(string.Empty, Utility.RemoveByteOrderMarkerIfPresent(utf32Le)); - Assert.Equal(string.Empty, Utility.RemoveByteOrderMarkerIfPresent(utf16Be)); - Assert.Equal(string.Empty, Utility.RemoveByteOrderMarkerIfPresent(utf16Le)); - Assert.Equal(string.Empty, Utility.RemoveByteOrderMarkerIfPresent(utf8)); + Assert.Equal(string.Empty, CoreUtil.RemoveByteOrderMarkerIfPresent(utf32Be)); + Assert.Equal(string.Empty, CoreUtil.RemoveByteOrderMarkerIfPresent(utf32Le)); + Assert.Equal(string.Empty, CoreUtil.RemoveByteOrderMarkerIfPresent(utf16Be)); + Assert.Equal(string.Empty, CoreUtil.RemoveByteOrderMarkerIfPresent(utf16Le)); + Assert.Equal(string.Empty, CoreUtil.RemoveByteOrderMarkerIfPresent(utf8)); - Assert.Equal("hello world", Utility.RemoveByteOrderMarkerIfPresent(utf32BeHelloWorld)); - Assert.Equal("hello world", Utility.RemoveByteOrderMarkerIfPresent(utf32LeHelloWorld)); - Assert.Equal("hello world", Utility.RemoveByteOrderMarkerIfPresent(utf16BeHelloWorld)); - Assert.Equal("hello world", Utility.RemoveByteOrderMarkerIfPresent(utf16LeHelloWorld)); - Assert.Equal("hello world", Utility.RemoveByteOrderMarkerIfPresent(utf8HelloWorld)); + Assert.Equal("hello world", CoreUtil.RemoveByteOrderMarkerIfPresent(utf32BeHelloWorld)); + Assert.Equal("hello world", CoreUtil.RemoveByteOrderMarkerIfPresent(utf32LeHelloWorld)); + Assert.Equal("hello world", CoreUtil.RemoveByteOrderMarkerIfPresent(utf16BeHelloWorld)); + Assert.Equal("hello world", CoreUtil.RemoveByteOrderMarkerIfPresent(utf16LeHelloWorld)); + Assert.Equal("hello world", CoreUtil.RemoveByteOrderMarkerIfPresent(utf8HelloWorld)); - Assert.Equal("hello world", Utility.RemoveByteOrderMarkerIfPresent(asciiMultipleChars)); - Assert.Equal("A", Utility.RemoveByteOrderMarkerIfPresent(asciiSingleChar)); + Assert.Equal("hello world", CoreUtil.RemoveByteOrderMarkerIfPresent(asciiMultipleChars)); + Assert.Equal("A", CoreUtil.RemoveByteOrderMarkerIfPresent(asciiSingleChar)); } [Fact] @@ -110,7 +111,7 @@ public class UtilityTests var sha1FromExternalTool = "75255cfd229a1ed1447abe1104f5635e69975d30"; var inputPackage = PathHelper.GetFixture("Squirrel.Core.1.0.0.0.nupkg"); var stream = File.OpenRead(inputPackage); - var sha1 = Utility.CalculateStreamSHA1(stream); + var sha1 = IoUtil.CalculateStreamSHA1(stream); Assert.NotEqual(sha1FromExternalTool, sha1); Assert.Equal(sha1FromExternalTool, sha1, StringComparer.OrdinalIgnoreCase); @@ -121,7 +122,7 @@ public class UtilityTests { using var logger = _output.BuildLoggerFor(); string tempDir; - using (Utility.GetTempDirectory(out tempDir)) { + using (TempUtil.GetTempDirectory(out tempDir)) { for (var i = 0; i < 50; i++) { var directory = Path.Combine(tempDir, newId()); CreateSampleDirectory(directory); @@ -135,7 +136,7 @@ public class UtilityTests var sw = new Stopwatch(); sw.Start(); - Utility.DeleteFileOrDirectoryHard(tempDir); + IoUtil.DeleteFileOrDirectoryHard(tempDir); sw.Stop(); logger.Info($"Delete took {sw.ElapsedMilliseconds}ms"); @@ -147,7 +148,7 @@ public class UtilityTests //public void CreateFakePackageSmokeTest() //{ // string path; - // using (Utility.GetTempDirectory(out path)) { + // using (TempUtil.GetTempDirectory(out path)) { // var output = IntegrationTestHelper.CreateFakeInstalledApp("0.3.0", path); // Assert.True(File.Exists(output)); // } @@ -161,7 +162,7 @@ public class UtilityTests [InlineData(".rels", false)] public void FileIsLikelyPEImageTest(string input, bool result) { - Assert.Equal(result, Utility.FileIsLikelyPEImage(input)); + Assert.Equal(result, PathUtil.FileIsLikelyPEImage(input)); } [Fact(Skip = "Only really need to run this test after changes to FileDownloader")] @@ -170,10 +171,10 @@ public class UtilityTests // this probably should use a local http server instead. const string testUrl = "http://speedtest.tele2.net/1MB.zip"; - var dl = Utility.CreateDefaultDownloader(); + var dl = HttpUtil.CreateDefaultDownloader(); List prog = new List(); - using (Utility.GetTempFileName(out var tempPath)) + using (TempUtil.GetTempFileName(out var tempPath)) await dl.DownloadFile(testUrl, tempPath, prog.Add); Assert.True(prog.Count > 10); diff --git a/test/Velopack.Tests/ZipPackageTests.cs b/test/Velopack.Tests/ZipPackageTests.cs index 7d177b34..5b9feb6b 100644 --- a/test/Velopack.Tests/ZipPackageTests.cs +++ b/test/Velopack.Tests/ZipPackageTests.cs @@ -2,6 +2,7 @@ using NuGet.Versioning; using Velopack.NuGet; using Velopack.Tests.TestHelpers; +using Velopack.Util; using ZipPackage = Velopack.NuGet.ZipPackage; namespace Velopack.Tests; @@ -19,9 +20,9 @@ public class ZipPackageTests { using var logger = _output.BuildLoggerFor(); - using var _1 = Utility.GetTempDirectory(out var tempDir); - using var _2 = Utility.GetTempDirectory(out var zipDir); - using var _3 = Utility.GetTempDirectory(out var extractedDir); + using var _1 = TempUtil.GetTempDirectory(out var tempDir); + using var _2 = TempUtil.GetTempDirectory(out var zipDir); + using var _3 = TempUtil.GetTempDirectory(out var extractedDir); var actual = Path.Combine(tempDir, "actual"); var actualFile = Path.Combine(actual, "file.txt"); @@ -56,7 +57,7 @@ public class ZipPackageTests [Fact] public void HasSameFilesAndDependenciesAsPackaging() { - using var _1 = Utility.GetTempDirectory(out var tempDir); + using var _1 = TempUtil.GetTempDirectory(out var tempDir); var inputPackage = PathHelper.GetFixture("slack-1.1.8-full.nupkg"); var copyPackage = Path.Combine(tempDir, "slack-1.1.8-full.nupkg"); File.Copy(inputPackage, copyPackage);