mirror of
https://github.com/velopack/velopack.git
synced 2025-10-25 15:19:22 +00:00
Updating to use the new Velopack Flow APIs (#180)
* WIP * Updated to use the new release group APIs * cleanup and increase timeouts --------- Co-authored-by: Caelan Sayler <git@caesay.com>
This commit is contained in:
@@ -13,7 +13,7 @@ public class PublishTask : MSBuildAsyncTask
|
||||
private static HttpClient HttpClient { get; } = new(new HmacAuthHttpClientHandler {
|
||||
InnerHandler = new HttpClientHandler()
|
||||
}) {
|
||||
Timeout = TimeSpan.FromMinutes(10)
|
||||
Timeout = TimeSpan.FromMinutes(60)
|
||||
};
|
||||
|
||||
[Required]
|
||||
|
||||
14
src/Velopack.Packaging/Flow/CreateReleaseGroupRequest.cs
Normal file
14
src/Velopack.Packaging/Flow/CreateReleaseGroupRequest.cs
Normal file
@@ -0,0 +1,14 @@
|
||||
#if NET6_0_OR_GREATER
|
||||
#else
|
||||
using System.Net.Http;
|
||||
#endif
|
||||
|
||||
#nullable enable
|
||||
namespace Velopack.Packaging.Flow;
|
||||
|
||||
internal sealed class CreateReleaseGroupRequest
|
||||
{
|
||||
public string? PackageId { get; set; }
|
||||
public string? Version { get; set; }
|
||||
public string? ChannelIdentifier { get; set; }
|
||||
}
|
||||
13
src/Velopack.Packaging/Flow/ReleaseGroup.cs
Normal file
13
src/Velopack.Packaging/Flow/ReleaseGroup.cs
Normal file
@@ -0,0 +1,13 @@
|
||||
#if NET6_0_OR_GREATER
|
||||
#else
|
||||
using System.Net.Http;
|
||||
#endif
|
||||
|
||||
#nullable enable
|
||||
namespace Velopack.Packaging.Flow;
|
||||
|
||||
internal sealed class ReleaseGroup
|
||||
{
|
||||
public Guid Id { get; set; }
|
||||
public string? Version { get; set; }
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
|
||||
#nullable enable
|
||||
|
||||
using NuGet.Versioning;
|
||||
|
||||
namespace Velopack.Packaging.Flow;
|
||||
|
||||
public class UploadInstallerOptions : UploadOptions
|
||||
{
|
||||
public string PackageId { get; }
|
||||
|
||||
public SemanticVersion Version { get; }
|
||||
|
||||
public UploadInstallerOptions(string packageId, SemanticVersion version, Stream releaseData, string fileName, string? channel)
|
||||
: base(releaseData, fileName, channel)
|
||||
{
|
||||
PackageId = packageId;
|
||||
Version = version;
|
||||
}
|
||||
}
|
||||
@@ -2,17 +2,9 @@
|
||||
|
||||
namespace Velopack.Packaging.Flow;
|
||||
|
||||
public class UploadOptions : VelopackServiceOptions
|
||||
public class UploadOptions(Stream releaseData, string fileName, string channel) : VelopackServiceOptions
|
||||
{
|
||||
public Stream ReleaseData { get; }
|
||||
public string FileName { get; }
|
||||
public string? Channel { get; }
|
||||
|
||||
public UploadOptions(Stream releaseData, string fileName, string? channel)
|
||||
{
|
||||
ReleaseData = releaseData;
|
||||
FileName = fileName;
|
||||
|
||||
Channel = channel;
|
||||
}
|
||||
public Stream ReleaseData { get; } = releaseData;
|
||||
public string FileName { get; } = fileName;
|
||||
public string Channel { get; } = channel;
|
||||
}
|
||||
|
||||
@@ -2,6 +2,9 @@
|
||||
using Microsoft.Identity.Client.Extensions.Msal;
|
||||
using NuGet.Versioning;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using System.Text;
|
||||
using System.IO;
|
||||
|
||||
|
||||
#if NET6_0_OR_GREATER
|
||||
using System.Net.Http.Json;
|
||||
@@ -86,7 +89,7 @@ public class VelopackFlowServiceClient(HttpClient HttpClient, ILogger Logger) :
|
||||
public async Task<Profile?> GetProfileAsync(VelopackServiceOptions? options, CancellationToken cancellationToken)
|
||||
{
|
||||
AssertAuthenticated();
|
||||
var endpoint = GetEndpoint("v1/user/profile", options);
|
||||
var endpoint = GetEndpoint("v1/user/profile", options?.VelopackBaseUrl);
|
||||
|
||||
return await HttpClient.GetFromJsonAsync<Profile>(endpoint, cancellationToken);
|
||||
}
|
||||
@@ -94,6 +97,8 @@ public class VelopackFlowServiceClient(HttpClient HttpClient, ILogger Logger) :
|
||||
public async Task UploadLatestReleaseAssetsAsync(string? channel, string releaseDirectory, string? serviceUrl,
|
||||
RuntimeOs os, CancellationToken cancellationToken)
|
||||
{
|
||||
AssertAuthenticated();
|
||||
|
||||
channel ??= ReleaseEntryHelper.GetDefaultChannel(os);
|
||||
ReleaseEntryHelper helper = new(releaseDirectory, channel, Logger, os);
|
||||
var latestAssets = helper.GetLatestAssets().ToList();
|
||||
@@ -120,73 +125,70 @@ public class VelopackFlowServiceClient(HttpClient HttpClient, ILogger Logger) :
|
||||
}
|
||||
}
|
||||
|
||||
Logger.LogInformation("Uploading {AssetCount} assets to Velopack ({ServiceUrl})", latestAssets.Count + installers.Count, serviceUrl);
|
||||
if (packageId is null) {
|
||||
Logger.LogError("No package ID found in release directory {ReleaseDirectory}", releaseDirectory);
|
||||
return;
|
||||
}
|
||||
if (version is null) {
|
||||
Logger.LogError("No version found in release directory {ReleaseDirectory}", releaseDirectory);
|
||||
return;
|
||||
}
|
||||
|
||||
Logger.LogInformation("Uploading {AssetCount} assets to Velopack Flow ({ServiceUrl})", latestAssets.Count + installers.Count, serviceUrl);
|
||||
|
||||
ReleaseGroup releaseGroup = await CreateReleaseGroupAsync(packageId, version, channel, serviceUrl, cancellationToken);
|
||||
|
||||
foreach (var assetFileName in files) {
|
||||
await UploadReleaseAssetAsync(releaseDirectory, assetFileName, serviceUrl, releaseGroup.Id, FileType.Release, cancellationToken).ConfigureAwait(false);
|
||||
|
||||
var latestPath = Path.Combine(releaseDirectory, assetFileName);
|
||||
|
||||
using var fileStream = File.OpenRead(latestPath);
|
||||
var options = new UploadOptions(fileStream, assetFileName, channel) {
|
||||
VelopackBaseUrl = serviceUrl
|
||||
};
|
||||
|
||||
await UploadReleaseAssetAsync(options, cancellationToken).ConfigureAwait(false);
|
||||
|
||||
Logger.LogInformation("Uploaded {FileName} to Velopack", assetFileName);
|
||||
Logger.LogInformation("Uploaded {FileName} to Velopack Flow", assetFileName);
|
||||
}
|
||||
|
||||
foreach (var installerFile in installers) {
|
||||
var latestPath = Path.Combine(releaseDirectory, installerFile);
|
||||
await UploadReleaseAssetAsync(releaseDirectory, installerFile, serviceUrl, releaseGroup.Id, FileType.Installer, cancellationToken).ConfigureAwait(false);
|
||||
|
||||
using var fileStream = File.OpenRead(latestPath);
|
||||
var options = new UploadInstallerOptions(packageId!, version!, fileStream, installerFile, channel) {
|
||||
VelopackBaseUrl = serviceUrl
|
||||
};
|
||||
|
||||
await UploadInstallerAssetAsync(options, cancellationToken).ConfigureAwait(false);
|
||||
|
||||
Logger.LogInformation("Uploaded {FileName} installer to Velopack", installerFile);
|
||||
Logger.LogInformation("Uploaded {FileName} installer to Velopack Flow", installerFile);
|
||||
}
|
||||
}
|
||||
|
||||
private async Task UploadReleaseAssetAsync(UploadOptions options, CancellationToken cancellationToken)
|
||||
private async Task<ReleaseGroup> CreateReleaseGroupAsync(
|
||||
string packageId, SemanticVersion version, string channel,
|
||||
string? velopackBaseUrl, CancellationToken cancellationToken)
|
||||
{
|
||||
AssertAuthenticated();
|
||||
|
||||
using var formData = new MultipartFormDataContent
|
||||
{
|
||||
{ new StringContent(options.Channel ?? ""), "Channel" }
|
||||
CreateReleaseGroupRequest request = new() {
|
||||
ChannelIdentifier = channel,
|
||||
PackageId = packageId,
|
||||
Version = version.ToNormalizedString()
|
||||
};
|
||||
|
||||
using var fileContent = new StreamContent(options.ReleaseData);
|
||||
formData.Add(fileContent, "File", options.FileName);
|
||||
|
||||
var endpoint = GetEndpoint("v1/upload-release", options);
|
||||
|
||||
var response = await HttpClient.PostAsync(endpoint, formData, cancellationToken);
|
||||
var endpoint = GetEndpoint("v1/releaseGroups/create", velopackBaseUrl);
|
||||
var response = await HttpClient.PostAsJsonAsync(endpoint, request, cancellationToken);
|
||||
|
||||
response.EnsureSuccessStatusCode();
|
||||
|
||||
return await response.Content.ReadFromJsonAsync<ReleaseGroup>(cancellationToken: cancellationToken)
|
||||
?? throw new InvalidOperationException($"Failed to create release group with version {version.ToNormalizedString()}");
|
||||
}
|
||||
|
||||
private async Task UploadInstallerAssetAsync(UploadInstallerOptions options, CancellationToken cancellationToken)
|
||||
private async Task UploadReleaseAssetAsync(string releaseDirectory, string fileName,
|
||||
string? serviceUrl, Guid releaseGroupId, FileType fileType, CancellationToken cancellationToken)
|
||||
{
|
||||
AssertAuthenticated();
|
||||
|
||||
using var formData = new MultipartFormDataContent
|
||||
{
|
||||
{ new StringContent(options.PackageId ?? ""), "PackageId" },
|
||||
{ new StringContent(options.Channel ?? ""), "Channel" },
|
||||
{ new StringContent(options.Version.ToNormalizedString() ?? ""), "Version" },
|
||||
{ new StringContent(releaseGroupId.ToString()), "ReleaseGroupId" },
|
||||
{ new StringContent(fileType.ToString()), "FileType" }
|
||||
};
|
||||
|
||||
using var fileContent = new StreamContent(options.ReleaseData);
|
||||
formData.Add(fileContent, "File", options.FileName);
|
||||
var latestPath = Path.Combine(releaseDirectory, fileName);
|
||||
|
||||
var endpoint = GetEndpoint("v1/upload-installer", options);
|
||||
using var fileStream = File.OpenRead(latestPath);
|
||||
|
||||
using var fileContent = new StreamContent(fileStream);
|
||||
formData.Add(fileContent, "File", fileName);
|
||||
|
||||
var endpoint = GetEndpoint("v1/releases/upload", serviceUrl);
|
||||
|
||||
var response = await HttpClient.PostAsync(endpoint, formData, cancellationToken);
|
||||
|
||||
response.EnsureSuccessStatusCode();
|
||||
}
|
||||
|
||||
@@ -211,8 +213,11 @@ public class VelopackFlowServiceClient(HttpClient HttpClient, ILogger Logger) :
|
||||
}
|
||||
|
||||
private static Uri GetEndpoint(string relativePath, VelopackServiceOptions? options)
|
||||
=> GetEndpoint(relativePath, options?.VelopackBaseUrl);
|
||||
|
||||
private static Uri GetEndpoint(string relativePath, string? velopackBaseUrl)
|
||||
{
|
||||
var baseUrl = options?.VelopackBaseUrl ?? VelopackServiceOptions.DefaultBaseUrl;
|
||||
var baseUrl = velopackBaseUrl ?? VelopackServiceOptions.DefaultBaseUrl;
|
||||
var endpoint = new Uri(relativePath, UriKind.Relative);
|
||||
return new(new Uri(baseUrl), endpoint);
|
||||
}
|
||||
@@ -311,4 +316,11 @@ public class VelopackFlowServiceClient(HttpClient HttpClient, ILogger Logger) :
|
||||
cacheHelper.RegisterCache(pca.UserTokenCache);
|
||||
return pca;
|
||||
}
|
||||
|
||||
private enum FileType
|
||||
{
|
||||
Unknown,
|
||||
Release,
|
||||
Installer,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
#nullable enable
|
||||
using System.Net.Http;
|
||||
using System.Text;
|
||||
|
||||
namespace Velopack.Packaging;
|
||||
|
||||
@@ -17,5 +18,23 @@ public static class HttpClientExtensions
|
||||
|
||||
return Newtonsoft.Json.JsonConvert.DeserializeObject<TValue>(await response.Content.ReadAsStringAsync());
|
||||
}
|
||||
|
||||
public static async Task<HttpResponseMessage> PostAsJsonAsync<TValue>(
|
||||
this HttpClient client,
|
||||
Uri? requestUri,
|
||||
TValue value,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
var content = new StringContent(Newtonsoft.Json.JsonConvert.SerializeObject(value), Encoding.UTF8, "application/json");
|
||||
return await client.PostAsync(requestUri, content, cancellationToken);
|
||||
}
|
||||
|
||||
public static async Task<TValue?> ReadFromJsonAsync<TValue>(
|
||||
this HttpContent content,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
var json = await content.ReadAsStringAsync();
|
||||
return Newtonsoft.Json.JsonConvert.DeserializeObject<TValue>(json);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using System.Text;
|
||||
using System.Text;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using NuGet.Versioning;
|
||||
using Velopack.Json;
|
||||
|
||||
@@ -196,7 +196,9 @@ public class Program
|
||||
{
|
||||
services.AddSingleton<IVelopackFlowServiceClient, VelopackFlowServiceClient>();
|
||||
services.AddSingleton<HmacAuthHttpClientHandler>();
|
||||
services.AddHttpClient().ConfigureHttpClientDefaults(x => x.AddHttpMessageHandler<HmacAuthHttpClientHandler>().ConfigureHttpClient(httpClient => httpClient.Timeout = TimeSpan.FromMinutes(10)));
|
||||
services.AddHttpClient().ConfigureHttpClientDefaults(x =>
|
||||
x.AddHttpMessageHandler<HmacAuthHttpClientHandler>()
|
||||
.ConfigureHttpClient(httpClient => httpClient.Timeout = TimeSpan.FromMinutes(60)));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using System;
|
||||
using System;
|
||||
using NuGet.Versioning;
|
||||
using Velopack.Sources;
|
||||
using System.Collections.Generic;
|
||||
|
||||
@@ -46,7 +46,7 @@ namespace Velopack.NuGet
|
||||
return ms.ToArray();
|
||||
}
|
||||
|
||||
private ZipArchiveEntry GetManifestEntry(ZipArchive zip)
|
||||
private static ZipArchiveEntry GetManifestEntry(ZipArchive zip)
|
||||
{
|
||||
var manifest = zip.Entries
|
||||
.FirstOrDefault(f => f.FullName.EndsWith(NugetUtil.ManifestExtension, StringComparison.OrdinalIgnoreCase));
|
||||
|
||||
@@ -9,7 +9,7 @@ using Velopack.Locators;
|
||||
namespace Velopack.Sources
|
||||
{
|
||||
/// <summary>
|
||||
/// Retrieves updates from the hosted Velopack service.
|
||||
/// Retrieves updates from the hosted Velopack service.
|
||||
/// </summary>
|
||||
public sealed class VelopackFlowUpdateSource : IUpdateSource
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user