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;
|
||||||
using System.Text.RegularExpressions;
|
using System.Text.RegularExpressions;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
|
using Velopack.Util;
|
||||||
|
|
||||||
namespace Velopack.Compression
|
namespace Velopack.Compression
|
||||||
{
|
{
|
||||||
@@ -29,7 +30,7 @@ namespace Velopack.Compression
|
|||||||
|
|
||||||
Log.Info($"Applying delta package from {deltaPackageZip} to delta staging directory.");
|
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);
|
EasyZip.ExtractZipToDirectory(Log, deltaPackageZip, deltaPath);
|
||||||
progress(10);
|
progress(10);
|
||||||
|
|
||||||
@@ -51,7 +52,7 @@ namespace Velopack.Compression
|
|||||||
pathsVisited.Add(DIFF_SUFFIX.Replace(file, "").ToLowerInvariant());
|
pathsVisited.Add(DIFF_SUFFIX.Replace(file, "").ToLowerInvariant());
|
||||||
applyDiffToFile(deltaPath, file, workingPath);
|
applyDiffToFile(deltaPath, file, workingPath);
|
||||||
var perc = (index + 1) / (double) files.Length * 100;
|
var perc = (index + 1) / (double) files.Length * 100;
|
||||||
progress(Utility.CalculateProgress((int) perc, 10, 90));
|
progress(CoreUtil.CalculateProgress((int) perc, 10, 90));
|
||||||
}
|
}
|
||||||
|
|
||||||
progress(80);
|
progress(80);
|
||||||
@@ -118,7 +119,7 @@ namespace Velopack.Compression
|
|||||||
var inputFile = Path.Combine(deltaPath, relativeFilePath);
|
var inputFile = Path.Combine(deltaPath, relativeFilePath);
|
||||||
var finalTarget = Path.Combine(workingDirectory, DIFF_SUFFIX.Replace(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
|
// NB: Zero-length diffs indicate the file hasn't actually changed
|
||||||
if (new FileInfo(inputFile).Length == 0) {
|
if (new FileInfo(inputFile).Length == 0) {
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
|
using Velopack.Util;
|
||||||
|
|
||||||
namespace Velopack.Compression
|
namespace Velopack.Compression
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ using System.Text;
|
|||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
|
using Velopack.Util;
|
||||||
|
|
||||||
namespace Velopack.Compression
|
namespace Velopack.Compression
|
||||||
{
|
{
|
||||||
@@ -17,7 +18,7 @@ namespace Velopack.Compression
|
|||||||
public static void ExtractZipToDirectory(ILogger logger, string inputFile, string outputDirectory, bool expandSymlinks = false)
|
public static void ExtractZipToDirectory(ILogger logger, string inputFile, string outputDirectory, bool expandSymlinks = false)
|
||||||
{
|
{
|
||||||
logger.Debug($"Extracting '{inputFile}' to '{outputDirectory}' using System.IO.Compression...");
|
logger.Debug($"Extracting '{inputFile}' to '{outputDirectory}' using System.IO.Compression...");
|
||||||
Utility.DeleteFileOrDirectoryHard(outputDirectory);
|
IoUtil.DeleteFileOrDirectoryHard(outputDirectory);
|
||||||
|
|
||||||
List<ZipArchiveEntry> symlinks = new();
|
List<ZipArchiveEntry> symlinks = new();
|
||||||
using (ZipArchive archive = ZipFile.Open(inputFile, ZipArchiveMode.Read)) {
|
using (ZipArchive archive = ZipFile.Open(inputFile, ZipArchiveMode.Read)) {
|
||||||
@@ -58,7 +59,7 @@ namespace Velopack.Compression
|
|||||||
using (var reader = new StreamReader(source.Open())) {
|
using (var reader = new StreamReader(source.Open())) {
|
||||||
var targetPath = reader.ReadToEnd();
|
var targetPath = reader.ReadToEnd();
|
||||||
var absolute = Path.GetFullPath(Path.Combine(Path.GetDirectoryName(fileDestinationPath)!, targetPath));
|
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");
|
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 dir is a symlink, write it as a file containing path to target
|
||||||
if (SymbolicLink.Exists(dir.FullName)) {
|
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");
|
throw new IOException("IO_SymlinkTargetNotInDirectory");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -169,7 +170,7 @@ namespace Velopack.Compression
|
|||||||
|
|
||||||
if (SymbolicLink.Exists(fileInfo.FullName)) {
|
if (SymbolicLink.Exists(fileInfo.FullName)) {
|
||||||
// Handle symlink: Store the symlink target instead of its content
|
// 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");
|
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 Microsoft.Extensions.Logging;
|
||||||
using NuGet.Versioning;
|
using NuGet.Versioning;
|
||||||
using Velopack.NuGet;
|
using Velopack.NuGet;
|
||||||
|
using Velopack.Util;
|
||||||
|
|
||||||
namespace Velopack.Locators
|
namespace Velopack.Locators
|
||||||
{
|
{
|
||||||
@@ -33,7 +34,7 @@ namespace Velopack.Locators
|
|||||||
public override string? Channel { get; }
|
public override string? Channel { get; }
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public override string? AppTempDir => CreateSubDirIfDoesNotExist(Utility.GetDefaultTempBaseDirectory(), AppId);
|
public override string? AppTempDir => CreateSubDirIfDoesNotExist(TempUtil.GetDefaultTempBaseDirectory(), AppId);
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public override string? PackagesDir => CreateSubDirIfDoesNotExist(PersistentTempDir, "packages");
|
public override string? PackagesDir => CreateSubDirIfDoesNotExist(PersistentTempDir, "packages");
|
||||||
@@ -70,7 +71,7 @@ namespace Velopack.Locators
|
|||||||
var rootDir = ourPath.Substring(0, ix);
|
var rootDir = ourPath.Substring(0, ix);
|
||||||
var contentsDir = Path.Combine(rootDir, "usr", "bin");
|
var contentsDir = Path.Combine(rootDir, "usr", "bin");
|
||||||
var updateExe = Path.Combine(contentsDir, "UpdateNix");
|
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 (!String.IsNullOrEmpty(AppImagePath) && File.Exists(AppImagePath)) {
|
||||||
if (File.Exists(updateExe) && PackageManifest.TryParseFromFile(metadataPath, out var manifest)) {
|
if (File.Exists(updateExe) && PackageManifest.TryParseFromFile(metadataPath, out var manifest)) {
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ using System.Runtime.Versioning;
|
|||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
using NuGet.Versioning;
|
using NuGet.Versioning;
|
||||||
using Velopack.NuGet;
|
using Velopack.NuGet;
|
||||||
|
using Velopack.Util;
|
||||||
|
|
||||||
namespace Velopack.Locators
|
namespace Velopack.Locators
|
||||||
{
|
{
|
||||||
@@ -30,7 +31,7 @@ namespace Velopack.Locators
|
|||||||
public override string? AppContentDir => RootAppDir;
|
public override string? AppContentDir => RootAppDir;
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public override string? AppTempDir => CreateSubDirIfDoesNotExist(Utility.GetDefaultTempBaseDirectory(), AppId);
|
public override string? AppTempDir => CreateSubDirIfDoesNotExist(TempUtil.GetDefaultTempBaseDirectory(), AppId);
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public override string? PackagesDir => CreateSubDirIfDoesNotExist(CachesAppDir, "packages");
|
public override string? PackagesDir => CreateSubDirIfDoesNotExist(CachesAppDir, "packages");
|
||||||
@@ -68,7 +69,7 @@ namespace Velopack.Locators
|
|||||||
var contentsDir = Path.Combine(appPath, "Contents");
|
var contentsDir = Path.Combine(appPath, "Contents");
|
||||||
var macosDir = Path.Combine(contentsDir, "MacOS");
|
var macosDir = Path.Combine(contentsDir, "MacOS");
|
||||||
var updateExe = Path.Combine(macosDir, "UpdateMac");
|
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)) {
|
if (File.Exists(updateExe) && PackageManifest.TryParseFromFile(metadataPath, out var manifest)) {
|
||||||
Log.Info("Located valid manifest file at: " + metadataPath);
|
Log.Info("Located valid manifest file at: " + metadataPath);
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ using System.Text;
|
|||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
using Microsoft.Extensions.Logging.Abstractions;
|
using Microsoft.Extensions.Logging.Abstractions;
|
||||||
using NuGet.Versioning;
|
using NuGet.Versioning;
|
||||||
|
using Velopack.Util;
|
||||||
|
|
||||||
namespace Velopack.Locators
|
namespace Velopack.Locators
|
||||||
{
|
{
|
||||||
@@ -162,7 +163,7 @@ namespace Velopack.Locators
|
|||||||
var buf = new byte[4096];
|
var buf = new byte[4096];
|
||||||
prng.NextBytes(buf);
|
prng.NextBytes(buf);
|
||||||
|
|
||||||
ret = Utility.CreateGuidFromHash(buf);
|
ret = GuidUtil.CreateGuidFromHash(buf);
|
||||||
try {
|
try {
|
||||||
File.WriteAllText(stagedUserIdFile, ret.ToString(), Encoding.UTF8);
|
File.WriteAllText(stagedUserIdFile, ret.ToString(), Encoding.UTF8);
|
||||||
Log.Info($"Generated new staging userId: {ret}");
|
Log.Info($"Generated new staging userId: {ret}");
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
using System.IO;
|
using System.IO;
|
||||||
|
using Velopack.Util;
|
||||||
|
|
||||||
namespace Velopack.Locators
|
namespace Velopack.Locators
|
||||||
{
|
{
|
||||||
@@ -13,7 +14,7 @@ namespace Velopack.Locators
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public static string GetLocalPackagePath(this IVelopackLocator locator, VelopackAsset velopackAsset)
|
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 Microsoft.Extensions.Logging;
|
||||||
using NuGet.Versioning;
|
using NuGet.Versioning;
|
||||||
using Velopack.NuGet;
|
using Velopack.NuGet;
|
||||||
|
using Velopack.Util;
|
||||||
|
|
||||||
namespace Velopack.Locators
|
namespace Velopack.Locators
|
||||||
{
|
{
|
||||||
@@ -69,7 +70,7 @@ namespace Velopack.Locators
|
|||||||
if (File.Exists(possibleUpdateExe)) {
|
if (File.Exists(possibleUpdateExe)) {
|
||||||
Log.Info("Update.exe found in parent directory");
|
Log.Info("Update.exe found in parent directory");
|
||||||
// we're running in a directory with an Update.exe in the 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)) {
|
if (PackageManifest.TryParseFromFile(manifestFile, out var manifest)) {
|
||||||
// ideal, the info we need is in a manifest file.
|
// ideal, the info we need is in a manifest file.
|
||||||
Log.Info("Located valid manifest file at: " + manifestFile);
|
Log.Info("Located valid manifest file at: " + manifestFile);
|
||||||
@@ -79,7 +80,7 @@ namespace Velopack.Locators
|
|||||||
UpdateExePath = possibleUpdateExe;
|
UpdateExePath = possibleUpdateExe;
|
||||||
AppContentDir = myDirPath;
|
AppContentDir = myDirPath;
|
||||||
Channel = manifest.Channel;
|
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.
|
// 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.");
|
Log.Warn("Legacy app-* directory detected, sq.version not found. Using directory name for AppId and Version.");
|
||||||
AppId = Path.GetFileName(Path.GetDirectoryName(possibleUpdateExe));
|
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.
|
// this is an attempt to handle the case where we are running in a nested current directory.
|
||||||
var rootDir = ourExePath.Substring(0, ixCurrent);
|
var rootDir = ourExePath.Substring(0, ixCurrent);
|
||||||
var currentDir = Path.Combine(rootDir, "current");
|
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"));
|
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.
|
// 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)) {
|
if (File.Exists(possibleUpdateExe) && PackageManifest.TryParseFromFile(manifestFile, out var manifest)) {
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ using System.Text;
|
|||||||
using System.Text.RegularExpressions;
|
using System.Text.RegularExpressions;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using NuGet.Versioning;
|
using NuGet.Versioning;
|
||||||
|
using Velopack.Util;
|
||||||
|
|
||||||
namespace Velopack
|
namespace Velopack
|
||||||
{
|
{
|
||||||
@@ -225,7 +226,7 @@ namespace Velopack
|
|||||||
string baseUrl = null;
|
string baseUrl = null;
|
||||||
string query = null;
|
string query = null;
|
||||||
|
|
||||||
if (Utility.IsHttpUrl(filename)) {
|
if (HttpUtil.IsHttpUrl(filename)) {
|
||||||
var uri = new Uri(filename);
|
var uri = new Uri(filename);
|
||||||
var path = uri.LocalPath;
|
var path = uri.LocalPath;
|
||||||
var authority = uri.GetLeftPart(UriPartial.Authority);
|
var authority = uri.GetLeftPart(UriPartial.Authority);
|
||||||
@@ -281,7 +282,7 @@ namespace Velopack
|
|||||||
return new ReleaseEntry[0];
|
return new ReleaseEntry[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
fileContents = Utility.RemoveByteOrderMarkerIfPresent(fileContents);
|
fileContents = CoreUtil.RemoveByteOrderMarkerIfPresent(fileContents);
|
||||||
|
|
||||||
var ret = fileContents.Split('\n')
|
var ret = fileContents.Split('\n')
|
||||||
.Where(x => !String.IsNullOrWhiteSpace(x))
|
.Where(x => !String.IsNullOrWhiteSpace(x))
|
||||||
@@ -302,7 +303,7 @@ namespace Velopack
|
|||||||
return new ReleaseEntry[0];
|
return new ReleaseEntry[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
fileContents = Utility.RemoveByteOrderMarkerIfPresent(fileContents);
|
fileContents = CoreUtil.RemoveByteOrderMarkerIfPresent(fileContents);
|
||||||
|
|
||||||
var ret = fileContents.Split('\n')
|
var ret = fileContents.Split('\n')
|
||||||
.Where(x => !String.IsNullOrWhiteSpace(x))
|
.Where(x => !String.IsNullOrWhiteSpace(x))
|
||||||
@@ -350,7 +351,7 @@ namespace Velopack
|
|||||||
Contract.Requires(file != null && file.CanRead);
|
Contract.Requires(file != null && file.CanRead);
|
||||||
Contract.Requires(!String.IsNullOrEmpty(filename));
|
Contract.Requires(!String.IsNullOrEmpty(filename));
|
||||||
|
|
||||||
var hash = Utility.CalculateStreamSHA1(file);
|
var hash = IoUtil.CalculateStreamSHA1(file);
|
||||||
return new ReleaseEntry(hash, filename, file.Length, baseUrl);
|
return new ReleaseEntry(hash, filename, file.Length, baseUrl);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -387,7 +388,7 @@ namespace Velopack
|
|||||||
var entries = entriesQueue.ToList();
|
var entries = entriesQueue.ToList();
|
||||||
|
|
||||||
if (writeToDisk) {
|
if (writeToDisk) {
|
||||||
using var _ = Utility.GetTempFileName(out var tempFile);
|
using var _ = TempUtil.GetTempFileName(out var tempFile);
|
||||||
using (var of = File.OpenWrite(tempFile)) {
|
using (var of = File.OpenWrite(tempFile)) {
|
||||||
if (entries.Count > 0) WriteReleaseFile(entries, of);
|
if (entries.Count > 0) WriteReleaseFile(entries, of);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ using System.Linq;
|
|||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
|
using Velopack.Util;
|
||||||
|
|
||||||
namespace Velopack.Sources
|
namespace Velopack.Sources
|
||||||
{
|
{
|
||||||
@@ -44,7 +45,7 @@ namespace Velopack.Sources
|
|||||||
RepoUri = new Uri(repoUrl.TrimEnd('/'));
|
RepoUri = new Uri(repoUrl.TrimEnd('/'));
|
||||||
AccessToken = accessToken;
|
AccessToken = accessToken;
|
||||||
Prerelease = prerelease;
|
Prerelease = prerelease;
|
||||||
Downloader = downloader ?? Utility.CreateDefaultDownloader();
|
Downloader = downloader ?? HttpUtil.CreateDefaultDownloader();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
@@ -69,7 +70,7 @@ namespace Velopack.Sources
|
|||||||
return new VelopackAssetFeed();
|
return new VelopackAssetFeed();
|
||||||
}
|
}
|
||||||
|
|
||||||
var releasesFileName = Utility.GetVeloReleaseIndexName(channel);
|
var releasesFileName = CoreUtil.GetVeloReleaseIndexName(channel);
|
||||||
List<GitBaseAsset> entries = new List<GitBaseAsset>();
|
List<GitBaseAsset> entries = new List<GitBaseAsset>();
|
||||||
|
|
||||||
foreach (var r in releases) {
|
foreach (var r in releases) {
|
||||||
@@ -83,7 +84,7 @@ namespace Velopack.Sources
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
var releaseBytes = await Downloader.DownloadBytes(assetUrl, Authorization, "application/octet-stream").ConfigureAwait(false);
|
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);
|
var feed = VelopackAssetFeed.FromJson(txt);
|
||||||
foreach (var f in feed.Assets) {
|
foreach (var f in feed.Assets) {
|
||||||
entries.Add(new GitBaseAsset(f, r));
|
entries.Add(new GitBaseAsset(f, r));
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ using System.Collections.Generic;
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Text.Json.Serialization;
|
using System.Text.Json.Serialization;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Velopack.Json;
|
using Velopack.Util;
|
||||||
namespace Velopack.Sources
|
namespace Velopack.Sources
|
||||||
{
|
{
|
||||||
/// <summary> Describes a Gitea release, including attached assets. </summary>
|
/// <summary> Describes a Gitea release, including attached assets. </summary>
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ using System.Collections.Generic;
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Text.Json.Serialization;
|
using System.Text.Json.Serialization;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Velopack.Json;
|
using Velopack.Util;
|
||||||
|
|
||||||
namespace Velopack.Sources
|
namespace Velopack.Sources
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Text.Json.Serialization;
|
using System.Text.Json.Serialization;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Velopack.Json;
|
using Velopack.Util;
|
||||||
|
|
||||||
namespace Velopack.Sources
|
namespace Velopack.Sources
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ using System.Threading;
|
|||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
using Velopack.NuGet;
|
using Velopack.NuGet;
|
||||||
|
using Velopack.Util;
|
||||||
|
|
||||||
namespace Velopack.Sources
|
namespace Velopack.Sources
|
||||||
{
|
{
|
||||||
@@ -32,7 +33,7 @@ namespace Velopack.Sources
|
|||||||
}
|
}
|
||||||
|
|
||||||
// if a feed exists in the folder, let's use that.
|
// 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)) {
|
if (File.Exists(feedLoc)) {
|
||||||
logger.Debug($"Found local file feed at '{feedLoc}'.");
|
logger.Debug($"Found local file feed at '{feedLoc}'.");
|
||||||
return Task.FromResult(VelopackAssetFeed.FromJson(File.ReadAllText(feedLoc)));
|
return Task.FromResult(VelopackAssetFeed.FromJson(File.ReadAllText(feedLoc)));
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ using System.Collections.Generic;
|
|||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
|
using Velopack.Util;
|
||||||
|
|
||||||
namespace Velopack.Sources
|
namespace Velopack.Sources
|
||||||
{
|
{
|
||||||
@@ -28,14 +29,14 @@ namespace Velopack.Sources
|
|||||||
public SimpleWebSource(Uri baseUri, IFileDownloader? downloader = null)
|
public SimpleWebSource(Uri baseUri, IFileDownloader? downloader = null)
|
||||||
{
|
{
|
||||||
BaseUri = baseUri;
|
BaseUri = baseUri;
|
||||||
Downloader = downloader ?? Utility.CreateDefaultDownloader();
|
Downloader = downloader ?? HttpUtil.CreateDefaultDownloader();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public async virtual Task<VelopackAssetFeed> GetReleaseFeed(ILogger logger, string channel, Guid? stagingId = null, VelopackAsset? latestLocalRelease = null)
|
public async virtual Task<VelopackAssetFeed> GetReleaseFeed(ILogger logger, string channel, Guid? stagingId = null, VelopackAsset? latestLocalRelease = null)
|
||||||
{
|
{
|
||||||
var releaseFilename = Utility.GetVeloReleaseIndexName(channel);
|
var releaseFilename = CoreUtil.GetVeloReleaseIndexName(channel);
|
||||||
var uri = Utility.AppendPathToUri(BaseUri, releaseFilename);
|
var uri = HttpUtil.AppendPathToUri(BaseUri, releaseFilename);
|
||||||
var args = new Dictionary<string, string>();
|
var args = new Dictionary<string, string>();
|
||||||
|
|
||||||
if (VelopackRuntimeInfo.SystemArch != RuntimeCpu.Unknown) {
|
if (VelopackRuntimeInfo.SystemArch != RuntimeCpu.Unknown) {
|
||||||
@@ -52,7 +53,7 @@ namespace Velopack.Sources
|
|||||||
args.Add("localVersion", latestLocalRelease.Version.ToString());
|
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}'.");
|
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
|
// 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
|
// 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
|
? releaseEntry.FileName
|
||||||
: Utility.AppendPathToUri(sourceBaseUri, releaseEntry.FileName).ToString();
|
: HttpUtil.AppendPathToUri(sourceBaseUri, releaseEntry.FileName).ToString();
|
||||||
|
|
||||||
logger.Info($"Downloading '{releaseEntry.FileName}' from '{source}'.");
|
logger.Info($"Downloading '{releaseEntry.FileName}' from '{source}'.");
|
||||||
await Downloader.DownloadFile(source, localFile, progress, cancelToken: cancelToken).ConfigureAwait(false);
|
await Downloader.DownloadFile(source, localFile, progress, cancelToken: cancelToken).ConfigureAwait(false);
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ using System.Collections.Generic;
|
|||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
using Velopack.Json;
|
using Velopack.Util;
|
||||||
using Velopack.Locators;
|
using Velopack.Locators;
|
||||||
|
|
||||||
namespace Velopack.Sources
|
namespace Velopack.Sources
|
||||||
@@ -19,7 +19,7 @@ namespace Velopack.Sources
|
|||||||
IFileDownloader? downloader = null)
|
IFileDownloader? downloader = null)
|
||||||
{
|
{
|
||||||
BaseUri = new Uri(baseUri);
|
BaseUri = new Uri(baseUri);
|
||||||
Downloader = downloader ?? Utility.CreateDefaultDownloader();
|
Downloader = downloader ?? HttpUtil.CreateDefaultDownloader();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary> The URL of the server hosting packages to update to. </summary>
|
/// <summary> The URL of the server hosting packages to update to. </summary>
|
||||||
@@ -33,7 +33,7 @@ namespace Velopack.Sources
|
|||||||
VelopackAsset? latestLocalRelease = null)
|
VelopackAsset? latestLocalRelease = null)
|
||||||
{
|
{
|
||||||
Uri baseUri = new(BaseUri, $"v1.0/manifest/");
|
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>();
|
var args = new Dictionary<string, string>();
|
||||||
|
|
||||||
if (VelopackRuntimeInfo.SystemArch != RuntimeCpu.Unknown) {
|
if (VelopackRuntimeInfo.SystemArch != RuntimeCpu.Unknown) {
|
||||||
@@ -52,7 +52,7 @@ namespace Velopack.Sources
|
|||||||
args.Add("id", VelopackLocator.GetDefault(logger).AppId ?? "");
|
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);
|
logger.LogInformation("Downloading releases from '{Uri}'.", uriAndQuery);
|
||||||
|
|
||||||
@@ -73,7 +73,7 @@ namespace Velopack.Sources
|
|||||||
}
|
}
|
||||||
if (localFile is null) throw new ArgumentNullException(nameof(localFile));
|
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}");
|
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;
|
||||||
using Microsoft.Extensions.Logging.Abstractions;
|
using Microsoft.Extensions.Logging.Abstractions;
|
||||||
using Velopack.Locators;
|
using Velopack.Locators;
|
||||||
|
using Velopack.Util;
|
||||||
|
|
||||||
namespace Velopack
|
namespace Velopack
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ using Velopack.Compression;
|
|||||||
using Velopack.Locators;
|
using Velopack.Locators;
|
||||||
using Velopack.NuGet;
|
using Velopack.NuGet;
|
||||||
using Velopack.Sources;
|
using Velopack.Sources;
|
||||||
|
using Velopack.Util;
|
||||||
|
|
||||||
namespace Velopack
|
namespace Velopack
|
||||||
{
|
{
|
||||||
@@ -126,7 +127,7 @@ namespace Velopack
|
|||||||
var feedObj = await Source.GetReleaseFeed(Log, Channel, betaId, latestLocalFull).ConfigureAwait(false);
|
var feedObj = await Source.GetReleaseFeed(Log, Channel, betaId, latestLocalFull).ConfigureAwait(false);
|
||||||
var feed = feedObj.Assets;
|
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) {
|
if (latestRemoteFull == null) {
|
||||||
Log.Info("No remote full releases found.");
|
Log.Info("No remote full releases found.");
|
||||||
return null;
|
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. " +
|
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.");
|
$"Only full update will be available.");
|
||||||
} else {
|
} 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);
|
string basePackagePath = Locator.GetLocalPackagePath(updates.BaseRelease);
|
||||||
if (!File.Exists(basePackagePath))
|
if (!File.Exists(basePackagePath))
|
||||||
throw new Exception($"Unable to find base package {basePackagePath} for delta update.");
|
throw new Exception($"Unable to find base package {basePackagePath} for delta update.");
|
||||||
EasyZip.ExtractZipToDirectory(Log, basePackagePath, deltaStagingDir);
|
EasyZip.ExtractZipToDirectory(Log, basePackagePath, deltaStagingDir);
|
||||||
|
|
||||||
reportProgress(10);
|
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);
|
.ConfigureAwait(false);
|
||||||
reportProgress(80);
|
reportProgress(80);
|
||||||
|
|
||||||
Log.Info("Delta updates completed, creating final update package.");
|
Log.Info("Delta updates completed, creating final update package.");
|
||||||
File.Delete(incompleteFile);
|
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);
|
cancelToken: cancelToken).ConfigureAwait(false);
|
||||||
File.Delete(completeFile);
|
File.Delete(completeFile);
|
||||||
File.Move(incompleteFile, completeFile);
|
File.Move(incompleteFile, completeFile);
|
||||||
@@ -297,7 +298,7 @@ namespace Velopack
|
|||||||
Log.Info("Verifying package checksum...");
|
Log.Info("Verifying package checksum...");
|
||||||
VerifyPackageChecksum(targetRelease, incompleteFile);
|
VerifyPackageChecksum(targetRelease, incompleteFile);
|
||||||
|
|
||||||
Utility.MoveFile(incompleteFile, completeFile, true);
|
IoUtil.MoveFile(incompleteFile, completeFile, true);
|
||||||
Log.Info("Full release download complete. Package moved to: " + completeFile);
|
Log.Info("Full release download complete. Package moved to: " + completeFile);
|
||||||
reportProgress(100);
|
reportProgress(100);
|
||||||
} finally {
|
} finally {
|
||||||
@@ -310,7 +311,7 @@ namespace Velopack
|
|||||||
if (zip.UpdateExeBytes == null) {
|
if (zip.UpdateExeBytes == null) {
|
||||||
Log.Error("Update.exe not found in package, skipping extraction.");
|
Log.Error("Update.exe not found in package, skipping extraction.");
|
||||||
} else {
|
} else {
|
||||||
await Utility.RetryAsync(async () => {
|
await IoUtil.RetryAsync(async () => {
|
||||||
using var ms = new MemoryStream(zip.UpdateExeBytes);
|
using var ms = new MemoryStream(zip.UpdateExeBytes);
|
||||||
using var fs = File.Create(updateExe);
|
using var fs = File.Create(updateExe);
|
||||||
await ms.CopyToAsync(fs).ConfigureAwait(false);
|
await ms.CopyToAsync(fs).ConfigureAwait(false);
|
||||||
@@ -352,7 +353,7 @@ namespace Velopack
|
|||||||
current -= component;
|
current -= component;
|
||||||
component = toIncrement / 100.0 * p;
|
component = toIncrement / 100.0 * p;
|
||||||
var progressOfStep = (int) Math.Round(current += component);
|
var progressOfStep = (int) Math.Round(current += component);
|
||||||
progress(Utility.CalculateProgress(progressOfStep, 0, 50));
|
progress(CoreUtil.CalculateProgress(progressOfStep, 0, 50));
|
||||||
}
|
}
|
||||||
}, cancelToken).ConfigureAwait(false);
|
}, cancelToken).ConfigureAwait(false);
|
||||||
VerifyPackageChecksum(x, targetFile);
|
VerifyPackageChecksum(x, targetFile);
|
||||||
@@ -372,7 +373,7 @@ namespace Velopack
|
|||||||
var packageFile = Locator.GetLocalPackagePath(rel);
|
var packageFile = Locator.GetLocalPackagePath(rel);
|
||||||
builder.ApplyDeltaPackageFast(extractedBasePackage, packageFile, x => {
|
builder.ApplyDeltaPackageFast(extractedBasePackage, packageFile, x => {
|
||||||
var progressOfStep = (int) (baseProgress + (progressStepSize * (x / 100d)));
|
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!;
|
var appPackageDir = Locator.PackagesDir!;
|
||||||
foreach (var l in Directory.EnumerateFiles(appPackageDir, "*.nupkg").ToArray()) {
|
foreach (var l in Directory.EnumerateFiles(appPackageDir, "*.nupkg").ToArray()) {
|
||||||
try {
|
try {
|
||||||
if (assetToKeep != null && Utility.FullPathEquals(l, assetToKeep)) {
|
if (assetToKeep != null && PathUtil.FullPathEquals(l, assetToKeep)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
Utility.DeleteFileOrDirectoryHard(l);
|
IoUtil.DeleteFileOrDirectoryHard(l);
|
||||||
Log.Trace(l + " deleted.");
|
Log.Trace(l + " deleted.");
|
||||||
} catch (Exception ex) {
|
} catch (Exception ex) {
|
||||||
Log.Warn(ex, "Failed to delete partial package: " + l);
|
Log.Warn(ex, "Failed to delete partial package: " + l);
|
||||||
@@ -404,7 +405,7 @@ namespace Velopack
|
|||||||
|
|
||||||
foreach (var l in Directory.EnumerateFiles(appPackageDir, "*.partial").ToArray()) {
|
foreach (var l in Directory.EnumerateFiles(appPackageDir, "*.partial").ToArray()) {
|
||||||
try {
|
try {
|
||||||
Utility.DeleteFileOrDirectoryHard(l);
|
IoUtil.DeleteFileOrDirectoryHard(l);
|
||||||
Log.Trace(l + " deleted.");
|
Log.Trace(l + " deleted.");
|
||||||
} catch (Exception ex) {
|
} catch (Exception ex) {
|
||||||
Log.Warn(ex, "Failed to delete partial package: " + l);
|
Log.Warn(ex, "Failed to delete partial package: " + l);
|
||||||
@@ -433,12 +434,12 @@ namespace Velopack
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(release.SHA256)) {
|
if (!string.IsNullOrEmpty(release.SHA256)) {
|
||||||
var hash = Utility.CalculateFileSHA256(targetPackage.FullName);
|
var hash = IoUtil.CalculateFileSHA256(targetPackage.FullName);
|
||||||
if (!hash.Equals(release.SHA256, StringComparison.Ordinal)) {
|
if (!hash.Equals(release.SHA256, StringComparison.Ordinal)) {
|
||||||
throw new ChecksumFailedException(targetPackage.FullName, $"SHA256 doesn't match ({release.SHA256} != {hash}).");
|
throw new ChecksumFailedException(targetPackage.FullName, $"SHA256 doesn't match ({release.SHA256} != {hash}).");
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
var hash = Utility.CalculateFileSHA1(targetPackage.FullName);
|
var hash = IoUtil.CalculateFileSHA1(targetPackage.FullName);
|
||||||
if (!hash.Equals(release.SHA1, StringComparison.OrdinalIgnoreCase)) {
|
if (!hash.Equals(release.SHA1, StringComparison.OrdinalIgnoreCase)) {
|
||||||
throw new ChecksumFailedException(targetPackage.FullName, $"SHA1 doesn't match ({release.SHA1} != {hash}).");
|
throw new ChecksumFailedException(targetPackage.FullName, $"SHA1 doesn't match ({release.SHA1} != {hash}).");
|
||||||
}
|
}
|
||||||
@@ -479,8 +480,8 @@ namespace Velopack
|
|||||||
if (String.IsNullOrWhiteSpace(urlOrPath)) {
|
if (String.IsNullOrWhiteSpace(urlOrPath)) {
|
||||||
throw new ArgumentException("Must pass a valid URL or file path to UpdateManager", nameof(urlOrPath));
|
throw new ArgumentException("Must pass a valid URL or file path to UpdateManager", nameof(urlOrPath));
|
||||||
}
|
}
|
||||||
if (Utility.IsHttpUrl(urlOrPath)) {
|
if (HttpUtil.IsHttpUrl(urlOrPath)) {
|
||||||
return new SimpleWebSource(urlOrPath, Utility.CreateDefaultDownloader());
|
return new SimpleWebSource(urlOrPath, HttpUtil.CreateDefaultDownloader());
|
||||||
} else {
|
} else {
|
||||||
return new SimpleFileSource(new DirectoryInfo(urlOrPath));
|
return new SimpleFileSource(new DirectoryInfo(urlOrPath));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ namespace System.Text.Json.Serialization
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
namespace Velopack.Json
|
namespace Velopack.Util
|
||||||
{
|
{
|
||||||
#if NET6_0_OR_GREATER
|
#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.Diagnostics.CodeAnalysis;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
|
|
||||||
namespace Velopack
|
namespace Velopack.Util
|
||||||
{
|
{
|
||||||
[ExcludeFromCodeCoverage]
|
[ExcludeFromCodeCoverage]
|
||||||
internal static class Disposable
|
internal static class Disposable
|
||||||
@@ -1,10 +1,13 @@
|
|||||||
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt
|
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Concurrent;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Diagnostics.CodeAnalysis;
|
using System.Diagnostics.CodeAnalysis;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
namespace Velopack
|
namespace Velopack.Util
|
||||||
{
|
{
|
||||||
[ExcludeFromCodeCoverage]
|
[ExcludeFromCodeCoverage]
|
||||||
internal static class EnumerableExtensions
|
internal static class EnumerableExtensions
|
||||||
@@ -45,6 +48,7 @@ namespace Velopack
|
|||||||
$" There were 2 or more.");
|
$" There were 2 or more.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -72,14 +76,14 @@ namespace Velopack
|
|||||||
/// <param name="source">Source sequence.</param>
|
/// <param name="source">Source sequence.</param>
|
||||||
/// <param name="keySelector">Key selector used to extract the key for each element in the 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>
|
/// <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)
|
if (source == null)
|
||||||
throw new ArgumentNullException("source");
|
throw new ArgumentNullException("source");
|
||||||
if (keySelector == null)
|
if (keySelector == null)
|
||||||
throw new ArgumentNullException("keySelector");
|
throw new ArgumentNullException("keySelector");
|
||||||
|
|
||||||
return MaxBy(source, keySelector, Comparer<TKey>.Default);
|
return MaxByPolyfill(source, keySelector, Comparer<TKey>.Default);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -91,7 +95,7 @@ namespace Velopack
|
|||||||
/// <param name="keySelector">Key selector used to extract the key for each element in the sequence.</param>
|
/// <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>
|
/// <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>
|
/// <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 (source == null) throw new ArgumentNullException("source");
|
||||||
if (keySelector == null) throw new ArgumentNullException("keySelector");
|
if (keySelector == null) throw new ArgumentNullException("keySelector");
|
||||||
@@ -127,5 +131,22 @@ namespace Velopack
|
|||||||
|
|
||||||
return result;
|
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 System.Diagnostics.CodeAnalysis;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
|
|
||||||
namespace Velopack
|
namespace Velopack.Util
|
||||||
{
|
{
|
||||||
[ExcludeFromCodeCoverage]
|
[ExcludeFromCodeCoverage]
|
||||||
internal static class LoggerExtensions
|
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.Diagnostics.CodeAnalysis;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
|
|
||||||
namespace Velopack
|
namespace Velopack.Util
|
||||||
{
|
{
|
||||||
[ExcludeFromCodeCoverage]
|
[ExcludeFromCodeCoverage]
|
||||||
internal static class ProcessStartExtensions
|
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
|
#if NET5_0_OR_GREATER
|
||||||
public static void AppendArgumentListSafe(this ProcessStartInfo psi, IEnumerable<string> args, out string debug)
|
public static void AppendArgumentListSafe(this ProcessStartInfo psi, IEnumerable<string> args, out string debug)
|
||||||
@@ -1,12 +1,11 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.ComponentModel;
|
using System.ComponentModel;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using Microsoft.Win32.SafeHandles;
|
using Microsoft.Win32.SafeHandles;
|
||||||
|
|
||||||
namespace Velopack
|
namespace Velopack.Util
|
||||||
{
|
{
|
||||||
internal static class SymbolicLink
|
internal static class SymbolicLink
|
||||||
{
|
{
|
||||||
@@ -28,14 +27,14 @@ namespace Velopack
|
|||||||
|
|
||||||
if (Directory.Exists(linkPath) || File.Exists(linkPath)) {
|
if (Directory.Exists(linkPath) || File.Exists(linkPath)) {
|
||||||
if (overwrite) {
|
if (overwrite) {
|
||||||
Utility.DeleteFileOrDirectoryHard(linkPath);
|
IoUtil.DeleteFileOrDirectoryHard(linkPath);
|
||||||
} else {
|
} else {
|
||||||
throw new IOException("Junction / symlink path already exists and overwrite parameter is false.");
|
throw new IOException("Junction / symlink path already exists and overwrite parameter is false.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var finalTarget = relative
|
var finalTarget = relative
|
||||||
? GetRelativePath(Path.GetDirectoryName(linkPath)!, targetPath)
|
? PathUtil.MakePathRelativeTo(Path.GetDirectoryName(linkPath)!, targetPath)
|
||||||
: targetPath;
|
: targetPath;
|
||||||
|
|
||||||
if (Directory.Exists(targetPath)) {
|
if (Directory.Exists(targetPath)) {
|
||||||
@@ -106,7 +105,7 @@ namespace Velopack
|
|||||||
var target = GetUnresolvedTarget(linkPath);
|
var target = GetUnresolvedTarget(linkPath);
|
||||||
if (relative) {
|
if (relative) {
|
||||||
if (Path.IsPathRooted(target)) {
|
if (Path.IsPathRooted(target)) {
|
||||||
return GetRelativePath(Path.GetDirectoryName(linkPath)!, target);
|
return PathUtil.MakePathRelativeTo(Path.GetDirectoryName(linkPath)!, target);
|
||||||
} else {
|
} else {
|
||||||
return target;
|
return target;
|
||||||
}
|
}
|
||||||
@@ -150,79 +149,8 @@ namespace Velopack
|
|||||||
return fsi != null && (fsi.Attributes & FileAttributes.ReparsePoint) != 0;
|
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
|
#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]
|
[Flags]
|
||||||
private enum EFileAttributes : uint
|
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 Microsoft.Extensions.Logging.Abstractions;
|
||||||
using NuGet.Versioning;
|
using NuGet.Versioning;
|
||||||
using Velopack.Locators;
|
using Velopack.Locators;
|
||||||
|
using Velopack.Util;
|
||||||
|
|
||||||
namespace Velopack
|
namespace Velopack
|
||||||
{
|
{
|
||||||
@@ -170,7 +171,7 @@ namespace Velopack
|
|||||||
log.Info("Starting Velopack App (Run).");
|
log.Info("Starting Velopack App (Run).");
|
||||||
|
|
||||||
if (VelopackRuntimeInfo.IsWindows && locator.AppId != null) {
|
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}'");
|
log.Info($"Setting current process explicit AppUserModelID to '{appUserModelId}'");
|
||||||
SetCurrentProcessExplicitAppUserModelID(appUserModelId);
|
SetCurrentProcessExplicitAppUserModelID(appUserModelId);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using NuGet.Versioning;
|
using NuGet.Versioning;
|
||||||
using Velopack.Json;
|
using Velopack.Util;
|
||||||
using Velopack.NuGet;
|
using Velopack.NuGet;
|
||||||
|
|
||||||
namespace Velopack
|
namespace Velopack
|
||||||
@@ -81,8 +81,8 @@ namespace Velopack
|
|||||||
NotesMarkdown = zip.ReleaseNotes,
|
NotesMarkdown = zip.ReleaseNotes,
|
||||||
NotesHTML = zip.ReleaseNotesHtml,
|
NotesHTML = zip.ReleaseNotesHtml,
|
||||||
Size = new FileInfo(filePath).Length,
|
Size = new FileInfo(filePath).Length,
|
||||||
SHA1 = Utility.CalculateFileSHA1(filePath),
|
SHA1 = IoUtil.CalculateFileSHA1(filePath),
|
||||||
SHA256 = Utility.CalculateFileSHA256(filePath),
|
SHA256 = IoUtil.CalculateFileSHA256(filePath),
|
||||||
FileName = Path.GetFileName(filePath),
|
FileName = Path.GetFileName(filePath),
|
||||||
Type = IsDeltaFile(filePath) ? VelopackAssetType.Delta : VelopackAssetType.Full,
|
Type = IsDeltaFile(filePath) ? VelopackAssetType.Delta : VelopackAssetType.Full,
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ using System.Linq;
|
|||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
using System.Runtime.Versioning;
|
using System.Runtime.Versioning;
|
||||||
using NuGet.Versioning;
|
using NuGet.Versioning;
|
||||||
|
using Velopack.Util;
|
||||||
|
|
||||||
#if !NETFRAMEWORK
|
#if !NETFRAMEWORK
|
||||||
using InteropArchitecture = System.Runtime.InteropServices.Architecture;
|
using InteropArchitecture = System.Runtime.InteropServices.Architecture;
|
||||||
@@ -221,7 +222,7 @@ namespace Velopack
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
if (IsWow64Process2(GetCurrentProcess(), out var _, out var nativeMachine)) {
|
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;
|
SystemArch = val;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ using Microsoft.Extensions.Logging;
|
|||||||
using Microsoft.Win32;
|
using Microsoft.Win32;
|
||||||
using NuGet.Versioning;
|
using NuGet.Versioning;
|
||||||
using Velopack.Sources;
|
using Velopack.Sources;
|
||||||
|
using Velopack.Util;
|
||||||
|
|
||||||
namespace Velopack.Windows
|
namespace Velopack.Windows
|
||||||
{
|
{
|
||||||
@@ -77,7 +78,7 @@ namespace Velopack.Windows
|
|||||||
{
|
{
|
||||||
var url = await GetDownloadUrl().ConfigureAwait(false);
|
var url = await GetDownloadUrl().ConfigureAwait(false);
|
||||||
log?.Info($"Downloading {Id} from {url} to {localPath}");
|
log?.Info($"Downloading {Id} from {url} to {localPath}");
|
||||||
downloader = downloader ?? Utility.CreateDefaultDownloader();
|
downloader = downloader ?? HttpUtil.CreateDefaultDownloader();
|
||||||
await downloader.DownloadFile(url, localPath, progress).ConfigureAwait(false);
|
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);
|
var archValid = Enum.TryParse<RuntimeCpu>(String.IsNullOrWhiteSpace(archstr) ? "x64" : archstr, true, out var cpu);
|
||||||
if (!archValid) {
|
if (!archValid) {
|
||||||
throw new ArgumentException($"Invalid machine architecture '{archstr}'. " +
|
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;
|
var type = DotnetRuntimeType.WindowsDesktop;
|
||||||
@@ -404,7 +405,7 @@ namespace Velopack.Windows
|
|||||||
_ => throw new NotImplementedException(),
|
_ => throw new NotImplementedException(),
|
||||||
};
|
};
|
||||||
|
|
||||||
downloader = downloader ?? Utility.CreateDefaultDownloader();
|
downloader = downloader ?? HttpUtil.CreateDefaultDownloader();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
return await downloader.DownloadString($"{UncachedDotNetFeed}/{runtime}/{channel}/latest.version").ConfigureAwait(false);
|
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 Microsoft.Extensions.Logging.Abstractions;
|
||||||
using Velopack.Locators;
|
using Velopack.Locators;
|
||||||
using Velopack.NuGet;
|
using Velopack.NuGet;
|
||||||
|
using Velopack.Util;
|
||||||
|
|
||||||
namespace Velopack.Windows
|
namespace Velopack.Windows
|
||||||
{
|
{
|
||||||
@@ -165,7 +166,7 @@ namespace Velopack.Windows
|
|||||||
Log.Info($"Creating shortcut for {relativeExeName} => {file}");
|
Log.Info($"Creating shortcut for {relativeExeName} => {file}");
|
||||||
|
|
||||||
ShellLink sl;
|
ShellLink sl;
|
||||||
Utility.Retry(() => {
|
IoUtil.Retry(() => {
|
||||||
File.Delete(file);
|
File.Delete(file);
|
||||||
|
|
||||||
var target = Path.Combine(currentDir, relativeExeName);
|
var target = Path.Combine(currentDir, relativeExeName);
|
||||||
@@ -280,7 +281,7 @@ namespace Velopack.Windows
|
|||||||
|
|
||||||
private ShortcutLocation[] GetLocations(ShortcutLocation flag)
|
private ShortcutLocation[] GetLocations(ShortcutLocation flag)
|
||||||
{
|
{
|
||||||
var locations = Utility.GetEnumValues<ShortcutLocation>();
|
var locations = CoreUtil.GetEnumValues<ShortcutLocation>();
|
||||||
return locations
|
return locations
|
||||||
.Where(x => x != ShortcutLocation.None)
|
.Where(x => x != ShortcutLocation.None)
|
||||||
.Where(x => flag.HasFlag(x))
|
.Where(x => flag.HasFlag(x))
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ using Azure.Storage;
|
|||||||
using Azure.Storage.Blobs;
|
using Azure.Storage.Blobs;
|
||||||
using Azure.Storage.Blobs.Models;
|
using Azure.Storage.Blobs.Models;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
|
using Velopack.Util;
|
||||||
|
|
||||||
namespace Velopack.Deployment;
|
namespace Velopack.Deployment;
|
||||||
|
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ using Velopack.NuGet;
|
|||||||
using Velopack.Packaging;
|
using Velopack.Packaging;
|
||||||
using Velopack.Packaging.Exceptions;
|
using Velopack.Packaging.Exceptions;
|
||||||
using Velopack.Sources;
|
using Velopack.Sources;
|
||||||
|
using Velopack.Util;
|
||||||
|
|
||||||
namespace Velopack.Deployment;
|
namespace Velopack.Deployment;
|
||||||
|
|
||||||
@@ -103,7 +104,7 @@ public class GitHubRepository(ILogger logger) : SourceRepository<GitHubDownloadO
|
|||||||
}
|
}
|
||||||
|
|
||||||
// check if there is an existing releasesFile to merge
|
// 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);
|
var releaseAsset = release.Assets.FirstOrDefault(a => a.Name == releasesFileName);
|
||||||
if (releaseAsset != null) {
|
if (releaseAsset != null) {
|
||||||
throw new UserInfoException($"There is already a remote asset named '{releasesFileName}', and merging release files on GitHub is not supported.");
|
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;
|
||||||
using Velopack.Packaging.Exceptions;
|
using Velopack.Packaging.Exceptions;
|
||||||
using Velopack.Sources;
|
using Velopack.Sources;
|
||||||
|
using Velopack.Util;
|
||||||
|
|
||||||
namespace Velopack.Deployment;
|
namespace Velopack.Deployment;
|
||||||
|
|
||||||
@@ -132,7 +133,7 @@ public class GiteaRepository : SourceRepository<GiteaDownloadOptions, GiteaSourc
|
|||||||
}
|
}
|
||||||
|
|
||||||
// check if there is an existing releasesFile to merge
|
// 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);
|
var releaseAsset = release.Assets.FirstOrDefault(a => a.Name == releasesFileName);
|
||||||
if (releaseAsset != null) {
|
if (releaseAsset != null) {
|
||||||
throw new UserInfoException($"There is already a remote asset named '{releasesFileName}', and merging release files on Gitea is not supported.");
|
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 Microsoft.Extensions.Logging;
|
||||||
using Velopack.Packaging;
|
using Velopack.Packaging;
|
||||||
using Velopack.Sources;
|
using Velopack.Sources;
|
||||||
|
using Velopack.Util;
|
||||||
|
|
||||||
namespace Velopack.Deployment;
|
namespace Velopack.Deployment;
|
||||||
|
|
||||||
@@ -26,7 +27,7 @@ public class LocalRepository(ILogger logger) : ObjectRepository<LocalDownloadOpt
|
|||||||
{
|
{
|
||||||
var target = Path.Combine(client.FullName, key);
|
var target = Path.Combine(client.FullName, key);
|
||||||
Log.Info("Deleting: " + target);
|
Log.Info("Deleting: " + target);
|
||||||
Utility.DeleteFileOrDirectoryHard(target);
|
IoUtil.DeleteFileOrDirectoryHard(target);
|
||||||
return Task.CompletedTask;
|
return Task.CompletedTask;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
using Amazon.S3;
|
using Amazon.S3;
|
||||||
using Amazon.S3.Model;
|
using Amazon.S3.Model;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
|
using Velopack.Util;
|
||||||
|
|
||||||
namespace Velopack.Deployment;
|
namespace Velopack.Deployment;
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
using System.Text;
|
using System.Text;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
using Velopack.Packaging;
|
using Velopack.Packaging;
|
||||||
|
using Velopack.Util;
|
||||||
|
|
||||||
namespace Velopack.Deployment;
|
namespace Velopack.Deployment;
|
||||||
|
|
||||||
@@ -37,7 +38,7 @@ public abstract class ObjectRepository<TDown, TUp, TClient> : DownRepository<TDo
|
|||||||
|
|
||||||
protected override async Task<VelopackAssetFeed> GetReleasesAsync(TDown options)
|
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 client = CreateClient(options);
|
||||||
var bytes = await GetObjectBytes(client, releasesName);
|
var bytes = await GetObjectBytes(client, releasesName);
|
||||||
if (bytes == null || bytes.Length == 0) {
|
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 };
|
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));
|
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);
|
await UploadObject(client, releasesName, new FileInfo(tmpReleases), true, noCache: true);
|
||||||
|
|
||||||
#pragma warning disable CS0612 // Type or member is obsolete
|
#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
|
#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));
|
File.WriteAllText(tmpReleases2, ReleaseEntryHelper.GetLegacyMigrationReleaseFeedString(newReleaseFeed));
|
||||||
await UploadObject(client, legacyKey, new FileInfo(tmpReleases2), true, noCache: true);
|
await UploadObject(client, legacyKey, new FileInfo(tmpReleases2), true, noCache: true);
|
||||||
|
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ using Microsoft.Extensions.Logging;
|
|||||||
using Velopack.Packaging;
|
using Velopack.Packaging;
|
||||||
using Velopack.Packaging.Abstractions;
|
using Velopack.Packaging.Abstractions;
|
||||||
using Velopack.Sources;
|
using Velopack.Sources;
|
||||||
|
using Velopack.Util;
|
||||||
|
|
||||||
namespace Velopack.Deployment;
|
namespace Velopack.Deployment;
|
||||||
|
|
||||||
@@ -83,8 +84,8 @@ public abstract class DownRepository<TDown> : IRepositoryCanDownload<TDown>
|
|||||||
Log.Warn($"File '{path}' already exists on disk. Verifying checksum...");
|
Log.Warn($"File '{path}' already exists on disk. Verifying checksum...");
|
||||||
|
|
||||||
bool hashMatch = (latest.SHA256 != null)
|
bool hashMatch = (latest.SHA256 != null)
|
||||||
? latest.SHA256 == Utility.CalculateFileSHA256(path)
|
? latest.SHA256 == IoUtil.CalculateFileSHA256(path)
|
||||||
: latest.SHA1 == Utility.CalculateFileSHA1(path);
|
: latest.SHA1 == IoUtil.CalculateFileSHA1(path);
|
||||||
|
|
||||||
if (hashMatch) {
|
if (hashMatch) {
|
||||||
Log.Info("Checksum matches. Finished.");
|
Log.Info("Checksum matches. Finished.");
|
||||||
@@ -99,12 +100,12 @@ public abstract class DownRepository<TDown> : IRepositoryCanDownload<TDown>
|
|||||||
Log.Info("Verifying checksum...");
|
Log.Info("Verifying checksum...");
|
||||||
string newHash;
|
string newHash;
|
||||||
if (!string.IsNullOrEmpty(latest.SHA256)) {
|
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}");
|
Log.Error($"Checksum mismatch, expected {latest.SHA256}, got {newHash}");
|
||||||
return;
|
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}");
|
Log.Error($"Checksum mismatch, expected {latest.SHA1}, got {newHash}");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
using ICSharpCode.SharpZipLib.Tar;
|
using ICSharpCode.SharpZipLib.Tar;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
using Velopack.Compression;
|
using Velopack.Compression;
|
||||||
|
using Velopack.Util;
|
||||||
|
|
||||||
namespace Velopack.Packaging.Unix;
|
namespace Velopack.Packaging.Unix;
|
||||||
|
|
||||||
@@ -81,8 +82,8 @@ public class AppImageTool
|
|||||||
|
|
||||||
Chmod.ChmodFileAsExecutable(outputFile);
|
Chmod.ChmodFileAsExecutable(outputFile);
|
||||||
} finally {
|
} finally {
|
||||||
Utility.DeleteFileOrDirectoryHard(tmpSquashFile);
|
IoUtil.DeleteFileOrDirectoryHard(tmpSquashFile);
|
||||||
Utility.DeleteFileOrDirectoryHard(tmpTarFile);
|
IoUtil.DeleteFileOrDirectoryHard(tmpTarFile);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
using ELFSharp.ELF;
|
using ELFSharp.ELF;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
using Velopack.Packaging.Abstractions;
|
using Velopack.Packaging.Abstractions;
|
||||||
|
using Velopack.Util;
|
||||||
|
|
||||||
namespace Velopack.Packaging.Unix.Commands;
|
namespace Velopack.Packaging.Unix.Commands;
|
||||||
|
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ using Microsoft.Extensions.Logging;
|
|||||||
using NuGet.Versioning;
|
using NuGet.Versioning;
|
||||||
using Velopack.Packaging.Abstractions;
|
using Velopack.Packaging.Abstractions;
|
||||||
using Velopack.Packaging.Exceptions;
|
using Velopack.Packaging.Exceptions;
|
||||||
|
using Velopack.Util;
|
||||||
|
|
||||||
namespace Velopack.Packaging.Unix.Commands;
|
namespace Velopack.Packaging.Unix.Commands;
|
||||||
|
|
||||||
@@ -69,7 +70,7 @@ public class OsxBundleCommandRunner : ICommand<OsxBundleOptions>
|
|||||||
var builder = new OsxStructureBuilder(packId, releaseDir.FullName);
|
var builder = new OsxStructureBuilder(packId, releaseDir.FullName);
|
||||||
if (Directory.Exists(builder.AppDirectory)) {
|
if (Directory.Exists(builder.AppDirectory)) {
|
||||||
_logger.Warn(builder.AppDirectory + " already exists, deleting...");
|
_logger.Warn(builder.AppDirectory + " already exists, deleting...");
|
||||||
Utility.DeleteFileOrDirectoryHard(builder.AppDirectory);
|
IoUtil.DeleteFileOrDirectoryHard(builder.AppDirectory);
|
||||||
}
|
}
|
||||||
|
|
||||||
builder.Build();
|
builder.Build();
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
using System.Runtime.Versioning;
|
using System.Runtime.Versioning;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
using Velopack.Packaging.Abstractions;
|
using Velopack.Packaging.Abstractions;
|
||||||
|
using Velopack.Util;
|
||||||
|
|
||||||
namespace Velopack.Packaging.Unix.Commands;
|
namespace Velopack.Packaging.Unix.Commands;
|
||||||
|
|
||||||
@@ -27,7 +28,7 @@ public class OsxPackCommandRunner : PackageBuilder<OsxPackOptions>
|
|||||||
|
|
||||||
if (deleteAppBundle) {
|
if (deleteAppBundle) {
|
||||||
Log.Debug("Removing temporary .app bundle.");
|
Log.Debug("Removing temporary .app bundle.");
|
||||||
Utility.DeleteFileOrDirectoryHard(appBundlePath);
|
IoUtil.DeleteFileOrDirectoryHard(appBundlePath);
|
||||||
}
|
}
|
||||||
|
|
||||||
var structure = new OsxStructureBuilder(dir.FullName);
|
var structure = new OsxStructureBuilder(dir.FullName);
|
||||||
@@ -98,7 +99,7 @@ public class OsxPackCommandRunner : PackageBuilder<OsxPackOptions>
|
|||||||
var packId = Options.PackId;
|
var packId = Options.PackId;
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(Options.SignInstallIdentity) && !string.IsNullOrEmpty(Options.NotaryProfile)) {
|
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
|
progress(-1); // indeterminate
|
||||||
helper.Notarize(pkgPath, Options.NotaryProfile, Options.Keychain);
|
helper.Notarize(pkgPath, Options.NotaryProfile, Options.Keychain);
|
||||||
progress(80);
|
progress(80);
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
using System.Runtime.Versioning;
|
using System.Runtime.Versioning;
|
||||||
using System.Security;
|
using System.Security;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
using Velopack.Json;
|
using Velopack.Util;
|
||||||
|
|
||||||
namespace Velopack.Packaging.Unix;
|
namespace Velopack.Packaging.Unix;
|
||||||
|
|
||||||
@@ -97,11 +97,11 @@ public class OsxBuildTools
|
|||||||
|
|
||||||
if (File.Exists(pkgOutputPath)) File.Delete(pkgOutputPath);
|
if (File.Exists(pkgOutputPath)) File.Delete(pkgOutputPath);
|
||||||
|
|
||||||
using var _1 = Utility.GetTempDirectory(out var tmp);
|
using var _1 = TempUtil.GetTempDirectory(out var tmp);
|
||||||
using var _2 = Utility.GetTempDirectory(out var tmpPayload1);
|
using var _2 = TempUtil.GetTempDirectory(out var tmpPayload1);
|
||||||
using var _3 = Utility.GetTempDirectory(out var tmpPayload2);
|
using var _3 = TempUtil.GetTempDirectory(out var tmpPayload2);
|
||||||
using var _4 = Utility.GetTempDirectory(out var tmpScripts);
|
using var _4 = TempUtil.GetTempDirectory(out var tmpScripts);
|
||||||
using var _5 = Utility.GetTempDirectory(out var tmpResources);
|
using var _5 = TempUtil.GetTempDirectory(out var tmpResources);
|
||||||
|
|
||||||
// copy .app to tmp folder
|
// copy .app to tmp folder
|
||||||
var bundleName = Path.GetFileName(appBundlePath);
|
var bundleName = Path.GetFileName(appBundlePath);
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
// https://raw.githubusercontent.com/egramtel/dotnet-bundle/master/DotNet.Bundle/PlistWriter.cs
|
// https://raw.githubusercontent.com/egramtel/dotnet-bundle/master/DotNet.Bundle/PlistWriter.cs
|
||||||
using System.Xml;
|
using System.Xml;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
|
using Velopack.Util;
|
||||||
|
|
||||||
namespace Velopack.Packaging.Unix;
|
namespace Velopack.Packaging.Unix;
|
||||||
|
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
using System.Text.RegularExpressions;
|
using System.Text.RegularExpressions;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
using Velopack.Packaging.Exceptions;
|
using Velopack.Packaging.Exceptions;
|
||||||
|
using Velopack.Util;
|
||||||
|
|
||||||
namespace Velopack.Packaging.Windows;
|
namespace Velopack.Packaging.Windows;
|
||||||
|
|
||||||
@@ -45,13 +46,13 @@ public class CodeSign
|
|||||||
if (String.IsNullOrEmpty(rootDir)) {
|
if (String.IsNullOrEmpty(rootDir)) {
|
||||||
pendingSign.Enqueue(f);
|
pendingSign.Enqueue(f);
|
||||||
} else {
|
} 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);
|
pendingSign.Enqueue(partialPath);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
using var _1 = Utility.GetTempFileName(out var signLogFile);
|
using var _1 = TempUtil.GetTempFileName(out var signLogFile);
|
||||||
var totalToSign = pendingSign.Count;
|
var totalToSign = pendingSign.Count;
|
||||||
|
|
||||||
if (signAsTemplate) {
|
if (signAsTemplate) {
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ using Velopack.Compression;
|
|||||||
using Velopack.NuGet;
|
using Velopack.NuGet;
|
||||||
using Velopack.Packaging.Abstractions;
|
using Velopack.Packaging.Abstractions;
|
||||||
using Velopack.Packaging.Exceptions;
|
using Velopack.Packaging.Exceptions;
|
||||||
|
using Velopack.Util;
|
||||||
using Velopack.Windows;
|
using Velopack.Windows;
|
||||||
|
|
||||||
namespace Velopack.Packaging.Windows.Commands;
|
namespace Velopack.Packaging.Windows.Commands;
|
||||||
@@ -17,7 +18,7 @@ public class WindowsPackCommandRunner : PackageBuilder<WindowsPackOptions>
|
|||||||
protected override Task CodeSign(Action<int> progress, string packDir)
|
protected override Task CodeSign(Action<int> progress, string packDir)
|
||||||
{
|
{
|
||||||
var filesToSign = new DirectoryInfo(packDir).GetAllFilesRecursively()
|
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)
|
.Select(x => x.FullName)
|
||||||
.ToArray();
|
.ToArray();
|
||||||
|
|
||||||
@@ -37,7 +38,7 @@ public class WindowsPackCommandRunner : PackageBuilder<WindowsPackOptions>
|
|||||||
// add nuspec metadata
|
// add nuspec metadata
|
||||||
ExtraNuspecMetadata["runtimeDependencies"] = GetRuntimeDependencies();
|
ExtraNuspecMetadata["runtimeDependencies"] = GetRuntimeDependencies();
|
||||||
ExtraNuspecMetadata["shortcutLocations"] = GetShortcutLocations();
|
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
|
// copy files to temp dir, so we can modify them
|
||||||
var dir = TempDir.CreateSubdirectory("PreprocessPackDirWin");
|
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)
|
protected override Task CreateSetupPackage(Action<int> progress, string releasePkg, string packDir, string targetSetupExe)
|
||||||
{
|
{
|
||||||
var bundledZip = new ZipPackage(releasePkg);
|
var bundledZip = new ZipPackage(releasePkg);
|
||||||
Utility.Retry(() => File.Copy(HelperFile.SetupPath, targetSetupExe, true));
|
IoUtil.Retry(() => File.Copy(HelperFile.SetupPath, targetSetupExe, true));
|
||||||
progress(10);
|
progress(10);
|
||||||
|
|
||||||
var editor = new ResourceEdit(targetSetupExe, Log);
|
var editor = new ResourceEdit(targetSetupExe, Log);
|
||||||
@@ -189,7 +190,7 @@ public class WindowsPackCommandRunner : PackageBuilder<WindowsPackOptions>
|
|||||||
progress(50);
|
progress(50);
|
||||||
Log.Debug("Signing Setup bundle");
|
Log.Debug("Signing Setup bundle");
|
||||||
var targetDir = Path.GetDirectoryName(targetSetupExe);
|
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)}'.");
|
Log.Debug($"Setup bundle created '{Path.GetFileName(targetSetupExe)}'.");
|
||||||
progress(100);
|
progress(100);
|
||||||
return Task.CompletedTask;
|
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);
|
File.Copy(Path.Combine(packDir, "Squirrel.exe"), Path.Combine(dir.FullName, "Update.exe"), true);
|
||||||
var current = dir.CreateSubdirectory("current");
|
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"));
|
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
|
// create a .portable file to indicate this is a portable package
|
||||||
File.Create(Path.Combine(dir.FullName, ".portable")).Close();
|
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);
|
progress(100);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -233,7 +234,7 @@ public class WindowsPackCommandRunner : PackageBuilder<WindowsPackOptions>
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
Utility.Retry(() => File.Copy(HelperFile.StubExecutablePath, targetStubPath, true));
|
IoUtil.Retry(() => File.Copy(HelperFile.StubExecutablePath, targetStubPath, true));
|
||||||
var edit = new ResourceEdit(targetStubPath, Log);
|
var edit = new ResourceEdit(targetStubPath, Log);
|
||||||
edit.CopyResourcesFrom(exeToCopy);
|
edit.CopyResourcesFrom(exeToCopy);
|
||||||
edit.Commit();
|
edit.Commit();
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ using Microsoft.Extensions.Logging;
|
|||||||
using NuGet.Versioning;
|
using NuGet.Versioning;
|
||||||
using Velopack.Packaging.Abstractions;
|
using Velopack.Packaging.Abstractions;
|
||||||
using Velopack.Packaging.Exceptions;
|
using Velopack.Packaging.Exceptions;
|
||||||
|
using Velopack.Util;
|
||||||
|
|
||||||
namespace Velopack.Packaging.Windows;
|
namespace Velopack.Packaging.Windows;
|
||||||
|
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ using AsmResolver.PE.Win32Resources.Icon;
|
|||||||
using AsmResolver.PE.Win32Resources.Version;
|
using AsmResolver.PE.Win32Resources.Version;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
using Velopack.NuGet;
|
using Velopack.NuGet;
|
||||||
|
using Velopack.Util;
|
||||||
|
|
||||||
namespace Velopack.Packaging.Windows;
|
namespace Velopack.Packaging.Windows;
|
||||||
|
|
||||||
@@ -181,7 +182,7 @@ public class ResourceEdit
|
|||||||
_file.OptionalHeader.SetDataDirectory(DataDirectoryIndex.ResourceDirectory,
|
_file.OptionalHeader.SetDataDirectory(DataDirectoryIndex.ResourceDirectory,
|
||||||
new DataDirectory(resourceBuffer.Rva, resourceBuffer.GetPhysicalSize()));
|
new DataDirectory(resourceBuffer.Rva, resourceBuffer.GetPhysicalSize()));
|
||||||
|
|
||||||
Utility.Retry(() => {
|
IoUtil.Retry(() => {
|
||||||
using var fs = File.Create(_exePath);
|
using var fs = File.Create(_exePath);
|
||||||
_file.Write(fs);
|
_file.Write(fs);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
using System.IO.MemoryMappedFiles;
|
using System.IO.MemoryMappedFiles;
|
||||||
|
using Velopack.Util;
|
||||||
|
|
||||||
namespace Velopack.Packaging.Windows;
|
namespace Velopack.Packaging.Windows;
|
||||||
|
|
||||||
@@ -31,7 +32,7 @@ public static class SetupBundle
|
|||||||
length = accessor.ReadInt64(position - 8);
|
length = accessor.ReadInt64(position - 8);
|
||||||
}
|
}
|
||||||
|
|
||||||
Utility.Retry(FindBundleHeader);
|
IoUtil.Retry(FindBundleHeader);
|
||||||
|
|
||||||
bundleOffset = offset;
|
bundleOffset = offset;
|
||||||
bundleLength = length;
|
bundleLength = length;
|
||||||
@@ -45,8 +46,8 @@ public static class SetupBundle
|
|||||||
Stream pkgStream = null, setupStream = null;
|
Stream pkgStream = null, setupStream = null;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
pkgStream = Utility.Retry(() => File.OpenRead(packagePath), retries: 10);
|
pkgStream = IoUtil.Retry(() => File.OpenRead(packagePath), retries: 10);
|
||||||
setupStream = Utility.Retry(() => File.Open(setupPath, FileMode.Append, FileAccess.Write), retries: 10);
|
setupStream = IoUtil.Retry(() => File.Open(setupPath, FileMode.Append, FileAccess.Write), retries: 10);
|
||||||
bundleOffset = setupStream.Position;
|
bundleOffset = setupStream.Position;
|
||||||
bundleLength = pkgStream.Length;
|
bundleLength = pkgStream.Length;
|
||||||
pkgStream.CopyTo(setupStream);
|
pkgStream.CopyTo(setupStream);
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
using Velopack.Json;
|
using Velopack.Packaging.Exceptions;
|
||||||
using Velopack.Packaging.Exceptions;
|
|
||||||
|
|
||||||
namespace Velopack.Packaging;
|
namespace Velopack.Packaging;
|
||||||
|
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
using Velopack.Compression;
|
using Velopack.Compression;
|
||||||
using Velopack.Packaging.Exceptions;
|
using Velopack.Packaging.Exceptions;
|
||||||
using Velopack.Packaging.Abstractions;
|
using Velopack.Packaging.Abstractions;
|
||||||
|
using Velopack.Util;
|
||||||
|
|
||||||
namespace Velopack.Packaging.Commands;
|
namespace Velopack.Packaging.Commands;
|
||||||
|
|
||||||
@@ -28,8 +29,8 @@ public class DeltaPatchCommandRunner : ICommand<DeltaPatchOptions>
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var tmp = Utility.GetDefaultTempBaseDirectory();
|
var tmp = TempUtil.GetDefaultTempBaseDirectory();
|
||||||
using var _1 = Utility.GetTempDirectory(out var workDir);
|
using var _1 = TempUtil.GetTempDirectory(out var workDir);
|
||||||
|
|
||||||
var delta = new DeltaEmbedded(HelperFile.GetZstdPath(), _logger, tmp);
|
var delta = new DeltaEmbedded(HelperFile.GetZstdPath(), _logger, tmp);
|
||||||
EasyZip.ExtractZipToDirectory(_logger, options.BasePackage, workDir);
|
EasyZip.ExtractZipToDirectory(_logger, options.BasePackage, workDir);
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ using System.Text;
|
|||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
using Velopack.Compression;
|
using Velopack.Compression;
|
||||||
using Velopack.Packaging.Exceptions;
|
using Velopack.Packaging.Exceptions;
|
||||||
|
using Velopack.Util;
|
||||||
|
|
||||||
namespace Velopack.Packaging;
|
namespace Velopack.Packaging;
|
||||||
|
|
||||||
@@ -61,8 +62,8 @@ public class DeltaPackageBuilder
|
|||||||
|
|
||||||
int fNew = 0, fSame = 0, fChanged = 0, fWarnings = 0, fProcessed = 0, fRemoved = 0;
|
int fNew = 0, fSame = 0, fChanged = 0, fWarnings = 0, fProcessed = 0, fRemoved = 0;
|
||||||
|
|
||||||
using (Utility.GetTempDirectory(out var baseTempPath))
|
using (TempUtil.GetTempDirectory(out var baseTempPath))
|
||||||
using (Utility.GetTempDirectory(out var tempPath)) {
|
using (TempUtil.GetTempDirectory(out var tempPath)) {
|
||||||
var baseTempInfo = new DirectoryInfo(baseTempPath);
|
var baseTempInfo = new DirectoryInfo(baseTempPath);
|
||||||
var tempInfo = new DirectoryInfo(tempPath);
|
var tempInfo = new DirectoryInfo(tempPath);
|
||||||
|
|
||||||
@@ -139,12 +140,12 @@ public class DeltaPackageBuilder
|
|||||||
targetFile.Delete();
|
targetFile.Delete();
|
||||||
baseLibFiles.Remove(relativePath);
|
baseLibFiles.Remove(relativePath);
|
||||||
var p = Interlocked.Increment(ref fProcessed);
|
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) {
|
} catch (Exception ex) {
|
||||||
_logger.Debug(ex, String.Format("Failed to create a delta for {0}", targetFile.Name));
|
_logger.Debug(ex, String.Format("Failed to create a delta for {0}", targetFile.Name));
|
||||||
Utility.DeleteFileOrDirectoryHard(targetFile.FullName + ".bsdiff", throwOnFailure: false);
|
IoUtil.DeleteFileOrDirectoryHard(targetFile.FullName + ".bsdiff", throwOnFailure: false);
|
||||||
Utility.DeleteFileOrDirectoryHard(targetFile.FullName + ".diff", throwOnFailure: false);
|
IoUtil.DeleteFileOrDirectoryHard(targetFile.FullName + ".diff", throwOnFailure: false);
|
||||||
Utility.DeleteFileOrDirectoryHard(targetFile.FullName + ".shasum", throwOnFailure: false);
|
IoUtil.DeleteFileOrDirectoryHard(targetFile.FullName + ".shasum", throwOnFailure: false);
|
||||||
Interlocked.Increment(ref fWarnings);
|
Interlocked.Increment(ref fWarnings);
|
||||||
throw;
|
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.");
|
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);
|
progress(100);
|
||||||
fRemoved = baseLibFiles.Count;
|
fRemoved = baseLibFiles.Count;
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using Velopack.Packaging.Exceptions;
|
using Velopack.Packaging.Exceptions;
|
||||||
|
using Velopack.Util;
|
||||||
|
|
||||||
namespace Velopack.Packaging;
|
namespace Velopack.Packaging;
|
||||||
|
|
||||||
@@ -38,7 +39,7 @@ public static class Exe
|
|||||||
|
|
||||||
public static string RunHostedCommand(string command, string workDir = null)
|
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();
|
File.Create(outputFile).Close();
|
||||||
|
|
||||||
var fileName = "cmd.exe";
|
var fileName = "cmd.exe";
|
||||||
@@ -64,7 +65,7 @@ public static class Exe
|
|||||||
|
|
||||||
process.WaitForExit();
|
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);
|
var result = (process.ExitCode, stdout, "", command);
|
||||||
ProcessFailedException.ThrowIfNonZero(result);
|
ProcessFailedException.ThrowIfNonZero(result);
|
||||||
return result.Item2;
|
return result.Item2;
|
||||||
@@ -72,7 +73,7 @@ public static class Exe
|
|||||||
|
|
||||||
public static void RunHostedCommandNoWait(string command, string workDir = null)
|
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();
|
File.Create(outputFile).Close();
|
||||||
|
|
||||||
var fileName = "cmd.exe";
|
var fileName = "cmd.exe";
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ using Velopack.Compression;
|
|||||||
using Velopack.NuGet;
|
using Velopack.NuGet;
|
||||||
using Velopack.Packaging.Abstractions;
|
using Velopack.Packaging.Abstractions;
|
||||||
using Velopack.Packaging.Exceptions;
|
using Velopack.Packaging.Exceptions;
|
||||||
|
using Velopack.Util;
|
||||||
|
|
||||||
namespace Velopack.Packaging;
|
namespace Velopack.Packaging;
|
||||||
|
|
||||||
@@ -90,7 +91,7 @@ public abstract class PackageBuilder<T> : ICommand<T>
|
|||||||
MainExePath = mainExePath;
|
MainExePath = mainExePath;
|
||||||
options.EntryExecutableName = Path.GetFileName(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);
|
TempDir = new DirectoryInfo(pkgTempDir);
|
||||||
Options = options;
|
Options = options;
|
||||||
|
|
||||||
@@ -158,7 +159,7 @@ public abstract class PackageBuilder<T> : ICommand<T>
|
|||||||
|
|
||||||
await ctx.RunTask("Post-process steps", (progress) => {
|
await ctx.RunTask("Post-process steps", (progress) => {
|
||||||
foreach (var f in filesToCopy) {
|
foreach (var f in filesToCopy) {
|
||||||
Utility.MoveFile(f.from, f.to, true);
|
IoUtil.MoveFile(f.from, f.to, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
ReleaseEntryHelper.UpdateReleaseFiles(releaseDir.FullName, Log);
|
ReleaseEntryHelper.UpdateReleaseFiles(releaseDir.FullName, Log);
|
||||||
@@ -261,7 +262,7 @@ public abstract class PackageBuilder<T> : ICommand<T>
|
|||||||
File.WriteAllText(nuspecPath, GenerateNuspecContent());
|
File.WriteAllText(nuspecPath, GenerateNuspecContent());
|
||||||
|
|
||||||
var appDir = stagingDir.CreateSubdirectory("lib").CreateSubdirectory("app");
|
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();
|
var metadataFiles = GetReleaseMetadataFiles();
|
||||||
foreach (var kvp in metadataFiles) {
|
foreach (var kvp in metadataFiles) {
|
||||||
@@ -270,7 +271,7 @@ public abstract class PackageBuilder<T> : ICommand<T>
|
|||||||
|
|
||||||
AddContentTypesAndRel(nuspecPath);
|
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);
|
progress(100);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
using System.Text;
|
using System.Text;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
using NuGet.Versioning;
|
using NuGet.Versioning;
|
||||||
using Velopack.Json;
|
|
||||||
using Velopack.NuGet;
|
using Velopack.NuGet;
|
||||||
|
using Velopack.Util;
|
||||||
|
|
||||||
namespace Velopack.Packaging;
|
namespace Velopack.Packaging;
|
||||||
|
|
||||||
@@ -64,7 +64,7 @@ public class ReleaseEntryHelper
|
|||||||
{
|
{
|
||||||
var releases = _releases.ContainsKey(_channel) ? _releases[_channel] : null;
|
var releases = _releases.ContainsKey(_channel) ? _releases[_channel] : null;
|
||||||
if (releases == null || !releases.Any()) return 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()
|
public IEnumerable<VelopackAsset> GetLatestAssets()
|
||||||
@@ -72,7 +72,7 @@ public class ReleaseEntryHelper
|
|||||||
if (!_releases.ContainsKey(_channel) || !_releases[_channel].Any())
|
if (!_releases.ContainsKey(_channel) || !_releases[_channel].Any())
|
||||||
return Enumerable.Empty<VelopackAsset>();
|
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}");
|
_logger.Info($"Latest release: {latest.FileName}");
|
||||||
|
|
||||||
var assets = _releases[_channel]
|
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
|
// 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
|
#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);
|
var path = Path.Combine(outputDir, name);
|
||||||
|
|
||||||
ReleaseEntry.WriteReleaseFile(
|
ReleaseEntry.WriteReleaseFile(
|
||||||
@@ -114,9 +113,8 @@ public class ReleaseEntryHelper
|
|||||||
.Where(entry => !entry.IsDelta),
|
.Where(entry => !entry.IsDelta),
|
||||||
path);
|
path);
|
||||||
#pragma warning restore CS0618 // Type or member is obsolete
|
#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() {
|
var feed = new VelopackAssetFeed() {
|
||||||
Assets = kvp.Value.OrderByDescending(v => v.Version).ThenBy(v => v.Type).ToArray(),
|
Assets = kvp.Value.OrderByDescending(v => v.Version).ThenBy(v => v.Type).ToArray(),
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
using Velopack.Packaging.Abstractions;
|
using Velopack.Packaging.Abstractions;
|
||||||
|
using Velopack.Util;
|
||||||
|
|
||||||
namespace Velopack.Vpk.Logging;
|
namespace Velopack.Vpk.Logging;
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
using System.Threading;
|
using System.Threading;
|
||||||
using Spectre.Console;
|
using Spectre.Console;
|
||||||
using Velopack.Packaging.Abstractions;
|
using Velopack.Packaging.Abstractions;
|
||||||
|
using Velopack.Util;
|
||||||
|
|
||||||
namespace Velopack.Vpk.Logging;
|
namespace Velopack.Vpk.Logging;
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
using System.ComponentModel;
|
using System.ComponentModel;
|
||||||
using Microsoft.Extensions.Configuration;
|
using Microsoft.Extensions.Configuration;
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
using Microsoft.Extensions.Hosting;
|
using Microsoft.Extensions.Hosting;
|
||||||
@@ -12,6 +12,7 @@ using Velopack.Packaging.Exceptions;
|
|||||||
using Velopack.Packaging.Flow;
|
using Velopack.Packaging.Flow;
|
||||||
using Velopack.Packaging.Unix.Commands;
|
using Velopack.Packaging.Unix.Commands;
|
||||||
using Velopack.Packaging.Windows.Commands;
|
using Velopack.Packaging.Windows.Commands;
|
||||||
|
using Velopack.Util;
|
||||||
using Velopack.Vpk.Commands;
|
using Velopack.Vpk.Commands;
|
||||||
using Velopack.Vpk.Commands.Deployment;
|
using Velopack.Vpk.Commands.Deployment;
|
||||||
using Velopack.Vpk.Commands.Flow;
|
using Velopack.Vpk.Commands.Flow;
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
using System.Threading;
|
using System.Threading;
|
||||||
using NuGet.Protocol.Core.Types;
|
using NuGet.Protocol.Core.Types;
|
||||||
|
using Velopack.Util;
|
||||||
|
|
||||||
namespace Velopack.Vpk.Updates;
|
namespace Velopack.Vpk.Updates;
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
using System.CommandLine;
|
using System.CommandLine;
|
||||||
|
using Velopack.Util;
|
||||||
using Velopack.Vpk.Commands.Deployment;
|
using Velopack.Vpk.Commands.Deployment;
|
||||||
|
|
||||||
namespace Velopack.CommandLine.Tests.Commands;
|
namespace Velopack.CommandLine.Tests.Commands;
|
||||||
@@ -9,7 +10,7 @@ public class LocalDownloadCommandTests : BaseCommandTests<LocalDownloadCommand>
|
|||||||
{
|
{
|
||||||
var command = new 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();
|
File.Create(Path.Combine(releaseDir, "test.txt")).Close();
|
||||||
|
|
||||||
ParseResult parseResult = command.ParseAndApply($"--path {releaseDir}");
|
ParseResult parseResult = command.ParseAndApply($"--path {releaseDir}");
|
||||||
@@ -22,7 +23,7 @@ public class LocalDownloadCommandTests : BaseCommandTests<LocalDownloadCommand>
|
|||||||
public void Path_WithEmptyPath_ParsesValue()
|
public void Path_WithEmptyPath_ParsesValue()
|
||||||
{
|
{
|
||||||
var command = new LocalDownloadCommand();
|
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}");
|
ParseResult parseResult = command.ParseAndApply($"--path {releaseDir}");
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
using NuGet.Versioning;
|
using NuGet.Versioning;
|
||||||
using Velopack.Deployment;
|
using Velopack.Deployment;
|
||||||
using Velopack.Sources;
|
using Velopack.Sources;
|
||||||
|
using Velopack.Util;
|
||||||
|
|
||||||
namespace Velopack.Packaging.Tests;
|
namespace Velopack.Packaging.Tests;
|
||||||
|
|
||||||
@@ -23,7 +24,7 @@ public class AzureDeploymentTests
|
|||||||
{
|
{
|
||||||
Skip.If(String.IsNullOrWhiteSpace(AZ_KEY), "VELOPACK_AZ_TEST_TOKEN is not set.");
|
Skip.If(String.IsNullOrWhiteSpace(AZ_KEY), "VELOPACK_AZ_TEST_TOKEN is not set.");
|
||||||
using var logger = _output.BuildLoggerFor<S3DeploymentTests>();
|
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"))
|
string channel = String.IsNullOrWhiteSpace(Environment.GetEnvironmentVariable("CI"))
|
||||||
? VelopackRuntimeInfo.SystemOs.GetOsShortName()
|
? VelopackRuntimeInfo.SystemOs.GetOsShortName()
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
using Neovolve.Logging.Xunit;
|
using Neovolve.Logging.Xunit;
|
||||||
using Velopack.Packaging.Exceptions;
|
using Velopack.Packaging.Exceptions;
|
||||||
using Velopack.Packaging.Windows;
|
using Velopack.Packaging.Windows;
|
||||||
|
using Velopack.Util;
|
||||||
using Velopack.Vpk;
|
using Velopack.Vpk;
|
||||||
using Velopack.Vpk.Logging;
|
using Velopack.Vpk.Logging;
|
||||||
|
|
||||||
@@ -35,7 +36,7 @@ public class CompatUtilTests
|
|||||||
{
|
{
|
||||||
Skip.IfNot(VelopackRuntimeInfo.IsWindows);
|
Skip.IfNot(VelopackRuntimeInfo.IsWindows);
|
||||||
using var logger = GetCompat(out var compat);
|
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();
|
var sample = PathHelper.GetAvaloniaSample();
|
||||||
Exe.InvokeAndThrowIfNonZero(
|
Exe.InvokeAndThrowIfNonZero(
|
||||||
"dotnet",
|
"dotnet",
|
||||||
@@ -56,7 +57,7 @@ public class CompatUtilTests
|
|||||||
{
|
{
|
||||||
Skip.IfNot(VelopackRuntimeInfo.IsWindows);
|
Skip.IfNot(VelopackRuntimeInfo.IsWindows);
|
||||||
using var logger = GetCompat(out var compat);
|
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();
|
var sample = PathHelper.GetAvaloniaSample();
|
||||||
Exe.InvokeAndThrowIfNonZero(
|
Exe.InvokeAndThrowIfNonZero(
|
||||||
"dotnet",
|
"dotnet",
|
||||||
@@ -77,7 +78,7 @@ public class CompatUtilTests
|
|||||||
{
|
{
|
||||||
Skip.IfNot(VelopackRuntimeInfo.IsWindows);
|
Skip.IfNot(VelopackRuntimeInfo.IsWindows);
|
||||||
using var logger = GetCompat(out var compat);
|
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();
|
var sample = PathHelper.GetWpfSample();
|
||||||
Exe.InvokeAndThrowIfNonZero(
|
Exe.InvokeAndThrowIfNonZero(
|
||||||
"dotnet",
|
"dotnet",
|
||||||
@@ -97,7 +98,7 @@ public class CompatUtilTests
|
|||||||
{
|
{
|
||||||
Skip.IfNot(VelopackRuntimeInfo.IsWindows);
|
Skip.IfNot(VelopackRuntimeInfo.IsWindows);
|
||||||
using var logger = GetCompat(out var compat);
|
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");
|
var sample = PathHelper.GetTestRootPath("TestApp");
|
||||||
Exe.InvokeAndThrowIfNonZero(
|
Exe.InvokeAndThrowIfNonZero(
|
||||||
"dotnet",
|
"dotnet",
|
||||||
@@ -114,7 +115,7 @@ public class CompatUtilTests
|
|||||||
{
|
{
|
||||||
Skip.IfNot(VelopackRuntimeInfo.IsWindows);
|
Skip.IfNot(VelopackRuntimeInfo.IsWindows);
|
||||||
using var logger = GetCompat(out var compat);
|
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");
|
var sample = PathHelper.GetTestRootPath("TestApp");
|
||||||
Exe.InvokeAndThrowIfNonZero(
|
Exe.InvokeAndThrowIfNonZero(
|
||||||
"dotnet",
|
"dotnet",
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
using Velopack.Packaging.Unix;
|
using Velopack.Packaging.Unix;
|
||||||
|
using Velopack.Util;
|
||||||
|
|
||||||
namespace Velopack.Packaging.Tests;
|
namespace Velopack.Packaging.Tests;
|
||||||
|
|
||||||
@@ -20,7 +21,7 @@ public class CrossCompile
|
|||||||
var rid = RID.Parse(target);
|
var rid = RID.Parse(target);
|
||||||
|
|
||||||
string id = $"from-{VelopackRuntimeInfo.SystemOs.GetOsShortName()}-targets-{rid.BaseRID.GetOsShortName()}";
|
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);
|
TestApp.PackTestApp(id, "1.0.0", id, tempDir, logger, targetRid: rid);
|
||||||
|
|
||||||
var artifactsDir = PathHelper.GetTestRootPath("artifacts");
|
var artifactsDir = PathHelper.GetTestRootPath("artifacts");
|
||||||
@@ -84,7 +85,7 @@ public class CrossCompile
|
|||||||
var appExe = Path.Combine(appRoot, "current", "TestApp.exe");
|
var appExe = Path.Combine(appRoot, "current", "TestApp.exe");
|
||||||
var appUpdate = Path.Combine(appRoot, "Update.exe");
|
var appUpdate = Path.Combine(appRoot, "Update.exe");
|
||||||
|
|
||||||
Utility.DeleteFileOrDirectoryHard(appRoot);
|
IoUtil.DeleteFileOrDirectoryHard(appRoot);
|
||||||
|
|
||||||
Assert.False(File.Exists(appExe));
|
Assert.False(File.Exists(appExe));
|
||||||
|
|
||||||
@@ -102,6 +103,6 @@ public class CrossCompile
|
|||||||
|
|
||||||
Assert.False(File.Exists(appExe));
|
Assert.False(File.Exists(appExe));
|
||||||
Assert.True(File.Exists(Path.Combine(appRoot, ".dead")));
|
Assert.True(File.Exists(Path.Combine(appRoot, ".dead")));
|
||||||
Utility.DeleteFileOrDirectoryHard(appRoot);
|
IoUtil.DeleteFileOrDirectoryHard(appRoot);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2,6 +2,7 @@
|
|||||||
using Velopack.Sources;
|
using Velopack.Sources;
|
||||||
using Octokit;
|
using Octokit;
|
||||||
using Velopack.Packaging.Exceptions;
|
using Velopack.Packaging.Exceptions;
|
||||||
|
using Velopack.Util;
|
||||||
|
|
||||||
namespace Velopack.Packaging.Tests;
|
namespace Velopack.Packaging.Tests;
|
||||||
|
|
||||||
@@ -22,8 +23,8 @@ public class GithubDeploymentTests
|
|||||||
{
|
{
|
||||||
Skip.If(String.IsNullOrWhiteSpace(GITHUB_TOKEN), "VELOPACK_GITHUB_TEST_TOKEN is not set.");
|
Skip.If(String.IsNullOrWhiteSpace(GITHUB_TOKEN), "VELOPACK_GITHUB_TEST_TOKEN is not set.");
|
||||||
using var logger = _output.BuildLoggerFor<GithubDeploymentTests>();
|
using var logger = _output.BuildLoggerFor<GithubDeploymentTests>();
|
||||||
using var _1 = Utility.GetTempDirectory(out var releaseDir);
|
using var _1 = TempUtil.GetTempDirectory(out var releaseDir);
|
||||||
using var _2 = Utility.GetTempDirectory(out var releaseDir2);
|
using var _2 = TempUtil.GetTempDirectory(out var releaseDir2);
|
||||||
using var ghvar = GitHubReleaseTest.Create("nomerge", logger);
|
using var ghvar = GitHubReleaseTest.Create("nomerge", logger);
|
||||||
var id = "GithubUpdateTest";
|
var id = "GithubUpdateTest";
|
||||||
TestApp.PackTestApp(id, $"0.0.1-{ghvar.UniqueSuffix}", "t1", releaseDir, logger);
|
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.");
|
Skip.If(String.IsNullOrWhiteSpace(GITHUB_TOKEN), "VELOPACK_GITHUB_TEST_TOKEN is not set.");
|
||||||
using var logger = _output.BuildLoggerFor<GithubDeploymentTests>();
|
using var logger = _output.BuildLoggerFor<GithubDeploymentTests>();
|
||||||
using var _1 = Utility.GetTempDirectory(out var releaseDir);
|
using var _1 = TempUtil.GetTempDirectory(out var releaseDir);
|
||||||
using var _2 = Utility.GetTempDirectory(out var releaseDir2);
|
using var _2 = TempUtil.GetTempDirectory(out var releaseDir2);
|
||||||
using var ghvar = GitHubReleaseTest.Create("mixmatched", logger);
|
using var ghvar = GitHubReleaseTest.Create("mixmatched", logger);
|
||||||
var id = "GithubUpdateTest";
|
var id = "GithubUpdateTest";
|
||||||
TestApp.PackTestApp(id, $"0.0.1-{ghvar.UniqueSuffix}", "t1", releaseDir, logger);
|
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.");
|
Skip.If(String.IsNullOrWhiteSpace(GITHUB_TOKEN), "VELOPACK_GITHUB_TEST_TOKEN is not set.");
|
||||||
using var logger = _output.BuildLoggerFor<GithubDeploymentTests>();
|
using var logger = _output.BuildLoggerFor<GithubDeploymentTests>();
|
||||||
using var _1 = Utility.GetTempDirectory(out var releaseDir);
|
using var _1 = TempUtil.GetTempDirectory(out var releaseDir);
|
||||||
using var _2 = Utility.GetTempDirectory(out var releaseDir2);
|
using var _2 = TempUtil.GetTempDirectory(out var releaseDir2);
|
||||||
using var ghvar = GitHubReleaseTest.Create("yesmerge", logger);
|
using var ghvar = GitHubReleaseTest.Create("yesmerge", logger);
|
||||||
var id = "GithubUpdateTest";
|
var id = "GithubUpdateTest";
|
||||||
TestApp.PackTestApp(id, $"0.0.1-{ghvar.UniqueSuffix}", "t1", releaseDir, logger);
|
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.");
|
Skip.If(String.IsNullOrWhiteSpace(GITHUB_TOKEN), "VELOPACK_GITHUB_TEST_TOKEN is not set.");
|
||||||
using var logger = _output.BuildLoggerFor<GithubDeploymentTests>();
|
using var logger = _output.BuildLoggerFor<GithubDeploymentTests>();
|
||||||
var id = "GithubUpdateTest";
|
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);
|
var (repoOwner, repoName) = GitHubRepository.GetOwnerAndRepo(GITHUB_REPOURL);
|
||||||
using var ghvar = GitHubReleaseTest.Create("integration", logger);
|
using var ghvar = GitHubReleaseTest.Create("integration", logger);
|
||||||
var releaseName = ghvar.ReleaseName;
|
var releaseName = ghvar.ReleaseName;
|
||||||
@@ -172,7 +173,7 @@ This is just a _test_!
|
|||||||
Assert.Equal(newVer, r.Version.ToNormalizedString());
|
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 {
|
gh.DownloadLatestFullPackageAsync(new GitHubDownloadOptions {
|
||||||
Token = GITHUB_TOKEN,
|
Token = GITHUB_TOKEN,
|
||||||
RepoUrl = GITHUB_REPOURL,
|
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.");
|
Skip.If(String.IsNullOrWhiteSpace(GITHUB_TOKEN), "VELOPACK_GITHUB_TEST_TOKEN is not set.");
|
||||||
using var logger = _output.BuildLoggerFor<GithubDeploymentTests>();
|
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);
|
using var ghvar = GitHubReleaseTest.Create("targetCommitish", logger, true);
|
||||||
var (repoOwner, repoName) = GitHubRepository.GetOwnerAndRepo(GITHUB_REPOURL);
|
var (repoOwner, repoName) = GitHubRepository.GetOwnerAndRepo(GITHUB_REPOURL);
|
||||||
var id = "GithubUpdateTest";
|
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.");
|
Skip.If(String.IsNullOrWhiteSpace(GITHUB_TOKEN), "VELOPACK_GITHUB_TEST_TOKEN is not set.");
|
||||||
using var logger = _output.BuildLoggerFor<GithubDeploymentTests>();
|
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);
|
using var ghvar = GitHubReleaseTest.Create("targetCommitish", logger, true);
|
||||||
var (repoOwner, repoName) = GitHubRepository.GetOwnerAndRepo(GITHUB_REPOURL);
|
var (repoOwner, repoName) = GitHubRepository.GetOwnerAndRepo(GITHUB_REPOURL);
|
||||||
var id = "GithubUpdateTest";
|
var id = "GithubUpdateTest";
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
using System.Runtime.Versioning;
|
using System.Runtime.Versioning;
|
||||||
using Velopack.Compression;
|
using Velopack.Compression;
|
||||||
|
using Velopack.Util;
|
||||||
|
|
||||||
namespace Velopack.Packaging.Tests;
|
namespace Velopack.Packaging.Tests;
|
||||||
|
|
||||||
@@ -20,9 +21,9 @@ public class OsxPackTests
|
|||||||
|
|
||||||
using var logger = _output.BuildLoggerFor<OsxPackTests>();
|
using var logger = _output.BuildLoggerFor<OsxPackTests>();
|
||||||
|
|
||||||
using var _1 = Utility.GetTempDirectory(out var tmpOutput);
|
using var _1 = TempUtil.GetTempDirectory(out var tmpOutput);
|
||||||
using var _2 = Utility.GetTempDirectory(out var tmpReleaseDir);
|
using var _2 = TempUtil.GetTempDirectory(out var tmpReleaseDir);
|
||||||
using var _3 = Utility.GetTempDirectory(out var unzipDir);
|
using var _3 = TempUtil.GetTempDirectory(out var unzipDir);
|
||||||
|
|
||||||
const string id = "MyAppId";
|
const string id = "MyAppId";
|
||||||
const string title = "MyAppTitle";
|
const string title = "MyAppTitle";
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ using AsmResolver.PE.Win32Resources.Icon;
|
|||||||
using AsmResolver.PE.Win32Resources.Version;
|
using AsmResolver.PE.Win32Resources.Version;
|
||||||
using Velopack.NuGet;
|
using Velopack.NuGet;
|
||||||
using Velopack.Packaging.Windows;
|
using Velopack.Packaging.Windows;
|
||||||
|
using Velopack.Util;
|
||||||
|
|
||||||
namespace Velopack.Packaging.Tests;
|
namespace Velopack.Packaging.Tests;
|
||||||
|
|
||||||
@@ -37,7 +38,7 @@ public class ResourceEditTests
|
|||||||
public void CommitResourcesInCorrectOrder()
|
public void CommitResourcesInCorrectOrder()
|
||||||
{
|
{
|
||||||
using var logger = _output.BuildLoggerFor<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.GetRustAsset("setup.exe");
|
var exe = PathHelper.GetRustAsset("setup.exe");
|
||||||
File.Copy(exe, tempFile);
|
File.Copy(exe, tempFile);
|
||||||
|
|
||||||
@@ -65,7 +66,7 @@ public class ResourceEditTests
|
|||||||
{
|
{
|
||||||
using var logger = _output.BuildLoggerFor<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);
|
CreateTestPEFileWithoutRsrc(tempFile);
|
||||||
|
|
||||||
var edit = new ResourceEdit(tempFile, logger);
|
var edit = new ResourceEdit(tempFile, logger);
|
||||||
@@ -81,7 +82,7 @@ public class ResourceEditTests
|
|||||||
{
|
{
|
||||||
using var logger = _output.BuildLoggerFor<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");
|
var exe = PathHelper.GetFixture("SquirrelAwareTweakedNetCoreApp.exe");
|
||||||
File.Copy(exe, tempFile);
|
File.Copy(exe, tempFile);
|
||||||
|
|
||||||
@@ -98,7 +99,7 @@ public class ResourceEditTests
|
|||||||
{
|
{
|
||||||
using var logger = _output.BuildLoggerFor<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");
|
var exe = PathHelper.GetFixture("atom.exe");
|
||||||
File.Copy(exe, tempFile);
|
File.Copy(exe, tempFile);
|
||||||
|
|
||||||
@@ -125,7 +126,7 @@ public class ResourceEditTests
|
|||||||
{
|
{
|
||||||
using var logger = _output.BuildLoggerFor<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);
|
CreateTestPEFileWithoutRsrc(tempFile);
|
||||||
|
|
||||||
var beforeRsrc = PEImage.FromFile(PEFile.FromFile(tempFile)).Resources;
|
var beforeRsrc = PEImage.FromFile(PEFile.FromFile(tempFile)).Resources;
|
||||||
@@ -146,7 +147,7 @@ public class ResourceEditTests
|
|||||||
public void SetVersionInfoWithPreExistingRsrc()
|
public void SetVersionInfoWithPreExistingRsrc()
|
||||||
{
|
{
|
||||||
using var logger = _output.BuildLoggerFor<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");
|
var exe = PathHelper.GetFixture("atom.exe");
|
||||||
File.Copy(exe, tempFile);
|
File.Copy(exe, tempFile);
|
||||||
|
|
||||||
@@ -165,7 +166,7 @@ public class ResourceEditTests
|
|||||||
public void SetVersionInfoWithoutRsrc()
|
public void SetVersionInfoWithoutRsrc()
|
||||||
{
|
{
|
||||||
using var logger = _output.BuildLoggerFor<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);
|
CreateTestPEFileWithoutRsrc(tempFile);
|
||||||
|
|
||||||
var nuspec = PathHelper.GetFixture("FullNuspec.nuspec");
|
var nuspec = PathHelper.GetFixture("FullNuspec.nuspec");
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
using NuGet.Versioning;
|
using NuGet.Versioning;
|
||||||
using Velopack.Deployment;
|
using Velopack.Deployment;
|
||||||
using Velopack.Sources;
|
using Velopack.Sources;
|
||||||
|
using Velopack.Util;
|
||||||
|
|
||||||
namespace Velopack.Packaging.Tests;
|
namespace Velopack.Packaging.Tests;
|
||||||
|
|
||||||
@@ -23,7 +24,7 @@ public class S3DeploymentTests
|
|||||||
{
|
{
|
||||||
Skip.If(String.IsNullOrWhiteSpace(B2_SECRET), "VELOPACK_B2_TEST_TOKEN is not set.");
|
Skip.If(String.IsNullOrWhiteSpace(B2_SECRET), "VELOPACK_B2_TEST_TOKEN is not set.");
|
||||||
using var logger = _output.BuildLoggerFor<S3DeploymentTests>();
|
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"))
|
string channel = String.IsNullOrWhiteSpace(Environment.GetEnvironmentVariable("CI"))
|
||||||
? VelopackRuntimeInfo.SystemOs.GetOsShortName()
|
? VelopackRuntimeInfo.SystemOs.GetOsShortName()
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
using System.Text.Json.Serialization;
|
using System.Text.Json.Serialization;
|
||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
using NuGet.Versioning;
|
using NuGet.Versioning;
|
||||||
using Velopack.Json;
|
|
||||||
using Velopack.Packaging;
|
using Velopack.Packaging;
|
||||||
using Velopack.Sources;
|
using Velopack.Sources;
|
||||||
using JsonPropertyNameAttribute = System.Text.Json.Serialization.JsonPropertyNameAttribute;
|
using JsonPropertyNameAttribute = System.Text.Json.Serialization.JsonPropertyNameAttribute;
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using Velopack.Packaging.Unix.Commands;
|
using Velopack.Packaging.Unix.Commands;
|
||||||
using Velopack.Packaging.Windows.Commands;
|
using Velopack.Packaging.Windows.Commands;
|
||||||
|
using Velopack.Util;
|
||||||
using Velopack.Vpk;
|
using Velopack.Vpk;
|
||||||
using Velopack.Vpk.Logging;
|
using Velopack.Vpk.Logging;
|
||||||
|
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ using Velopack.Compression;
|
|||||||
using Velopack.Packaging.Commands;
|
using Velopack.Packaging.Commands;
|
||||||
using Velopack.Packaging.Exceptions;
|
using Velopack.Packaging.Exceptions;
|
||||||
using Velopack.Packaging.Windows.Commands;
|
using Velopack.Packaging.Windows.Commands;
|
||||||
|
using Velopack.Util;
|
||||||
using Velopack.Vpk;
|
using Velopack.Vpk;
|
||||||
using Velopack.Vpk.Logging;
|
using Velopack.Vpk.Logging;
|
||||||
using Velopack.Windows;
|
using Velopack.Windows;
|
||||||
@@ -37,9 +38,9 @@ public class WindowsPackTests
|
|||||||
|
|
||||||
using var logger = _output.BuildLoggerFor<WindowsPackTests>();
|
using var logger = _output.BuildLoggerFor<WindowsPackTests>();
|
||||||
|
|
||||||
using var _1 = Utility.GetTempDirectory(out var tmpOutput);
|
using var _1 = TempUtil.GetTempDirectory(out var tmpOutput);
|
||||||
using var _2 = Utility.GetTempDirectory(out var tmpReleaseDir);
|
using var _2 = TempUtil.GetTempDirectory(out var tmpReleaseDir);
|
||||||
using var _3 = Utility.GetTempDirectory(out var unzipDir);
|
using var _3 = TempUtil.GetTempDirectory(out var unzipDir);
|
||||||
|
|
||||||
var exe = "testapp.exe";
|
var exe = "testapp.exe";
|
||||||
var pdb = Path.ChangeExtension(exe, ".pdb");
|
var pdb = Path.ChangeExtension(exe, ".pdb");
|
||||||
@@ -106,8 +107,8 @@ public class WindowsPackTests
|
|||||||
|
|
||||||
using var logger = _output.BuildLoggerFor<WindowsPackTests>();
|
using var logger = _output.BuildLoggerFor<WindowsPackTests>();
|
||||||
|
|
||||||
using var _1 = Utility.GetTempDirectory(out var tmpOutput);
|
using var _1 = TempUtil.GetTempDirectory(out var tmpOutput);
|
||||||
using var _2 = Utility.GetTempDirectory(out var tmpReleaseDir);
|
using var _2 = TempUtil.GetTempDirectory(out var tmpReleaseDir);
|
||||||
|
|
||||||
var exe = "testapp.exe";
|
var exe = "testapp.exe";
|
||||||
var pdb = Path.ChangeExtension(exe, ".pdb");
|
var pdb = Path.ChangeExtension(exe, ".pdb");
|
||||||
@@ -139,8 +140,8 @@ public class WindowsPackTests
|
|||||||
|
|
||||||
using var logger = _output.BuildLoggerFor<WindowsPackTests>();
|
using var logger = _output.BuildLoggerFor<WindowsPackTests>();
|
||||||
|
|
||||||
using var _1 = Utility.GetTempDirectory(out var tmpOutput);
|
using var _1 = TempUtil.GetTempDirectory(out var tmpOutput);
|
||||||
using var _2 = Utility.GetTempDirectory(out var tmpReleaseDir);
|
using var _2 = TempUtil.GetTempDirectory(out var tmpReleaseDir);
|
||||||
|
|
||||||
var exe = "testapp.exe";
|
var exe = "testapp.exe";
|
||||||
var pdb = Path.ChangeExtension(exe, ".pdb");
|
var pdb = Path.ChangeExtension(exe, ".pdb");
|
||||||
@@ -177,9 +178,9 @@ public class WindowsPackTests
|
|||||||
|
|
||||||
using var logger = _output.BuildLoggerFor<WindowsPackTests>();
|
using var logger = _output.BuildLoggerFor<WindowsPackTests>();
|
||||||
|
|
||||||
using var _1 = Utility.GetTempDirectory(out var tmpOutput);
|
using var _1 = TempUtil.GetTempDirectory(out var tmpOutput);
|
||||||
using var _2 = Utility.GetTempDirectory(out var tmpReleaseDir);
|
using var _2 = TempUtil.GetTempDirectory(out var tmpReleaseDir);
|
||||||
using var _3 = Utility.GetTempDirectory(out var tmpInstallDir);
|
using var _3 = TempUtil.GetTempDirectory(out var tmpInstallDir);
|
||||||
|
|
||||||
var exe = "testapp.exe";
|
var exe = "testapp.exe";
|
||||||
var pdb = Path.ChangeExtension(exe, ".pdb");
|
var pdb = Path.ChangeExtension(exe, ".pdb");
|
||||||
@@ -260,8 +261,8 @@ public class WindowsPackTests
|
|||||||
{
|
{
|
||||||
Skip.IfNot(VelopackRuntimeInfo.IsWindows);
|
Skip.IfNot(VelopackRuntimeInfo.IsWindows);
|
||||||
using var logger = _output.BuildLoggerFor<WindowsPackTests>();
|
using var logger = _output.BuildLoggerFor<WindowsPackTests>();
|
||||||
using var _1 = Utility.GetTempDirectory(out var releaseDir);
|
using var _1 = TempUtil.GetTempDirectory(out var releaseDir);
|
||||||
using var _2 = Utility.GetTempDirectory(out var installDir);
|
using var _2 = TempUtil.GetTempDirectory(out var installDir);
|
||||||
string id = "SquirrelAutoUpdateTest";
|
string id = "SquirrelAutoUpdateTest";
|
||||||
var appPath = Path.Combine(installDir, "current", "TestApp.exe");
|
var appPath = Path.Combine(installDir, "current", "TestApp.exe");
|
||||||
|
|
||||||
@@ -293,7 +294,7 @@ public class WindowsPackTests
|
|||||||
[SkippableFact]
|
[SkippableFact]
|
||||||
public void TestPackGeneratesValidDelta()
|
public void TestPackGeneratesValidDelta()
|
||||||
{
|
{
|
||||||
using var _1 = Utility.GetTempDirectory(out var releaseDir);
|
using var _1 = TempUtil.GetTempDirectory(out var releaseDir);
|
||||||
Skip.IfNot(VelopackRuntimeInfo.IsWindows);
|
Skip.IfNot(VelopackRuntimeInfo.IsWindows);
|
||||||
using var logger = _output.BuildLoggerFor<WindowsPackTests>();
|
using var logger = _output.BuildLoggerFor<WindowsPackTests>();
|
||||||
string id = "SquirrelDeltaTest";
|
string id = "SquirrelDeltaTest";
|
||||||
@@ -304,7 +305,7 @@ public class WindowsPackTests
|
|||||||
// did a zsdiff get created for our v2 update?
|
// did a zsdiff get created for our v2 update?
|
||||||
var deltaPath = Path.Combine(releaseDir, $"{id}-2.0.0-delta.nupkg");
|
var deltaPath = Path.Combine(releaseDir, $"{id}-2.0.0-delta.nupkg");
|
||||||
Assert.True(File.Exists(deltaPath));
|
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);
|
EasyZip.ExtractZipToDirectory(logger, deltaPath, extractDir);
|
||||||
var extractDllDiff = Path.Combine(extractDir, "lib", "app", "testapp.dll.zsdiff");
|
var extractDllDiff = Path.Combine(extractDir, "lib", "app", "testapp.dll.zsdiff");
|
||||||
var extractDllShasum = Path.Combine(extractDir, "lib", "app", "testapp.dll.shasum");
|
var extractDllShasum = Path.Combine(extractDir, "lib", "app", "testapp.dll.shasum");
|
||||||
@@ -366,8 +367,8 @@ public class WindowsPackTests
|
|||||||
{
|
{
|
||||||
Skip.IfNot(VelopackRuntimeInfo.IsWindows);
|
Skip.IfNot(VelopackRuntimeInfo.IsWindows);
|
||||||
using var logger = _output.BuildLoggerFor<WindowsPackTests>();
|
using var logger = _output.BuildLoggerFor<WindowsPackTests>();
|
||||||
using var _1 = Utility.GetTempDirectory(out var releaseDir);
|
using var _1 = TempUtil.GetTempDirectory(out var releaseDir);
|
||||||
using var _2 = Utility.GetTempDirectory(out var installDir);
|
using var _2 = TempUtil.GetTempDirectory(out var installDir);
|
||||||
string id = "SquirrelHookTest";
|
string id = "SquirrelHookTest";
|
||||||
var appPath = Path.Combine(installDir, "current", "TestApp.exe");
|
var appPath = Path.Combine(installDir, "current", "TestApp.exe");
|
||||||
|
|
||||||
@@ -415,8 +416,8 @@ public class WindowsPackTests
|
|||||||
{
|
{
|
||||||
Skip.IfNot(VelopackRuntimeInfo.IsWindows);
|
Skip.IfNot(VelopackRuntimeInfo.IsWindows);
|
||||||
using var logger = _output.BuildLoggerFor<WindowsPackTests>();
|
using var logger = _output.BuildLoggerFor<WindowsPackTests>();
|
||||||
using var _1 = Utility.GetTempDirectory(out var releaseDir);
|
using var _1 = TempUtil.GetTempDirectory(out var releaseDir);
|
||||||
using var _2 = Utility.GetTempDirectory(out var installDir);
|
using var _2 = TempUtil.GetTempDirectory(out var installDir);
|
||||||
|
|
||||||
string id = "SquirrelIntegrationTest";
|
string id = "SquirrelIntegrationTest";
|
||||||
|
|
||||||
@@ -507,7 +508,7 @@ public class WindowsPackTests
|
|||||||
|
|
||||||
var rootDir = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "LegacyTestApp");
|
var rootDir = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "LegacyTestApp");
|
||||||
if (Directory.Exists(rootDir)) {
|
if (Directory.Exists(rootDir)) {
|
||||||
Utility.Retry(() => Utility.DeleteFileOrDirectoryHard(rootDir), 10, 1000);
|
IoUtil.Retry(() => IoUtil.DeleteFileOrDirectoryHard(rootDir), 10, 1000);
|
||||||
}
|
}
|
||||||
|
|
||||||
var setup = PathHelper.GetFixture(fixture);
|
var setup = PathHelper.GetFixture(fixture);
|
||||||
@@ -520,7 +521,7 @@ public class WindowsPackTests
|
|||||||
Assert.True(File.Exists(appExe));
|
Assert.True(File.Exists(appExe));
|
||||||
Assert.True(File.Exists(updateExe));
|
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);
|
PackTestApp("LegacyTestApp", "2.0.0", "hello!", releaseDir, logger);
|
||||||
|
|
||||||
RunNoCoverage(appExe, new string[] { "download", releaseDir }, currentDir, logger, exitCode: 0);
|
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");
|
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);
|
return File.Open(outputFile, FileMode.Open, FileAccess.ReadWrite, FileShare.None);
|
||||||
}, 10, 1000, logger);
|
}, 10, 1000, logger);
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
|
using Velopack.Util;
|
||||||
|
|
||||||
namespace Velopack.Tests;
|
namespace Velopack.Tests;
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
#pragma warning disable CS0618 // Type or member is obsolete
|
#pragma warning disable CS0618 // Type or member is obsolete
|
||||||
using System.Runtime.Versioning;
|
using System.Runtime.Versioning;
|
||||||
using Velopack.Locators;
|
using Velopack.Locators;
|
||||||
|
using Velopack.Util;
|
||||||
using Velopack.Windows;
|
using Velopack.Windows;
|
||||||
|
|
||||||
namespace Velopack.Tests;
|
namespace Velopack.Tests;
|
||||||
@@ -21,7 +22,7 @@ public class ShortcutTests
|
|||||||
using var logger = _output.BuildLoggerFor<ShortcutTests>();
|
using var logger = _output.BuildLoggerFor<ShortcutTests>();
|
||||||
string exeName = "NotSquirrelAwareApp.exe";
|
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 packages = Directory.CreateDirectory(Path.Combine(rootDir, "packages"));
|
||||||
var current = Directory.CreateDirectory(Path.Combine(rootDir, "current"));
|
var current = Directory.CreateDirectory(Path.Combine(rootDir, "current"));
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
using System.IO.Compression;
|
using System.IO.Compression;
|
||||||
using Velopack.Compression;
|
using Velopack.Compression;
|
||||||
|
using Velopack.Util;
|
||||||
|
|
||||||
namespace Velopack.Tests;
|
namespace Velopack.Tests;
|
||||||
|
|
||||||
@@ -8,14 +9,14 @@ public class SymbolicLinkTests
|
|||||||
[Fact]
|
[Fact]
|
||||||
public void Exists_NoSuchFile()
|
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$$$")));
|
Assert.False(SymbolicLink.Exists(Path.Combine(tempFolder, "$$$NoSuchFolder$$$")));
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void Exists_IsADirectory()
|
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();
|
File.Create(Path.Combine(tempFolder, "AFile")).Close();
|
||||||
|
|
||||||
Assert.False(SymbolicLink.Exists(Path.Combine(tempFolder, "AFile")));
|
Assert.False(SymbolicLink.Exists(Path.Combine(tempFolder, "AFile")));
|
||||||
@@ -24,7 +25,7 @@ public class SymbolicLinkTests
|
|||||||
[Fact]
|
[Fact]
|
||||||
public void CreateDirectory_VerifyExists_GetTarget_Delete()
|
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 targetFolder = Path.Combine(tempFolder, "ADirectory");
|
||||||
string junctionPoint = Path.Combine(tempFolder, "SymLink");
|
string junctionPoint = Path.Combine(tempFolder, "SymLink");
|
||||||
|
|
||||||
@@ -64,7 +65,7 @@ public class SymbolicLinkTests
|
|||||||
[Fact]
|
[Fact]
|
||||||
public void CreateFile_VerifyExists_GetTarget_Delete()
|
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 tmpFile = Path.Combine(tempFolder, "AFile");
|
||||||
var symFile = Path.Combine(tempFolder, "SymFile");
|
var symFile = Path.Combine(tempFolder, "SymFile");
|
||||||
File.Create(tmpFile).Close();
|
File.Create(tmpFile).Close();
|
||||||
@@ -92,7 +93,7 @@ public class SymbolicLinkTests
|
|||||||
[Fact]
|
[Fact]
|
||||||
public void CreateFile_RelativePath()
|
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 subDir = Directory.CreateDirectory(Path.Combine(tempFolder, "SubDir")).FullName;
|
||||||
|
|
||||||
var tmpFile = Path.Combine(tempFolder, "AFile");
|
var tmpFile = Path.Combine(tempFolder, "AFile");
|
||||||
@@ -122,7 +123,7 @@ public class SymbolicLinkTests
|
|||||||
[Fact]
|
[Fact]
|
||||||
public void CreateDirectory_RelativePath()
|
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 subDir = Directory.CreateDirectory(Path.Combine(tempFolder, "SubDir")).FullName;
|
||||||
var subSubDir = Directory.CreateDirectory(Path.Combine(subDir, "SubSub")).FullName;
|
var subSubDir = Directory.CreateDirectory(Path.Combine(subDir, "SubSub")).FullName;
|
||||||
var subDir2 = Directory.CreateDirectory(Path.Combine(tempFolder, "SubDir2")).FullName;
|
var subDir2 = Directory.CreateDirectory(Path.Combine(tempFolder, "SubDir2")).FullName;
|
||||||
@@ -145,7 +146,7 @@ public class SymbolicLinkTests
|
|||||||
[Fact]
|
[Fact]
|
||||||
public void Create_ThrowsIfOverwriteNotSpecifiedAndDirectoryExists()
|
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 targetFolder = Path.Combine(tempFolder, "ADirectory");
|
||||||
string junctionPoint = Path.Combine(tempFolder, "SymLink");
|
string junctionPoint = Path.Combine(tempFolder, "SymLink");
|
||||||
|
|
||||||
@@ -156,7 +157,7 @@ public class SymbolicLinkTests
|
|||||||
[Fact]
|
[Fact]
|
||||||
public void Create_OverwritesIfSpecifiedAndDirectoryExists()
|
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 targetFolder = Path.Combine(tempFolder, "ADirectory");
|
||||||
string junctionPoint = Path.Combine(tempFolder, "SymLink");
|
string junctionPoint = Path.Combine(tempFolder, "SymLink");
|
||||||
|
|
||||||
@@ -171,7 +172,7 @@ public class SymbolicLinkTests
|
|||||||
[Fact]
|
[Fact]
|
||||||
public void Create_ThrowsIfTargetDirectoryDoesNotExist()
|
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 targetFolder = Path.Combine(tempFolder, "ADirectory");
|
||||||
string junctionPoint = Path.Combine(tempFolder, "SymLink");
|
string junctionPoint = Path.Combine(tempFolder, "SymLink");
|
||||||
Assert.Throws<IOException>(() => SymbolicLink.Create(junctionPoint, targetFolder, false));
|
Assert.Throws<IOException>(() => SymbolicLink.Create(junctionPoint, targetFolder, false));
|
||||||
@@ -180,21 +181,21 @@ public class SymbolicLinkTests
|
|||||||
[Fact]
|
[Fact]
|
||||||
public void GetTarget_NonExistentJunctionPoint()
|
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")));
|
Assert.Throws<IOException>(() => SymbolicLink.GetTarget(Path.Combine(tempFolder, "SymLink")));
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void GetTarget_CalledOnADirectoryThatIsNotAJunctionPoint()
|
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));
|
Assert.Throws<IOException>(() => SymbolicLink.GetTarget(tempFolder));
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void GetTarget_CalledOnAFile()
|
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();
|
File.Create(Path.Combine(tempFolder, "AFile")).Close();
|
||||||
|
|
||||||
Assert.Throws<IOException>(() => SymbolicLink.GetTarget(Path.Combine(tempFolder, "AFile")));
|
Assert.Throws<IOException>(() => SymbolicLink.GetTarget(Path.Combine(tempFolder, "AFile")));
|
||||||
@@ -204,21 +205,21 @@ public class SymbolicLinkTests
|
|||||||
public void Delete_NonExistentJunctionPoint()
|
public void Delete_NonExistentJunctionPoint()
|
||||||
{
|
{
|
||||||
// Should do nothing.
|
// 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"));
|
SymbolicLink.Delete(Path.Combine(tempFolder, "SymLink"));
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void Delete_CalledOnADirectoryThatIsNotAJunctionPoint()
|
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));
|
Assert.Throws<IOException>(() => SymbolicLink.Delete(tempFolder));
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void Delete_CalledOnAFile()
|
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();
|
File.Create(Path.Combine(tempFolder, "AFile")).Close();
|
||||||
|
|
||||||
Assert.Throws<IOException>(() => SymbolicLink.Delete(Path.Combine(tempFolder, "AFile")));
|
Assert.Throws<IOException>(() => SymbolicLink.Delete(Path.Combine(tempFolder, "AFile")));
|
||||||
@@ -227,7 +228,7 @@ public class SymbolicLinkTests
|
|||||||
[Fact]
|
[Fact]
|
||||||
public async Task ComplexSymlinkDirGetsZippedCorrectly()
|
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 temp = new DirectoryInfo(tempFolder);
|
||||||
var versions = temp.CreateSubdirectory("Versions");
|
var versions = temp.CreateSubdirectory("Versions");
|
||||||
var a = versions.CreateSubdirectory("A");
|
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, "Resources"), Path.Combine(versions.FullName, "Current", "Resources"), false, true);
|
||||||
SymbolicLink.Create(Path.Combine(temp.FullName, "App"), Path.Combine(versions.FullName, "Current", "App"), 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");
|
var output = Path.Combine(tempOutput, "output.zip");
|
||||||
|
|
||||||
await EasyZip.CreateZipFromDirectoryAsync(NullLogger.Instance, output, tempFolder);
|
await EasyZip.CreateZipFromDirectoryAsync(NullLogger.Instance, output, tempFolder);
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
using System.Text;
|
using System.Text;
|
||||||
using Velopack.Packaging;
|
using Velopack.Packaging;
|
||||||
using Velopack.Sources;
|
using Velopack.Sources;
|
||||||
|
using Velopack.Util;
|
||||||
|
|
||||||
namespace Velopack.Tests.TestHelpers;
|
namespace Velopack.Tests.TestHelpers;
|
||||||
|
|
||||||
@@ -16,8 +17,8 @@ internal class FakeFixtureRepository : Sources.IFileDownloader
|
|||||||
|
|
||||||
public FakeFixtureRepository(string pkgId, bool mockLatestFullVer, string channel = null)
|
public FakeFixtureRepository(string pkgId, bool mockLatestFullVer, string channel = null)
|
||||||
{
|
{
|
||||||
_releasesName = Utility.GetReleasesFileName(channel);
|
_releasesName = CoreUtil.GetReleasesFileName(channel);
|
||||||
_releasesNameNew = Utility.GetVeloReleaseIndexName(channel);
|
_releasesNameNew = CoreUtil.GetVeloReleaseIndexName(channel);
|
||||||
_pkgId = pkgId;
|
_pkgId = pkgId;
|
||||||
var releases = ReleaseEntry.BuildReleasesFile(PathHelper.GetFixturesDir(), false)
|
var releases = ReleaseEntry.BuildReleasesFile(PathHelper.GetFixturesDir(), false)
|
||||||
.Where(r => r.OriginalFilename.StartsWith(_pkgId))
|
.Where(r => r.OriginalFilename.StartsWith(_pkgId))
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
using System.Net;
|
using System.Net;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
using Velopack.Util;
|
||||||
|
|
||||||
namespace Velopack.Tests;
|
namespace Velopack.Tests;
|
||||||
|
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ using Velopack.Packaging;
|
|||||||
using Velopack.Locators;
|
using Velopack.Locators;
|
||||||
using Velopack.Sources;
|
using Velopack.Sources;
|
||||||
using Velopack.Tests.TestHelpers;
|
using Velopack.Tests.TestHelpers;
|
||||||
|
using Velopack.Util;
|
||||||
|
|
||||||
namespace Velopack.Tests;
|
namespace Velopack.Tests;
|
||||||
|
|
||||||
@@ -107,7 +108,7 @@ public class UpdateManagerTests
|
|||||||
var fixture = PathHelper.GetFixture("AvaloniaCrossPlat-1.0.11-win-full.nupkg");
|
var fixture = PathHelper.GetFixture("AvaloniaCrossPlat-1.0.11-win-full.nupkg");
|
||||||
|
|
||||||
using var logger = _output.BuildLoggerFor<UpdateManagerTests>();
|
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() {
|
var dl = new FakeDownloader() {
|
||||||
MockedResponseBytes = Encoding.UTF8.GetBytes(SimpleJson.SerializeObject(
|
MockedResponseBytes = Encoding.UTF8.GetBytes(SimpleJson.SerializeObject(
|
||||||
new VelopackAssetFeed {
|
new VelopackAssetFeed {
|
||||||
@@ -117,7 +118,7 @@ public class UpdateManagerTests
|
|||||||
Version = new SemanticVersion(1, 0, 11),
|
Version = new SemanticVersion(1, 0, 11),
|
||||||
Type = VelopackAssetType.Full,
|
Type = VelopackAssetType.Full,
|
||||||
FileName = $"https://mysite.com/releases/AvaloniaCrossPlat$-1.1.0.nupkg",
|
FileName = $"https://mysite.com/releases/AvaloniaCrossPlat$-1.1.0.nupkg",
|
||||||
SHA1 = Utility.CalculateFileSHA1(fixture),
|
SHA1 = IoUtil.CalculateFileSHA1(fixture),
|
||||||
Size = new FileInfo(fixture).Length,
|
Size = new FileInfo(fixture).Length,
|
||||||
} }
|
} }
|
||||||
}))
|
}))
|
||||||
@@ -145,7 +146,7 @@ public class UpdateManagerTests
|
|||||||
public void CheckFromLocal()
|
public void CheckFromLocal()
|
||||||
{
|
{
|
||||||
using var logger = _output.BuildLoggerFor<UpdateManagerTests>();
|
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 dl = GetMockDownloaderNoDelta();
|
||||||
var source = new SimpleWebSource("http://any.com", dl);
|
var source = new SimpleWebSource("http://any.com", dl);
|
||||||
var locator = new TestVelopackLocator("MyCoolApp", "1.0.0", tempPath, logger);
|
var locator = new TestVelopackLocator("MyCoolApp", "1.0.0", tempPath, logger);
|
||||||
@@ -162,7 +163,7 @@ public class UpdateManagerTests
|
|||||||
public void CheckFromLocalWithChannel()
|
public void CheckFromLocalWithChannel()
|
||||||
{
|
{
|
||||||
using var logger = _output.BuildLoggerFor<UpdateManagerTests>();
|
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 dl = GetMockDownloaderNoDelta();
|
||||||
var source = new SimpleWebSource("http://any.com", dl);
|
var source = new SimpleWebSource("http://any.com", dl);
|
||||||
var locator = new TestVelopackLocator("MyCoolApp", "1.0.0", tempPath, logger);
|
var locator = new TestVelopackLocator("MyCoolApp", "1.0.0", tempPath, logger);
|
||||||
@@ -180,7 +181,7 @@ public class UpdateManagerTests
|
|||||||
public void CheckForSameAsInstalledVersion()
|
public void CheckForSameAsInstalledVersion()
|
||||||
{
|
{
|
||||||
using var logger = _output.BuildLoggerFor<UpdateManagerTests>();
|
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 dl = GetMockDownloaderWith2Delta();
|
||||||
var myVer = new VelopackAsset() {
|
var myVer = new VelopackAsset() {
|
||||||
PackageId = "MyCoolApp",
|
PackageId = "MyCoolApp",
|
||||||
@@ -220,7 +221,7 @@ public class UpdateManagerTests
|
|||||||
public void CheckForLowerThanInstalledVersion()
|
public void CheckForLowerThanInstalledVersion()
|
||||||
{
|
{
|
||||||
using var logger = _output.BuildLoggerFor<UpdateManagerTests>();
|
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 dl = GetMockDownloaderWith2Delta();
|
||||||
var myVer = new VelopackAsset() {
|
var myVer = new VelopackAsset() {
|
||||||
PackageId = "MyCoolApp",
|
PackageId = "MyCoolApp",
|
||||||
@@ -254,7 +255,7 @@ public class UpdateManagerTests
|
|||||||
public void CheckFromLocalWithDelta()
|
public void CheckFromLocalWithDelta()
|
||||||
{
|
{
|
||||||
using var logger = _output.BuildLoggerFor<UpdateManagerTests>();
|
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 dl = GetMockDownloaderWith2Delta();
|
||||||
var myVer = new VelopackAsset() {
|
var myVer = new VelopackAsset() {
|
||||||
PackageId = "MyCoolApp",
|
PackageId = "MyCoolApp",
|
||||||
@@ -282,7 +283,7 @@ public class UpdateManagerTests
|
|||||||
string version = "3.4.287";
|
string version = "3.4.287";
|
||||||
|
|
||||||
using var logger = _output.BuildLoggerFor<UpdateManagerTests>();
|
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 repo = new FakeFixtureRepository(id, false);
|
||||||
var source = new SimpleWebSource("http://any.com", repo);
|
var source = new SimpleWebSource("http://any.com", repo);
|
||||||
var locator = new TestVelopackLocator(id, "1.0.0", packagesDir, logger);
|
var locator = new TestVelopackLocator(id, "1.0.0", packagesDir, logger);
|
||||||
@@ -316,7 +317,7 @@ public class UpdateManagerTests
|
|||||||
string version = "3.4.287";
|
string version = "3.4.287";
|
||||||
|
|
||||||
using var logger = _output.BuildLoggerFor<UpdateManagerTests>();
|
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 repo = new FakeFixtureRepository(id, false);
|
||||||
var source = new SimpleWebSource("http://any.com", repo);
|
var source = new SimpleWebSource("http://any.com", repo);
|
||||||
var locator = new TestVelopackLocator(id, "1.0.0", packagesDir, logger);
|
var locator = new TestVelopackLocator(id, "1.0.0", packagesDir, logger);
|
||||||
@@ -347,7 +348,7 @@ public class UpdateManagerTests
|
|||||||
public void NoDeltaIfNoBasePackage()
|
public void NoDeltaIfNoBasePackage()
|
||||||
{
|
{
|
||||||
using var logger = _output.BuildLoggerFor<UpdateManagerTests>();
|
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 dl = GetMockDownloaderWith2Delta();
|
||||||
var source = new SimpleWebSource("http://any.com", dl);
|
var source = new SimpleWebSource("http://any.com", dl);
|
||||||
var locator = new TestVelopackLocator("MyCoolApp", "1.0.0", tempPath, logger: logger);
|
var locator = new TestVelopackLocator("MyCoolApp", "1.0.0", tempPath, logger: logger);
|
||||||
@@ -363,7 +364,7 @@ public class UpdateManagerTests
|
|||||||
public void CheckFromLocalWithDeltaNoLocalPackage()
|
public void CheckFromLocalWithDeltaNoLocalPackage()
|
||||||
{
|
{
|
||||||
using var logger = _output.BuildLoggerFor<UpdateManagerTests>();
|
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 dl = GetMockDownloaderWith2Delta();
|
||||||
var source = new SimpleWebSource("http://any.com", dl);
|
var source = new SimpleWebSource("http://any.com", dl);
|
||||||
var locator = new TestVelopackLocator("MyCoolApp", "1.0.0", tempPath, logger: logger);
|
var locator = new TestVelopackLocator("MyCoolApp", "1.0.0", tempPath, logger: logger);
|
||||||
@@ -380,7 +381,7 @@ public class UpdateManagerTests
|
|||||||
{
|
{
|
||||||
// https://github.com/caesay/SquirrelCustomLauncherTestApp
|
// https://github.com/caesay/SquirrelCustomLauncherTestApp
|
||||||
using var logger = _output.BuildLoggerFor<UpdateManagerTests>();
|
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 locator = new TestVelopackLocator("MyCoolApp", "1.0.0", tempPath, logger);
|
||||||
var source = new GithubSource("https://github.com/caesay/SquirrelCustomLauncherTestApp", null, false);
|
var source = new GithubSource("https://github.com/caesay/SquirrelCustomLauncherTestApp", null, false);
|
||||||
var um = new UpdateManager(source, null, logger, locator);
|
var um = new UpdateManager(source, null, logger, locator);
|
||||||
@@ -394,7 +395,7 @@ public class UpdateManagerTests
|
|||||||
{
|
{
|
||||||
// https://github.com/caesay/SquirrelCustomLauncherTestApp
|
// https://github.com/caesay/SquirrelCustomLauncherTestApp
|
||||||
using var logger = _output.BuildLoggerFor<UpdateManagerTests>();
|
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 locator = new TestVelopackLocator("MyCoolApp", "1.0.0", tempPath, logger);
|
||||||
var source = new GithubSource("https://github.com/caesay/SquirrelCustomLauncherTestApp", null, false);
|
var source = new GithubSource("https://github.com/caesay/SquirrelCustomLauncherTestApp", null, false);
|
||||||
var opt = new UpdateOptions { ExplicitChannel = "hello" };
|
var opt = new UpdateOptions { ExplicitChannel = "hello" };
|
||||||
@@ -406,7 +407,7 @@ public class UpdateManagerTests
|
|||||||
{
|
{
|
||||||
// https://github.com/caesay/SquirrelCustomLauncherTestApp
|
// https://github.com/caesay/SquirrelCustomLauncherTestApp
|
||||||
using var logger = _output.BuildLoggerFor<UpdateManagerTests>();
|
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 locator = new TestVelopackLocator("MyCoolApp", "1.0.0", tempPath, logger);
|
||||||
var source = new GiteaSource("https://gitea.com/remco1271/VeloPackTest", null, false);
|
var source = new GiteaSource("https://gitea.com/remco1271/VeloPackTest", null, false);
|
||||||
var um = new UpdateManager(source, null, logger, locator);
|
var um = new UpdateManager(source, null, logger, locator);
|
||||||
@@ -419,7 +420,7 @@ public class UpdateManagerTests
|
|||||||
public void CheckFromEmptyFileSource()
|
public void CheckFromEmptyFileSource()
|
||||||
{
|
{
|
||||||
using var logger = _output.BuildLoggerFor<UpdateManagerTests>();
|
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 source = new SimpleFileSource(new DirectoryInfo(tempPath));
|
||||||
var locator = new TestVelopackLocator("MyCoolApp", "1.0.0", tempPath, logger);
|
var locator = new TestVelopackLocator("MyCoolApp", "1.0.0", tempPath, logger);
|
||||||
var um = new UpdateManager(source, null, logger, locator);
|
var um = new UpdateManager(source, null, logger, locator);
|
||||||
@@ -431,7 +432,7 @@ public class UpdateManagerTests
|
|||||||
public void NoUpdatesIfCurrentEqualsRemoteVersion()
|
public void NoUpdatesIfCurrentEqualsRemoteVersion()
|
||||||
{
|
{
|
||||||
using var logger = _output.BuildLoggerFor<UpdateManagerTests>();
|
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 dl = GetMockDownloaderNoDelta();
|
||||||
var source = new SimpleWebSource("http://any.com", dl);
|
var source = new SimpleWebSource("http://any.com", dl);
|
||||||
var locator = new TestVelopackLocator("MyCoolApp", "1.1.0", tempPath, logger);
|
var locator = new TestVelopackLocator("MyCoolApp", "1.1.0", tempPath, logger);
|
||||||
@@ -444,7 +445,7 @@ public class UpdateManagerTests
|
|||||||
public void NoUpdatesIfCurrentGreaterThanRemoteVersion()
|
public void NoUpdatesIfCurrentGreaterThanRemoteVersion()
|
||||||
{
|
{
|
||||||
using var logger = _output.BuildLoggerFor<UpdateManagerTests>();
|
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 dl = GetMockDownloaderNoDelta();
|
||||||
var source = new SimpleWebSource("http://any.com", dl);
|
var source = new SimpleWebSource("http://any.com", dl);
|
||||||
var locator = new TestVelopackLocator("MyCoolApp", "1.2.0", tempPath, logger);
|
var locator = new TestVelopackLocator("MyCoolApp", "1.2.0", tempPath, logger);
|
||||||
@@ -459,7 +460,7 @@ public class UpdateManagerTests
|
|||||||
public void DownloadsLatestFullVersion(string id, string version)
|
public void DownloadsLatestFullVersion(string id, string version)
|
||||||
{
|
{
|
||||||
using var logger = _output.BuildLoggerFor<UpdateManagerTests>();
|
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 repo = new FakeFixtureRepository(id, false);
|
||||||
var source = new SimpleWebSource("http://any.com", repo);
|
var source = new SimpleWebSource("http://any.com", repo);
|
||||||
var locator = new TestVelopackLocator(id, "1.0.0", packagesDir, logger);
|
var locator = new TestVelopackLocator(id, "1.0.0", packagesDir, logger);
|
||||||
@@ -484,7 +485,7 @@ public class UpdateManagerTests
|
|||||||
{
|
{
|
||||||
Skip.If(VelopackRuntimeInfo.IsLinux);
|
Skip.If(VelopackRuntimeInfo.IsLinux);
|
||||||
using var logger = _output.BuildLoggerFor<UpdateManagerTests>();
|
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 repo = new FakeFixtureRepository(id, true);
|
||||||
var source = new SimpleWebSource("http://any.com", repo);
|
var source = new SimpleWebSource("http://any.com", repo);
|
||||||
|
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
using System.Runtime.Versioning;
|
using System.Runtime.Versioning;
|
||||||
using System.Security.Cryptography;
|
using System.Security.Cryptography;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
using Velopack.Util;
|
||||||
using Velopack.Windows;
|
using Velopack.Windows;
|
||||||
|
|
||||||
namespace Velopack.Tests;
|
namespace Velopack.Tests;
|
||||||
@@ -26,7 +27,7 @@ public class UtilityTests
|
|||||||
{
|
{
|
||||||
Skip.IfNot(VelopackRuntimeInfo.IsWindows);
|
Skip.IfNot(VelopackRuntimeInfo.IsWindows);
|
||||||
var exp = Path.GetFullPath(expected);
|
var exp = Path.GetFullPath(expected);
|
||||||
var normal = Utility.NormalizePath(input);
|
var normal = PathUtil.NormalizePath(input);
|
||||||
Assert.Equal(exp, normal);
|
Assert.Equal(exp, normal);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -43,7 +44,7 @@ public class UtilityTests
|
|||||||
public void FileIsInDirectory(string directory, string file, bool isIn)
|
public void FileIsInDirectory(string directory, string file, bool isIn)
|
||||||
{
|
{
|
||||||
Skip.IfNot(VelopackRuntimeInfo.IsWindows);
|
Skip.IfNot(VelopackRuntimeInfo.IsWindows);
|
||||||
var fileInDir = Utility.IsFileInDirectory(file, directory);
|
var fileInDir = PathUtil.IsFileInDirectory(file, directory);
|
||||||
Assert.Equal(isIn, fileInDir);
|
Assert.Equal(isIn, fileInDir);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -84,24 +85,24 @@ public class UtilityTests
|
|||||||
var emptyString = string.Empty;
|
var emptyString = string.Empty;
|
||||||
string nullString = null;
|
string nullString = null;
|
||||||
byte[] nullByteArray = { };
|
byte[] nullByteArray = { };
|
||||||
Assert.Equal(string.Empty, Utility.RemoveByteOrderMarkerIfPresent(emptyString));
|
Assert.Equal(string.Empty, CoreUtil.RemoveByteOrderMarkerIfPresent(emptyString));
|
||||||
Assert.Equal(string.Empty, Utility.RemoveByteOrderMarkerIfPresent(nullString));
|
Assert.Equal(string.Empty, CoreUtil.RemoveByteOrderMarkerIfPresent(nullString));
|
||||||
Assert.Equal(string.Empty, Utility.RemoveByteOrderMarkerIfPresent(nullByteArray));
|
Assert.Equal(string.Empty, CoreUtil.RemoveByteOrderMarkerIfPresent(nullByteArray));
|
||||||
|
|
||||||
Assert.Equal(string.Empty, Utility.RemoveByteOrderMarkerIfPresent(utf32Be));
|
Assert.Equal(string.Empty, CoreUtil.RemoveByteOrderMarkerIfPresent(utf32Be));
|
||||||
Assert.Equal(string.Empty, Utility.RemoveByteOrderMarkerIfPresent(utf32Le));
|
Assert.Equal(string.Empty, CoreUtil.RemoveByteOrderMarkerIfPresent(utf32Le));
|
||||||
Assert.Equal(string.Empty, Utility.RemoveByteOrderMarkerIfPresent(utf16Be));
|
Assert.Equal(string.Empty, CoreUtil.RemoveByteOrderMarkerIfPresent(utf16Be));
|
||||||
Assert.Equal(string.Empty, Utility.RemoveByteOrderMarkerIfPresent(utf16Le));
|
Assert.Equal(string.Empty, CoreUtil.RemoveByteOrderMarkerIfPresent(utf16Le));
|
||||||
Assert.Equal(string.Empty, Utility.RemoveByteOrderMarkerIfPresent(utf8));
|
Assert.Equal(string.Empty, CoreUtil.RemoveByteOrderMarkerIfPresent(utf8));
|
||||||
|
|
||||||
Assert.Equal("hello world", Utility.RemoveByteOrderMarkerIfPresent(utf32BeHelloWorld));
|
Assert.Equal("hello world", CoreUtil.RemoveByteOrderMarkerIfPresent(utf32BeHelloWorld));
|
||||||
Assert.Equal("hello world", Utility.RemoveByteOrderMarkerIfPresent(utf32LeHelloWorld));
|
Assert.Equal("hello world", CoreUtil.RemoveByteOrderMarkerIfPresent(utf32LeHelloWorld));
|
||||||
Assert.Equal("hello world", Utility.RemoveByteOrderMarkerIfPresent(utf16BeHelloWorld));
|
Assert.Equal("hello world", CoreUtil.RemoveByteOrderMarkerIfPresent(utf16BeHelloWorld));
|
||||||
Assert.Equal("hello world", Utility.RemoveByteOrderMarkerIfPresent(utf16LeHelloWorld));
|
Assert.Equal("hello world", CoreUtil.RemoveByteOrderMarkerIfPresent(utf16LeHelloWorld));
|
||||||
Assert.Equal("hello world", Utility.RemoveByteOrderMarkerIfPresent(utf8HelloWorld));
|
Assert.Equal("hello world", CoreUtil.RemoveByteOrderMarkerIfPresent(utf8HelloWorld));
|
||||||
|
|
||||||
Assert.Equal("hello world", Utility.RemoveByteOrderMarkerIfPresent(asciiMultipleChars));
|
Assert.Equal("hello world", CoreUtil.RemoveByteOrderMarkerIfPresent(asciiMultipleChars));
|
||||||
Assert.Equal("A", Utility.RemoveByteOrderMarkerIfPresent(asciiSingleChar));
|
Assert.Equal("A", CoreUtil.RemoveByteOrderMarkerIfPresent(asciiSingleChar));
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
@@ -110,7 +111,7 @@ public class UtilityTests
|
|||||||
var sha1FromExternalTool = "75255cfd229a1ed1447abe1104f5635e69975d30";
|
var sha1FromExternalTool = "75255cfd229a1ed1447abe1104f5635e69975d30";
|
||||||
var inputPackage = PathHelper.GetFixture("Squirrel.Core.1.0.0.0.nupkg");
|
var inputPackage = PathHelper.GetFixture("Squirrel.Core.1.0.0.0.nupkg");
|
||||||
var stream = File.OpenRead(inputPackage);
|
var stream = File.OpenRead(inputPackage);
|
||||||
var sha1 = Utility.CalculateStreamSHA1(stream);
|
var sha1 = IoUtil.CalculateStreamSHA1(stream);
|
||||||
|
|
||||||
Assert.NotEqual(sha1FromExternalTool, sha1);
|
Assert.NotEqual(sha1FromExternalTool, sha1);
|
||||||
Assert.Equal(sha1FromExternalTool, sha1, StringComparer.OrdinalIgnoreCase);
|
Assert.Equal(sha1FromExternalTool, sha1, StringComparer.OrdinalIgnoreCase);
|
||||||
@@ -121,7 +122,7 @@ public class UtilityTests
|
|||||||
{
|
{
|
||||||
using var logger = _output.BuildLoggerFor<UtilityTests>();
|
using var logger = _output.BuildLoggerFor<UtilityTests>();
|
||||||
string tempDir;
|
string tempDir;
|
||||||
using (Utility.GetTempDirectory(out tempDir)) {
|
using (TempUtil.GetTempDirectory(out tempDir)) {
|
||||||
for (var i = 0; i < 50; i++) {
|
for (var i = 0; i < 50; i++) {
|
||||||
var directory = Path.Combine(tempDir, newId());
|
var directory = Path.Combine(tempDir, newId());
|
||||||
CreateSampleDirectory(directory);
|
CreateSampleDirectory(directory);
|
||||||
@@ -135,7 +136,7 @@ public class UtilityTests
|
|||||||
|
|
||||||
var sw = new Stopwatch();
|
var sw = new Stopwatch();
|
||||||
sw.Start();
|
sw.Start();
|
||||||
Utility.DeleteFileOrDirectoryHard(tempDir);
|
IoUtil.DeleteFileOrDirectoryHard(tempDir);
|
||||||
sw.Stop();
|
sw.Stop();
|
||||||
logger.Info($"Delete took {sw.ElapsedMilliseconds}ms");
|
logger.Info($"Delete took {sw.ElapsedMilliseconds}ms");
|
||||||
|
|
||||||
@@ -147,7 +148,7 @@ public class UtilityTests
|
|||||||
//public void CreateFakePackageSmokeTest()
|
//public void CreateFakePackageSmokeTest()
|
||||||
//{
|
//{
|
||||||
// string path;
|
// string path;
|
||||||
// using (Utility.GetTempDirectory(out path)) {
|
// using (TempUtil.GetTempDirectory(out path)) {
|
||||||
// var output = IntegrationTestHelper.CreateFakeInstalledApp("0.3.0", path);
|
// var output = IntegrationTestHelper.CreateFakeInstalledApp("0.3.0", path);
|
||||||
// Assert.True(File.Exists(output));
|
// Assert.True(File.Exists(output));
|
||||||
// }
|
// }
|
||||||
@@ -161,7 +162,7 @@ public class UtilityTests
|
|||||||
[InlineData(".rels", false)]
|
[InlineData(".rels", false)]
|
||||||
public void FileIsLikelyPEImageTest(string input, bool result)
|
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")]
|
[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.
|
// this probably should use a local http server instead.
|
||||||
const string testUrl = "http://speedtest.tele2.net/1MB.zip";
|
const string testUrl = "http://speedtest.tele2.net/1MB.zip";
|
||||||
|
|
||||||
var dl = Utility.CreateDefaultDownloader();
|
var dl = HttpUtil.CreateDefaultDownloader();
|
||||||
|
|
||||||
List<int> prog = new List<int>();
|
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);
|
await dl.DownloadFile(testUrl, tempPath, prog.Add);
|
||||||
|
|
||||||
Assert.True(prog.Count > 10);
|
Assert.True(prog.Count > 10);
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
using NuGet.Versioning;
|
using NuGet.Versioning;
|
||||||
using Velopack.NuGet;
|
using Velopack.NuGet;
|
||||||
using Velopack.Tests.TestHelpers;
|
using Velopack.Tests.TestHelpers;
|
||||||
|
using Velopack.Util;
|
||||||
using ZipPackage = Velopack.NuGet.ZipPackage;
|
using ZipPackage = Velopack.NuGet.ZipPackage;
|
||||||
|
|
||||||
namespace Velopack.Tests;
|
namespace Velopack.Tests;
|
||||||
@@ -19,9 +20,9 @@ public class ZipPackageTests
|
|||||||
{
|
{
|
||||||
using var logger = _output.BuildLoggerFor<ZipPackageTests>();
|
using var logger = _output.BuildLoggerFor<ZipPackageTests>();
|
||||||
|
|
||||||
using var _1 = Utility.GetTempDirectory(out var tempDir);
|
using var _1 = TempUtil.GetTempDirectory(out var tempDir);
|
||||||
using var _2 = Utility.GetTempDirectory(out var zipDir);
|
using var _2 = TempUtil.GetTempDirectory(out var zipDir);
|
||||||
using var _3 = Utility.GetTempDirectory(out var extractedDir);
|
using var _3 = TempUtil.GetTempDirectory(out var extractedDir);
|
||||||
|
|
||||||
var actual = Path.Combine(tempDir, "actual");
|
var actual = Path.Combine(tempDir, "actual");
|
||||||
var actualFile = Path.Combine(actual, "file.txt");
|
var actualFile = Path.Combine(actual, "file.txt");
|
||||||
@@ -56,7 +57,7 @@ public class ZipPackageTests
|
|||||||
[Fact]
|
[Fact]
|
||||||
public void HasSameFilesAndDependenciesAsPackaging()
|
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 inputPackage = PathHelper.GetFixture("slack-1.1.8-full.nupkg");
|
||||||
var copyPackage = Path.Combine(tempDir, "slack-1.1.8-full.nupkg");
|
var copyPackage = Path.Combine(tempDir, "slack-1.1.8-full.nupkg");
|
||||||
File.Copy(inputPackage, copyPackage);
|
File.Copy(inputPackage, copyPackage);
|
||||||
|
|||||||
Reference in New Issue
Block a user