From 7cc73f584e71859fb9e7f93ca60d7d2620cdee65 Mon Sep 17 00:00:00 2001 From: Caelan Sayler Date: Fri, 26 Jan 2024 10:35:50 +0000 Subject: [PATCH] Swap out AutoMapper for Mapperly --- .editorconfig | 6 +- src/Velopack.Vpk/CommandMapper.cs | 166 ------------------ src/Velopack.Vpk/Commands/_OutputCommand.cs | 6 +- src/Velopack.Vpk/OptionMapper.cs | 37 ++++ src/Velopack.Vpk/Program.cs | 102 ++++++++++- src/Velopack.Vpk/Velopack.Vpk.csproj | 2 +- .../AutoMapperTests.cs | 18 -- 7 files changed, 146 insertions(+), 191 deletions(-) delete mode 100644 src/Velopack.Vpk/CommandMapper.cs create mode 100644 src/Velopack.Vpk/OptionMapper.cs delete mode 100644 test/Velopack.CommandLine.Tests/AutoMapperTests.cs diff --git a/.editorconfig b/.editorconfig index 2556ef7c..ce0e8678 100644 --- a/.editorconfig +++ b/.editorconfig @@ -199,4 +199,8 @@ cpp_space_around_ternary_operator = insert cpp_wrap_preserve_blocks = one_liners [*.{csproj,manifest,nuspec,wxs,props}] -indent_size = 2 \ No newline at end of file +indent_size = 2 + +[*.cs] +dotnet_diagnostic.RMG012.severity = error # Unmapped target member +dotnet_diagnostic.RMG020.severity = error # Unmapped source member \ No newline at end of file diff --git a/src/Velopack.Vpk/CommandMapper.cs b/src/Velopack.Vpk/CommandMapper.cs deleted file mode 100644 index 54fafa8d..00000000 --- a/src/Velopack.Vpk/CommandMapper.cs +++ /dev/null @@ -1,166 +0,0 @@ -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/_OutputCommand.cs b/src/Velopack.Vpk/Commands/_OutputCommand.cs index c855813f..ee853a66 100644 --- a/src/Velopack.Vpk/Commands/_OutputCommand.cs +++ b/src/Velopack.Vpk/Commands/_OutputCommand.cs @@ -4,7 +4,7 @@ namespace Velopack.Vpk.Commands { public abstract class OutputCommand : BaseCommand { - public string ReleaseDirectory { get; private set; } + public string ReleaseDir { get; private set; } public string Channel { get; private set; } @@ -15,7 +15,7 @@ namespace Velopack.Vpk.Commands protected OutputCommand(string name, string description) : base(name, description) { - ReleaseDirectoryOption = AddOption((v) => ReleaseDirectory = v.ToFullNameOrNull(), "-o", "--outputDir") + ReleaseDirectoryOption = AddOption((v) => ReleaseDir = v.ToFullNameOrNull(), "-o", "--outputDir") .SetDescription("Output directory for created packages.") .SetArgumentHelpName("DIR") .SetDefault(new DirectoryInfo("Releases")); @@ -29,7 +29,7 @@ namespace Velopack.Vpk.Commands public DirectoryInfo GetReleaseDirectory() { - var di = new DirectoryInfo(ReleaseDirectory); + var di = new DirectoryInfo(ReleaseDir); if (!di.Exists) di.Create(); return di; } diff --git a/src/Velopack.Vpk/OptionMapper.cs b/src/Velopack.Vpk/OptionMapper.cs new file mode 100644 index 00000000..3227ca0e --- /dev/null +++ b/src/Velopack.Vpk/OptionMapper.cs @@ -0,0 +1,37 @@ +using Riok.Mapperly.Abstractions; +using Velopack.Deployment; +using Velopack.Packaging.Commands; +using Velopack.Packaging.Unix.Commands; +using Velopack.Packaging.Windows.Commands; +using Velopack.Vpk.Commands; + +namespace Velopack.Vpk; + +[Mapper( + RequiredMappingStrategy = RequiredMappingStrategy.Target, + EnabledConversions = MappingConversionType.None)] +public static partial class OptionMapper +{ + public static partial TDest Map(object source); + + public static partial OsxPackOptions ToOptions(this OsxPackCommand cmd); + public static partial WindowsPackOptions ToOptions(this WindowsPackCommand cmd); + public static partial LinuxPackOptions ToOptions(this LinuxPackCommand cmd); + public static partial OsxBundleOptions ToOptions(this OsxBundleCommand cmd); + public static partial GitHubDownloadOptions ToOptions(this GitHubDownloadCommand cmd); + public static partial GitHubUploadOptions ToOptions(this GitHubUploadCommand cmd); + public static partial HttpDownloadOptions ToOptions(this HttpDownloadCommand cmd); + public static partial S3DownloadOptions ToOptions(this S3DownloadCommand cmd); + public static partial S3UploadOptions ToOptions(this S3UploadCommand cmd); + public static partial DeltaGenOptions ToOptions(this DeltaGenCommand cmd); + public static partial DeltaPatchOptions ToOptions(this DeltaPatchCommand cmd); + + private static DirectoryInfo StringToDirectoryInfo(string t) + { + var di = new DirectoryInfo(t); + if (!di.Exists) di.Create(); + return di; + } + + private static RID StringToRID(string t) => RID.Parse(t); +} diff --git a/src/Velopack.Vpk/Program.cs b/src/Velopack.Vpk/Program.cs index a3247b38..cb73c739 100644 --- a/src/Velopack.Vpk/Program.cs +++ b/src/Velopack.Vpk/Program.cs @@ -3,9 +3,15 @@ using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; using Serilog; using Serilog.Events; +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.Logging; +using Velopack.Vpk.Updates; namespace Velopack.Vpk; @@ -22,7 +28,7 @@ public class Program .SetDescription("Disable console colors and interactive components."); public static readonly string INTRO - = $"Velopack CLI {VelopackRuntimeInfo.VelopackDisplayVersion} for creating and distributing releases."; + = $"Velopack CLI {VelopackRuntimeInfo.VelopackDisplayVersion}, for distributing applications."; public static async Task Main(string[] args) { @@ -66,15 +72,107 @@ public class Program var host = builder.Build(); var logger = host.Services.GetRequiredService(); + var provider = host.Services; var rootCommand = new CliRootCommand(INTRO) { VerboseOption, LegacyConsole, }; - rootCommand.PopulateVelopackCommands(host.Services); + 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); var cli = new CliConfiguration(rootCommand); return await cli.InvokeAsync(args); } } + +public static class ProgramCommandExtensions +{ + public 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); + }); + } + + public 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); + }); + } + + public 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() + { + 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 options = OptionMapper.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; + } +} \ No newline at end of file diff --git a/src/Velopack.Vpk/Velopack.Vpk.csproj b/src/Velopack.Vpk/Velopack.Vpk.csproj index c1a6be76..d158c108 100644 --- a/src/Velopack.Vpk/Velopack.Vpk.csproj +++ b/src/Velopack.Vpk/Velopack.Vpk.csproj @@ -43,7 +43,7 @@ - + diff --git a/test/Velopack.CommandLine.Tests/AutoMapperTests.cs b/test/Velopack.CommandLine.Tests/AutoMapperTests.cs deleted file mode 100644 index 6caffa87..00000000 --- a/test/Velopack.CommandLine.Tests/AutoMapperTests.cs +++ /dev/null @@ -1,18 +0,0 @@ -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(); - } - } -}