Add local upload command

This commit is contained in:
Caelan Sayler
2024-03-28 10:45:02 +00:00
parent 44ad132a72
commit 1d47c9bd04
8 changed files with 108 additions and 16 deletions

View File

@@ -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<LocalDownloadOptions, SimpleFileSource>(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<LocalDownloadOptions, LocalUploadOptions, DirectoryInfo>(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<byte[]> 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<VelopackAssetFeed> GetReleasesAsync(LocalDownloadOptions options)
{
var source = new SimpleFileSource(options.TargetPath);
return source.GetReleaseFeed(channel: options.Channel, logger: Log);
}
}

View File

@@ -46,7 +46,7 @@ public abstract class ObjectRepository<TDown, TUp, TClient> : DownRepository<TDo
return VelopackAssetFeed.FromJson(Encoding.UTF8.GetString(bytes));
}
public async Task UploadMissingAssetsAsync(TUp options)
public virtual async Task UploadMissingAssetsAsync(TUp options)
{
var build = BuildAssets.Read(options.ReleaseDir.FullName, options.Channel);
var client = CreateClient(options);

View File

@@ -0,0 +1,17 @@
namespace Velopack.Vpk.Commands.Deployment;
public class LocalBaseCommand : OutputCommand
{
public DirectoryInfo TargetPath { get; private set; }
public LocalBaseCommand(string command, string description)
: base(command, description)
{
AddOption<DirectoryInfo>((p) => TargetPath = p, "--path")
.SetDescription("File path to copy releases from.")
.SetArgumentHelpName("PATH")
.MustNotBeEmpty()
.MustExist()
.SetRequired();
}
}

View File

@@ -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<DirectoryInfo>((p) => Path = p, "--path")
.SetDescription("Path to download releases from.")
.MustExist()
.SetRequired();
}
}

View File

@@ -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<int>((x) => KeepMaxReleases = x, "--keepMaxReleases")
.SetDescription("The maximum number of releases to keep in the bucket, anything older will be deleted.")
.SetArgumentHelpName("COUNT");
AddOption<bool>((x) => ForceRegenerate = x, "--regenerate")
.SetDescription("Force regenerate the releases.{channel}.json file in the target directory.");
ReleaseDirectoryOption.SetRequired();
ReleaseDirectoryOption.MustNotBeEmpty();
}
}

View File

@@ -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);

View File

@@ -87,14 +87,15 @@ public class Program
downloadCommand.AddRepositoryDownload<GitHubDownloadCommand, GitHubRepository, GitHubDownloadOptions>(provider);
downloadCommand.AddRepositoryDownload<S3DownloadCommand, S3Repository, S3DownloadOptions>(provider);
downloadCommand.AddRepositoryDownload<AzureDownloadCommand, AzureRepository, AzureDownloadOptions>(provider);
downloadCommand.AddRepositoryDownload<HttpDownloadCommand, HttpRepository, HttpDownloadOptions>(provider);
downloadCommand.AddRepositoryDownload<LocalDownloadCommand, LocalRepository, LocalDownloadOptions>(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);
uploadCommand.AddRepositoryUpload<AzureUploadCommand, AzureRepository, AzureUploadOptions>(provider);
uploadCommand.AddRepositoryUpload<LocalUploadCommand, LocalRepository, LocalUploadOptions>(provider);
rootCommand.Add(uploadCommand);
var deltaCommand = new CliCommand("delta", "Utilities for creating or applying delta packages.");

View File

@@ -13,7 +13,7 @@ public class LocalDownloadCommandTests : BaseCommandTests<LocalDownloadCommand>
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);
}