From 90d685bb2185270398b7f7e4f8ca4577ae274265 Mon Sep 17 00:00:00 2001 From: Caelan Sayler Date: Thu, 25 Jan 2024 21:28:18 +0000 Subject: [PATCH] Decouple Spectre from Packaging, refactor command/compat --- src/Velopack.Deployment/_Repository.cs | 3 +- .../Commands/LinuxPackCommandRunner.cs | 9 +- .../Commands/LinuxPackOptions.cs | 1 + .../Commands/OsxBundleCommandRunner.cs | 9 +- .../Commands/OsxBundleOptions.cs | 6 +- .../Commands/OsxPackCommandRunner.cs | 5 +- .../Commands/OsxPackOptions.cs | 4 +- .../Commands/WindowsPackCommandRunner.cs | 6 +- .../Commands/WindowsPackOptions.cs | 4 +- .../Abstractions/ICommand.cs | 9 + .../Abstractions/IFancyConsole.cs | 9 + .../Abstractions/IFancyConsoleProgress.cs | 7 + .../{ => Abstractions}/INugetPackCommand.cs | 2 +- .../Abstractions/IOutputOptions.cs | 13 + .../Abstractions/IPackOptions.cs | 9 + .../Abstractions/IPlatformOptions.cs | 13 + .../Commands/DeltaGenCommandRunner.cs | 7 +- .../Commands/DeltaPatchCommandRunner.cs | 7 +- src/Velopack.Packaging/ICommand.cs | 9 - src/Velopack.Packaging/IPackOptions.cs | 11 - src/Velopack.Packaging/PackageBuilder.cs | 9 +- src/Velopack.Packaging/Progress.cs | 77 ------ .../Velopack.Packaging.csproj | 1 - src/Velopack.Vpk/CommandMapper.cs | 166 +++++++++++++ src/Velopack.Vpk/Commands/DeltaGenCommand.cs | 4 +- .../Commands/GitHubDownloadCommand.cs | 4 +- .../Commands/GitHubUploadCommand.cs | 4 +- src/Velopack.Vpk/Commands/LinuxPackCommand.cs | 4 +- src/Velopack.Vpk/Commands/OsxPackCommand.cs | 4 +- .../Commands/WindowsReleasifyCommand.cs | 4 +- src/Velopack.Vpk/Compat/EmbeddedRunner.cs | 227 ------------------ src/Velopack.Vpk/Compat/ICommandRunner.cs | 19 -- src/Velopack.Vpk/Compat/RunnerFactory.cs | 89 ------- src/Velopack.Vpk/Logging/BasicConsole.cs | 42 ++++ src/Velopack.Vpk/Logging/SpectreConsole.cs | 74 ++++++ src/Velopack.Vpk/Program.cs | 60 +---- src/Velopack.Vpk/Velopack.Vpk.csproj | 2 + .../AutoMapperTests.cs | 18 ++ .../Commands/GitHubCommandTests.cs | 2 +- .../Commands/WindowsCommandTests.cs | 2 +- test/Velopack.Packaging.Tests/TestApp.cs | 7 +- .../Velopack.Packaging.Tests.csproj | 4 +- .../WindowsPackTests.cs | 13 +- 43 files changed, 444 insertions(+), 535 deletions(-) create mode 100644 src/Velopack.Packaging/Abstractions/ICommand.cs create mode 100644 src/Velopack.Packaging/Abstractions/IFancyConsole.cs create mode 100644 src/Velopack.Packaging/Abstractions/IFancyConsoleProgress.cs rename src/Velopack.Packaging/{ => Abstractions}/INugetPackCommand.cs (83%) create mode 100644 src/Velopack.Packaging/Abstractions/IOutputOptions.cs create mode 100644 src/Velopack.Packaging/Abstractions/IPackOptions.cs create mode 100644 src/Velopack.Packaging/Abstractions/IPlatformOptions.cs delete mode 100644 src/Velopack.Packaging/ICommand.cs delete mode 100644 src/Velopack.Packaging/IPackOptions.cs delete mode 100644 src/Velopack.Packaging/Progress.cs create mode 100644 src/Velopack.Vpk/CommandMapper.cs delete mode 100644 src/Velopack.Vpk/Compat/EmbeddedRunner.cs delete mode 100644 src/Velopack.Vpk/Compat/ICommandRunner.cs delete mode 100644 src/Velopack.Vpk/Compat/RunnerFactory.cs create mode 100644 src/Velopack.Vpk/Logging/BasicConsole.cs create mode 100644 src/Velopack.Vpk/Logging/SpectreConsole.cs create mode 100644 test/Velopack.CommandLine.Tests/AutoMapperTests.cs diff --git a/src/Velopack.Deployment/_Repository.cs b/src/Velopack.Deployment/_Repository.cs index 01c9ea2b..2864c89e 100644 --- a/src/Velopack.Deployment/_Repository.cs +++ b/src/Velopack.Deployment/_Repository.cs @@ -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(); diff --git a/src/Velopack.Packaging.Unix/Commands/LinuxPackCommandRunner.cs b/src/Velopack.Packaging.Unix/Commands/LinuxPackCommandRunner.cs index c2377030..56214fd8 100644 --- a/src/Velopack.Packaging.Unix/Commands/LinuxPackCommandRunner.cs +++ b/src/Velopack.Packaging.Unix/Commands/LinuxPackCommandRunner.cs @@ -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 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; diff --git a/src/Velopack.Packaging.Unix/Commands/LinuxPackOptions.cs b/src/Velopack.Packaging.Unix/Commands/LinuxPackOptions.cs index 888b5651..14357d77 100644 --- a/src/Velopack.Packaging.Unix/Commands/LinuxPackOptions.cs +++ b/src/Velopack.Packaging.Unix/Commands/LinuxPackOptions.cs @@ -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 { diff --git a/src/Velopack.Packaging.Unix/Commands/OsxBundleCommandRunner.cs b/src/Velopack.Packaging.Unix/Commands/OsxBundleCommandRunner.cs index bccdb70a..26f525de 100644 --- a/src/Velopack.Packaging.Unix/Commands/OsxBundleCommandRunner.cs +++ b/src/Velopack.Packaging.Unix/Commands/OsxBundleCommandRunner.cs @@ -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 { 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; diff --git a/src/Velopack.Packaging.Unix/Commands/OsxBundleOptions.cs b/src/Velopack.Packaging.Unix/Commands/OsxBundleOptions.cs index b11af13f..5bde4d99 100644 --- a/src/Velopack.Packaging.Unix/Commands/OsxBundleOptions.cs +++ b/src/Velopack.Packaging.Unix/Commands/OsxBundleOptions.cs @@ -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; } diff --git a/src/Velopack.Packaging.Unix/Commands/OsxPackCommandRunner.cs b/src/Velopack.Packaging.Unix/Commands/OsxPackCommandRunner.cs index 3fe0f6e2..e415a20f 100644 --- a/src/Velopack.Packaging.Unix/Commands/OsxPackCommandRunner.cs +++ b/src/Velopack.Packaging.Unix/Commands/OsxPackCommandRunner.cs @@ -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 { - public OsxPackCommandRunner(ILogger logger) - : base(RuntimeOs.OSX, logger) + public OsxPackCommandRunner(ILogger logger, IFancyConsole console) + : base(RuntimeOs.OSX, logger, console) { } diff --git a/src/Velopack.Packaging.Unix/Commands/OsxPackOptions.cs b/src/Velopack.Packaging.Unix/Commands/OsxPackOptions.cs index 14d2c80a..95778193 100644 --- a/src/Velopack.Packaging.Unix/Commands/OsxPackOptions.cs +++ b/src/Velopack.Packaging.Unix/Commands/OsxPackOptions.cs @@ -1,4 +1,6 @@ -namespace Velopack.Packaging.Unix.Commands; +using Velopack.Packaging.Abstractions; + +namespace Velopack.Packaging.Unix.Commands; public class OsxPackOptions : OsxBundleOptions, IPackOptions { diff --git a/src/Velopack.Packaging.Windows/Commands/WindowsPackCommandRunner.cs b/src/Velopack.Packaging.Windows/Commands/WindowsPackCommandRunner.cs index 53de9578..aac828bb 100644 --- a/src/Velopack.Packaging.Windows/Commands/WindowsPackCommandRunner.cs +++ b/src/Velopack.Packaging.Windows/Commands/WindowsPackCommandRunner.cs @@ -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 { - public WindowsPackCommandRunner(ILogger logger) - : base(RuntimeOs.Windows, logger) + public WindowsPackCommandRunner(ILogger logger, IFancyConsole console) + : base(RuntimeOs.Windows, logger, console) { } diff --git a/src/Velopack.Packaging.Windows/Commands/WindowsPackOptions.cs b/src/Velopack.Packaging.Windows/Commands/WindowsPackOptions.cs index 478f26b8..a0cb32d4 100644 --- a/src/Velopack.Packaging.Windows/Commands/WindowsPackOptions.cs +++ b/src/Velopack.Packaging.Windows/Commands/WindowsPackOptions.cs @@ -1,4 +1,6 @@ -namespace Velopack.Packaging.Windows.Commands; +using Velopack.Packaging.Abstractions; + +namespace Velopack.Packaging.Windows.Commands; public class WindowsPackOptions : WindowsReleasifyOptions, INugetPackCommand, IPackOptions { diff --git a/src/Velopack.Packaging/Abstractions/ICommand.cs b/src/Velopack.Packaging/Abstractions/ICommand.cs new file mode 100644 index 00000000..9d98799b --- /dev/null +++ b/src/Velopack.Packaging/Abstractions/ICommand.cs @@ -0,0 +1,9 @@ +using Microsoft.Extensions.Logging; + +namespace Velopack.Packaging.Abstractions +{ + public interface ICommand where TOpt : class + { + Task Run(TOpt options); + } +} diff --git a/src/Velopack.Packaging/Abstractions/IFancyConsole.cs b/src/Velopack.Packaging/Abstractions/IFancyConsole.cs new file mode 100644 index 00000000..4ace3d72 --- /dev/null +++ b/src/Velopack.Packaging/Abstractions/IFancyConsole.cs @@ -0,0 +1,9 @@ +using Microsoft.Extensions.Logging; + +namespace Velopack.Packaging.Abstractions +{ + public interface IFancyConsole + { + Task ExecuteProgressAsync(Func action); + } +} diff --git a/src/Velopack.Packaging/Abstractions/IFancyConsoleProgress.cs b/src/Velopack.Packaging/Abstractions/IFancyConsoleProgress.cs new file mode 100644 index 00000000..11d0c777 --- /dev/null +++ b/src/Velopack.Packaging/Abstractions/IFancyConsoleProgress.cs @@ -0,0 +1,7 @@ +namespace Velopack.Packaging.Abstractions +{ + public interface IFancyConsoleProgress + { + Task RunTask(string name, Func, Task> fn); + } +} diff --git a/src/Velopack.Packaging/INugetPackCommand.cs b/src/Velopack.Packaging/Abstractions/INugetPackCommand.cs similarity index 83% rename from src/Velopack.Packaging/INugetPackCommand.cs rename to src/Velopack.Packaging/Abstractions/INugetPackCommand.cs index 9b5aafb7..449e2bed 100644 --- a/src/Velopack.Packaging/INugetPackCommand.cs +++ b/src/Velopack.Packaging/Abstractions/INugetPackCommand.cs @@ -1,4 +1,4 @@ -namespace Velopack.Packaging; +namespace Velopack.Packaging.Abstractions; public interface INugetPackCommand { diff --git a/src/Velopack.Packaging/Abstractions/IOutputOptions.cs b/src/Velopack.Packaging/Abstractions/IOutputOptions.cs new file mode 100644 index 00000000..a9698e7e --- /dev/null +++ b/src/Velopack.Packaging/Abstractions/IOutputOptions.cs @@ -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; } + } +} diff --git a/src/Velopack.Packaging/Abstractions/IPackOptions.cs b/src/Velopack.Packaging/Abstractions/IPackOptions.cs new file mode 100644 index 00000000..67771cfe --- /dev/null +++ b/src/Velopack.Packaging/Abstractions/IPackOptions.cs @@ -0,0 +1,9 @@ +namespace Velopack.Packaging.Abstractions +{ + public interface IPackOptions : INugetPackCommand, IPlatformOptions + { + string Channel { get; } + DeltaMode DeltaMode { get; } + string EntryExecutableName { get; } + } +} diff --git a/src/Velopack.Packaging/Abstractions/IPlatformOptions.cs b/src/Velopack.Packaging/Abstractions/IPlatformOptions.cs new file mode 100644 index 00000000..02650da2 --- /dev/null +++ b/src/Velopack.Packaging/Abstractions/IPlatformOptions.cs @@ -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; } + } +} diff --git a/src/Velopack.Packaging/Commands/DeltaGenCommandRunner.cs b/src/Velopack.Packaging/Commands/DeltaGenCommandRunner.cs index e9985d02..023b2606 100644 --- a/src/Velopack.Packaging/Commands/DeltaGenCommandRunner.cs +++ b/src/Velopack.Packaging/Commands/DeltaGenCommandRunner.cs @@ -1,19 +1,22 @@ using Microsoft.Extensions.Logging; +using Velopack.Packaging.Abstractions; namespace Velopack.Packaging.Commands { public class DeltaGenCommandRunner : ICommand { 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) => { diff --git a/src/Velopack.Packaging/Commands/DeltaPatchCommandRunner.cs b/src/Velopack.Packaging/Commands/DeltaPatchCommandRunner.cs index ae823c25..af135f59 100644 --- a/src/Velopack.Packaging/Commands/DeltaPatchCommandRunner.cs +++ b/src/Velopack.Packaging/Commands/DeltaPatchCommandRunner.cs @@ -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 { 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); diff --git a/src/Velopack.Packaging/ICommand.cs b/src/Velopack.Packaging/ICommand.cs deleted file mode 100644 index 3c5b91f1..00000000 --- a/src/Velopack.Packaging/ICommand.cs +++ /dev/null @@ -1,9 +0,0 @@ -using Microsoft.Extensions.Logging; - -namespace Velopack.Packaging -{ - internal interface ICommand where TOpt : class - { - Task Run(TOpt options); - } -} diff --git a/src/Velopack.Packaging/IPackOptions.cs b/src/Velopack.Packaging/IPackOptions.cs deleted file mode 100644 index 698c79ad..00000000 --- a/src/Velopack.Packaging/IPackOptions.cs +++ /dev/null @@ -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; } - } -} diff --git a/src/Velopack.Packaging/PackageBuilder.cs b/src/Velopack.Packaging/PackageBuilder.cs index 55cbd231..37171cdb 100644 --- a/src/Velopack.Packaging/PackageBuilder.cs +++ b/src/Velopack.Packaging/PackageBuilder.cs @@ -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)); diff --git a/src/Velopack.Packaging/Progress.cs b/src/Velopack.Packaging/Progress.cs deleted file mode 100644 index f5d0d8bf..00000000 --- a/src/Velopack.Packaging/Progress.cs +++ /dev/null @@ -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 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, 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}[/]"); - } - } -} diff --git a/src/Velopack.Packaging/Velopack.Packaging.csproj b/src/Velopack.Packaging/Velopack.Packaging.csproj index 60eaa4b4..ef4691d3 100644 --- a/src/Velopack.Packaging/Velopack.Packaging.csproj +++ b/src/Velopack.Packaging/Velopack.Packaging.csproj @@ -13,7 +13,6 @@ - diff --git a/src/Velopack.Vpk/CommandMapper.cs b/src/Velopack.Vpk/CommandMapper.cs new file mode 100644 index 00000000..54fafa8d --- /dev/null +++ b/src/Velopack.Vpk/CommandMapper.cs @@ -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 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(provider); + } else if (VelopackRuntimeInfo.IsOSX) { + rootCommand.AddCommand(provider); + rootCommand.AddCommand(provider); + } else if (VelopackRuntimeInfo.IsLinux) { + rootCommand.AddCommand(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(provider); + downloadCommand.AddRepositoryDownload(provider); + downloadCommand.AddRepositoryDownload(provider); + rootCommand.Add(downloadCommand); + + var uploadCommand = new CliCommand("upload", "Upload local package(s) to a remote update source."); + uploadCommand.AddRepositoryUpload(provider); + uploadCommand.AddRepositoryUpload(provider); + rootCommand.Add(uploadCommand); + + var deltaCommand = new CliCommand("delta", "Utilities for creating or applying delta packages."); + deltaCommand.AddCommand(provider); + deltaCommand.AddCommand(provider); + rootCommand.Add(deltaCommand); + } + + private static MapperConfiguration GetMapperConfig() + { + return new MapperConfiguration(cfg => { + cfg.CreatePlatformMap(); + cfg.CreatePlatformMap(); + cfg.CreatePlatformMap(); + cfg.CreateOutputMap(); + cfg.CreateOutputMap(); + cfg.CreateOutputMap(); + cfg.CreateOutputMap(); + cfg.CreateOutputMap(); + cfg.CreateOutputMap(); + cfg.CreateMap(); + cfg.CreateMap(); + }); + } + + private static CliCommand AddCommand(this CliCommand parent, IServiceProvider provider) + where TCli : BaseCommand, new() + where TCmd : ICommand + where TOpt : class, new() + { + return parent.Add(provider, (options) => { + var runner = ActivatorUtilities.CreateInstance(provider); + return runner.Run(options); + }); + } + + private static CliCommand AddRepositoryDownload(this CliCommand parent, IServiceProvider provider) + where TCli : BaseCommand, new() + where TCmd : IRepositoryCanDownload + where TOpt : RepositoryOptions, new() + { + return parent.Add(provider, (options) => { + var runner = ActivatorUtilities.CreateInstance(provider); + return runner.DownloadLatestFullPackageAsync(options); + }); + } + + private static CliCommand AddRepositoryUpload(this CliCommand parent, IServiceProvider provider) + where TCli : BaseCommand, new() + where TCmd : IRepositoryCanUpload + where TOpt : RepositoryOptions, new() + { + return parent.Add(provider, (options) => { + var runner = ActivatorUtilities.CreateInstance(provider); + return runner.UploadMissingAssetsAsync(options); + }); + } + + private static CliCommand Add(this CliCommand parent, IServiceProvider provider, Func 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(); + logger.LogInformation($"[bold]{Program.INTRO}[/]"); + var updateCheck = new UpdateChecker(logger); + await updateCheck.CheckForUpdates(); + + command.SetProperties(ctx); + var mapper = GetMapperConfig().CreateMapper(); + var options = mapper.Map(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 CreatePlatformMap( + this IMapperConfigurationExpression cfg) + where TSource : PlatformCommand + where TDestination : IPackOptions + { + return cfg.CreateMap() + .ForMember(x => x.ReleaseDir, x => x.MapFrom(z => z.GetReleaseDirectory())) + .ForMember(x => x.TargetRuntime, x => x.MapFrom(z => z.GetRid())); + } + + private static IMappingExpression CreateOutputMap( + this IMapperConfigurationExpression cfg) + where TSource : OutputCommand + where TDestination : IOutputOptions + { + return cfg.CreateMap() + .ForMember(x => x.ReleaseDir, x => x.MapFrom(z => z.GetReleaseDirectory())); + } +} diff --git a/src/Velopack.Vpk/Commands/DeltaGenCommand.cs b/src/Velopack.Vpk/Commands/DeltaGenCommand.cs index 45aeef5d..15a6d907 100644 --- a/src/Velopack.Vpk/Commands/DeltaGenCommand.cs +++ b/src/Velopack.Vpk/Commands/DeltaGenCommand.cs @@ -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((v) => Delta = v, "--mode") + AddOption((v) => DeltaMode = v, "--mode") .SetDefault(DeltaMode.BestSpeed) .SetDescription("Set the delta generation mode."); diff --git a/src/Velopack.Vpk/Commands/GitHubDownloadCommand.cs b/src/Velopack.Vpk/Commands/GitHubDownloadCommand.cs index 6a2d27b3..64b4909f 100644 --- a/src/Velopack.Vpk/Commands/GitHubDownloadCommand.cs +++ b/src/Velopack.Vpk/Commands/GitHubDownloadCommand.cs @@ -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((v) => Pre = v, "--pre") + AddOption((v) => Prerelease = v, "--pre") .SetDescription("Get latest pre-release instead of stable."); } } diff --git a/src/Velopack.Vpk/Commands/GitHubUploadCommand.cs b/src/Velopack.Vpk/Commands/GitHubUploadCommand.cs index 11dfc6c2..9074a66f 100644 --- a/src/Velopack.Vpk/Commands/GitHubUploadCommand.cs +++ b/src/Velopack.Vpk/Commands/GitHubUploadCommand.cs @@ -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((v) => Publish = v, "--publish") .SetDescription("Create and publish instead of leaving as draft."); - AddOption((v) => Pre = v, "--pre") + AddOption((v) => Prerelease = v, "--pre") .SetDescription("Create as pre-release instead of stable."); AddOption((v) => Merge = v, "--merge") diff --git a/src/Velopack.Vpk/Commands/LinuxPackCommand.cs b/src/Velopack.Vpk/Commands/LinuxPackCommand.cs index c6fa92d0..244a6a96 100644 --- a/src/Velopack.Vpk/Commands/LinuxPackCommand.cs +++ b/src/Velopack.Vpk/Commands/LinuxPackCommand.cs @@ -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((v) => Delta = v, "--delta") + AddOption((v) => DeltaMode = v, "--delta") .SetDefault(DeltaMode.BestSpeed) .SetDescription("Set the delta generation mode."); diff --git a/src/Velopack.Vpk/Commands/OsxPackCommand.cs b/src/Velopack.Vpk/Commands/OsxPackCommand.cs index ab383f6b..087e61f0 100644 --- a/src/Velopack.Vpk/Commands/OsxPackCommand.cs +++ b/src/Velopack.Vpk/Commands/OsxPackCommand.cs @@ -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((v) => Delta = v, "--delta") + AddOption((v) => DeltaMode = v, "--delta") .SetDefault(DeltaMode.BestSpeed) .SetDescription("Set the delta generation mode."); diff --git a/src/Velopack.Vpk/Commands/WindowsReleasifyCommand.cs b/src/Velopack.Vpk/Commands/WindowsReleasifyCommand.cs index 55e05512..587df90a 100644 --- a/src/Velopack.Vpk/Commands/WindowsReleasifyCommand.cs +++ b/src/Velopack.Vpk/Commands/WindowsReleasifyCommand.cs @@ -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((v) => Delta = v, "--delta") + AddOption((v) => DeltaMode = v, "--delta") .SetDefault(DeltaMode.BestSpeed) .SetDescription("Set the delta generation mode."); diff --git a/src/Velopack.Vpk/Compat/EmbeddedRunner.cs b/src/Velopack.Vpk/Compat/EmbeddedRunner.cs deleted file mode 100644 index 8eb59541..00000000 --- a/src/Velopack.Vpk/Compat/EmbeddedRunner.cs +++ /dev/null @@ -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); - } -} diff --git a/src/Velopack.Vpk/Compat/ICommandRunner.cs b/src/Velopack.Vpk/Compat/ICommandRunner.cs deleted file mode 100644 index f400e7e1..00000000 --- a/src/Velopack.Vpk/Compat/ICommandRunner.cs +++ /dev/null @@ -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); -} diff --git a/src/Velopack.Vpk/Compat/RunnerFactory.cs b/src/Velopack.Vpk/Compat/RunnerFactory.cs deleted file mode 100644 index 26b8c645..00000000 --- a/src/Velopack.Vpk/Compat/RunnerFactory.cs +++ /dev/null @@ -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 CreateAndExecuteAsync(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 CreateAsync(T options) - { - if (_config.GetValue("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()); - } -} diff --git a/src/Velopack.Vpk/Logging/BasicConsole.cs b/src/Velopack.Vpk/Logging/BasicConsole.cs new file mode 100644 index 00000000..4b7c0134 --- /dev/null +++ b/src/Velopack.Vpk/Logging/BasicConsole.cs @@ -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 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, Task> fn) + { + _logger.Info("Starting: " + name); + await Task.Run(() => fn(_ => { })); + _logger.Info("Complete: " + name); + } + } + } +} diff --git a/src/Velopack.Vpk/Logging/SpectreConsole.cs b/src/Velopack.Vpk/Logging/SpectreConsole.cs new file mode 100644 index 00000000..7ae793f1 --- /dev/null +++ b/src/Velopack.Vpk/Logging/SpectreConsole.cs @@ -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 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, 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}[/]"); + } + } + } +} diff --git a/src/Velopack.Vpk/Program.cs b/src/Velopack.Vpk/Program.cs index ba2edd48..a3247b38 100644 --- a/src/Velopack.Vpk/Program.cs +++ b/src/Velopack.Vpk/Program.cs @@ -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().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(); conf.WriteTo.Console(); } else { - Packaging.Progress.IsEnabled = true; + builder.Services.AddSingleton(); conf.WriteTo.Spectre(); } @@ -68,59 +65,16 @@ public class Program builder.Logging.AddSerilog(); var host = builder.Build(); - var logFactory = host.Services.GetRequiredService(); - var logger = logFactory.CreateLogger("vpk"); + var logger = host.Services.GetRequiredService(); - Runner = new RunnerFactory(logger, host.Services.GetRequiredService()); - - 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(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; - } } diff --git a/src/Velopack.Vpk/Velopack.Vpk.csproj b/src/Velopack.Vpk/Velopack.Vpk.csproj index 5008050d..c1a6be76 100644 --- a/src/Velopack.Vpk/Velopack.Vpk.csproj +++ b/src/Velopack.Vpk/Velopack.Vpk.csproj @@ -42,6 +42,8 @@ + + diff --git a/test/Velopack.CommandLine.Tests/AutoMapperTests.cs b/test/Velopack.CommandLine.Tests/AutoMapperTests.cs new file mode 100644 index 00000000..6caffa87 --- /dev/null +++ b/test/Velopack.CommandLine.Tests/AutoMapperTests.cs @@ -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(); + } + } +} diff --git a/test/Velopack.CommandLine.Tests/Commands/GitHubCommandTests.cs b/test/Velopack.CommandLine.Tests/Commands/GitHubCommandTests.cs index 274fbbbf..00f2745a 100644 --- a/test/Velopack.CommandLine.Tests/Commands/GitHubCommandTests.cs +++ b/test/Velopack.CommandLine.Tests/Commands/GitHubCommandTests.cs @@ -68,7 +68,7 @@ public class GitHubDownloadCommandTests : GitHubCommandTests : BaseCommandTests 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] diff --git a/test/Velopack.Packaging.Tests/TestApp.cs b/test/Velopack.Packaging.Tests/TestApp.cs index 0fe52b17..a7461f3b 100644 --- a/test/Velopack.Packaging.Tests/TestApp.cs +++ b/test/Velopack.Packaging.Tests/TestApp.cs @@ -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(); diff --git a/test/Velopack.Packaging.Tests/Velopack.Packaging.Tests.csproj b/test/Velopack.Packaging.Tests/Velopack.Packaging.Tests.csproj index 78d2a836..71c45818 100644 --- a/test/Velopack.Packaging.Tests/Velopack.Packaging.Tests.csproj +++ b/test/Velopack.Packaging.Tests/Velopack.Packaging.Tests.csproj @@ -5,9 +5,7 @@ - - - + diff --git a/test/Velopack.Packaging.Tests/WindowsPackTests.cs b/test/Velopack.Packaging.Tests/WindowsPackTests.cs index c53387bb..82df806f 100644 --- a/test/Velopack.Packaging.Tests/WindowsPackTests.cs +++ b/test/Velopack.Packaging.Tests/WindowsPackTests.cs @@ -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(() => 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);