mirror of
https://github.com/velopack/velopack.git
synced 2025-10-25 15:19:22 +00:00
Fix signature change with profile endpoint
Fixing up the publish command to work like the MSBuild task This corrects a lot of drift that had occurred between the CLI commands and the MSBuild task.
This commit is contained in:
@@ -1,6 +1,4 @@
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Net.Http;
|
||||
using System.Net.Http.Headers;
|
||||
using System.Threading;
|
||||
@@ -13,7 +11,6 @@ internal class HmacAuthHttpClientHandler : HttpClientHandler
|
||||
{
|
||||
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
|
||||
{
|
||||
Debugger.Launch();
|
||||
if (request.Headers.Authorization?.Scheme == HmacHelper.HmacScheme &&
|
||||
request.Headers.Authorization.Parameter is { } authParameter &&
|
||||
authParameter.Split(':') is var keyParts &&
|
||||
|
||||
@@ -1,12 +1,7 @@
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Net.Http;
|
||||
using System.Net.Http;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Build.Framework;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using NuGet.Versioning;
|
||||
using Velopack.Packaging;
|
||||
using Velopack.Packaging.Flow;
|
||||
|
||||
namespace Velopack.Build;
|
||||
@@ -22,14 +17,12 @@ public class PublishTask : MSBuildAsyncTask
|
||||
|
||||
public string? Channel { get; set; }
|
||||
|
||||
public string? Version { get; set; }
|
||||
|
||||
public string? ApiKey { get; set; }
|
||||
|
||||
protected override async Task<bool> ExecuteAsync()
|
||||
{
|
||||
//System.Diagnostics.Debugger.Launch();
|
||||
VelopackFlowServiceClient client = new(HttpClient, Logger);
|
||||
IVelopackFlowServiceClient client = new VelopackFlowServiceClient(HttpClient, Logger);
|
||||
if (!await client.LoginAsync(new() {
|
||||
AllowDeviceCodeFlow = false,
|
||||
AllowInteractiveLogin = false,
|
||||
@@ -40,61 +33,9 @@ public class PublishTask : MSBuildAsyncTask
|
||||
return true;
|
||||
}
|
||||
|
||||
Channel ??= ReleaseEntryHelper.GetDefaultChannel(VelopackRuntimeInfo.SystemOs);
|
||||
ReleaseEntryHelper helper = new(ReleaseDirectory, Channel, Logger);
|
||||
var latestAssets = helper.GetLatestAssets().ToList();
|
||||
await client.UploadLatestReleaseAssetsAsync(Channel, ReleaseDirectory, ServiceUrl)
|
||||
.ConfigureAwait(false);
|
||||
|
||||
List<string> installers = [];
|
||||
|
||||
List<string> files = latestAssets.Select(x => x.FileName).ToList();
|
||||
string? packageId = null;
|
||||
SemanticVersion? version = null;
|
||||
if (latestAssets.Count > 0) {
|
||||
packageId = latestAssets[0].PackageId;
|
||||
version = latestAssets[0].Version;
|
||||
|
||||
if (VelopackRuntimeInfo.IsWindows || VelopackRuntimeInfo.IsOSX) {
|
||||
var setupName = ReleaseEntryHelper.GetSuggestedSetupName(packageId, Channel);
|
||||
if (File.Exists(Path.Combine(ReleaseDirectory, setupName))) {
|
||||
installers.Add(setupName);
|
||||
}
|
||||
}
|
||||
|
||||
var portableName = ReleaseEntryHelper.GetSuggestedPortableName(packageId, Channel);
|
||||
if (File.Exists(Path.Combine(ReleaseDirectory, portableName))) {
|
||||
installers.Add(portableName);
|
||||
}
|
||||
}
|
||||
|
||||
Logger.LogInformation("Preparing to upload {AssetCount} assets to Velopack ({ServiceUrl})", latestAssets.Count + installers.Count, ServiceUrl);
|
||||
|
||||
foreach (var assetFileName in files) {
|
||||
|
||||
var latestPath = Path.Combine(ReleaseDirectory, assetFileName);
|
||||
|
||||
using var fileStream = File.OpenRead(latestPath);
|
||||
var options = new UploadOptions(fileStream, assetFileName, Channel) {
|
||||
VelopackBaseUrl = ServiceUrl
|
||||
};
|
||||
|
||||
await client.UploadReleaseAssetAsync(options).ConfigureAwait(false);
|
||||
|
||||
Logger.LogInformation("Uploaded {FileName} to Velopack", assetFileName);
|
||||
}
|
||||
|
||||
foreach (var installerFile in installers) {
|
||||
var latestPath = Path.Combine(ReleaseDirectory, installerFile);
|
||||
|
||||
using var fileStream = File.OpenRead(latestPath);
|
||||
var options = new UploadInstallerOptions(packageId!, version!, fileStream, installerFile, Channel) {
|
||||
VelopackBaseUrl = ServiceUrl
|
||||
};
|
||||
|
||||
await client.UploadInstallerAssetAsync(options).ConfigureAwait(false);
|
||||
|
||||
Logger.LogInformation("Uploaded {FileName} installer to Velopack", installerFile);
|
||||
}
|
||||
return true;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,62 +0,0 @@
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Velopack.NuGet;
|
||||
using Velopack.Packaging;
|
||||
using Velopack.Packaging.Flow;
|
||||
using Velopack.Sources;
|
||||
|
||||
namespace Velopack.Deployment;
|
||||
|
||||
|
||||
public class VelopackFlowDownloadOptions : RepositoryOptions
|
||||
{
|
||||
public string Version { get; set; }
|
||||
}
|
||||
|
||||
public class VelopackFlowUploadOptions : VelopackFlowDownloadOptions
|
||||
{
|
||||
}
|
||||
|
||||
public class VelopackFlowRepository : SourceRepository<VelopackFlowDownloadOptions, VelopackFlowUpdateSource>, IRepositoryCanUpload<VelopackFlowUploadOptions>
|
||||
{
|
||||
private static HttpClient Client { get; } = new HttpClient {
|
||||
BaseAddress = new Uri(VelopackServiceOptions.DefaultBaseUrl)
|
||||
};
|
||||
|
||||
public VelopackFlowRepository(ILogger logger)
|
||||
: base(logger)
|
||||
{ }
|
||||
|
||||
public override VelopackFlowUpdateSource CreateSource(VelopackFlowDownloadOptions options)
|
||||
{
|
||||
return new VelopackFlowUpdateSource();
|
||||
}
|
||||
|
||||
public async Task UploadMissingAssetsAsync(VelopackFlowUploadOptions options)
|
||||
{
|
||||
var helper = new ReleaseEntryHelper(options.ReleaseDir.FullName, options.Channel, Log);
|
||||
var latest = helper.GetLatestAssets().ToList();
|
||||
|
||||
Log.Info($"Preparing to upload {latest.Count} assets to Velopack");
|
||||
|
||||
foreach (var asset in latest) {
|
||||
|
||||
var latestPath = Path.Combine(options.ReleaseDir.FullName, asset.FileName);
|
||||
ZipPackage zipPackage = new(latestPath);
|
||||
var semVer = options.Version ?? asset.Version.ToString();
|
||||
|
||||
using var formData = new MultipartFormDataContent
|
||||
{
|
||||
{ new StringContent(options.Channel), "Channel" },
|
||||
};
|
||||
|
||||
using var fileStream = File.OpenRead(latestPath);
|
||||
using var fileContent = new StreamContent(fileStream);
|
||||
formData.Add(fileContent, "File", asset.FileName);
|
||||
|
||||
var response = await Client.PostAsync("api/v1/upload", formData);
|
||||
response.EnsureSuccessStatusCode();
|
||||
|
||||
Log.Info($" Uploaded {asset.FileName} to Velopack");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3,6 +3,12 @@
|
||||
#nullable enable
|
||||
public class Profile
|
||||
{
|
||||
public string? Name { get; set; }
|
||||
public string? Id { get; set; }
|
||||
public string? DisplayName { get; set; }
|
||||
public string? Email { get; set; }
|
||||
|
||||
public string? GetDisplayName()
|
||||
{
|
||||
return DisplayName ?? Email ?? "<unknown>";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
using Microsoft.Identity.Client;
|
||||
using Microsoft.Identity.Client.Extensions.Msal;
|
||||
|
||||
using Velopack.Packaging.Abstractions;
|
||||
|
||||
using NuGet.Versioning;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
#if NET6_0_OR_GREATER
|
||||
using System.Net.Http.Json;
|
||||
@@ -19,10 +18,11 @@ public interface IVelopackFlowServiceClient
|
||||
Task LogoutAsync(VelopackServiceOptions? options = null);
|
||||
|
||||
Task<Profile?> GetProfileAsync(VelopackServiceOptions? options = null);
|
||||
Task UploadReleaseAssetAsync(UploadOptions options);
|
||||
|
||||
Task UploadLatestReleaseAssetsAsync(string? channel, string releaseDirectory, string? serviceUrl);
|
||||
}
|
||||
|
||||
public class VelopackFlowServiceClient(HttpClient HttpClient, IConsole Console) : IVelopackFlowServiceClient
|
||||
public class VelopackFlowServiceClient(HttpClient HttpClient, ILogger Logger) : IVelopackFlowServiceClient
|
||||
{
|
||||
private static readonly string[] Scopes = ["openid", "offline_access"];
|
||||
|
||||
@@ -33,7 +33,7 @@ public class VelopackFlowServiceClient(HttpClient HttpClient, IConsole Console)
|
||||
public async Task<bool> LoginAsync(VelopackLoginOptions? options = null)
|
||||
{
|
||||
options ??= new VelopackLoginOptions();
|
||||
Console.WriteLine($"Preparing to login to Velopack ({options.VelopackBaseUrl})");
|
||||
Logger.LogInformation("Preparing to login to Velopack ({ServiceUrl})", options.VelopackBaseUrl);
|
||||
|
||||
var authConfiguration = await GetAuthConfigurationAsync(options);
|
||||
|
||||
@@ -42,7 +42,7 @@ public class VelopackFlowServiceClient(HttpClient HttpClient, IConsole Console)
|
||||
if (!string.IsNullOrWhiteSpace(options.ApiKey)) {
|
||||
HttpClient.DefaultRequestHeaders.Authorization = new(HmacHelper.HmacScheme, options.ApiKey);
|
||||
var profile = await GetProfileAsync(options);
|
||||
Console.WriteLine($"{profile?.Name} logged into Velopack with API key");
|
||||
Logger.LogInformation("{UserName} logged into Velopack with API key", profile?.GetDisplayName());
|
||||
return true;
|
||||
} else {
|
||||
AuthenticationResult? rv = null;
|
||||
@@ -60,10 +60,10 @@ public class VelopackFlowServiceClient(HttpClient HttpClient, IConsole Console)
|
||||
HttpClient.DefaultRequestHeaders.Authorization = new("Bearer", rv.IdToken ?? rv.AccessToken);
|
||||
var profile = await GetProfileAsync(options);
|
||||
|
||||
Console.WriteLine($"{profile?.Name} logged into Velopack");
|
||||
Logger.LogInformation("{UserName} logged into Velopack", profile?.GetDisplayName());
|
||||
return true;
|
||||
} else {
|
||||
Console.WriteLine("Failed to login to Velopack");
|
||||
Logger.LogError("Failed to login to Velopack");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -78,9 +78,9 @@ public class VelopackFlowServiceClient(HttpClient HttpClient, IConsole Console)
|
||||
// clear the cache
|
||||
while ((await pca.GetAccountsAsync()).FirstOrDefault() is { } account) {
|
||||
await pca.RemoveAsync(account);
|
||||
Console.WriteLine($"Logged out of {account.Username}");
|
||||
Logger.LogInformation("Logged out of {Username}", account.Username);
|
||||
}
|
||||
Console.WriteLine("Cleared saved login(s) for Velopack");
|
||||
Logger.LogInformation("Cleared saved login(s) for Velopack");
|
||||
}
|
||||
|
||||
public async Task<Profile?> GetProfileAsync(VelopackServiceOptions? options = null)
|
||||
@@ -91,7 +91,65 @@ public class VelopackFlowServiceClient(HttpClient HttpClient, IConsole Console)
|
||||
return await HttpClient.GetFromJsonAsync<Profile>(endpoint);
|
||||
}
|
||||
|
||||
public async Task UploadReleaseAssetAsync(UploadOptions options)
|
||||
public async Task UploadLatestReleaseAssetsAsync(string? channel, string releaseDirectory, string? serviceUrl)
|
||||
{
|
||||
channel ??= ReleaseEntryHelper.GetDefaultChannel(VelopackRuntimeInfo.SystemOs);
|
||||
ReleaseEntryHelper helper = new(releaseDirectory, channel, Logger);
|
||||
var latestAssets = helper.GetLatestAssets().ToList();
|
||||
|
||||
List<string> installers = [];
|
||||
|
||||
List<string> files = latestAssets.Select(x => x.FileName).ToList();
|
||||
string? packageId = null;
|
||||
SemanticVersion? version = null;
|
||||
if (latestAssets.Count > 0) {
|
||||
packageId = latestAssets[0].PackageId;
|
||||
version = latestAssets[0].Version;
|
||||
|
||||
if (VelopackRuntimeInfo.IsWindows || VelopackRuntimeInfo.IsOSX) {
|
||||
var setupName = ReleaseEntryHelper.GetSuggestedSetupName(packageId, channel);
|
||||
if (File.Exists(Path.Combine(releaseDirectory, setupName))) {
|
||||
installers.Add(setupName);
|
||||
}
|
||||
}
|
||||
|
||||
var portableName = ReleaseEntryHelper.GetSuggestedPortableName(packageId, channel);
|
||||
if (File.Exists(Path.Combine(releaseDirectory, portableName))) {
|
||||
installers.Add(portableName);
|
||||
}
|
||||
}
|
||||
|
||||
Logger.LogInformation("Preparing to upload {AssetCount} assets to Velopack ({ServiceUrl})", latestAssets.Count + installers.Count, serviceUrl);
|
||||
|
||||
foreach (var assetFileName in files) {
|
||||
|
||||
var latestPath = Path.Combine(releaseDirectory, assetFileName);
|
||||
|
||||
using var fileStream = File.OpenRead(latestPath);
|
||||
var options = new UploadOptions(fileStream, assetFileName, channel) {
|
||||
VelopackBaseUrl = serviceUrl
|
||||
};
|
||||
|
||||
await UploadReleaseAssetAsync(options).ConfigureAwait(false);
|
||||
|
||||
Logger.LogInformation("Uploaded {FileName} to Velopack", assetFileName);
|
||||
}
|
||||
|
||||
foreach (var installerFile in installers) {
|
||||
var latestPath = Path.Combine(releaseDirectory, installerFile);
|
||||
|
||||
using var fileStream = File.OpenRead(latestPath);
|
||||
var options = new UploadInstallerOptions(packageId!, version!, fileStream, installerFile, channel) {
|
||||
VelopackBaseUrl = serviceUrl
|
||||
};
|
||||
|
||||
await UploadInstallerAssetAsync(options).ConfigureAwait(false);
|
||||
|
||||
Logger.LogInformation("Uploaded {FileName} installer to Velopack", installerFile);
|
||||
}
|
||||
}
|
||||
|
||||
private async Task UploadReleaseAssetAsync(UploadOptions options)
|
||||
{
|
||||
AssertAuthenticated();
|
||||
|
||||
@@ -110,7 +168,7 @@ public class VelopackFlowServiceClient(HttpClient HttpClient, IConsole Console)
|
||||
response.EnsureSuccessStatusCode();
|
||||
}
|
||||
|
||||
public async Task UploadInstallerAssetAsync(UploadInstallerOptions options)
|
||||
private async Task UploadInstallerAssetAsync(UploadInstallerOptions options)
|
||||
{
|
||||
AssertAuthenticated();
|
||||
|
||||
@@ -207,11 +265,11 @@ public class VelopackFlowServiceClient(HttpClient HttpClient, IConsole Console)
|
||||
// * The timeout specified by the server for the lifetime of this code (typically ~15 minutes) has been reached
|
||||
// * The developing application calls the Cancel() method on a CancellationToken sent into the method.
|
||||
// If this occurs, an OperationCanceledException will be thrown (see catch below for more details).
|
||||
Console.WriteLine(deviceCodeResult.Message);
|
||||
Logger.LogInformation(deviceCodeResult.Message);
|
||||
return Task.FromResult(0);
|
||||
}).ExecuteAsync();
|
||||
|
||||
Console.WriteLine(result.Account.Username);
|
||||
Logger.LogInformation(result.Account.Username);
|
||||
return result;
|
||||
} catch (MsalException) {
|
||||
}
|
||||
|
||||
@@ -12,4 +12,4 @@ public class LoginCommandRunner(IVelopackFlowServiceClient Client) : ICommand<Lo
|
||||
VelopackBaseUrl = options.VelopackBaseUrl
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
21
src/Velopack.Vpk/Commands/Flow/PublishCommand.cs
Normal file
21
src/Velopack.Vpk/Commands/Flow/PublishCommand.cs
Normal file
@@ -0,0 +1,21 @@
|
||||
namespace Velopack.Vpk.Commands.Flow;
|
||||
|
||||
#nullable enable
|
||||
public class PublishCommand : VelopackServiceCommand
|
||||
{
|
||||
public string ReleaseDirectory { get; set; } = "";
|
||||
|
||||
public string? Channel { get; set; }
|
||||
|
||||
|
||||
public PublishCommand()
|
||||
: base("publish", "Uploads a release to Velopack's hosted service")
|
||||
{
|
||||
AddOption<string>(v => ReleaseDirectory = v, "--releaseDir")
|
||||
.SetDescription("The directory containing the Velopack release files.")
|
||||
.SetRequired();
|
||||
|
||||
AddOption<string>(v => Channel = v, "-c", "--channel")
|
||||
.SetDescription("The channel for the release");
|
||||
}
|
||||
}
|
||||
22
src/Velopack.Vpk/Commands/Flow/PublishCommandRunner.cs
Normal file
22
src/Velopack.Vpk/Commands/Flow/PublishCommandRunner.cs
Normal file
@@ -0,0 +1,22 @@
|
||||
using Velopack.Packaging.Abstractions;
|
||||
using Velopack.Packaging.Flow;
|
||||
|
||||
namespace Velopack.Vpk.Commands.Flow;
|
||||
|
||||
public class PublishCommandRunner(IVelopackFlowServiceClient Client) : ICommand<PublishOptions>
|
||||
{
|
||||
public async Task Run(PublishOptions options)
|
||||
{
|
||||
if (!await Client.LoginAsync(new VelopackLoginOptions() {
|
||||
AllowCacheCredentials = true,
|
||||
AllowDeviceCodeFlow = false,
|
||||
AllowInteractiveLogin = false,
|
||||
ApiKey = options.ApiKey,
|
||||
VelopackBaseUrl = options.VelopackBaseUrl
|
||||
})) {
|
||||
return;
|
||||
}
|
||||
|
||||
await Client.UploadLatestReleaseAssetsAsync(options.Channel, options.ReleaseDirectory, options.VelopackBaseUrl);
|
||||
}
|
||||
}
|
||||
11
src/Velopack.Vpk/Commands/Flow/PublishOptions.cs
Normal file
11
src/Velopack.Vpk/Commands/Flow/PublishOptions.cs
Normal file
@@ -0,0 +1,11 @@
|
||||
using Velopack.Packaging.Flow;
|
||||
|
||||
namespace Velopack.Vpk.Commands.Flow;
|
||||
|
||||
#nullable enable
|
||||
public sealed class PublishOptions : VelopackServiceOptions
|
||||
{
|
||||
public string ReleaseDirectory { get; set; } = "";
|
||||
|
||||
public string? Channel { get; set; }
|
||||
}
|
||||
@@ -1,21 +0,0 @@
|
||||
namespace Velopack.Vpk.Commands.Flow;
|
||||
#nullable enable
|
||||
|
||||
public abstract class VelopackBaseCommand : OutputCommand
|
||||
{
|
||||
public string? TeamName { get; private set; }
|
||||
|
||||
public string? ProjectName { get; private set; }
|
||||
|
||||
protected VelopackBaseCommand(string name, string description)
|
||||
: base(name, description)
|
||||
{
|
||||
AddOption<string>((v) => TeamName = v, "--team-name", "-t")
|
||||
.SetDescription("The name of the team")
|
||||
.SetRequired();
|
||||
|
||||
AddOption<string>((v) => ProjectName = v, "--project-name", "-p")
|
||||
.SetDescription("The name of the project")
|
||||
.SetRequired();
|
||||
}
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
namespace Velopack.Vpk.Commands.Flow;
|
||||
#nullable enable
|
||||
|
||||
public class VelopackPublishCommand : VelopackBaseCommand
|
||||
{
|
||||
public string? Version { get; set; }
|
||||
|
||||
public VelopackPublishCommand()
|
||||
: base("publish", "Uploads a release to Velopack's hosted service")
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
@@ -33,7 +33,7 @@ public static partial class OptionMapper
|
||||
public static partial DeltaPatchOptions ToOptions(this DeltaPatchCommand cmd);
|
||||
public static partial LoginOptions ToOptions(this LoginCommand cmd);
|
||||
public static partial LogoutOptions ToOptions(this LogoutCommand cmd);
|
||||
public static partial VelopackFlowUploadOptions ToOptions(this VelopackPublishCommand cmd);
|
||||
public static partial PublishOptions ToOptions(this PublishCommand cmd);
|
||||
|
||||
private static DirectoryInfo StringToDirectoryInfo(string t)
|
||||
{
|
||||
|
||||
@@ -103,14 +103,14 @@ public class Program
|
||||
deltaCommand.AddCommand<DeltaPatchCommand, DeltaPatchCommandRunner, DeltaPatchOptions>(provider);
|
||||
rootCommand.Add(deltaCommand);
|
||||
|
||||
#if DEBUG
|
||||
rootCommand.AddCommand<LoginCommand, LoginCommandRunner, LoginOptions>(provider);
|
||||
rootCommand.AddCommand<LogoutCommand, LogoutCommandRunner, LogoutOptions>(provider);
|
||||
rootCommand.AddRepositoryUpload<VelopackPublishCommand, VelopackFlowRepository, VelopackFlowUploadOptions>(provider);
|
||||
#endif
|
||||
HideCommand(rootCommand.AddCommand<LoginCommand, LoginCommandRunner, LoginOptions>(provider));
|
||||
HideCommand(rootCommand.AddCommand<LogoutCommand, LogoutCommandRunner, LogoutOptions>(provider));
|
||||
HideCommand(rootCommand.AddCommand<PublishCommand, PublishCommandRunner, PublishOptions>(provider));
|
||||
|
||||
var cli = new CliConfiguration(rootCommand);
|
||||
return await cli.InvokeAsync(args);
|
||||
|
||||
static void HideCommand(CliCommand command) => command.Hidden = true;
|
||||
}
|
||||
|
||||
private static void SetupConfig(IHostApplicationBuilder builder)
|
||||
|
||||
Reference in New Issue
Block a user