From 1d47c9bd04cdb463a99dbb2ea554c3a999b916aa Mon Sep 17 00:00:00 2001 From: Caelan Sayler Date: Thu, 28 Mar 2024 10:45:02 +0000 Subject: [PATCH] Add local upload command --- src/Velopack.Deployment/LocalRepository.cs | 67 +++++++++++++++++-- src/Velopack.Deployment/_ObjectRepository.cs | 2 +- .../Commands/Deployment/LocalBaseCommand.cs | 17 +++++ .../Deployment/LocalDownloadCommand.cs | 10 +-- .../Commands/Deployment/LocalUploadCommand.cs | 22 ++++++ src/Velopack.Vpk/OptionMapper.cs | 1 + src/Velopack.Vpk/Program.cs | 3 +- .../Commands/LocalDownloadCommandTests.cs | 2 +- 8 files changed, 108 insertions(+), 16 deletions(-) create mode 100644 src/Velopack.Vpk/Commands/Deployment/LocalBaseCommand.cs create mode 100644 src/Velopack.Vpk/Commands/Deployment/LocalUploadCommand.cs diff --git a/src/Velopack.Deployment/LocalRepository.cs b/src/Velopack.Deployment/LocalRepository.cs index eb374a2a..4e48c991 100644 --- a/src/Velopack.Deployment/LocalRepository.cs +++ b/src/Velopack.Deployment/LocalRepository.cs @@ -1,17 +1,74 @@ using Microsoft.Extensions.Logging; +using Velopack.Packaging; using Velopack.Sources; namespace Velopack.Deployment; -public class LocalDownloadOptions : RepositoryOptions +public class LocalDownloadOptions : RepositoryOptions, IObjectDownloadOptions { - public DirectoryInfo Path { get; set; } + public DirectoryInfo TargetPath { get; set; } } -public class LocalRepository(ILogger logger) : SourceRepository(logger) +public class LocalUploadOptions : LocalDownloadOptions, IObjectUploadOptions { - public override SimpleFileSource CreateSource(LocalDownloadOptions options) + public bool ForceRegenerate { get; set; } + public int KeepMaxReleases { get; set; } +} + +public class LocalRepository(ILogger logger) : ObjectRepository(logger) +{ + protected override DirectoryInfo CreateClient(LocalDownloadOptions options) { - return new SimpleFileSource(options.Path); + return options.TargetPath; + } + + protected override Task DeleteObject(DirectoryInfo client, string key) + { + var target = Path.Combine(client.FullName, key); + Log.Info("Deleting: " + target); + Utility.DeleteFileOrDirectoryHard(target); + return Task.CompletedTask; + } + + protected override Task GetObjectBytes(DirectoryInfo client, string key) + { + var target = Path.Combine(client.FullName, key); + Log.Info("Reading: " + target); + return File.ReadAllBytesAsync(target); + } + + protected override Task UploadObject(DirectoryInfo client, string key, FileInfo f, bool overwriteRemote, bool noCache) + { + var target = Path.Combine(client.FullName, key); + if (File.Exists(target) && !overwriteRemote) { + Log.Info($"Skipping upload of {key} as it already exists in the repository and overwrite=false."); + return Task.CompletedTask; + } + + Log.Info($"Uploading '{f.FullName}' to '{target}'."); + File.Copy(f.FullName, target, overwriteRemote); + return Task.CompletedTask; + } + + public override Task UploadMissingAssetsAsync(LocalUploadOptions options) + { + if (options.ForceRegenerate) { + Log.Info("Force regenerating release index files..."); + ReleaseEntryHelper.UpdateReleaseFiles(options.TargetPath.FullName, Log); + } + + return base.UploadMissingAssetsAsync(options); + } + + protected override Task SaveEntryToFileAsync(LocalDownloadOptions options, VelopackAsset entry, string filePath) + { + var source = new SimpleFileSource(options.TargetPath); + return source.DownloadReleaseEntry(Log, entry, filePath, (i) => { }, default); + } + + protected override Task GetReleasesAsync(LocalDownloadOptions options) + { + var source = new SimpleFileSource(options.TargetPath); + return source.GetReleaseFeed(channel: options.Channel, logger: Log); } } diff --git a/src/Velopack.Deployment/_ObjectRepository.cs b/src/Velopack.Deployment/_ObjectRepository.cs index 4be1aff2..fbd6bc5d 100644 --- a/src/Velopack.Deployment/_ObjectRepository.cs +++ b/src/Velopack.Deployment/_ObjectRepository.cs @@ -46,7 +46,7 @@ public abstract class ObjectRepository : DownRepository((p) => TargetPath = p, "--path") + .SetDescription("File path to copy releases from.") + .SetArgumentHelpName("PATH") + .MustNotBeEmpty() + .MustExist() + .SetRequired(); + } +} diff --git a/src/Velopack.Vpk/Commands/Deployment/LocalDownloadCommand.cs b/src/Velopack.Vpk/Commands/Deployment/LocalDownloadCommand.cs index 9e4876bd..8a86d15e 100644 --- a/src/Velopack.Vpk/Commands/Deployment/LocalDownloadCommand.cs +++ b/src/Velopack.Vpk/Commands/Deployment/LocalDownloadCommand.cs @@ -1,15 +1,9 @@ namespace Velopack.Vpk.Commands.Deployment; -public class LocalDownloadCommand : OutputCommand +public class LocalDownloadCommand : LocalBaseCommand { - public DirectoryInfo Path { get; private set; } - public LocalDownloadCommand() - : base("local", "Download latest release from a local path source.") + : base("local", "Download latest release from a local path or network share.") { - AddOption((p) => Path = p, "--path") - .SetDescription("Path to download releases from.") - .MustExist() - .SetRequired(); } } diff --git a/src/Velopack.Vpk/Commands/Deployment/LocalUploadCommand.cs b/src/Velopack.Vpk/Commands/Deployment/LocalUploadCommand.cs new file mode 100644 index 00000000..612e5e44 --- /dev/null +++ b/src/Velopack.Vpk/Commands/Deployment/LocalUploadCommand.cs @@ -0,0 +1,22 @@ +namespace Velopack.Vpk.Commands.Deployment; + +public class LocalUploadCommand : LocalBaseCommand +{ + public int KeepMaxReleases { get; private set; } + + public bool ForceRegenerate { get; private set; } + + public LocalUploadCommand() + : base("local", "Upload releases to a local path or network share.") + { + AddOption((x) => KeepMaxReleases = x, "--keepMaxReleases") + .SetDescription("The maximum number of releases to keep in the bucket, anything older will be deleted.") + .SetArgumentHelpName("COUNT"); + + AddOption((x) => ForceRegenerate = x, "--regenerate") + .SetDescription("Force regenerate the releases.{channel}.json file in the target directory."); + + ReleaseDirectoryOption.SetRequired(); + ReleaseDirectoryOption.MustNotBeEmpty(); + } +} diff --git a/src/Velopack.Vpk/OptionMapper.cs b/src/Velopack.Vpk/OptionMapper.cs index 2c19e55d..dc05080a 100644 --- a/src/Velopack.Vpk/OptionMapper.cs +++ b/src/Velopack.Vpk/OptionMapper.cs @@ -24,6 +24,7 @@ public static partial class OptionMapper public static partial GitHubUploadOptions ToOptions(this GitHubUploadCommand cmd); public static partial HttpDownloadOptions ToOptions(this HttpDownloadCommand cmd); public static partial LocalDownloadOptions ToOptions(this LocalDownloadCommand cmd); + public static partial LocalUploadOptions ToOptions(this LocalUploadCommand cmd); public static partial S3DownloadOptions ToOptions(this S3DownloadCommand cmd); public static partial S3UploadOptions ToOptions(this S3UploadCommand cmd); public static partial AzureDownloadOptions ToOptions(this AzureDownloadCommand cmd); diff --git a/src/Velopack.Vpk/Program.cs b/src/Velopack.Vpk/Program.cs index 270ecf0a..79278837 100644 --- a/src/Velopack.Vpk/Program.cs +++ b/src/Velopack.Vpk/Program.cs @@ -87,14 +87,15 @@ public class Program downloadCommand.AddRepositoryDownload(provider); downloadCommand.AddRepositoryDownload(provider); downloadCommand.AddRepositoryDownload(provider); - 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); uploadCommand.AddRepositoryUpload(provider); + uploadCommand.AddRepositoryUpload(provider); rootCommand.Add(uploadCommand); var deltaCommand = new CliCommand("delta", "Utilities for creating or applying delta packages."); diff --git a/test/Velopack.CommandLine.Tests/Commands/LocalDownloadCommandTests.cs b/test/Velopack.CommandLine.Tests/Commands/LocalDownloadCommandTests.cs index 11e24e9e..3c1de508 100644 --- a/test/Velopack.CommandLine.Tests/Commands/LocalDownloadCommandTests.cs +++ b/test/Velopack.CommandLine.Tests/Commands/LocalDownloadCommandTests.cs @@ -13,7 +13,7 @@ public class LocalDownloadCommandTests : BaseCommandTests ParseResult parseResult = command.ParseAndApply($"--path {directory.FullName}"); Assert.Empty(parseResult.Errors); - Assert.Equal(directory.FullName, command.Path.FullName); + Assert.Equal(directory.FullName, command.TargetPath.FullName); Directory.Delete(directory.FullName); }