mirror of
https://github.com/velopack/velopack.git
synced 2025-10-25 15:19:22 +00:00
Detect VelopackApp.Run() in any method in main module
This commit is contained in:
@@ -85,4 +85,9 @@ public class MSBuildLogger(TaskLoggingHelper loggingHelper) : ILogger, IFancyCon
|
|||||||
{
|
{
|
||||||
Log(LogLevel.Information, 0, null, null, (object? state, Exception? exception) => text);
|
Log(LogLevel.Information, 0, null, null, (object? state, Exception? exception) => text);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public string EscapeMarkup(string text)
|
||||||
|
{
|
||||||
|
return text;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -33,11 +33,12 @@ public class WindowsPackCommandRunner : PackageBuilder<WindowsPackOptions>
|
|||||||
if (Directory.EnumerateFiles(packDir, "*.application").Any(f => File.ReadAllText(f).Contains("clickonce"))) {
|
if (Directory.EnumerateFiles(packDir, "*.application").Any(f => File.ReadAllText(f).Contains("clickonce"))) {
|
||||||
throw new ArgumentException(
|
throw new ArgumentException(
|
||||||
"Velopack does not support building releases for ClickOnce applications. " +
|
"Velopack does not support building releases for ClickOnce applications. " +
|
||||||
"Please publish your application to a folder without ClickOnce.");
|
"Please remove all ClickOnce properties from your .csproj before continuing.");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!Options.SkipVelopackAppCheck) {
|
if (!Options.SkipVelopackAppCheck) {
|
||||||
DotnetUtil.VerifyVelopackApp(MainExePath, Log);
|
var compat = new CompatUtil(Log, Console);
|
||||||
|
compat.Verify(MainExePath);
|
||||||
} else {
|
} else {
|
||||||
Log.Info("Skipping VelopackApp.Build.Run() check.");
|
Log.Info("Skipping VelopackApp.Build.Run() check.");
|
||||||
}
|
}
|
||||||
|
|||||||
241
src/Velopack.Packaging.Windows/CompatUtil.cs
Normal file
241
src/Velopack.Packaging.Windows/CompatUtil.cs
Normal file
@@ -0,0 +1,241 @@
|
|||||||
|
using AsmResolver.DotNet;
|
||||||
|
using AsmResolver.DotNet.Bundles;
|
||||||
|
using AsmResolver.DotNet.Serialized;
|
||||||
|
using AsmResolver.PE;
|
||||||
|
using AsmResolver.PE.DotNet.Cil;
|
||||||
|
using AsmResolver.PE.Win32Resources.Version;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
using NuGet.Versioning;
|
||||||
|
using Velopack.Packaging.Abstractions;
|
||||||
|
using Velopack.Packaging.Exceptions;
|
||||||
|
|
||||||
|
namespace Velopack.Packaging.Windows;
|
||||||
|
|
||||||
|
public class CompatUtil
|
||||||
|
{
|
||||||
|
private readonly ILogger _log;
|
||||||
|
private readonly IFancyConsole _console;
|
||||||
|
|
||||||
|
public CompatUtil(ILogger logger, IFancyConsole console)
|
||||||
|
{
|
||||||
|
_log = logger;
|
||||||
|
_console = console;
|
||||||
|
}
|
||||||
|
|
||||||
|
public NuGetVersion Verify(string exeFile)
|
||||||
|
{
|
||||||
|
VerifyVelopackApp(exeFile);
|
||||||
|
return VerifyVelopackVersion(exeFile);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void VerifyVelopackApp(string exeFile)
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
AssemblyDefinition mainAssy = LoadDotnetAssembly(exeFile);
|
||||||
|
if (mainAssy == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ModuleDefinition mainModule = mainAssy.Modules.Single();
|
||||||
|
var result = SearchAssemblyForVelopackApp(mainModule);
|
||||||
|
if (result == null) {
|
||||||
|
// if we've iterated the whole main assembly and not found the call, then the velopack builder is missing
|
||||||
|
throw new UserInfoException($"Unable to verify VelopackApp is called. " +
|
||||||
|
"Please ensure that 'VelopackApp.Build().Run()' is present in your Program.Main().");
|
||||||
|
}
|
||||||
|
|
||||||
|
result = _console.EscapeMarkup(result);
|
||||||
|
|
||||||
|
// could be regular Program.Main or async Main which has a slightly different signature.
|
||||||
|
if (result.Contains("Program") && result.Contains("Main")) {
|
||||||
|
_log.Info($"[green underline]Verified VelopackApp.Run()[/] in '{result}'.");
|
||||||
|
} else {
|
||||||
|
_log.Warn($"VelopackApp.Run() was found in method '{result}', which does not look like your application's entry point." +
|
||||||
|
$"It is [underline yellow]strongly recommended[/] that you move this to the very beginning of your Main() method. ");
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (Exception ex) when (ex is not UserInfoException) {
|
||||||
|
_log.Error("Unable to verify VelopackApp: " + ex.Message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public NuGetVersion VerifyVelopackVersion(string exeFile)
|
||||||
|
{
|
||||||
|
var rawVersion = GetVelopackVersion(exeFile);
|
||||||
|
if (rawVersion == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
var dllVersion = new Version(rawVersion.ToString("V", VersionFormatter.Instance));
|
||||||
|
var myVersion = new Version(VelopackRuntimeInfo.VelopackNugetVersion.ToString("V", VersionFormatter.Instance));
|
||||||
|
if (dllVersion == myVersion) {
|
||||||
|
return new NuGetVersion(dllVersion);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dllVersion > myVersion) {
|
||||||
|
throw new UserInfoException($"Velopack library version is greater than vpk version ({dllVersion} > {myVersion}). This can cause compatibility issues, please update vpk first.");
|
||||||
|
}
|
||||||
|
|
||||||
|
_log.Warn($"Velopack library version is lower than vpk version ({dllVersion} < {myVersion}). This can occasionally cause compatibility issues.");
|
||||||
|
return new NuGetVersion(dllVersion);
|
||||||
|
}
|
||||||
|
|
||||||
|
private string SearchAssemblyForVelopackApp(ModuleDefinition mainModule)
|
||||||
|
{
|
||||||
|
MethodDefinition entryPoint = mainModule.ManagedEntryPointMethod;
|
||||||
|
|
||||||
|
string SearchMethod(MethodDefinition method)
|
||||||
|
{
|
||||||
|
foreach (var instr in method.CilMethodBody.Instructions) {
|
||||||
|
if (instr.OpCode.Code is CilCode.Call or CilCode.Callvirt or CilCode.Calli) {
|
||||||
|
var operand = instr.Operand as SerializedMemberReference;
|
||||||
|
if (operand != null) {
|
||||||
|
if (operand.Name == "Run" && operand.DeclaringType.FullName == "Velopack.VelopackApp") {
|
||||||
|
// success!
|
||||||
|
return method.FullName;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
string SearchType(TypeDefinition type)
|
||||||
|
{
|
||||||
|
// search all methods in type
|
||||||
|
foreach (var method in type.Methods) {
|
||||||
|
if (method.HasMethodBody) {
|
||||||
|
var result = SearchMethod(method);
|
||||||
|
if (result != null) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// then, search all nested types
|
||||||
|
foreach (var nestedType in type.NestedTypes) {
|
||||||
|
var result = SearchType(nestedType);
|
||||||
|
if (result != null) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// search entry point first
|
||||||
|
string result;
|
||||||
|
if ((result = SearchMethod(entryPoint)) != null) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// then, iterate all methods in the main module
|
||||||
|
foreach (var topType in mainModule.TopLevelTypes) {
|
||||||
|
if ((result = SearchType(topType)) != null) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public NuGetVersion GetVelopackVersion(string exeFile)
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
var velopackDll = FindVelopackDll(exeFile);
|
||||||
|
if (velopackDll == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
var versionInfo = VersionInfoResource.FromDirectory(velopackDll.Resources);
|
||||||
|
var actualInfo = versionInfo.GetChild<StringFileInfo>(StringFileInfo.StringFileInfoKey);
|
||||||
|
var versionTable = actualInfo.Tables[0];
|
||||||
|
var productVersion = versionTable.Where(v => v.Key == StringTable.ProductVersionKey).FirstOrDefault();
|
||||||
|
return NuGetVersion.Parse(productVersion.Value);
|
||||||
|
} catch (Exception ex) {
|
||||||
|
// don't really care
|
||||||
|
_log.Debug(ex, "Unable to read Velopack.dll version info.");
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private IPEImage FindVelopackDll(string exeFile)
|
||||||
|
{
|
||||||
|
var versionFile = Path.Combine(Path.GetDirectoryName(exeFile), "Velopack.dll");
|
||||||
|
if (File.Exists(versionFile)) {
|
||||||
|
_log.Debug(exeFile + " has Velopack.dll in the same directory.");
|
||||||
|
return PEImage.FromFile(versionFile);
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
var bundle = BundleManifest.FromFile(exeFile);
|
||||||
|
IList<BundleFile> embeddedFiles = bundle.Files;
|
||||||
|
var velopackEmbedded = embeddedFiles.SingleOrDefault(f => f.Type == BundleFileType.Assembly && f.RelativePath == "Velopack.dll");
|
||||||
|
if (velopackEmbedded != null && velopackEmbedded.TryGetReader(out var readerVel)) {
|
||||||
|
_log.Debug(exeFile + " has Velopack.dll embedded in a SingleFileHost.");
|
||||||
|
return PEImage.FromReader(readerVel);
|
||||||
|
}
|
||||||
|
} catch (BadImageFormatException) {
|
||||||
|
// not an AppHost / SingleFileHost binary
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private AssemblyDefinition LoadDotnetAssembly(string exeFile)
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
var assy = AssemblyDefinition.FromFile(exeFile);
|
||||||
|
return assy;
|
||||||
|
} catch (BadImageFormatException) {
|
||||||
|
// not a .Net Framework binary
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
var bundle = BundleManifest.FromFile(exeFile);
|
||||||
|
IList<BundleFile> embeddedFiles = null;
|
||||||
|
|
||||||
|
try {
|
||||||
|
embeddedFiles = bundle.Files;
|
||||||
|
} catch {
|
||||||
|
// not a SingleFileHost binary, so we'll search on disk
|
||||||
|
var parentDir = Path.GetDirectoryName(exeFile);
|
||||||
|
var diskFile = Path.Combine(parentDir, Path.GetFileNameWithoutExtension(exeFile) + ".dll");
|
||||||
|
if (File.Exists(diskFile)) {
|
||||||
|
return AssemblyDefinition.FromFile(diskFile);
|
||||||
|
}
|
||||||
|
|
||||||
|
var runtimeConfigFile = Directory.EnumerateFiles(parentDir, "*.runtimeconfig.json").SingleOrDefault();
|
||||||
|
var possNameRuntime = Path.Combine(parentDir,
|
||||||
|
Path.GetFileNameWithoutExtension(Path.GetFileNameWithoutExtension(runtimeConfigFile)) + ".dll");
|
||||||
|
if (File.Exists(possNameRuntime)) {
|
||||||
|
return AssemblyDefinition.FromFile(possNameRuntime);
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
var runtimeConfig = embeddedFiles.SingleOrDefault(f => f.Type == BundleFileType.RuntimeConfigJson);
|
||||||
|
if (runtimeConfig != null) {
|
||||||
|
var possName1 = Path.GetFileNameWithoutExtension(Path.GetFileNameWithoutExtension(runtimeConfig.RelativePath)) + ".dll";
|
||||||
|
var file = embeddedFiles.SingleOrDefault(f => f.Type == BundleFileType.Assembly && f.RelativePath == possName1);
|
||||||
|
if (file != null && file.TryGetReader(out var reader)) {
|
||||||
|
return AssemblyDefinition.FromReader(reader);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var possName2 = Path.GetFileNameWithoutExtension(exeFile) + ".dll";
|
||||||
|
var file2 = embeddedFiles.SingleOrDefault(f => f.Type == BundleFileType.Assembly && f.RelativePath == possName2);
|
||||||
|
if (file2 != null && file2.TryGetReader(out var reader2)) {
|
||||||
|
return AssemblyDefinition.FromReader(reader2);
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
} catch (BadImageFormatException) {
|
||||||
|
// not an AppHost / SingleFileHost binary
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,151 +0,0 @@
|
|||||||
using AsmResolver.DotNet;
|
|
||||||
using AsmResolver.DotNet.Bundles;
|
|
||||||
using AsmResolver.DotNet.Serialized;
|
|
||||||
using AsmResolver.PE;
|
|
||||||
using AsmResolver.PE.DotNet.Cil;
|
|
||||||
using AsmResolver.PE.Win32Resources.Version;
|
|
||||||
using Microsoft.Extensions.Logging;
|
|
||||||
using NuGet.Versioning;
|
|
||||||
using Velopack.Packaging.Exceptions;
|
|
||||||
|
|
||||||
namespace Velopack.Packaging.Windows;
|
|
||||||
|
|
||||||
public class DotnetUtil
|
|
||||||
{
|
|
||||||
public static NuGetVersion VerifyVelopackApp(string exeFile, ILogger log)
|
|
||||||
{
|
|
||||||
try {
|
|
||||||
NuGetVersion velopackVersion = null;
|
|
||||||
IPEImage velopackDll = null;
|
|
||||||
AssemblyDefinition mainAssy = null;
|
|
||||||
mainAssy ??= LoadFullFramework(exeFile, ref velopackDll);
|
|
||||||
mainAssy ??= LoadDncBundle(exeFile, ref velopackDll);
|
|
||||||
|
|
||||||
if (mainAssy == null) {
|
|
||||||
// not a dotnet binary
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (velopackDll != null) {
|
|
||||||
try {
|
|
||||||
var versionInfo = VersionInfoResource.FromDirectory(velopackDll.Resources);
|
|
||||||
var actualInfo = versionInfo.GetChild<StringFileInfo>(StringFileInfo.StringFileInfoKey);
|
|
||||||
var versionTable = actualInfo.Tables[0];
|
|
||||||
var productVersion = versionTable.Where(v => v.Key == StringTable.ProductVersionKey).FirstOrDefault();
|
|
||||||
velopackVersion = NuGetVersion.Parse(productVersion.Value);
|
|
||||||
} catch (Exception ex) {
|
|
||||||
// don't really care
|
|
||||||
log.Debug(ex, "Unable to read Velopack.dll version info.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var mainModule = mainAssy.Modules.Single();
|
|
||||||
var entryPoint = mainModule.ManagedEntryPointMethod;
|
|
||||||
|
|
||||||
foreach (var instr in entryPoint.CilMethodBody.Instructions) {
|
|
||||||
if (instr.OpCode.Code is CilCode.Call or CilCode.Callvirt or CilCode.Calli) {
|
|
||||||
SerializedMemberReference operand = instr.Operand as SerializedMemberReference;
|
|
||||||
if (operand != null && operand.IsMethod) {
|
|
||||||
if (operand.Name == "Run" && operand.DeclaringType.FullName == "Velopack.VelopackApp") {
|
|
||||||
// success!
|
|
||||||
if (velopackVersion != null) {
|
|
||||||
log.Info($"Verified VelopackApp.Run() in '{entryPoint.FullName}', version {velopackVersion}.");
|
|
||||||
if (velopackVersion != VelopackRuntimeInfo.VelopackProductVersion) {
|
|
||||||
log.Warn(exeFile + " was built with a different version of Velopack than this tool. " +
|
|
||||||
$"This may cause compatibility issues. Expected {VelopackRuntimeInfo.VelopackProductVersion}, " +
|
|
||||||
$"but found {velopackVersion}.");
|
|
||||||
}
|
|
||||||
return velopackVersion;
|
|
||||||
} else {
|
|
||||||
log.Warn("VelopackApp verified at entry point, but ProductVersion could not be checked.");
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// if we've iterated the whole main method and not found the call, then the velopack builder is missing
|
|
||||||
throw new UserInfoException($"Unable to verify VelopackApp, in application main method '{entryPoint.FullName}'. " +
|
|
||||||
"Please ensure that 'VelopackApp.Build().Run()' is present in your Program.Main().");
|
|
||||||
|
|
||||||
} catch (Exception ex) when (ex is not UserInfoException) {
|
|
||||||
log.Error("Unable to verify VelopackApp: " + ex.Message);
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static AssemblyDefinition LoadFullFramework(string exeFile, ref IPEImage velopackDll)
|
|
||||||
{
|
|
||||||
try {
|
|
||||||
var assy = AssemblyDefinition.FromFile(exeFile);
|
|
||||||
var versionFile = Path.Combine(Path.GetDirectoryName(exeFile), "Velopack.dll");
|
|
||||||
if (File.Exists(versionFile)) {
|
|
||||||
velopackDll = PEImage.FromFile(versionFile);
|
|
||||||
}
|
|
||||||
return assy;
|
|
||||||
} catch (BadImageFormatException) {
|
|
||||||
// not a .Net Framework binary
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static AssemblyDefinition LoadDncBundle(string exeFile, ref IPEImage velopackDll)
|
|
||||||
{
|
|
||||||
try {
|
|
||||||
var bundle = BundleManifest.FromFile(exeFile);
|
|
||||||
IList<BundleFile> embeddedFiles = null;
|
|
||||||
|
|
||||||
try {
|
|
||||||
embeddedFiles = bundle.Files;
|
|
||||||
} catch {
|
|
||||||
// not a SingleFileHost binary, so we'll search on disk
|
|
||||||
var parentDir = Path.GetDirectoryName(exeFile);
|
|
||||||
var versionFile = Path.Combine(parentDir, "Velopack.dll");
|
|
||||||
if (File.Exists(versionFile)) {
|
|
||||||
velopackDll = PEImage.FromFile(versionFile);
|
|
||||||
}
|
|
||||||
|
|
||||||
var diskFile = Path.Combine(parentDir, Path.GetFileNameWithoutExtension(exeFile) + ".dll");
|
|
||||||
if (File.Exists(diskFile)) {
|
|
||||||
return AssemblyDefinition.FromFile(diskFile);
|
|
||||||
}
|
|
||||||
|
|
||||||
var runtimeConfigFile = Directory.EnumerateFiles(parentDir, "*.runtimeconfig.json").SingleOrDefault();
|
|
||||||
var possNameRuntime = Path.Combine(parentDir,
|
|
||||||
Path.GetFileNameWithoutExtension(Path.GetFileNameWithoutExtension(runtimeConfigFile)) + ".dll");
|
|
||||||
if (File.Exists(possNameRuntime)) {
|
|
||||||
return AssemblyDefinition.FromFile(possNameRuntime);
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
var velopackEmbedded = embeddedFiles.SingleOrDefault(f => f.Type == BundleFileType.Assembly && f.RelativePath == "Velopack.dll");
|
|
||||||
if (velopackEmbedded != null && velopackEmbedded.TryGetReader(out var readerVel)) {
|
|
||||||
velopackDll = PEImage.FromReader(readerVel);
|
|
||||||
}
|
|
||||||
|
|
||||||
var runtimeConfig = embeddedFiles.SingleOrDefault(f => f.Type == BundleFileType.RuntimeConfigJson);
|
|
||||||
if (runtimeConfig != null) {
|
|
||||||
var possName1 = Path.GetFileNameWithoutExtension(Path.GetFileNameWithoutExtension(runtimeConfig.RelativePath)) + ".dll";
|
|
||||||
var file = embeddedFiles.SingleOrDefault(f => f.Type == BundleFileType.Assembly && f.RelativePath == possName1);
|
|
||||||
if (file != null && file.TryGetReader(out var reader)) {
|
|
||||||
return AssemblyDefinition.FromReader(reader);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var possName2 = Path.GetFileNameWithoutExtension(exeFile) + ".dll";
|
|
||||||
var file2 = embeddedFiles.SingleOrDefault(f => f.Type == BundleFileType.Assembly && f.RelativePath == possName2);
|
|
||||||
if (file2 != null && file2.TryGetReader(out var reader2)) {
|
|
||||||
return AssemblyDefinition.FromReader(reader2);
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
} catch (BadImageFormatException) {
|
|
||||||
// not an AppHost / SingleFileHost binary
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -7,4 +7,6 @@ public interface IFancyConsole : IConsole
|
|||||||
void WriteTable(string tableName, IEnumerable<IEnumerable<string>> rows, bool hasHeaderRow = true);
|
void WriteTable(string tableName, IEnumerable<IEnumerable<string>> rows, bool hasHeaderRow = true);
|
||||||
|
|
||||||
Task<bool> PromptYesNo(string prompt, bool? defaultValue = null, TimeSpan? timeout = null);
|
Task<bool> PromptYesNo(string prompt, bool? defaultValue = null, TimeSpan? timeout = null);
|
||||||
|
|
||||||
|
string EscapeMarkup(string text);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,6 +13,11 @@ public class BasicConsole : IFancyConsole
|
|||||||
this.defaultFactory = defaultFactory;
|
this.defaultFactory = defaultFactory;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public string EscapeMarkup(string text)
|
||||||
|
{
|
||||||
|
return text;
|
||||||
|
}
|
||||||
|
|
||||||
public async Task ExecuteProgressAsync(Func<IFancyConsoleProgress, Task> action)
|
public async Task ExecuteProgressAsync(Func<IFancyConsoleProgress, Task> action)
|
||||||
{
|
{
|
||||||
var start = DateTime.UtcNow;
|
var start = DateTime.UtcNow;
|
||||||
|
|||||||
@@ -15,6 +15,11 @@ public class SpectreConsole : IFancyConsole
|
|||||||
this.defaultFactory = defaultFactory;
|
this.defaultFactory = defaultFactory;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public string EscapeMarkup(string text)
|
||||||
|
{
|
||||||
|
return Markup.Escape(text);
|
||||||
|
}
|
||||||
|
|
||||||
public async Task ExecuteProgressAsync(Func<IFancyConsoleProgress, Task> action)
|
public async Task ExecuteProgressAsync(Func<IFancyConsoleProgress, Task> action)
|
||||||
{
|
{
|
||||||
var start = DateTime.UtcNow;
|
var start = DateTime.UtcNow;
|
||||||
|
|||||||
@@ -7,6 +7,10 @@ try {
|
|||||||
bool shouldExit = false;
|
bool shouldExit = false;
|
||||||
bool shouldAutoUpdate = args.Any(a => a.Equals("--autoupdate", StringComparison.OrdinalIgnoreCase));
|
bool shouldAutoUpdate = args.Any(a => a.Equals("--autoupdate", StringComparison.OrdinalIgnoreCase));
|
||||||
|
|
||||||
|
#if USE_ASYNC_MAIN
|
||||||
|
await Task.Delay(10).ConfigureAwait(false);
|
||||||
|
#endif
|
||||||
|
|
||||||
#if !NO_VELO_BUILDER
|
#if !NO_VELO_BUILDER
|
||||||
VelopackApp.Build()
|
VelopackApp.Build()
|
||||||
.SetAutoApplyOnStartup(shouldAutoUpdate)
|
.SetAutoApplyOnStartup(shouldAutoUpdate)
|
||||||
|
|||||||
@@ -10,6 +10,10 @@
|
|||||||
<DefineConstants>NO_VELO_BUILDER</DefineConstants>
|
<DefineConstants>NO_VELO_BUILDER</DefineConstants>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<PropertyGroup Condition=" $(UseAsyncMain) != '' ">
|
||||||
|
<DefineConstants>USE_ASYNC_MAIN</DefineConstants>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\..\src\Velopack\Velopack.csproj" />
|
<ProjectReference Include="..\..\src\Velopack\Velopack.csproj" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|||||||
@@ -1,30 +1,39 @@
|
|||||||
using Velopack.Packaging.Exceptions;
|
using Divergic.Logging.Xunit;
|
||||||
|
using Velopack.Packaging.Exceptions;
|
||||||
using Velopack.Packaging.Windows;
|
using Velopack.Packaging.Windows;
|
||||||
|
using Velopack.Vpk.Logging;
|
||||||
|
|
||||||
namespace Velopack.Packaging.Tests;
|
namespace Velopack.Packaging.Tests;
|
||||||
|
|
||||||
public class DotnetUtilTests
|
public class CompatUtilTests
|
||||||
{
|
{
|
||||||
private readonly ITestOutputHelper _output;
|
private readonly ITestOutputHelper _output;
|
||||||
|
|
||||||
public DotnetUtilTests(ITestOutputHelper output)
|
public CompatUtilTests(ITestOutputHelper output)
|
||||||
{
|
{
|
||||||
_output = output;
|
_output = output;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private ICacheLogger<CompatUtilTests> GetCompat(out CompatUtil compat)
|
||||||
|
{
|
||||||
|
var logger = _output.BuildLoggerFor<CompatUtilTests>();
|
||||||
|
compat = new CompatUtil(logger, new BasicConsole(logger, new DefaultPromptValueFactory(true)));
|
||||||
|
return logger;
|
||||||
|
}
|
||||||
|
|
||||||
[SkippableFact]
|
[SkippableFact]
|
||||||
public void NonDotnetBinaryPasses()
|
public void NonDotnetBinaryPasses()
|
||||||
{
|
{
|
||||||
Skip.IfNot(VelopackRuntimeInfo.IsWindows);
|
Skip.IfNot(VelopackRuntimeInfo.IsWindows);
|
||||||
using var logger = _output.BuildLoggerFor<DotnetUtilTests>();
|
using var logger = GetCompat(out var compat);
|
||||||
Assert.Null(DotnetUtil.VerifyVelopackApp(PathHelper.GetRustAsset("testapp.exe"), logger));
|
Assert.Null(compat.Verify(PathHelper.GetRustAsset("testapp.exe")));
|
||||||
}
|
}
|
||||||
|
|
||||||
[SkippableFact]
|
[SkippableFact]
|
||||||
public void PublishSingleFilePasses()
|
public void PublishSingleFilePasses()
|
||||||
{
|
{
|
||||||
Skip.IfNot(VelopackRuntimeInfo.IsWindows);
|
Skip.IfNot(VelopackRuntimeInfo.IsWindows);
|
||||||
using var logger = _output.BuildLoggerFor<DotnetUtilTests>();
|
using var logger = GetCompat(out var compat);
|
||||||
using var _1 = Utility.GetTempDirectory(out var dir);
|
using var _1 = Utility.GetTempDirectory(out var dir);
|
||||||
var sample = PathHelper.GetAvaloniaSample();
|
var sample = PathHelper.GetAvaloniaSample();
|
||||||
Exe.InvokeAndThrowIfNonZero(
|
Exe.InvokeAndThrowIfNonZero(
|
||||||
@@ -34,18 +43,18 @@ public class DotnetUtilTests
|
|||||||
sample);
|
sample);
|
||||||
|
|
||||||
var path = Path.Combine(dir, "AvaloniaCrossPlat.exe");
|
var path = Path.Combine(dir, "AvaloniaCrossPlat.exe");
|
||||||
Assert.Equal(VelopackRuntimeInfo.VelopackProductVersion, DotnetUtil.VerifyVelopackApp(path, logger));
|
Assert.Equal(VelopackRuntimeInfo.VelopackProductVersion, compat.Verify(path));
|
||||||
|
|
||||||
var newPath = Path.Combine(dir, "AvaloniaCrossPlat-asd2.exe");
|
var newPath = Path.Combine(dir, "AvaloniaCrossPlat-asd2.exe");
|
||||||
File.Move(path, newPath);
|
File.Move(path, newPath);
|
||||||
Assert.Equal(VelopackRuntimeInfo.VelopackProductVersion, DotnetUtil.VerifyVelopackApp(newPath, logger));
|
Assert.Equal(VelopackRuntimeInfo.VelopackProductVersion, compat.Verify(newPath));
|
||||||
}
|
}
|
||||||
|
|
||||||
[SkippableFact]
|
[SkippableFact]
|
||||||
public void PublishDotnet6Passes()
|
public void PublishDotnet6Passes()
|
||||||
{
|
{
|
||||||
Skip.IfNot(VelopackRuntimeInfo.IsWindows);
|
Skip.IfNot(VelopackRuntimeInfo.IsWindows);
|
||||||
using var logger = _output.BuildLoggerFor<DotnetUtilTests>();
|
using var logger = GetCompat(out var compat);
|
||||||
using var _1 = Utility.GetTempDirectory(out var dir);
|
using var _1 = Utility.GetTempDirectory(out var dir);
|
||||||
var sample = PathHelper.GetAvaloniaSample();
|
var sample = PathHelper.GetAvaloniaSample();
|
||||||
Exe.InvokeAndThrowIfNonZero(
|
Exe.InvokeAndThrowIfNonZero(
|
||||||
@@ -55,18 +64,18 @@ public class DotnetUtilTests
|
|||||||
sample);
|
sample);
|
||||||
|
|
||||||
var path = Path.Combine(dir, "AvaloniaCrossPlat.exe");
|
var path = Path.Combine(dir, "AvaloniaCrossPlat.exe");
|
||||||
Assert.Equal(VelopackRuntimeInfo.VelopackProductVersion, DotnetUtil.VerifyVelopackApp(path, logger));
|
Assert.Equal(VelopackRuntimeInfo.VelopackProductVersion, compat.Verify(path));
|
||||||
|
|
||||||
var newPath = Path.Combine(dir, "AvaloniaCrossPlat-asd2.exe");
|
var newPath = Path.Combine(dir, "AvaloniaCrossPlat-asd2.exe");
|
||||||
File.Move(path, newPath);
|
File.Move(path, newPath);
|
||||||
Assert.Equal(VelopackRuntimeInfo.VelopackProductVersion, DotnetUtil.VerifyVelopackApp(newPath, logger));
|
Assert.Equal(VelopackRuntimeInfo.VelopackProductVersion, compat.Verify(newPath));
|
||||||
}
|
}
|
||||||
|
|
||||||
[SkippableFact]
|
[SkippableFact]
|
||||||
public void PublishNet48Passes()
|
public void PublishNet48Passes()
|
||||||
{
|
{
|
||||||
Skip.IfNot(VelopackRuntimeInfo.IsWindows);
|
Skip.IfNot(VelopackRuntimeInfo.IsWindows);
|
||||||
using var logger = _output.BuildLoggerFor<DotnetUtilTests>();
|
using var logger = GetCompat(out var compat);
|
||||||
using var _1 = Utility.GetTempDirectory(out var dir);
|
using var _1 = Utility.GetTempDirectory(out var dir);
|
||||||
var sample = PathHelper.GetWpfSample();
|
var sample = PathHelper.GetWpfSample();
|
||||||
Exe.InvokeAndThrowIfNonZero(
|
Exe.InvokeAndThrowIfNonZero(
|
||||||
@@ -75,18 +84,18 @@ public class DotnetUtilTests
|
|||||||
sample);
|
sample);
|
||||||
|
|
||||||
var path = Path.Combine(dir, "VeloWpfSample.exe");
|
var path = Path.Combine(dir, "VeloWpfSample.exe");
|
||||||
Assert.NotNull(DotnetUtil.VerifyVelopackApp(path, logger));
|
Assert.NotNull(compat.Verify(path));
|
||||||
|
|
||||||
var newPath = Path.Combine(dir, "VeloWpfSample-asd2.exe");
|
var newPath = Path.Combine(dir, "VeloWpfSample-asd2.exe");
|
||||||
File.Move(path, newPath);
|
File.Move(path, newPath);
|
||||||
Assert.NotNull(DotnetUtil.VerifyVelopackApp(newPath, logger));
|
Assert.NotNull(compat.Verify(newPath));
|
||||||
}
|
}
|
||||||
|
|
||||||
[SkippableFact]
|
[SkippableFact]
|
||||||
public void UnawareDotnetAppFails()
|
public void UnawareDotnetAppFails()
|
||||||
{
|
{
|
||||||
Skip.IfNot(VelopackRuntimeInfo.IsWindows);
|
Skip.IfNot(VelopackRuntimeInfo.IsWindows);
|
||||||
using var logger = _output.BuildLoggerFor<DotnetUtilTests>();
|
using var logger = GetCompat(out var compat);
|
||||||
using var _1 = Utility.GetTempDirectory(out var dir);
|
using var _1 = Utility.GetTempDirectory(out var dir);
|
||||||
var sample = PathHelper.GetTestRootPath("TestApp");
|
var sample = PathHelper.GetTestRootPath("TestApp");
|
||||||
Exe.InvokeAndThrowIfNonZero(
|
Exe.InvokeAndThrowIfNonZero(
|
||||||
@@ -96,6 +105,27 @@ public class DotnetUtilTests
|
|||||||
sample);
|
sample);
|
||||||
|
|
||||||
var path = Path.Combine(dir, "TestApp.exe");
|
var path = Path.Combine(dir, "TestApp.exe");
|
||||||
Assert.Throws<UserInfoException>(() => DotnetUtil.VerifyVelopackApp(path, logger));
|
Assert.Throws<UserInfoException>(() => compat.Verify(path));
|
||||||
|
}
|
||||||
|
|
||||||
|
[SkippableFact]
|
||||||
|
public void PublishAsyncMainPasses()
|
||||||
|
{
|
||||||
|
Skip.IfNot(VelopackRuntimeInfo.IsWindows);
|
||||||
|
using var logger = GetCompat(out var compat);
|
||||||
|
using var _1 = Utility.GetTempDirectory(out var dir);
|
||||||
|
var sample = PathHelper.GetTestRootPath("TestApp");
|
||||||
|
Exe.InvokeAndThrowIfNonZero(
|
||||||
|
"dotnet",
|
||||||
|
new string[] { "publish", "--no-self-contained", "-r", "win-x64", "-o", dir,
|
||||||
|
"-p:UseAsyncMain=true" },
|
||||||
|
sample);
|
||||||
|
|
||||||
|
var path = Path.Combine(dir, "TestApp.exe");
|
||||||
|
Assert.NotNull(compat.Verify(path));
|
||||||
|
|
||||||
|
var newPath = Path.Combine(dir, "VeloWpfSample-asd2.exe");
|
||||||
|
File.Move(path, newPath);
|
||||||
|
Assert.NotNull(compat.Verify(newPath));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user