mirror of
https://github.com/velopack/velopack.git
synced 2025-10-25 15:19:22 +00:00
Refactor util into separate classes
This commit is contained in:
@@ -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) {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Velopack.Util;
|
||||
|
||||
namespace Velopack.Compression
|
||||
{
|
||||
|
||||
@@ -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<ZipArchiveEntry> 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");
|
||||
}
|
||||
|
||||
|
||||
@@ -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";
|
||||
|
||||
/// <summary>
|
||||
/// Calculates the total percentage of a specific step that should report within a specific range.
|
||||
/// <para />
|
||||
/// If a step needs to report between 50 -> 75 %, this method should be used as CalculateProgress(percentage, 50, 75).
|
||||
/// </summary>
|
||||
/// <param name="percentageOfCurrentStep">The percentage of the current step, a value between 0 and 100.</param>
|
||||
/// <param name="stepStartPercentage">The start percentage of the range the current step represents.</param>
|
||||
/// <param name="stepEndPercentage">The end percentage of the range the current step represents.</param>
|
||||
/// <returns>The calculated percentage that can be reported about the total progress.</returns>
|
||||
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<int> CreateProgressDelegate(Action<int> 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<byte[], byte[], bool> 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<TEnum>(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<FileInfo> GetAllFilesRecursively(this DirectoryInfo rootPath)
|
||||
{
|
||||
if (rootPath == null) return Enumerable.Empty<FileInfo>();
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="CalculateStreamSHA256"/>
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get SHA256 hash of the specified file and returns the result as a base64 encoded string (with length 44)
|
||||
/// </summary>
|
||||
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<T>(this Func<T> 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<Task> 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<T> RetryAsync<T>(this Func<Task<T>> 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<T>(this Task<T> task)
|
||||
{
|
||||
return task.ConfigureAwait(false).GetAwaiter().GetResult();
|
||||
}
|
||||
|
||||
public static void GetAwaiterResult(this Task task)
|
||||
{
|
||||
task.ConfigureAwait(false).GetAwaiter().GetResult();
|
||||
}
|
||||
|
||||
public static Task ForEachAsync<T>(this IEnumerable<T> source, Action<T> body, int degreeOfParallelism = 4)
|
||||
{
|
||||
return ForEachAsync(source, x => Task.Run(() => body(x)), degreeOfParallelism);
|
||||
}
|
||||
|
||||
public static Task ForEachAsync<T>(this IEnumerable<T> source, Func<T, Task> 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);
|
||||
}));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Escapes file name such that the file name is safe for writing to disk in the packages folder
|
||||
/// </summary>
|
||||
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));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Repeatedly tries various methods to delete a file system object. Optionally renames the directory first.
|
||||
/// Optionally ignores errors.
|
||||
/// </summary>
|
||||
/// <param name="path">The path of the file system entity to delete.</param>
|
||||
/// <param name="throwOnFailure">Whether this function should throw if the delete fails.</param>
|
||||
/// <param name="renameFirst">Try to rename this object first before deleting. Can help prevent partial delete of folders.</param>
|
||||
/// <param name="logger">Logger for diagnostic messages.</param>
|
||||
/// <returns>True if the file system object was deleted, false otherwise.</returns>
|
||||
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<ReleaseEntry> 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<ReleaseEntry> localReleases, RID compatibleRid)
|
||||
//{
|
||||
// return FindCompatibleVersions(localReleases, compatibleRid).FirstOrDefault(f => !f.IsDelta);
|
||||
//}
|
||||
|
||||
//public static IEnumerable<ReleaseEntry> FindCompatibleVersions(IEnumerable<ReleaseEntry> 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<int> GetExitCodeAsync(this Process p)
|
||||
{
|
||||
#if NET5_0_OR_GREATER
|
||||
await p.WaitForExitAsync().ConfigureAwait(false);
|
||||
return p.ExitCode;
|
||||
#else
|
||||
var tcs = new TaskCompletionSource<int>();
|
||||
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<KeyValuePair<string, string>> 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);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The namespace for fully-qualified domain names (from RFC 4122, Appendix C).
|
||||
/// </summary>
|
||||
public static readonly Guid DnsNamespace = new Guid("6ba7b810-9dad-11d1-80b4-00c04fd430c8");
|
||||
|
||||
/// <summary>
|
||||
/// The namespace for URLs (from RFC 4122, Appendix C).
|
||||
/// </summary>
|
||||
public static readonly Guid UrlNamespace = new Guid("6ba7b811-9dad-11d1-80b4-00c04fd430c8");
|
||||
|
||||
/// <summary>
|
||||
/// The namespace for ISO OIDs (from RFC 4122, Appendix C).
|
||||
/// </summary>
|
||||
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<TEnum>() where TEnum : struct, Enum
|
||||
{
|
||||
#if NET6_0_OR_GREATER
|
||||
return Enum.GetValues<TEnum>();
|
||||
#else
|
||||
return Enum.GetValues(typeof(TEnum)).Cast<TEnum>().ToArray();
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string? AppTempDir => CreateSubDirIfDoesNotExist(Utility.GetDefaultTempBaseDirectory(), AppId);
|
||||
public override string? AppTempDir => CreateSubDirIfDoesNotExist(TempUtil.GetDefaultTempBaseDirectory(), AppId);
|
||||
|
||||
/// <inheritdoc />
|
||||
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)) {
|
||||
|
||||
@@ -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;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string? AppTempDir => CreateSubDirIfDoesNotExist(Utility.GetDefaultTempBaseDirectory(), AppId);
|
||||
public override string? AppTempDir => CreateSubDirIfDoesNotExist(TempUtil.GetDefaultTempBaseDirectory(), AppId);
|
||||
|
||||
/// <inheritdoc />
|
||||
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);
|
||||
|
||||
@@ -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}");
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System.IO;
|
||||
using Velopack.Util;
|
||||
|
||||
namespace Velopack.Locators
|
||||
{
|
||||
@@ -13,7 +14,7 @@ namespace Velopack.Locators
|
||||
/// </summary>
|
||||
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));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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)) {
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
@@ -69,7 +70,7 @@ namespace Velopack.Sources
|
||||
return new VelopackAssetFeed();
|
||||
}
|
||||
|
||||
var releasesFileName = Utility.GetVeloReleaseIndexName(channel);
|
||||
var releasesFileName = CoreUtil.GetVeloReleaseIndexName(channel);
|
||||
List<GitBaseAsset> entries = new List<GitBaseAsset>();
|
||||
|
||||
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));
|
||||
|
||||
@@ -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
|
||||
{
|
||||
/// <summary> Describes a Gitea release, including attached assets. </summary>
|
||||
|
||||
@@ -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
|
||||
{
|
||||
|
||||
@@ -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
|
||||
{
|
||||
|
||||
@@ -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)));
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public async virtual Task<VelopackAssetFeed> 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<string, string>();
|
||||
|
||||
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);
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
/// <summary> The URL of the server hosting packages to update to. </summary>
|
||||
@@ -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<string, string>();
|
||||
|
||||
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}");
|
||||
|
||||
|
||||
@@ -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
|
||||
{
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
|
||||
@@ -22,7 +22,7 @@ namespace System.Text.Json.Serialization
|
||||
}
|
||||
#endif
|
||||
|
||||
namespace Velopack.Json
|
||||
namespace Velopack.Util
|
||||
{
|
||||
#if NET6_0_OR_GREATER
|
||||
|
||||
142
src/lib-csharp/Util/CoreUtil.cs
Normal file
142
src/lib-csharp/Util/CoreUtil.cs
Normal file
@@ -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}";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Calculates the total percentage of a specific step that should report within a specific range.
|
||||
/// <para />
|
||||
/// If a step needs to report between 50 -> 75 %, this method should be used as CalculateProgress(percentage, 50, 75).
|
||||
/// </summary>
|
||||
/// <param name="percentageOfCurrentStep">The percentage of the current step, a value between 0 and 100.</param>
|
||||
/// <param name="stepStartPercentage">The start percentage of the range the current step represents.</param>
|
||||
/// <param name="stepEndPercentage">The end percentage of the range the current step represents.</param>
|
||||
/// <returns>The calculated percentage that can be reported about the total progress.</returns>
|
||||
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<int> CreateProgressDelegate(Action<int> 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<byte[], byte[], bool> 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<T>(this Task<T> task)
|
||||
{
|
||||
return task.ConfigureAwait(false).GetAwaiter().GetResult();
|
||||
}
|
||||
|
||||
public static void GetAwaiterResult(this Task task)
|
||||
{
|
||||
task.ConfigureAwait(false).GetAwaiter().GetResult();
|
||||
}
|
||||
|
||||
public static bool TryParseEnumU16<TEnum>(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<TEnum>() where TEnum : struct, Enum
|
||||
{
|
||||
#if NET6_0_OR_GREATER
|
||||
return Enum.GetValues<TEnum>();
|
||||
#else
|
||||
return Enum.GetValues(typeof(TEnum)).Cast<TEnum>().ToArray();
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,7 +2,7 @@
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Threading;
|
||||
|
||||
namespace Velopack
|
||||
namespace Velopack.Util
|
||||
{
|
||||
[ExcludeFromCodeCoverage]
|
||||
internal static class Disposable
|
||||
@@ -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
|
||||
/// <param name="source">Source sequence.</param>
|
||||
/// <param name="keySelector">Key selector used to extract the key for each element in the sequence.</param>
|
||||
/// <returns>List with the elements that share the same maximum key value.</returns>
|
||||
public static IList<TSource> MaxBy<TSource, TKey>(this IEnumerable<TSource> source, Func<TSource, TKey> keySelector)
|
||||
public static IList<TSource> MaxByPolyfill<TSource, TKey>(this IEnumerable<TSource> source, Func<TSource, TKey> keySelector)
|
||||
{
|
||||
if (source == null)
|
||||
throw new ArgumentNullException("source");
|
||||
if (keySelector == null)
|
||||
throw new ArgumentNullException("keySelector");
|
||||
|
||||
return MaxBy(source, keySelector, Comparer<TKey>.Default);
|
||||
|
||||
return MaxByPolyfill(source, keySelector, Comparer<TKey>.Default);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -91,12 +95,12 @@ namespace Velopack
|
||||
/// <param name="keySelector">Key selector used to extract the key for each element in the sequence.</param>
|
||||
/// <param name="comparer">Comparer used to determine the maximum key value.</param>
|
||||
/// <returns>List with the elements that share the same maximum key value.</returns>
|
||||
public static IList<TSource> MaxBy<TSource, TKey>(this IEnumerable<TSource> source, Func<TSource, TKey> keySelector, IComparer<TKey> comparer)
|
||||
public static IList<TSource> MaxByPolyfill<TSource, TKey>(this IEnumerable<TSource> source, Func<TSource, TKey> keySelector, IComparer<TKey> 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<T>(this IEnumerable<T> source, Action<T> body, int degreeOfParallelism = 4)
|
||||
{
|
||||
return ForEachAsync(source, x => Task.Run(() => body(x)), degreeOfParallelism);
|
||||
}
|
||||
|
||||
public static Task ForEachAsync<T>(this IEnumerable<T> source, Func<T, Task> 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);
|
||||
}));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
88
src/lib-csharp/Util/GuidUtil.cs
Normal file
88
src/lib-csharp/Util/GuidUtil.cs
Normal file
@@ -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);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The namespace for fully-qualified domain names (from RFC 4122, Appendix C).
|
||||
/// </summary>
|
||||
public static readonly Guid DnsNamespace = new Guid("6ba7b810-9dad-11d1-80b4-00c04fd430c8");
|
||||
|
||||
/// <summary>
|
||||
/// The namespace for URLs (from RFC 4122, Appendix C).
|
||||
/// </summary>
|
||||
public static readonly Guid UrlNamespace = new Guid("6ba7b811-9dad-11d1-80b4-00c04fd430c8");
|
||||
|
||||
/// <summary>
|
||||
/// The namespace for ISO OIDs (from RFC 4122, Appendix C).
|
||||
/// </summary>
|
||||
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]);
|
||||
}
|
||||
}
|
||||
}
|
||||
52
src/lib-csharp/Util/HttpUtil.cs
Normal file
52
src/lib-csharp/Util/HttpUtil.cs
Normal file
@@ -0,0 +1,52 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Velopack.Util
|
||||
{
|
||||
internal static class HttpUtil
|
||||
{
|
||||
public static Uri AddQueryParamsToUri(Uri uri, IEnumerable<KeyValuePair<string, string>> 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, "");
|
||||
}
|
||||
}
|
||||
}
|
||||
218
src/lib-csharp/Util/IoUtil.cs
Normal file
218
src/lib-csharp/Util/IoUtil.cs
Normal file
@@ -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<FileInfo> GetAllFilesRecursively(this DirectoryInfo? rootPath)
|
||||
{
|
||||
if (rootPath == null) return Enumerable.Empty<FileInfo>();
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="CalculateStreamSHA256"/>
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get SHA256 hash of the specified file and returns the result as a base64 encoded string (with length 44)
|
||||
/// </summary>
|
||||
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
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Repeatedly tries various methods to delete a file system object. Optionally renames the directory first.
|
||||
/// Optionally ignores errors.
|
||||
/// </summary>
|
||||
/// <param name="path">The path of the file system entity to delete.</param>
|
||||
/// <param name="throwOnFailure">Whether this function should throw if the delete fails.</param>
|
||||
/// <param name="renameFirst">Try to rename this object first before deleting. Can help prevent partial delete of folders.</param>
|
||||
/// <param name="logger">Logger for diagnostic messages.</param>
|
||||
/// <returns>True if the file system object was deleted, false otherwise.</returns>
|
||||
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<T>(this Func<T> 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<Task> 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<T> RetryAsync<T>(this Func<Task<T>> 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,7 +2,7 @@
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace Velopack
|
||||
namespace Velopack.Util
|
||||
{
|
||||
[ExcludeFromCodeCoverage]
|
||||
internal static class LoggerExtensions
|
||||
154
src/lib-csharp/Util/PathUtil.cs
Normal file
154
src/lib-csharp/Util/PathUtil.cs
Normal file
@@ -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);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Escapes file name such that the file name is safe for writing to disk in the packages folder
|
||||
/// </summary>
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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<int> GetExitCodeAsync(this Process p)
|
||||
{
|
||||
#if NET5_0_OR_GREATER
|
||||
await p.WaitForExitAsync().ConfigureAwait(false);
|
||||
return p.ExitCode;
|
||||
#else
|
||||
var tcs = new TaskCompletionSource<int>();
|
||||
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<string> args, out string debug)
|
||||
@@ -210,4 +234,4 @@ namespace Velopack
|
||||
private const char Quote = '\"';
|
||||
private const char Backslash = '\\';
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
{
|
||||
83
src/lib-csharp/Util/TempUtil.cs
Normal file
83
src/lib-csharp/Util/TempUtil.cs
Normal file
@@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
};
|
||||
|
||||
@@ -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<RuntimeCpu>(nativeMachine, out var val)) {
|
||||
if (CoreUtil.TryParseEnumU16<RuntimeCpu>(nativeMachine, out var val)) {
|
||||
SystemArch = val;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<RuntimeCpu>(String.IsNullOrWhiteSpace(archstr) ? "x64" : archstr, true, out var cpu);
|
||||
if (!archValid) {
|
||||
throw new ArgumentException($"Invalid machine architecture '{archstr}'. " +
|
||||
$"Valid values: {String.Join(", ", Utility.GetEnumValues<RuntimeCpu>())}");
|
||||
$"Valid values: {String.Join(", ", CoreUtil.GetEnumValues<RuntimeCpu>())}");
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
@@ -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<ShortcutLocation>();
|
||||
var locations = CoreUtil.GetEnumValues<ShortcutLocation>();
|
||||
return locations
|
||||
.Where(x => x != ShortcutLocation.None)
|
||||
.Where(x => flag.HasFlag(x))
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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<GitHubDownloadO
|
||||
}
|
||||
|
||||
// check if there is an existing releasesFile to merge
|
||||
var releasesFileName = Utility.GetVeloReleaseIndexName(options.Channel);
|
||||
var releasesFileName = CoreUtil.GetVeloReleaseIndexName(options.Channel);
|
||||
var releaseAsset = release.Assets.FirstOrDefault(a => 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.");
|
||||
|
||||
@@ -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<GiteaDownloadOptions, GiteaSourc
|
||||
}
|
||||
|
||||
// check if there is an existing releasesFile to merge
|
||||
var releasesFileName = Utility.GetVeloReleaseIndexName(options.Channel);
|
||||
var releasesFileName = CoreUtil.GetVeloReleaseIndexName(options.Channel);
|
||||
var releaseAsset = release.Assets.FirstOrDefault(a => 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.");
|
||||
|
||||
@@ -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<LocalDownloadOpt
|
||||
{
|
||||
var target = Path.Combine(client.FullName, key);
|
||||
Log.Info("Deleting: " + target);
|
||||
Utility.DeleteFileOrDirectoryHard(target);
|
||||
IoUtil.DeleteFileOrDirectoryHard(target);
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
using Amazon.S3;
|
||||
using Amazon.S3.Model;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Velopack.Util;
|
||||
|
||||
namespace Velopack.Deployment;
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using System.Text;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Velopack.Packaging;
|
||||
using Velopack.Util;
|
||||
|
||||
namespace Velopack.Deployment;
|
||||
|
||||
@@ -37,7 +38,7 @@ public abstract class ObjectRepository<TDown, TUp, TClient> : DownRepository<TDo
|
||||
|
||||
protected override async Task<VelopackAssetFeed> 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<TDown, TUp, TClient> : DownRepository<TDo
|
||||
|
||||
var newReleaseFeed = new VelopackAssetFeed { Assets = releaseEntries };
|
||||
|
||||
using var _1 = Utility.GetTempFileName(out var tmpReleases);
|
||||
using var _1 = TempUtil.GetTempFileName(out var tmpReleases);
|
||||
File.WriteAllText(tmpReleases, ReleaseEntryHelper.GetAssetFeedJson(newReleaseFeed));
|
||||
var releasesName = Utility.GetVeloReleaseIndexName(options.Channel);
|
||||
var releasesName = CoreUtil.GetVeloReleaseIndexName(options.Channel);
|
||||
await UploadObject(client, releasesName, new FileInfo(tmpReleases), true, noCache: true);
|
||||
|
||||
#pragma warning disable CS0612 // Type or member is obsolete
|
||||
var legacyKey = Utility.GetReleasesFileName(options.Channel);
|
||||
var legacyKey = CoreUtil.GetReleasesFileName(options.Channel);
|
||||
#pragma warning restore CS0612 // Type or member is obsolete
|
||||
using var _2 = Utility.GetTempFileName(out var tmpReleases2);
|
||||
using var _2 = TempUtil.GetTempFileName(out var tmpReleases2);
|
||||
File.WriteAllText(tmpReleases2, ReleaseEntryHelper.GetLegacyMigrationReleaseFeedString(newReleaseFeed));
|
||||
await UploadObject(client, legacyKey, new FileInfo(tmpReleases2), true, noCache: true);
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@ using Microsoft.Extensions.Logging;
|
||||
using Velopack.Packaging;
|
||||
using Velopack.Packaging.Abstractions;
|
||||
using Velopack.Sources;
|
||||
using Velopack.Util;
|
||||
|
||||
namespace Velopack.Deployment;
|
||||
|
||||
@@ -83,8 +84,8 @@ public abstract class DownRepository<TDown> : IRepositoryCanDownload<TDown>
|
||||
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<TDown> : IRepositoryCanDownload<TDown>
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using ELFSharp.ELF;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Velopack.Packaging.Abstractions;
|
||||
using Velopack.Util;
|
||||
|
||||
namespace Velopack.Packaging.Unix.Commands;
|
||||
|
||||
|
||||
@@ -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<OsxBundleOptions>
|
||||
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();
|
||||
|
||||
@@ -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<OsxPackOptions>
|
||||
|
||||
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<OsxPackOptions>
|
||||
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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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<WindowsPackOptions>
|
||||
protected override Task CodeSign(Action<int> 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<WindowsPackOptions>
|
||||
// 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<WindowsPackOptions>
|
||||
protected override Task CreateSetupPackage(Action<int> 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<WindowsPackOptions>
|
||||
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<WindowsPackOptions>
|
||||
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<WindowsPackOptions>
|
||||
// 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<WindowsPackOptions>
|
||||
}
|
||||
|
||||
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();
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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);
|
||||
});
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
using Velopack.Json;
|
||||
using Velopack.Packaging.Exceptions;
|
||||
using Velopack.Packaging.Exceptions;
|
||||
|
||||
namespace Velopack.Packaging;
|
||||
|
||||
|
||||
@@ -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<DeltaPatchOptions>
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -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<T> : ICommand<T>
|
||||
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<T> : ICommand<T>
|
||||
|
||||
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<T> : ICommand<T>
|
||||
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<T> : ICommand<T>
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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<VelopackAsset> GetLatestAssets()
|
||||
@@ -72,7 +72,7 @@ public class ReleaseEntryHelper
|
||||
if (!_releases.ContainsKey(_channel) || !_releases[_channel].Any())
|
||||
return Enumerable.Empty<VelopackAsset>();
|
||||
|
||||
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(),
|
||||
};
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using Velopack.Packaging.Abstractions;
|
||||
using Velopack.Util;
|
||||
|
||||
namespace Velopack.Vpk.Logging;
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using System.Threading;
|
||||
using Spectre.Console;
|
||||
using Velopack.Packaging.Abstractions;
|
||||
using Velopack.Util;
|
||||
|
||||
namespace Velopack.Vpk.Logging;
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using System.Threading;
|
||||
using NuGet.Protocol.Core.Types;
|
||||
using Velopack.Util;
|
||||
|
||||
namespace Velopack.Vpk.Updates;
|
||||
|
||||
|
||||
@@ -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<LocalDownloadCommand>
|
||||
{
|
||||
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<LocalDownloadCommand>
|
||||
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}");
|
||||
|
||||
|
||||
@@ -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<S3DeploymentTests>();
|
||||
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()
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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<GithubDeploymentTests>();
|
||||
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<GithubDeploymentTests>();
|
||||
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<GithubDeploymentTests>();
|
||||
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<GithubDeploymentTests>();
|
||||
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<GithubDeploymentTests>();
|
||||
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<GithubDeploymentTests>();
|
||||
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";
|
||||
|
||||
@@ -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<OsxPackTests>();
|
||||
|
||||
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";
|
||||
|
||||
@@ -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<ResourceEditTests>();
|
||||
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<ResourceEditTests>();
|
||||
|
||||
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<ResourceEditTests>();
|
||||
|
||||
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<ResourceEditTests>();
|
||||
|
||||
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<ResourceEditTests>();
|
||||
|
||||
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<ResourceEditTests>();
|
||||
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<ResourceEditTests>();
|
||||
using var _1 = Utility.GetTempFileName(out var tempFile);
|
||||
using var _1 = TempUtil.GetTempFileName(out var tempFile);
|
||||
CreateTestPEFileWithoutRsrc(tempFile);
|
||||
|
||||
var nuspec = PathHelper.GetFixture("FullNuspec.nuspec");
|
||||
|
||||
@@ -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<S3DeploymentTests>();
|
||||
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()
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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<WindowsPackTests>();
|
||||
|
||||
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<WindowsPackTests>();
|
||||
|
||||
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<WindowsPackTests>();
|
||||
|
||||
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<WindowsPackTests>();
|
||||
|
||||
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<WindowsPackTests>();
|
||||
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<WindowsPackTests>();
|
||||
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<WindowsPackTests>();
|
||||
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<WindowsPackTests>();
|
||||
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);
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using System.Diagnostics;
|
||||
using System.Runtime.InteropServices;
|
||||
using Velopack.Util;
|
||||
|
||||
namespace Velopack.Tests;
|
||||
|
||||
|
||||
@@ -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<ShortcutTests>();
|
||||
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"));
|
||||
|
||||
|
||||
@@ -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<IOException>(() => 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<IOException>(() => 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<IOException>(() => 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<IOException>(() => 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<IOException>(() => 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<IOException>(() => 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);
|
||||
|
||||
@@ -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))
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using System.Net;
|
||||
using System.Text;
|
||||
using Velopack.Util;
|
||||
|
||||
namespace Velopack.Tests;
|
||||
|
||||
|
||||
@@ -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<UpdateManagerTests>();
|
||||
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<UpdateManagerTests>();
|
||||
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<UpdateManagerTests>();
|
||||
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<UpdateManagerTests>();
|
||||
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<UpdateManagerTests>();
|
||||
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<UpdateManagerTests>();
|
||||
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<UpdateManagerTests>();
|
||||
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<UpdateManagerTests>();
|
||||
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<UpdateManagerTests>();
|
||||
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<UpdateManagerTests>();
|
||||
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<UpdateManagerTests>();
|
||||
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<UpdateManagerTests>();
|
||||
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<UpdateManagerTests>();
|
||||
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<UpdateManagerTests>();
|
||||
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<UpdateManagerTests>();
|
||||
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<UpdateManagerTests>();
|
||||
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<UpdateManagerTests>();
|
||||
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<UpdateManagerTests>();
|
||||
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);
|
||||
|
||||
|
||||
@@ -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<UtilityTests>();
|
||||
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<int> prog = new List<int>();
|
||||
using (Utility.GetTempFileName(out var tempPath))
|
||||
using (TempUtil.GetTempFileName(out var tempPath))
|
||||
await dl.DownloadFile(testUrl, tempPath, prog.Add);
|
||||
|
||||
Assert.True(prog.Count > 10);
|
||||
|
||||
@@ -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<ZipPackageTests>();
|
||||
|
||||
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);
|
||||
|
||||
Reference in New Issue
Block a user