mirror of
https://github.com/velopack/velopack.git
synced 2025-10-25 15:19:22 +00:00
Fix azure folder uploading and align with s3 implementation
This commit is contained in:
@@ -20,8 +20,8 @@ public class AzureDownloadOptions : RepositoryOptions, IObjectDownloadOptions
|
||||
public string Container { get; set; }
|
||||
|
||||
public string SasToken { get; set; }
|
||||
|
||||
public string Folder { get; set; }
|
||||
|
||||
public string Prefix { get; set; }
|
||||
}
|
||||
|
||||
public class AzureUploadOptions : AzureDownloadOptions, IObjectUploadOptions
|
||||
@@ -29,13 +29,26 @@ public class AzureUploadOptions : AzureDownloadOptions, IObjectUploadOptions
|
||||
public int KeepMaxReleases { get; set; }
|
||||
}
|
||||
|
||||
public class AzureRepository : ObjectRepository<AzureDownloadOptions, AzureUploadOptions, BlobContainerClient>
|
||||
public class AzureBlobClient(BlobContainerClient client, string prefix)
|
||||
{
|
||||
public virtual Task DeleteBlobIfExistsAsync(string key, CancellationToken cancellationToken = default)
|
||||
{
|
||||
return client.DeleteBlobIfExistsAsync(prefix + key, cancellationToken: cancellationToken);
|
||||
}
|
||||
|
||||
public virtual BlobClient GetBlobClient(string key)
|
||||
{
|
||||
return client.GetBlobClient(prefix + key);
|
||||
}
|
||||
}
|
||||
|
||||
public class AzureRepository : ObjectRepository<AzureDownloadOptions, AzureUploadOptions, AzureBlobClient>
|
||||
{
|
||||
public AzureRepository(ILogger logger) : base(logger)
|
||||
{
|
||||
}
|
||||
|
||||
protected override BlobContainerClient CreateClient(AzureDownloadOptions options)
|
||||
protected override AzureBlobClient CreateClient(AzureDownloadOptions options)
|
||||
{
|
||||
var serviceUrl = options.Endpoint ?? "https://" + options.Account + ".blob.core.windows.net";
|
||||
if (options.Endpoint == null) {
|
||||
@@ -54,16 +67,22 @@ public class AzureRepository : ObjectRepository<AzureDownloadOptions, AzureUploa
|
||||
client = new BlobServiceClient(new Uri(serviceUrl), new StorageSharedKeyCredential(options.Account, options.Key), clientOptions);
|
||||
}
|
||||
|
||||
return client.GetBlobContainerClient(options.Container);
|
||||
var containerClient = client.GetBlobContainerClient(options.Container);
|
||||
|
||||
var prefix = options.Prefix?.Trim();
|
||||
if (prefix == null) {
|
||||
prefix = "";
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(prefix) && !prefix.EndsWith("/")) {
|
||||
prefix += "/";
|
||||
}
|
||||
|
||||
return new AzureBlobClient(containerClient, prefix);
|
||||
}
|
||||
|
||||
protected override async Task DeleteObject(BlobContainerClient client, string key)
|
||||
protected override async Task DeleteObject(AzureBlobClient client, string key)
|
||||
{
|
||||
// Prepend folder path if specified (using _uploadFolder since this is called during upload)
|
||||
if (!string.IsNullOrEmpty(_uploadFolder)) {
|
||||
key = _uploadFolder.TrimEnd('/') + "/" + key;
|
||||
}
|
||||
|
||||
await RetryAsync(
|
||||
async () => {
|
||||
await client.DeleteBlobIfExistsAsync(key);
|
||||
@@ -71,7 +90,7 @@ public class AzureRepository : ObjectRepository<AzureDownloadOptions, AzureUploa
|
||||
"Deleting " + key);
|
||||
}
|
||||
|
||||
protected override async Task<byte[]> GetObjectBytes(BlobContainerClient client, string key)
|
||||
protected override async Task<byte[]> GetObjectBytes(AzureBlobClient client, string key)
|
||||
{
|
||||
return await RetryAsyncRet(
|
||||
async () => {
|
||||
@@ -90,12 +109,6 @@ public class AzureRepository : ObjectRepository<AzureDownloadOptions, AzureUploa
|
||||
protected override async Task<VelopackAssetFeed> GetReleasesAsync(AzureDownloadOptions options)
|
||||
{
|
||||
var releasesName = CoreUtil.GetVeloReleaseIndexName(options.Channel);
|
||||
|
||||
// Prepend folder path if specified
|
||||
if (!string.IsNullOrEmpty(options.Folder)) {
|
||||
releasesName = options.Folder.TrimEnd('/') + "/" + releasesName;
|
||||
}
|
||||
|
||||
var client = CreateClient(options);
|
||||
var bytes = await GetObjectBytes(client, releasesName);
|
||||
if (bytes == null || bytes.Length == 0) {
|
||||
@@ -109,40 +122,15 @@ public class AzureRepository : ObjectRepository<AzureDownloadOptions, AzureUploa
|
||||
await RetryAsync(
|
||||
async () => {
|
||||
var client = CreateClient(options);
|
||||
var key = entry.FileName;
|
||||
|
||||
// Prepend folder path if specified
|
||||
if (!string.IsNullOrEmpty(options.Folder)) {
|
||||
key = options.Folder.TrimEnd('/') + "/" + key;
|
||||
}
|
||||
|
||||
var obj = client.GetBlobClient(key);
|
||||
var obj = client.GetBlobClient(entry.FileName);
|
||||
using var response = await obj.DownloadToAsync(filePath, CancellationToken.None);
|
||||
},
|
||||
$"Downloading {entry.FileName}...");
|
||||
}
|
||||
|
||||
public override async Task UploadMissingAssetsAsync(AzureUploadOptions options)
|
||||
|
||||
protected override async Task UploadObject(AzureBlobClient client, string key, FileInfo f, bool overwriteRemote, bool noCache)
|
||||
{
|
||||
// Store the folder in a private field for use in UploadObject
|
||||
// Note: Azure Blob Storage will handle path validation and reject invalid paths
|
||||
_uploadFolder = options.Folder;
|
||||
try {
|
||||
await base.UploadMissingAssetsAsync(options);
|
||||
} finally {
|
||||
_uploadFolder = null;
|
||||
}
|
||||
}
|
||||
|
||||
private string _uploadFolder;
|
||||
|
||||
protected override async Task UploadObject(BlobContainerClient client, string key, FileInfo f, bool overwriteRemote, bool noCache)
|
||||
{
|
||||
// Prepend folder path if specified
|
||||
if (!string.IsNullOrEmpty(_uploadFolder)) {
|
||||
key = _uploadFolder.TrimEnd('/') + "/" + key;
|
||||
}
|
||||
|
||||
var blobClient = client.GetBlobClient(key);
|
||||
try {
|
||||
var properties = await blobClient.GetPropertiesAsync();
|
||||
|
||||
@@ -12,31 +12,37 @@ public class AzureBaseCommand : OutputCommand
|
||||
|
||||
public string SasToken { get; private set; }
|
||||
|
||||
public string Prefix { get; private set; }
|
||||
|
||||
public double Timeout { get; private set; }
|
||||
|
||||
protected AzureBaseCommand(string name, string description)
|
||||
: base(name, description)
|
||||
{
|
||||
AddOption<string>((v) => Account = v, "--account")
|
||||
.SetDescription("Account name")
|
||||
.SetDescription("Account name.")
|
||||
.SetArgumentHelpName("ACCOUNT")
|
||||
.SetRequired();
|
||||
|
||||
var key = AddOption<string>((v) => Key = v, "--key")
|
||||
.SetDescription("Account secret key")
|
||||
.SetDescription("Account secret key.")
|
||||
.SetArgumentHelpName("KEY");
|
||||
|
||||
var sas = AddOption<string>((v) => SasToken = v, "--sas")
|
||||
.SetDescription("Shared access signature token (not the url)")
|
||||
.SetDescription("Shared access signature token (not the url).")
|
||||
.SetArgumentHelpName("TOKEN");
|
||||
|
||||
AddOption<string>((v) => Container = v, "--container")
|
||||
.SetDescription("Azure container name")
|
||||
.SetDescription("Azure storage container name.")
|
||||
.SetArgumentHelpName("NAME")
|
||||
.SetRequired();
|
||||
|
||||
AddOption<string>((v) => Prefix = v, "--prefix")
|
||||
.SetDescription("Optional blob filename path prefix.")
|
||||
.SetArgumentHelpName("PREFIX");
|
||||
|
||||
AddOption<Uri>((v) => Endpoint = v.ToAbsoluteOrNull(), "--endpoint")
|
||||
.SetDescription("Service url (eg. https://<account-name>.blob.core.windows.net)")
|
||||
.SetDescription("Service url (eg. https://<account-name>.blob.core.windows.net).")
|
||||
.SetArgumentHelpName("URL")
|
||||
.MustBeValidHttpUri();
|
||||
|
||||
|
||||
@@ -2,13 +2,8 @@
|
||||
|
||||
public class AzureDownloadCommand : AzureBaseCommand
|
||||
{
|
||||
public string Folder { get; private set; }
|
||||
|
||||
public AzureDownloadCommand()
|
||||
: base("az", "Download latest release from an Azure Blob Storage container.")
|
||||
{
|
||||
AddOption<string>((x) => Folder = x, "--folder")
|
||||
.SetDescription("The folder path within the container where files are stored.")
|
||||
.SetArgumentHelpName("PATH");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,8 +3,6 @@
|
||||
public class AzureUploadCommand : AzureBaseCommand
|
||||
{
|
||||
public int KeepMaxReleases { get; private set; }
|
||||
|
||||
public string Folder { get; private set; }
|
||||
|
||||
public AzureUploadCommand()
|
||||
: base("az", "Upload releases to an Azure Blob Storage container.")
|
||||
@@ -13,10 +11,6 @@ public class AzureUploadCommand : AzureBaseCommand
|
||||
.SetDescription("The maximum number of releases to keep in the container, anything older will be deleted.")
|
||||
.SetArgumentHelpName("COUNT");
|
||||
|
||||
AddOption<string>((x) => Folder = x, "--folder")
|
||||
.SetDescription("The folder path within the container where files should be uploaded.")
|
||||
.SetArgumentHelpName("PATH");
|
||||
|
||||
ReleaseDirectoryOption.SetRequired();
|
||||
ReleaseDirectoryOption.MustNotBeEmpty();
|
||||
}
|
||||
|
||||
@@ -55,7 +55,7 @@ public class S3BaseCommand : OutputCommand
|
||||
.SetRequired();
|
||||
|
||||
AddOption<string>((v) => Prefix = v, "--prefix")
|
||||
.SetDescription("Prefix to the S3 url.")
|
||||
.SetDescription("Optional filename path prefix.")
|
||||
.SetArgumentHelpName("PREFIX");
|
||||
|
||||
AddOption<bool>((v) => DisablePathStyle = v, "--disablePathStyle")
|
||||
|
||||
Reference in New Issue
Block a user