Decouple Spectre from Packaging, refactor command/compat

This commit is contained in:
Caelan Sayler
2024-01-25 21:28:18 +00:00
parent 29a5db626b
commit 90d685bb21
43 changed files with 444 additions and 535 deletions

View File

@@ -1,10 +1,11 @@
using Microsoft.Extensions.Logging;
using Velopack.Packaging;
using Velopack.Packaging.Abstractions;
using Velopack.Sources;
namespace Velopack.Deployment;
public class RepositoryOptions
public class RepositoryOptions : IOutputOptions
{
public string Channel { get; set; } = ReleaseEntryHelper.GetDefaultChannel();

View File

@@ -6,6 +6,7 @@ using System.Text;
using System.Threading.Tasks;
using ELFSharp.ELF;
using Microsoft.Extensions.Logging;
using Velopack.Packaging.Abstractions;
namespace Velopack.Packaging.Unix.Commands
{
@@ -14,8 +15,8 @@ namespace Velopack.Packaging.Unix.Commands
{
protected string PortablePackagePath { get; set; }
public LinuxPackCommandRunner(ILogger logger)
: base(RuntimeOs.Linux, logger)
public LinuxPackCommandRunner(ILogger logger, IFancyConsole console)
: base(RuntimeOs.Linux, logger, console)
{
}
@@ -71,8 +72,8 @@ Categories=Development;
protected override Task CreatePortablePackage(Action<int> progress, string packDir, string outputPath)
{
progress(-1);
var machine = Options.TargetRuntime.HasArchitecture
? Options.TargetRuntime.Architecture
var machine = Options.TargetRuntime.HasArchitecture
? Options.TargetRuntime.Architecture
: GetMachineForBinary(MainExePath);
AppImageTool.CreateLinuxAppImage(packDir, outputPath, machine, Log);
PortablePackagePath = outputPath;

View File

@@ -3,6 +3,7 @@ using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Velopack.Packaging.Abstractions;
namespace Velopack.Packaging.Unix.Commands
{

View File

@@ -1,11 +1,12 @@
using System.Text.RegularExpressions;
using Microsoft.Extensions.Logging;
using NuGet.Versioning;
using Velopack.Packaging.Abstractions;
using Velopack.Packaging.Exceptions;
namespace Velopack.Packaging.Unix.Commands;
public class OsxBundleCommandRunner
public class OsxBundleCommandRunner : ICommand<OsxBundleOptions>
{
private readonly ILogger _logger;
@@ -14,6 +15,12 @@ public class OsxBundleCommandRunner
_logger = logger;
}
public Task Run(OsxBundleOptions options)
{
Bundle(options);
return Task.CompletedTask;
}
public string Bundle(OsxBundleOptions options)
{
var icon = options.Icon;

View File

@@ -1,6 +1,8 @@
namespace Velopack.Packaging.Unix.Commands;
using Velopack.Packaging.Abstractions;
public class OsxBundleOptions
namespace Velopack.Packaging.Unix.Commands;
public class OsxBundleOptions : IOutputOptions
{
public DirectoryInfo ReleaseDir { get; set; }

View File

@@ -1,13 +1,14 @@
using System.Runtime.Versioning;
using Microsoft.Extensions.Logging;
using Velopack.Packaging.Abstractions;
namespace Velopack.Packaging.Unix.Commands;
[SupportedOSPlatform("osx")]
public class OsxPackCommandRunner : PackageBuilder<OsxPackOptions>
{
public OsxPackCommandRunner(ILogger logger)
: base(RuntimeOs.OSX, logger)
public OsxPackCommandRunner(ILogger logger, IFancyConsole console)
: base(RuntimeOs.OSX, logger, console)
{
}

View File

@@ -1,4 +1,6 @@
namespace Velopack.Packaging.Unix.Commands;
using Velopack.Packaging.Abstractions;
namespace Velopack.Packaging.Unix.Commands;
public class OsxPackOptions : OsxBundleOptions, IPackOptions
{

View File

@@ -2,9 +2,9 @@
using AsmResolver.PE;
using AsmResolver.PE.File.Headers;
using Microsoft.Extensions.Logging;
using Spectre.Console;
using Velopack.Compression;
using Velopack.NuGet;
using Velopack.Packaging.Abstractions;
using Velopack.Packaging.Exceptions;
using Velopack.Windows;
@@ -13,8 +13,8 @@ namespace Velopack.Packaging.Windows.Commands;
[SupportedOSPlatform("windows")]
public class WindowsPackCommandRunner : PackageBuilder<WindowsPackOptions>
{
public WindowsPackCommandRunner(ILogger logger)
: base(RuntimeOs.Windows, logger)
public WindowsPackCommandRunner(ILogger logger, IFancyConsole console)
: base(RuntimeOs.Windows, logger, console)
{
}

View File

@@ -1,4 +1,6 @@
namespace Velopack.Packaging.Windows.Commands;
using Velopack.Packaging.Abstractions;
namespace Velopack.Packaging.Windows.Commands;
public class WindowsPackOptions : WindowsReleasifyOptions, INugetPackCommand, IPackOptions
{

View File

@@ -0,0 +1,9 @@
using Microsoft.Extensions.Logging;
namespace Velopack.Packaging.Abstractions
{
public interface ICommand<TOpt> where TOpt : class
{
Task Run(TOpt options);
}
}

View File

@@ -0,0 +1,9 @@
using Microsoft.Extensions.Logging;
namespace Velopack.Packaging.Abstractions
{
public interface IFancyConsole
{
Task ExecuteProgressAsync(Func<IFancyConsoleProgress, Task> action);
}
}

View File

@@ -0,0 +1,7 @@
namespace Velopack.Packaging.Abstractions
{
public interface IFancyConsoleProgress
{
Task RunTask(string name, Func<Action<int>, Task> fn);
}
}

View File

@@ -1,4 +1,4 @@
namespace Velopack.Packaging;
namespace Velopack.Packaging.Abstractions;
public interface INugetPackCommand
{

View File

@@ -0,0 +1,13 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Velopack.Packaging.Abstractions
{
public interface IOutputOptions
{
DirectoryInfo ReleaseDir { get; }
}
}

View File

@@ -0,0 +1,9 @@
namespace Velopack.Packaging.Abstractions
{
public interface IPackOptions : INugetPackCommand, IPlatformOptions
{
string Channel { get; }
DeltaMode DeltaMode { get; }
string EntryExecutableName { get; }
}
}

View File

@@ -0,0 +1,13 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Velopack.Packaging.Abstractions
{
public interface IPlatformOptions : IOutputOptions
{
RID TargetRuntime { get; }
}
}

View File

@@ -1,19 +1,22 @@
using Microsoft.Extensions.Logging;
using Velopack.Packaging.Abstractions;
namespace Velopack.Packaging.Commands
{
public class DeltaGenCommandRunner : ICommand<DeltaGenOptions>
{
private readonly ILogger _logger;
private readonly IFancyConsole _console;
public DeltaGenCommandRunner(ILogger logger)
public DeltaGenCommandRunner(ILogger logger, IFancyConsole console)
{
_logger = logger;
_console = console;
}
public async Task Run(DeltaGenOptions options)
{
await Progress.ExecuteAsync(_logger, async (ctx) => {
await _console.ExecuteProgressAsync(async (ctx) => {
var pold = new ReleasePackage(options.BasePackage);
var pnew = new ReleasePackage(options.NewPackage);
await ctx.RunTask($"Building delta {pold.Version} -> {pnew.Version}", (progress) => {

View File

@@ -1,16 +1,19 @@
using Microsoft.Extensions.Logging;
using Velopack.Compression;
using Velopack.Packaging.Exceptions;
using Velopack.Packaging.Abstractions;
namespace Velopack.Packaging.Commands
{
public class DeltaPatchCommandRunner : ICommand<DeltaPatchOptions>
{
private readonly ILogger _logger;
private readonly IFancyConsole _console;
public DeltaPatchCommandRunner(ILogger logger)
public DeltaPatchCommandRunner(ILogger logger, IFancyConsole console)
{
_logger = logger;
_console = console;
}
public async Task Run(DeltaPatchOptions options)
@@ -31,7 +34,7 @@ namespace Velopack.Packaging.Commands
var delta = new DeltaEmbedded(HelperFile.GetZstdPath(), _logger, tmp);
EasyZip.ExtractZipToDirectory(_logger, options.BasePackage, workDir);
await Progress.ExecuteAsync(_logger, async (ctx) => {
await _console.ExecuteProgressAsync(async (ctx) => {
foreach (var f in options.PatchFiles) {
await ctx.RunTask($"Applying {f.Name}", (progress) => {
delta.ApplyDeltaPackageFast(workDir, f.FullName, progress);

View File

@@ -1,9 +0,0 @@
using Microsoft.Extensions.Logging;
namespace Velopack.Packaging
{
internal interface ICommand<TOpt> where TOpt : class
{
Task Run(TOpt options);
}
}

View File

@@ -1,11 +0,0 @@
namespace Velopack.Packaging
{
public interface IPackOptions : INugetPackCommand
{
RID TargetRuntime { get; }
DirectoryInfo ReleaseDir { get; }
string Channel { get; }
DeltaMode DeltaMode { get; }
string EntryExecutableName { get; }
}
}

View File

@@ -4,10 +4,10 @@ using System.Security;
using System.Text.RegularExpressions;
using Microsoft.Extensions.Logging;
using NuGet.Versioning;
using Spectre.Console;
using Velopack.Compression;
using Velopack.NuGet;
using Velopack.Packaging.Exceptions;
using Velopack.Packaging.Abstractions;
namespace Velopack.Packaging
{
@@ -18,6 +18,8 @@ namespace Velopack.Packaging
protected ILogger Log { get; }
protected IFancyConsole Console { get; }
protected DirectoryInfo TempDir { get; private set; }
protected T Options { get; private set; }
@@ -30,10 +32,11 @@ namespace Velopack.Packaging
private readonly Regex REGEX_EXCLUDES = new Regex(@".*[\\\/]createdump.*|.*\.vshost\..*|.*\.nupkg$|.*\.pdb$", RegexOptions.IgnoreCase | RegexOptions.Compiled);
public PackageBuilder(RuntimeOs supportedOs, ILogger logger)
public PackageBuilder(RuntimeOs supportedOs, ILogger logger, IFancyConsole console)
{
SupportedTargetOs = supportedOs;
Log = logger;
Console = console;
}
public async Task Run(T options)
@@ -90,7 +93,7 @@ namespace Velopack.Packaging
return incomplete;
}
await Progress.ExecuteAsync(Log, async (ctx) => {
await Console.ExecuteProgressAsync(async (ctx) => {
ReleasePackage prev = null;
await ctx.RunTask("Pre-process steps", async (progress) => {
prev = entryHelper.GetPreviousFullRelease(NuGetVersion.Parse(packVersion));

View File

@@ -1,77 +0,0 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
using Spectre.Console;
namespace Velopack.Packaging
{
public class Progress
{
private readonly ILogger _logger;
private readonly ProgressContext _context;
public static bool IsEnabled { get; set; }
private Progress(ILogger logger, ProgressContext context)
{
_logger = logger;
_context = context;
}
public static async Task ExecuteAsync(ILogger logger, Func<Progress, Task> action)
{
var start = DateTime.UtcNow;
if (IsEnabled) {
await AnsiConsole.Progress()
.AutoRefresh(true)
.AutoClear(false)
.HideCompleted(false)
.Columns(new ProgressColumn[] {
new SpinnerColumn(),
new TaskDescriptionColumn(),
new ProgressBarColumn(),
new PercentageColumn(),
new ElapsedTimeColumn(),
})
.StartAsync(async ctx => await action(new Progress(logger, ctx)));
} else {
await action(new Progress(logger, null));
}
logger.Info($"[bold]Finished in {DateTime.UtcNow - start}.[/]");
}
public async Task RunTask(string name, Func<Action<int>, Task> fn)
{
var level = IsEnabled ? LogLevel.Debug : LogLevel.Information;
_logger.Log(level, "Starting: " + name);
if (IsEnabled) {
var task = _context.AddTask($"[italic]{name}[/]");
task.StartTask();
void progress(int p)
{
if (p < 0) {
task.IsIndeterminate = true;
} else {
task.IsIndeterminate = false;
task.Value = Math.Min(100, p);
}
}
await Task.Run(() => fn(progress)).ConfigureAwait(false);
task.IsIndeterminate = false;
task.StopTask();
} else {
await Task.Run(() => fn((_) => { })).ConfigureAwait(false);
}
_logger.Log(level, $"[bold]Complete: {name}[/]");
}
}
}

View File

@@ -13,7 +13,6 @@
<ItemGroup>
<PackageReference Include="System.Linq.Async" Version="6.0.1" />
<PackageReference Include="Spectre.Console" Version="0.48.0" />
</ItemGroup>
</Project>

View File

@@ -0,0 +1,166 @@
using AutoMapper;
using AutoMapper.Internal;
using Microsoft.Extensions.DependencyInjection;
using Velopack.Deployment;
using Velopack.Packaging.Abstractions;
using Velopack.Packaging.Commands;
using Velopack.Packaging.Exceptions;
using Velopack.Packaging.Unix.Commands;
using Velopack.Packaging.Windows.Commands;
using Velopack.Vpk.Commands;
using Velopack.Vpk.Updates;
namespace Velopack.Vpk;
public static class CommandMapper
{
private static readonly List<TypePair> RequiredCommandMaps = new();
public static void Validate()
{
var config = GetMapperConfig();
config.AssertConfigurationIsValid();
var rootCommand = new CliRootCommand();
rootCommand.PopulateVelopackCommands(null);
var global = (IGlobalConfiguration) config;
foreach (var pair in RequiredCommandMaps) {
var map = global.FindTypeMapFor(pair);
if (map == null) {
throw new Exception($"Missing map for {pair.SourceType.Name} -> {pair.DestinationType.Name}");
}
}
}
public static void PopulateVelopackCommands(this CliRootCommand rootCommand, IServiceProvider provider)
{
if (VelopackRuntimeInfo.IsWindows) {
rootCommand.AddCommand<WindowsPackCommand, WindowsPackCommandRunner, WindowsPackOptions>(provider);
} else if (VelopackRuntimeInfo.IsOSX) {
rootCommand.AddCommand<OsxBundleCommand, OsxBundleCommandRunner, OsxBundleOptions>(provider);
rootCommand.AddCommand<OsxPackCommand, OsxPackCommandRunner, OsxPackOptions>(provider);
} else if (VelopackRuntimeInfo.IsLinux) {
rootCommand.AddCommand<LinuxPackCommand, LinuxPackCommandRunner, LinuxPackOptions>(provider);
} else {
throw new NotSupportedException("Unsupported OS platform: " + VelopackRuntimeInfo.SystemOs.GetOsLongName());
}
var downloadCommand = new CliCommand("download", "Download's the latest release from a remote update source.");
downloadCommand.AddRepositoryDownload<GitHubDownloadCommand, GitHubRepository, GitHubDownloadOptions>(provider);
downloadCommand.AddRepositoryDownload<S3DownloadCommand, S3Repository, S3DownloadOptions>(provider);
downloadCommand.AddRepositoryDownload<HttpDownloadCommand, HttpRepository, HttpDownloadOptions>(provider);
rootCommand.Add(downloadCommand);
var uploadCommand = new CliCommand("upload", "Upload local package(s) to a remote update source.");
uploadCommand.AddRepositoryUpload<GitHubUploadCommand, GitHubRepository, GitHubUploadOptions>(provider);
uploadCommand.AddRepositoryUpload<S3UploadCommand, S3Repository, S3UploadOptions>(provider);
rootCommand.Add(uploadCommand);
var deltaCommand = new CliCommand("delta", "Utilities for creating or applying delta packages.");
deltaCommand.AddCommand<DeltaGenCommand, DeltaGenCommandRunner, DeltaGenOptions>(provider);
deltaCommand.AddCommand<DeltaPatchCommand, DeltaPatchCommandRunner, DeltaPatchOptions>(provider);
rootCommand.Add(deltaCommand);
}
private static MapperConfiguration GetMapperConfig()
{
return new MapperConfiguration(cfg => {
cfg.CreatePlatformMap<OsxPackCommand, OsxPackOptions>();
cfg.CreatePlatformMap<WindowsPackCommand, WindowsPackOptions>();
cfg.CreatePlatformMap<LinuxPackCommand, LinuxPackOptions>();
cfg.CreateOutputMap<OsxBundleCommand, OsxBundleOptions>();
cfg.CreateOutputMap<GitHubDownloadCommand, GitHubDownloadOptions>();
cfg.CreateOutputMap<GitHubUploadCommand, GitHubUploadOptions>();
cfg.CreateOutputMap<HttpDownloadCommand, HttpDownloadOptions>();
cfg.CreateOutputMap<S3DownloadCommand, S3DownloadOptions>();
cfg.CreateOutputMap<S3UploadCommand, S3UploadOptions>();
cfg.CreateMap<DeltaGenCommand, DeltaGenOptions>();
cfg.CreateMap<DeltaPatchCommand, DeltaPatchOptions>();
});
}
private static CliCommand AddCommand<TCli, TCmd, TOpt>(this CliCommand parent, IServiceProvider provider)
where TCli : BaseCommand, new()
where TCmd : ICommand<TOpt>
where TOpt : class, new()
{
return parent.Add<TCli, TOpt>(provider, (options) => {
var runner = ActivatorUtilities.CreateInstance<TCmd>(provider);
return runner.Run(options);
});
}
private static CliCommand AddRepositoryDownload<TCli, TCmd, TOpt>(this CliCommand parent, IServiceProvider provider)
where TCli : BaseCommand, new()
where TCmd : IRepositoryCanDownload<TOpt>
where TOpt : RepositoryOptions, new()
{
return parent.Add<TCli, TOpt>(provider, (options) => {
var runner = ActivatorUtilities.CreateInstance<TCmd>(provider);
return runner.DownloadLatestFullPackageAsync(options);
});
}
private static CliCommand AddRepositoryUpload<TCli, TCmd, TOpt>(this CliCommand parent, IServiceProvider provider)
where TCli : BaseCommand, new()
where TCmd : IRepositoryCanUpload<TOpt>
where TOpt : RepositoryOptions, new()
{
return parent.Add<TCli, TOpt>(provider, (options) => {
var runner = ActivatorUtilities.CreateInstance<TCmd>(provider);
return runner.UploadMissingAssetsAsync(options);
});
}
private static CliCommand Add<TCli, TOpt>(this CliCommand parent, IServiceProvider provider, Func<TOpt, Task> fn)
where TCli : BaseCommand, new()
where TOpt : class, new()
{
RequiredCommandMaps.Add(new TypePair(typeof(TCli), typeof(TOpt)));
var command = new TCli();
command.SetAction(async (ctx, token) => {
var logger = provider.GetRequiredService<ILogger>();
logger.LogInformation($"[bold]{Program.INTRO}[/]");
var updateCheck = new UpdateChecker(logger);
await updateCheck.CheckForUpdates();
command.SetProperties(ctx);
var mapper = GetMapperConfig().CreateMapper();
var options = mapper.Map<TOpt>(command);
try {
await fn(options);
return 0;
} catch (Exception ex) when (ex is ProcessFailedException or UserInfoException) {
// some exceptions are just user info / user error, so don't need a stack trace.
logger.Fatal($"[bold orange3]{ex.Message}[/]");
return -1;
} catch (Exception ex) {
logger.Fatal(ex, $"Command {typeof(TCli).Name} had an exception.");
return -1;
}
});
parent.Subcommands.Add(command);
return command;
}
private static IMappingExpression<TSource, TDestination> CreatePlatformMap<TSource, TDestination>(
this IMapperConfigurationExpression cfg)
where TSource : PlatformCommand
where TDestination : IPackOptions
{
return cfg.CreateMap<TSource, TDestination>()
.ForMember(x => x.ReleaseDir, x => x.MapFrom(z => z.GetReleaseDirectory()))
.ForMember(x => x.TargetRuntime, x => x.MapFrom(z => z.GetRid()));
}
private static IMappingExpression<TSource, TDestination> CreateOutputMap<TSource, TDestination>(
this IMapperConfigurationExpression cfg)
where TSource : OutputCommand
where TDestination : IOutputOptions
{
return cfg.CreateMap<TSource, TDestination>()
.ForMember(x => x.ReleaseDir, x => x.MapFrom(z => z.GetReleaseDirectory()));
}
}

View File

@@ -4,7 +4,7 @@ namespace Velopack.Vpk.Commands
{
public class DeltaGenCommand : BaseCommand
{
public DeltaMode Delta { get; set; }
public DeltaMode DeltaMode { get; set; }
public string BasePackage { get; set; }
@@ -16,7 +16,7 @@ namespace Velopack.Vpk.Commands
: base("generate", "Generate a delta patch from two full releases.")
{
AddOption<DeltaMode>((v) => Delta = v, "--mode")
AddOption<DeltaMode>((v) => DeltaMode = v, "--mode")
.SetDefault(DeltaMode.BestSpeed)
.SetDescription("Set the delta generation mode.");

View File

@@ -2,12 +2,12 @@
public class GitHubDownloadCommand : GitHubBaseCommand
{
public bool Pre { get; private set; }
public bool Prerelease { get; private set; }
public GitHubDownloadCommand()
: base("github", "Download latest release from GitHub repository.")
{
AddOption<bool>((v) => Pre = v, "--pre")
AddOption<bool>((v) => Prerelease = v, "--pre")
.SetDescription("Get latest pre-release instead of stable.");
}
}

View File

@@ -8,7 +8,7 @@ public class GitHubUploadCommand : GitHubBaseCommand
public string TagName { get; private set; }
public bool Pre { get; private set; }
public bool Prerelease { get; private set; }
public bool Merge { get; private set; }
@@ -18,7 +18,7 @@ public class GitHubUploadCommand : GitHubBaseCommand
AddOption<bool>((v) => Publish = v, "--publish")
.SetDescription("Create and publish instead of leaving as draft.");
AddOption<bool>((v) => Pre = v, "--pre")
AddOption<bool>((v) => Prerelease = v, "--pre")
.SetDescription("Create as pre-release instead of stable.");
AddOption<bool>((v) => Merge = v, "--merge")

View File

@@ -29,7 +29,7 @@ namespace Velopack.Vpk.Commands
public bool PackIsAppDir { get; private set; }
public DeltaMode Delta { get; set; } = DeltaMode.BestSpeed;
public DeltaMode DeltaMode { get; set; } = DeltaMode.BestSpeed;
public LinuxPackCommand()
: this("pack", "Create's a Linux .AppImage bundle from a folder containing application files.")
@@ -78,7 +78,7 @@ namespace Velopack.Vpk.Commands
.SetArgumentHelpName("PATH")
.MustExist();
AddOption<DeltaMode>((v) => Delta = v, "--delta")
AddOption<DeltaMode>((v) => DeltaMode = v, "--delta")
.SetDefault(DeltaMode.BestSpeed)
.SetDescription("Set the delta generation mode.");

View File

@@ -6,7 +6,7 @@ public class OsxPackCommand : OsxBundleCommand
{
public string ReleaseNotes { get; private set; }
public DeltaMode Delta { get; private set; }
public DeltaMode DeltaMode { get; private set; }
public bool NoPackage { get; private set; }
@@ -34,7 +34,7 @@ public class OsxPackCommand : OsxBundleCommand
.SetArgumentHelpName("PATH")
.MustExist();
AddOption<DeltaMode>((v) => Delta = v, "--delta")
AddOption<DeltaMode>((v) => DeltaMode = v, "--delta")
.SetDefault(DeltaMode.BestSpeed)
.SetDescription("Set the delta generation mode.");

View File

@@ -6,7 +6,7 @@ public class WindowsReleasifyCommand : WindowsSigningCommand
{
public string Package { get; set; }
public DeltaMode Delta { get; private set; }
public DeltaMode DeltaMode { get; private set; }
public string Runtimes { get; private set; }
@@ -34,7 +34,7 @@ public class WindowsReleasifyCommand : WindowsSigningCommand
protected WindowsReleasifyCommand(string name, string description)
: base(name, description)
{
AddOption<DeltaMode>((v) => Delta = v, "--delta")
AddOption<DeltaMode>((v) => DeltaMode = v, "--delta")
.SetDefault(DeltaMode.BestSpeed)
.SetDescription("Set the delta generation mode.");

View File

@@ -1,227 +0,0 @@
using System.Runtime.Versioning;
using Velopack.Deployment;
using Velopack.Packaging.Commands;
using Velopack.Packaging.Unix.Commands;
using Velopack.Packaging.Windows.Commands;
using Velopack.Vpk.Commands;
namespace Velopack.Vpk.Compat;
public class EmbeddedRunner : ICommandRunner
{
private readonly ILogger _logger;
public EmbeddedRunner(ILogger logger)
{
_logger = logger;
}
[SupportedOSPlatform("osx")]
public virtual Task ExecuteBundleOsx(OsxBundleCommand command)
{
var options = new OsxBundleOptions {
BundleId = command.BundleId,
PackAuthors = command.PackAuthors,
EntryExecutableName = command.EntryExecutableName,
Icon = command.Icon,
PackDirectory = command.PackDirectory,
PackId = command.PackId,
PackTitle = command.PackTitle,
PackVersion = command.PackVersion,
ReleaseDir = command.GetReleaseDirectory(),
InfoPlistPath = command.InfoPlistPath,
};
new OsxBundleCommandRunner(_logger).Bundle(options);
return Task.CompletedTask;
}
[SupportedOSPlatform("osx")]
public virtual Task ExecutePackOsx(OsxPackCommand command)
{
var options = new OsxPackOptions {
BundleId = command.BundleId,
PackAuthors = command.PackAuthors,
EntryExecutableName = command.EntryExecutableName,
Icon = command.Icon,
PackDirectory = command.PackDirectory,
PackId = command.PackId,
PackTitle = command.PackTitle,
Channel = command.Channel,
PackVersion = command.PackVersion,
TargetRuntime = command.GetRid(),
ReleaseDir = command.GetReleaseDirectory(),
DeltaMode = command.Delta,
NoPackage = command.NoPackage,
NotaryProfile = command.NotaryProfile,
PackageConclusion = command.PackageConclusion,
PackageLicense = command.PackageLicense,
PackageReadme = command.PackageReadme,
PackageWelcome = command.PackageWelcome,
ReleaseNotes = command.ReleaseNotes,
SigningAppIdentity = command.SigningAppIdentity,
SigningEntitlements = command.SigningEntitlements,
SigningInstallIdentity = command.SigningInstallIdentity,
InfoPlistPath = command.InfoPlistPath,
};
return new OsxPackCommandRunner(_logger).Run(options);
}
[SupportedOSPlatform("windows")]
public virtual Task ExecutePackWindows(WindowsPackCommand command)
{
var options = new WindowsPackOptions {
TargetRuntime = command.GetRid(),
ReleaseDir = command.GetReleaseDirectory(),
Package = command.Package,
Icon = command.Icon,
DeltaMode = command.Delta,
SignParameters = command.SignParameters,
EntryExecutableName = command.EntryExecutableName,
PackAuthors = command.PackAuthors,
PackDirectory = command.PackDirectory,
Channel = command.Channel,
PackId = command.PackId,
PackTitle = command.PackTitle,
PackVersion = command.PackVersion,
ReleaseNotes = command.ReleaseNotes,
Runtimes = command.Runtimes,
SignParallel = command.SignParallel,
SignSkipDll = command.SignSkipDll,
SignTemplate = command.SignTemplate,
SplashImage = command.SplashImage,
};
return new WindowsPackCommandRunner(_logger).Run(options);
}
[SupportedOSPlatform("linux")]
public virtual Task ExecutePackLinux(LinuxPackCommand command)
{
var options = new LinuxPackOptions {
TargetRuntime = command.GetRid(),
ReleaseDir = command.GetReleaseDirectory(),
Icon = command.Icon,
DeltaMode = command.Delta,
EntryExecutableName = command.EntryExecutableName,
PackAuthors = command.PackAuthors,
PackDirectory = command.PackDirectory,
Channel = command.Channel,
PackId = command.PackId,
PackTitle = command.PackTitle,
PackVersion = command.PackVersion,
ReleaseNotes = command.ReleaseNotes,
PackIsAppDir = command.PackIsAppDir,
};
return new LinuxPackCommandRunner(_logger).Run(options);
}
//public virtual Task ExecuteReleasifyWindows(WindowsReleasifyCommand command)
//{
// var options = new WindowsReleasifyOptions {
// TargetRuntime = command.GetRid(),
// ReleaseDir = command.GetReleaseDirectory(),
// Package = command.Package,
// Icon = command.Icon,
// DeltaMode = command.Delta,
// SignParameters = command.SignParameters,
// EntryExecutableName = command.EntryExecutableName,
// Runtimes = command.Runtimes,
// Channel = command.Channel,
// SignParallel = command.SignParallel,
// SignSkipDll = command.SignSkipDll,
// SignTemplate = command.SignTemplate,
// SplashImage = command.SplashImage,
// };
// new WindowsPackCommandRunner(_logger).Releasify(options);
// return Task.CompletedTask;
//}
public virtual Task ExecuteGithubDownload(GitHubDownloadCommand command)
{
var options = new GitHubDownloadOptions {
Prerelease = command.Pre,
ReleaseDir = command.GetReleaseDirectory(),
RepoUrl = command.RepoUrl,
Token = command.Token,
Channel = command.Channel,
};
return new GitHubRepository(_logger).DownloadLatestFullPackageAsync(options);
}
public virtual Task ExecuteGithubUpload(GitHubUploadCommand command)
{
var options = new GitHubUploadOptions {
Prerelease = command.Pre,
ReleaseDir = command.GetReleaseDirectory(),
RepoUrl = command.RepoUrl,
Token = command.Token,
Publish = command.Publish,
ReleaseName = command.ReleaseName,
Channel = command.Channel,
Merge = command.Merge,
TagName = command.TagName,
};
return new GitHubRepository(_logger).UploadMissingAssetsAsync(options);
}
public virtual Task ExecuteHttpDownload(HttpDownloadCommand command)
{
var options = new HttpDownloadOptions {
ReleaseDir = command.GetReleaseDirectory(),
Url = command.Url,
Channel = command.Channel,
};
return new HttpRepository(_logger).DownloadLatestFullPackageAsync(options);
}
public virtual Task ExecuteS3Download(S3DownloadCommand command)
{
var options = new S3DownloadOptions {
Bucket = command.Bucket,
Endpoint = command.Endpoint,
Session = command.Session,
KeyId = command.KeyId,
Region = command.Region,
ReleaseDir = command.GetReleaseDirectory(),
Secret = command.Secret,
Channel = command.Channel,
};
return new S3Repository(_logger).DownloadLatestFullPackageAsync(options);
}
public virtual Task ExecuteS3Upload(S3UploadCommand command)
{
var options = new S3UploadOptions {
Bucket = command.Bucket,
Endpoint = command.Endpoint,
KeyId = command.KeyId,
Session = command.Session,
Region = command.Region,
ReleaseDir = command.GetReleaseDirectory(),
Secret = command.Secret,
Channel = command.Channel,
KeepMaxReleases = command.KeepMaxReleases,
};
return new S3Repository(_logger).UploadMissingAssetsAsync(options);
}
public virtual Task ExecuteDeltaGen(DeltaGenCommand command)
{
var options = new DeltaGenOptions {
BasePackage = command.BasePackage,
NewPackage = command.NewPackage,
OutputFile = command.OutputFile,
DeltaMode = command.Delta,
};
return new DeltaGenCommandRunner(_logger).Run(options);
}
public virtual Task ExecuteDeltaPatch(DeltaPatchCommand command)
{
var options = new DeltaPatchOptions {
BasePackage = command.BasePackage,
PatchFiles = command.PatchFiles,
OutputFile = command.OutputFile,
};
return new DeltaPatchCommandRunner(_logger).Run(options);
}
}

View File

@@ -1,19 +0,0 @@
using Velopack.Vpk.Commands;
namespace Velopack.Vpk.Compat;
public interface ICommandRunner
{
public Task ExecuteGithubDownload(GitHubDownloadCommand command);
public Task ExecuteGithubUpload(GitHubUploadCommand command);
public Task ExecuteHttpDownload(HttpDownloadCommand command);
public Task ExecuteS3Download(S3DownloadCommand command);
public Task ExecuteS3Upload(S3UploadCommand command);
public Task ExecuteBundleOsx(OsxBundleCommand command);
public Task ExecutePackOsx(OsxPackCommand command);
//public Task ExecuteReleasifyWindows(WindowsReleasifyCommand command);
public Task ExecutePackWindows(WindowsPackCommand command);
public Task ExecutePackLinux(LinuxPackCommand command);
public Task ExecuteDeltaGen(DeltaGenCommand command);
public Task ExecuteDeltaPatch(DeltaPatchCommand command);
}

View File

@@ -1,89 +0,0 @@
using Microsoft.Extensions.Configuration;
using Velopack.Packaging.Exceptions;
using Velopack.Vpk.Commands;
using Velopack.Vpk.Updates;
namespace Velopack.Vpk.Compat;
public class RunnerFactory
{
private const string NUGET_PACKAGE_NAME = "Velopack";
private readonly ILogger _logger;
private readonly IConfiguration _config;
public RunnerFactory(ILogger logger, IConfiguration config)
{
_logger = logger;
_config = config;
}
public async Task<int> CreateAndExecuteAsync<T>(string commandName, T options) where T : BaseCommand
{
_logger.LogInformation($"[bold]{Program.INTRO}[/]");
var runner = await CreateAsync(options);
var method = typeof(ICommandRunner).GetMethod(commandName);
try {
await (Task) method.Invoke(runner, new object[] { options });
return 0;
} catch (Exception ex) when (ex is ProcessFailedException or UserInfoException) {
// some exceptions are just user info / user error, so don't need a stack trace.
_logger.Fatal($"[bold orange3]{ex.Message}[/]");
return -1;
} catch (Exception ex) {
_logger.Fatal(ex, $"Command {commandName} had an exception.");
return -1;
}
}
private async Task<ICommandRunner> CreateAsync<T>(T options)
{
if (_config.GetValue<bool?>("SKIP_UPDATE_CHECK") != true) {
var updateCheck = new UpdateChecker(_logger);
await updateCheck.CheckForUpdates();
}
return new EmbeddedRunner(_logger);
//if (options is not PlatformCommand) {
//}
//var cmd = (PlatformCommand) (object) options;
//var solutionDir = FindSolutionDirectory(cmd.SolutionDir?.FullName);
//if (solutionDir is null) {
// throw new Exception($"Could not find '.sln'. Specify solution or solution directory with '--solution='.");
//}
//var version = new SdkVersionLocator(_logger).Search(solutionDir, NUGET_PACKAGE_NAME);
//var myVer = VelopackRuntimeInfo.VelopackNugetVersion;
//if (version != myVer) {
// _logger.Warn($"Installed SDK is {version}, while vpk is {myVer}, this is not recommended when building packages.");
//}
//return new EmbeddedRunner(_logger);
}
private string FindSolutionDirectory(string slnArgument)
{
if (!String.IsNullOrWhiteSpace(slnArgument)) {
if (File.Exists(slnArgument) && slnArgument.EndsWith(".sln", StringComparison.InvariantCultureIgnoreCase)) {
// we were given a sln file as argument
return Path.GetDirectoryName(Path.GetFullPath(slnArgument));
}
if (Directory.Exists(slnArgument) && Directory.EnumerateFiles(slnArgument, "*.sln").Any()) {
return Path.GetFullPath(slnArgument);
}
}
// try to find the solution directory from cwd
var cwd = Environment.CurrentDirectory;
var slnSearchDirs = new string[] {
cwd,
Path.Combine(cwd, ".."),
Path.Combine(cwd, "..", ".."),
};
return slnSearchDirs.FirstOrDefault(d => Directory.EnumerateFiles(d, "*.sln").Any());
}
}

View File

@@ -0,0 +1,42 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Spectre.Console;
using Velopack.Packaging.Abstractions;
namespace Velopack.Vpk.Logging
{
public class BasicConsole : IFancyConsole
{
private readonly ILogger logger;
public BasicConsole(ILogger logger)
{
this.logger = logger;
}
public async Task ExecuteProgressAsync(Func<IFancyConsoleProgress, Task> action)
{
await action(new Progress(logger));
}
private class Progress : IFancyConsoleProgress
{
private readonly ILogger _logger;
public Progress(ILogger logger)
{
_logger = logger;
}
public async Task RunTask(string name, Func<Action<int>, Task> fn)
{
_logger.Info("Starting: " + name);
await Task.Run(() => fn(_ => { }));
_logger.Info("Complete: " + name);
}
}
}
}

View File

@@ -0,0 +1,74 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Spectre.Console;
using Velopack.Packaging.Abstractions;
namespace Velopack.Vpk.Logging
{
public class SpectreConsole : IFancyConsole
{
private readonly ILogger logger;
public SpectreConsole(ILogger logger)
{
this.logger = logger;
}
public async Task ExecuteProgressAsync(Func<IFancyConsoleProgress, Task> action)
{
var start = DateTime.UtcNow;
await AnsiConsole.Progress()
.AutoRefresh(true)
.AutoClear(false)
.HideCompleted(false)
.Columns(new ProgressColumn[] {
new SpinnerColumn(),
new TaskDescriptionColumn(),
new ProgressBarColumn(),
new PercentageColumn(),
new ElapsedTimeColumn(),
})
.StartAsync(async ctx => await action(new Progress(logger, ctx)));
logger.Info($"[bold]Finished in {DateTime.UtcNow - start}.[/]");
}
private class Progress : IFancyConsoleProgress
{
private readonly ILogger _logger;
private readonly ProgressContext _context;
public Progress(ILogger logger, ProgressContext context)
{
_logger = logger;
_context = context;
}
public async Task RunTask(string name, Func<Action<int>, Task> fn)
{
_logger.Log(LogLevel.Debug, "Starting: " + name);
var task = _context.AddTask($"[italic]{name}[/]");
task.StartTask();
void progress(int p)
{
if (p < 0) {
task.IsIndeterminate = true;
} else {
task.IsIndeterminate = false;
task.Value = Math.Min(100, p);
}
}
await Task.Run(() => fn(progress)).ConfigureAwait(false);
task.IsIndeterminate = false;
task.StopTask();
_logger.Log(LogLevel.Debug, $"[bold]Complete: {name}[/]");
}
}
}
}

View File

@@ -3,10 +3,8 @@ using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Serilog;
using Serilog.Events;
using Spectre.Console;
using Velopack.Packaging;
using Velopack.Packaging.Abstractions;
using Velopack.Vpk.Commands;
using Velopack.Vpk.Compat;
using Velopack.Vpk.Logging;
namespace Velopack.Vpk;
@@ -23,8 +21,6 @@ public class Program
.SetRecursive(true)
.SetDescription("Disable console colors and interactive components.");
private static RunnerFactory Runner { get; set; }
public static readonly string INTRO
= $"Velopack CLI {VelopackRuntimeInfo.VelopackDisplayVersion} for creating and distributing releases.";
@@ -49,6 +45,7 @@ public class Program
});
builder.Configuration.AddEnvironmentVariables("VPK_");
builder.Services.AddTransient(s => s.GetService<ILoggerFactory>().CreateLogger("vpk"));
var conf = new LoggerConfiguration()
.MinimumLevel.Is(verbose ? LogEventLevel.Debug : LogEventLevel.Information)
@@ -57,10 +54,10 @@ public class Program
if (legacyConsole) {
// spectre can have issues with redirected output, so we disable it.
Packaging.Progress.IsEnabled = false;
builder.Services.AddSingleton<IFancyConsole, BasicConsole>();
conf.WriteTo.Console();
} else {
Packaging.Progress.IsEnabled = true;
builder.Services.AddSingleton<IFancyConsole, SpectreConsole>();
conf.WriteTo.Spectre();
}
@@ -68,59 +65,16 @@ public class Program
builder.Logging.AddSerilog();
var host = builder.Build();
var logFactory = host.Services.GetRequiredService<ILoggerFactory>();
var logger = logFactory.CreateLogger("vpk");
var logger = host.Services.GetRequiredService<Microsoft.Extensions.Logging.ILogger>();
Runner = new RunnerFactory(logger, host.Services.GetRequiredService<IConfiguration>());
CliRootCommand rootCommand = new CliRootCommand(INTRO) {
var rootCommand = new CliRootCommand(INTRO) {
VerboseOption,
LegacyConsole,
};
switch (VelopackRuntimeInfo.SystemOs) {
case RuntimeOs.Windows:
Add(rootCommand, new WindowsPackCommand(), nameof(ICommandRunner.ExecutePackWindows));
break;
case RuntimeOs.OSX:
Add(rootCommand, new OsxBundleCommand(), nameof(ICommandRunner.ExecuteBundleOsx));
Add(rootCommand, new OsxPackCommand(), nameof(ICommandRunner.ExecutePackOsx));
break;
case RuntimeOs.Linux:
Add(rootCommand, new LinuxPackCommand(), nameof(ICommandRunner.ExecutePackLinux));
break;
default:
throw new NotSupportedException("Unsupported OS platform: " + VelopackRuntimeInfo.SystemOs.GetOsLongName());
}
CliCommand downloadCommand = new CliCommand("download", "Download's the latest release from a remote update source.");
Add(downloadCommand, new HttpDownloadCommand(), nameof(ICommandRunner.ExecuteHttpDownload));
Add(downloadCommand, new S3DownloadCommand(), nameof(ICommandRunner.ExecuteS3Download));
Add(downloadCommand, new GitHubDownloadCommand(), nameof(ICommandRunner.ExecuteGithubDownload));
rootCommand.Add(downloadCommand);
var uploadCommand = new CliCommand("upload", "Upload local package(s) to a remote update source.");
Add(uploadCommand, new S3UploadCommand(), nameof(ICommandRunner.ExecuteS3Upload));
Add(uploadCommand, new GitHubUploadCommand(), nameof(ICommandRunner.ExecuteGithubUpload));
rootCommand.Add(uploadCommand);
var deltaCommand = new CliCommand("delta", "Utilities for creating or applying delta packages.");
Add(deltaCommand, new DeltaGenCommand(), nameof(ICommandRunner.ExecuteDeltaGen));
Add(deltaCommand, new DeltaPatchCommand(), nameof(ICommandRunner.ExecuteDeltaPatch));
rootCommand.Add(deltaCommand);
rootCommand.PopulateVelopackCommands(host.Services);
var cli = new CliConfiguration(rootCommand);
return await cli.InvokeAsync(args);
}
private static CliCommand Add<T>(CliCommand parent, T command, string commandName)
where T : BaseCommand
{
command.SetAction((ctx, token) => {
command.SetProperties(ctx);
return Runner.CreateAndExecuteAsync(commandName, command);
});
parent.Subcommands.Add(command);
return command;
}
}

View File

@@ -42,6 +42,8 @@
<PackageReference Include="Serilog.Extensions.Hosting" Version="8.0.0" />
<PackageReference Include="System.CommandLine" Version="2.0.0-beta4.23407.1" />
<PackageReference Include="NuGet.Protocol" Version="6.8.0" />
<PackageReference Include="Spectre.Console" Version="0.48.0" />
<PackageReference Include="AutoMapper" Version="12.0.1" />
</ItemGroup>
<ItemGroup>

View File

@@ -0,0 +1,18 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Velopack.Vpk;
namespace Velopack.CommandLine.Tests
{
public class AutoMapperTests
{
[Fact]
public void AutoMapperConfigIsValid()
{
CommandMapper.Validate();
}
}
}

View File

@@ -68,7 +68,7 @@ public class GitHubDownloadCommandTests : GitHubCommandTests<GitHubDownloadComma
string cli = GetRequiredDefaultOptions() + "--pre";
ParseResult parseResult = command.ParseAndApply(cli);
Assert.True(command.Pre);
Assert.True(command.Prerelease);
}
}

View File

@@ -40,7 +40,7 @@ public abstract class ReleaseCommandTests<T> : BaseCommandTests<T>
string cli = GetRequiredDefaultOptions() + "--delta none";
ParseResult parseResult = command.ParseAndApply(cli);
Assert.True(command.Delta == Packaging.DeltaMode.None);
Assert.True(command.DeltaMode == Packaging.DeltaMode.None);
}
[Fact]

View File

@@ -6,6 +6,7 @@ using System.Text;
using System.Threading.Tasks;
using Velopack.Packaging.Unix.Commands;
using Velopack.Packaging.Windows.Commands;
using Velopack.Vpk.Logging;
namespace Velopack.Packaging.Tests
{
@@ -46,7 +47,7 @@ namespace Velopack.Packaging.Tests
ReleaseNotes = releaseNotes,
Channel = channel,
};
var runner = new WindowsPackCommandRunner(logger);
var runner = new WindowsPackCommandRunner(logger, new BasicConsole(logger));
runner.Run(options).GetAwaiterResult();
} else if (VelopackRuntimeInfo.IsOSX) {
var options = new OsxPackOptions {
@@ -60,7 +61,7 @@ namespace Velopack.Packaging.Tests
ReleaseNotes = releaseNotes,
Channel = channel,
};
var runner = new OsxPackCommandRunner(logger);
var runner = new OsxPackCommandRunner(logger, new BasicConsole(logger));
runner.Run(options).GetAwaiterResult();
} else if (VelopackRuntimeInfo.IsLinux) {
var options = new LinuxPackOptions {
@@ -74,7 +75,7 @@ namespace Velopack.Packaging.Tests
ReleaseNotes = releaseNotes,
Channel = channel,
};
var runner = new LinuxPackCommandRunner(logger);
var runner = new LinuxPackCommandRunner(logger, new BasicConsole(logger));
runner.Run(options).GetAwaiterResult();
} else {
throw new PlatformNotSupportedException();

View File

@@ -5,9 +5,7 @@
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\src\Velopack.Deployment\Velopack.Deployment.csproj" />
<ProjectReference Include="..\..\src\Velopack.Packaging.Unix\Velopack.Packaging.Unix.csproj" />
<ProjectReference Include="..\..\src\Velopack.Packaging.Windows\Velopack.Packaging.Windows.csproj" />
<ProjectReference Include="..\..\src\Velopack.Vpk\Velopack.Vpk.csproj" />
</ItemGroup>
<ItemGroup>

View File

@@ -9,6 +9,7 @@ using Velopack.Compression;
using Velopack.Packaging.Commands;
using Velopack.Packaging.Exceptions;
using Velopack.Packaging.Windows.Commands;
using Velopack.Vpk.Logging;
using Velopack.Windows;
namespace Velopack.Packaging.Tests;
@@ -55,7 +56,7 @@ public class WindowsPackTests
Channel = "asd123"
};
var runner = new WindowsPackCommandRunner(logger);
var runner = new WindowsPackCommandRunner(logger, new BasicConsole(logger));
runner.Run(options).GetAwaiterResult();
var nupkgPath = Path.Combine(tmpReleaseDir, $"{id}-{version}-asd123-full.nupkg");
@@ -118,7 +119,7 @@ public class WindowsPackTests
TargetRuntime = RID.Parse("win"),
};
var runner = new WindowsPackCommandRunner(logger);
var runner = new WindowsPackCommandRunner(logger, new BasicConsole(logger));
runner.Run(options).GetAwaiterResult();
Assert.Throws<UserInfoException>(() => runner.Run(options).GetAwaiterResult());
@@ -155,7 +156,7 @@ public class WindowsPackTests
Channel = "hello",
};
var runner = new WindowsPackCommandRunner(logger);
var runner = new WindowsPackCommandRunner(logger, new BasicConsole(logger));
runner.Run(options).GetAwaiterResult();
options.TargetRuntime = RID.Parse("win10.0.19043-x86");
@@ -190,7 +191,7 @@ public class WindowsPackTests
PackDirectory = tmpOutput,
};
var runner = new WindowsPackCommandRunner(logger);
var runner = new WindowsPackCommandRunner(logger, new BasicConsole(logger));
runner.Run(options).GetAwaiterResult();
var setupPath1 = Path.Combine(tmpReleaseDir, $"{id}-win-Setup.exe");
@@ -305,7 +306,7 @@ public class WindowsPackTests
// apply delta and check package
var output = Path.Combine(releaseDir, "delta.patched");
new DeltaPatchCommandRunner(logger).Run(new DeltaPatchOptions {
new DeltaPatchCommandRunner(logger, new BasicConsole(logger)).Run(new DeltaPatchOptions {
BasePackage = Path.Combine(releaseDir, $"{id}-1.0.0-full.nupkg"),
OutputFile = output,
PatchFiles = new[] { new FileInfo(deltaPath) },
@@ -679,7 +680,7 @@ public class WindowsPackTests
PackDirectory = Path.Combine(projDir, "publish"),
};
var runner = new WindowsPackCommandRunner(logger);
var runner = new WindowsPackCommandRunner(logger, new BasicConsole(logger));
runner.Run(options).GetAwaiterResult();
} finally {
File.WriteAllText(testStringFile, oldText);