mirror of
https://github.com/velopack/velopack.git
synced 2025-10-24 15:19:23 +00:00
Refactor projects & add NSwag codegen
This commit is contained in:
12
Velopack.sln
12
Velopack.sln
@@ -55,6 +55,10 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Velopack.Build", "src\vpk\V
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Velopack.IcoLib", "src\vpk\Velopack.IcoLib\Velopack.IcoLib.csproj", "{8A0A980A-D51C-458E-8942-00BC900FD2D0}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Velopack.Flow", "src\vpk\Velopack.Flow\Velopack.Flow.csproj", "{DC836A20-E770-451A-B26E-4CC74633F741}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Velopack.Core", "src\vpk\Velopack.Core\Velopack.Core.csproj", "{2A60F8BE-EAD4-4229-A04E-52A37C8F1D0E}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
@@ -121,6 +125,14 @@ Global
|
||||
{8A0A980A-D51C-458E-8942-00BC900FD2D0}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{8A0A980A-D51C-458E-8942-00BC900FD2D0}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{8A0A980A-D51C-458E-8942-00BC900FD2D0}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{DC836A20-E770-451A-B26E-4CC74633F741}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{DC836A20-E770-451A-B26E-4CC74633F741}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{DC836A20-E770-451A-B26E-4CC74633F741}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{DC836A20-E770-451A-B26E-4CC74633F741}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{2A60F8BE-EAD4-4229-A04E-52A37C8F1D0E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{2A60F8BE-EAD4-4229-A04E-52A37C8F1D0E}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{2A60F8BE-EAD4-4229-A04E-52A37C8F1D0E}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{2A60F8BE-EAD4-4229-A04E-52A37C8F1D0E}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
|
||||
@@ -6,6 +6,8 @@ using System.Runtime.InteropServices;
|
||||
[assembly: InternalsVisibleTo("Velopack.Packaging.Tests, PublicKey=" + SNK.SHA1)]
|
||||
[assembly: InternalsVisibleTo("Velopack.CommandLine.Tests, PublicKey=" + SNK.SHA1)]
|
||||
[assembly: InternalsVisibleTo("Velopack, PublicKey=" + SNK.SHA1)]
|
||||
[assembly: InternalsVisibleTo("Velopack.Core, PublicKey=" + SNK.SHA1)]
|
||||
[assembly: InternalsVisibleTo("Velopack.Flow, PublicKey=" + SNK.SHA1)]
|
||||
[assembly: InternalsVisibleTo("Velopack.Deployment, PublicKey=" + SNK.SHA1)]
|
||||
[assembly: InternalsVisibleTo("Velopack.Packaging, PublicKey=" + SNK.SHA1)]
|
||||
[assembly: InternalsVisibleTo("Velopack.Packaging.Windows, PublicKey=" + SNK.SHA1)]
|
||||
|
||||
@@ -4,7 +4,7 @@ using System.Threading.Tasks;
|
||||
using Microsoft.Build.Framework;
|
||||
using Microsoft.Build.Utilities;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Velopack.Packaging.Abstractions;
|
||||
using Velopack.Core.Abstractions;
|
||||
using ILogger = Microsoft.Extensions.Logging.ILogger;
|
||||
using Task = System.Threading.Tasks.Task;
|
||||
|
||||
|
||||
@@ -1,53 +1,54 @@
|
||||
using System;
|
||||
using System.Net.Http;
|
||||
using System.Threading;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Build.Framework;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Velopack.Packaging.Flow;
|
||||
using Velopack.Flow;
|
||||
|
||||
namespace Velopack.Build;
|
||||
|
||||
public class PublishTask : MSBuildAsyncTask
|
||||
{
|
||||
private static HttpClient HttpClient { get; } = new(new HmacAuthHttpClientHandler {
|
||||
InnerHandler = new HttpClientHandler()
|
||||
}) {
|
||||
Timeout = TimeSpan.FromMinutes(60)
|
||||
};
|
||||
|
||||
[Required]
|
||||
public string ReleaseDirectory { get; set; } = "";
|
||||
|
||||
public string ServiceUrl { get; set; } = VelopackServiceOptions.DefaultBaseUrl;
|
||||
public string? ServiceUrl { get; set; }
|
||||
|
||||
public string? Channel { get; set; }
|
||||
|
||||
public string? ApiKey { get; set; }
|
||||
|
||||
public bool NoWaitForLive { get; set; }
|
||||
|
||||
public double Timeout { get; set; } = 30d;
|
||||
|
||||
public bool WaitForLive { get; set; }
|
||||
|
||||
protected override async Task<bool> ExecuteAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
// //System.Diagnostics.Debugger.Launch();
|
||||
// IVelopackFlowServiceClient client = new VelopackFlowServiceClient(HttpClient, Logger, Logger);
|
||||
// if (!await client.LoginAsync(new() {
|
||||
// AllowDeviceCodeFlow = false,
|
||||
// AllowInteractiveLogin = false,
|
||||
// VelopackBaseUrl = ServiceUrl,
|
||||
// ApiKey = ApiKey
|
||||
// }, false, cancellationToken).ConfigureAwait(false)) {
|
||||
// Logger.LogWarning("Not logged into Velopack Flow service, skipping publish. Please run vpk login.");
|
||||
// return true;
|
||||
// }
|
||||
//
|
||||
// // todo: currently it's not possible to cross-compile for different OSes using Velopack.Build
|
||||
// var targetOs = VelopackRuntimeInfo.SystemOs;
|
||||
//
|
||||
// await client.UploadLatestReleaseAssetsAsync(Channel, ReleaseDirectory, ServiceUrl, targetOs, NoWaitForLive, cancellationToken)
|
||||
// .ConfigureAwait(false);
|
||||
//
|
||||
// return true;
|
||||
//System.Diagnostics.Debugger.Launch();
|
||||
var options = new VelopackFlowServiceOptions {
|
||||
VelopackBaseUrl = ServiceUrl,
|
||||
ApiKey = ApiKey,
|
||||
Timeout = Timeout,
|
||||
};
|
||||
|
||||
var loginOptions = new VelopackFlowLoginOptions() {
|
||||
AllowCacheCredentials = true,
|
||||
AllowDeviceCodeFlow = false,
|
||||
AllowInteractiveLogin = false,
|
||||
};
|
||||
|
||||
var client = new VelopackFlowServiceClient(options, Logger, Logger);
|
||||
CancellationToken token = CancellationToken.None;
|
||||
if (!await client.LoginAsync(loginOptions, false, token)) {
|
||||
Logger.LogWarning("Not logged into Velopack Flow service, skipping publish. Please run vpk login.");
|
||||
return true;
|
||||
}
|
||||
|
||||
// todo: currently it's not possible to cross-compile for different OSes using Velopack.Build
|
||||
var targetOs = VelopackRuntimeInfo.SystemOs;
|
||||
|
||||
await client.UploadLatestReleaseAssetsAsync(Channel, ReleaseDirectory, targetOs, WaitForLive, cancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using Riok.Mapperly.Abstractions;
|
||||
using Velopack.Core;
|
||||
using Velopack.Packaging;
|
||||
using Velopack.Packaging.Unix.Commands;
|
||||
using Velopack.Packaging.Windows.Commands;
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Velopack.Flow\Velopack.Flow.csproj" />
|
||||
<ProjectReference Include="..\Velopack.Packaging.Unix\Velopack.Packaging.Unix.csproj" />
|
||||
<ProjectReference Include="..\Velopack.Packaging.Windows\Velopack.Packaging.Windows.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
namespace Velopack.Packaging.Abstractions;
|
||||
namespace Velopack.Core.Abstractions;
|
||||
|
||||
public interface ICommand<TOpt> where TOpt : class
|
||||
{
|
||||
@@ -1,4 +1,5 @@
|
||||
namespace Velopack.Packaging.Abstractions;
|
||||
namespace Velopack.Core.Abstractions;
|
||||
|
||||
public interface IConsole
|
||||
{
|
||||
void WriteLine(string message = "");
|
||||
@@ -1,4 +1,4 @@
|
||||
namespace Velopack.Packaging.Abstractions;
|
||||
namespace Velopack.Core.Abstractions;
|
||||
|
||||
public interface IFancyConsole : IConsole
|
||||
{
|
||||
@@ -1,4 +1,4 @@
|
||||
namespace Velopack.Packaging.Abstractions;
|
||||
namespace Velopack.Core.Abstractions;
|
||||
|
||||
public interface IFancyConsoleProgress
|
||||
{
|
||||
@@ -1,8 +1,9 @@
|
||||
using Newtonsoft.Json;
|
||||
using Velopack.Packaging.Exceptions;
|
||||
using Velopack.Util;
|
||||
|
||||
namespace Velopack.Packaging;
|
||||
#nullable disable
|
||||
|
||||
namespace Velopack.Core;
|
||||
|
||||
public class BuildAssets
|
||||
{
|
||||
@@ -14,7 +15,7 @@ public class BuildAssets
|
||||
public List<VelopackAsset> GetReleaseEntries()
|
||||
{
|
||||
return GetFilePaths().Where(x => x.EndsWith(".nupkg", StringComparison.OrdinalIgnoreCase))
|
||||
.Select(f => VelopackAsset.FromNupkg(f))
|
||||
.Select(VelopackAsset.FromNupkg)
|
||||
.ToList();
|
||||
}
|
||||
|
||||
55
src/vpk/Velopack.Core/DefaultName.cs
Normal file
55
src/vpk/Velopack.Core/DefaultName.cs
Normal file
@@ -0,0 +1,55 @@
|
||||
using NuGet.Versioning;
|
||||
|
||||
namespace Velopack.Core;
|
||||
|
||||
public static class DefaultName
|
||||
{
|
||||
public static string GetSuggestedReleaseName(string id, string version, string channel, bool delta, RuntimeOs os)
|
||||
{
|
||||
var suffix = GetUniqueAssetSuffix(channel);
|
||||
version = SemanticVersion.Parse(version).ToNormalizedString();
|
||||
if (os == RuntimeOs.Windows && channel == GetDefaultChannel(RuntimeOs.Windows)) {
|
||||
return $"{id}-{version}{(delta ? "-delta" : "-full")}.nupkg";
|
||||
}
|
||||
|
||||
return $"{id}-{version}{suffix}{(delta ? "-delta" : "-full")}.nupkg";
|
||||
}
|
||||
|
||||
public static string GetSuggestedPortableName(string id, string channel, RuntimeOs os)
|
||||
{
|
||||
var suffix = GetUniqueAssetSuffix(channel);
|
||||
if (os == RuntimeOs.Linux) {
|
||||
if (channel == GetDefaultChannel(RuntimeOs.Linux)) {
|
||||
return $"{id}.AppImage";
|
||||
} else {
|
||||
return $"{id}{suffix}.AppImage";
|
||||
}
|
||||
} else {
|
||||
return $"{id}{suffix}-Portable.zip";
|
||||
}
|
||||
}
|
||||
|
||||
public static string GetSuggestedSetupName(string id, string channel, RuntimeOs os)
|
||||
{
|
||||
var suffix = GetUniqueAssetSuffix(channel);
|
||||
if (os == RuntimeOs.Windows)
|
||||
return $"{id}{suffix}-Setup.exe";
|
||||
else if (os == RuntimeOs.OSX)
|
||||
return $"{id}{suffix}-Setup.pkg";
|
||||
else
|
||||
throw new PlatformNotSupportedException("Platform not supported.");
|
||||
}
|
||||
|
||||
private static string GetUniqueAssetSuffix(string channel)
|
||||
{
|
||||
return "-" + channel;
|
||||
}
|
||||
|
||||
public static string GetDefaultChannel(RuntimeOs os)
|
||||
{
|
||||
if (os == RuntimeOs.Windows) return "win";
|
||||
if (os == RuntimeOs.OSX) return "osx";
|
||||
if (os == RuntimeOs.Linux) return "linux";
|
||||
throw new NotSupportedException("Unsupported OS: " + os);
|
||||
}
|
||||
}
|
||||
@@ -4,7 +4,7 @@
|
||||
using System.Net.Http;
|
||||
using System.Text;
|
||||
|
||||
namespace Velopack.Packaging;
|
||||
namespace Velopack.Core;
|
||||
|
||||
public static class HttpClientExtensions
|
||||
{
|
||||
@@ -16,7 +16,7 @@ public static class HttpClientExtensions
|
||||
var response = await client.GetAsync(requestUri, cancellationToken);
|
||||
response.EnsureSuccessStatusCode();
|
||||
|
||||
return Newtonsoft.Json.JsonConvert.DeserializeObject<TValue>(await response.Content.ReadAsStringAsync());
|
||||
return SimpleJson.DeserializeObject<TValue>(await response.Content.ReadAsStringAsync());
|
||||
}
|
||||
|
||||
public static async Task<HttpResponseMessage> PostAsJsonAsync<TValue>(
|
||||
@@ -25,7 +25,7 @@ public static class HttpClientExtensions
|
||||
TValue value,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
var content = new StringContent(Newtonsoft.Json.JsonConvert.SerializeObject(value), Encoding.UTF8, "application/json");
|
||||
var content = new StringContent(SimpleJson.SerializeObject(value), Encoding.UTF8, "application/json");
|
||||
return await client.PostAsync(requestUri, content, cancellationToken);
|
||||
}
|
||||
|
||||
@@ -35,7 +35,7 @@ public static class HttpClientExtensions
|
||||
TValue value,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
var content = new StringContent(Newtonsoft.Json.JsonConvert.SerializeObject(value), Encoding.UTF8, "application/json");
|
||||
var content = new StringContent(SimpleJson.SerializeObject(value), Encoding.UTF8, "application/json");
|
||||
return await client.PutAsync(requestUri, content, cancellationToken);
|
||||
}
|
||||
|
||||
@@ -44,7 +44,7 @@ public static class HttpClientExtensions
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
var json = await content.ReadAsStringAsync();
|
||||
return Newtonsoft.Json.JsonConvert.DeserializeObject<TValue>(json);
|
||||
return SimpleJson.DeserializeObject<TValue>(json);
|
||||
}
|
||||
|
||||
public static async Task<string> ReadAsStringAsync(this HttpContent content, CancellationToken _)
|
||||
@@ -2,7 +2,7 @@
|
||||
using Newtonsoft.Json.Converters;
|
||||
using NuGet.Versioning;
|
||||
|
||||
namespace Velopack.Packaging;
|
||||
namespace Velopack.Core;
|
||||
|
||||
public class SimpleJson
|
||||
{
|
||||
@@ -11,7 +11,7 @@ public class SimpleJson
|
||||
NullValueHandling = NullValueHandling.Ignore,
|
||||
};
|
||||
|
||||
public static T DeserializeObject<T>(string json)
|
||||
public static T? DeserializeObject<T>(string json)
|
||||
{
|
||||
return JsonConvert.DeserializeObject<T>(json, Options);
|
||||
}
|
||||
@@ -23,15 +23,15 @@ public class SimpleJson
|
||||
|
||||
private class SemanticVersionConverter : JsonConverter<SemanticVersion>
|
||||
{
|
||||
public override SemanticVersion ReadJson(JsonReader reader, Type objectType, SemanticVersion existingValue, bool hasExistingValue,
|
||||
public override SemanticVersion? ReadJson(JsonReader reader, Type objectType, SemanticVersion? existingValue, bool hasExistingValue,
|
||||
JsonSerializer serializer)
|
||||
{
|
||||
string s = reader.Value as string;
|
||||
string? s = reader.Value as string;
|
||||
if (s == null) return null;
|
||||
return SemanticVersion.Parse(s);
|
||||
}
|
||||
|
||||
public override void WriteJson(JsonWriter writer, SemanticVersion value, JsonSerializer serializer)
|
||||
public override void WriteJson(JsonWriter writer, SemanticVersion? value, JsonSerializer serializer)
|
||||
{
|
||||
if (value != null) {
|
||||
writer.WriteValue(value.ToFullString());
|
||||
@@ -1,7 +1,7 @@
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Runtime.Serialization;
|
||||
|
||||
namespace Velopack.Packaging.Exceptions;
|
||||
namespace Velopack.Core;
|
||||
|
||||
/// <summary>
|
||||
/// Denotes that an error has occurred for which a stack trace should not be printed.
|
||||
20
src/vpk/Velopack.Core/Velopack.Core.csproj
Normal file
20
src/vpk/Velopack.Core/Velopack.Core.csproj
Normal file
@@ -0,0 +1,20 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFrameworks>net472;net6.0</TargetFrameworks>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<NoWarn>$(NoWarn);CA2007;CS8002</NoWarn>
|
||||
<Nullable>enable</Nullable>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\lib-csharp\Velopack.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
|
||||
<PackageReference Include="System.Net.Http" Version="4.3.4" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -1,6 +1,7 @@
|
||||
using System.Text;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Octokit;
|
||||
using Velopack.Core;
|
||||
using Velopack.NuGet;
|
||||
using Velopack.Packaging;
|
||||
using Velopack.Packaging.Exceptions;
|
||||
@@ -131,7 +132,7 @@ public class GitHubRepository(ILogger logger) : SourceRepository<GitHubDownloadO
|
||||
},
|
||||
"Uploading " + releasesFileName);
|
||||
|
||||
if (options.Channel == ReleaseEntryHelper.GetDefaultChannel(RuntimeOs.Windows)) {
|
||||
if (options.Channel == DefaultName.GetDefaultChannel(RuntimeOs.Windows)) {
|
||||
var legacyReleasesContent = ReleaseEntryHelper.GetLegacyMigrationReleaseFeedString(feed);
|
||||
var legacyReleasesBytes = Encoding.UTF8.GetBytes(legacyReleasesContent);
|
||||
await RetryAsync(
|
||||
|
||||
@@ -4,9 +4,9 @@ using Gitea.Net.Api;
|
||||
using Gitea.Net.Client;
|
||||
using Gitea.Net.Model;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Velopack.Core;
|
||||
using Velopack.NuGet;
|
||||
using Velopack.Packaging;
|
||||
using Velopack.Packaging.Exceptions;
|
||||
using Velopack.Sources;
|
||||
using Velopack.Util;
|
||||
|
||||
@@ -155,7 +155,7 @@ public class GiteaRepository : SourceRepository<GiteaDownloadOptions, GiteaSourc
|
||||
await apiInstance.RepoCreateReleaseAttachmentAsync(repoOwner, repoName, release.Id, releasesFileName, new MemoryStream(Encoding.UTF8.GetBytes(json)));
|
||||
}, "Uploading " + releasesFileName);
|
||||
|
||||
if (options.Channel == ReleaseEntryHelper.GetDefaultChannel(RuntimeOs.Windows)) {
|
||||
if (options.Channel == DefaultName.GetDefaultChannel(RuntimeOs.Windows)) {
|
||||
var legacyReleasesContent = ReleaseEntryHelper.GetLegacyMigrationReleaseFeedString(feed);
|
||||
var legacyReleasesBytes = Encoding.UTF8.GetBytes(legacyReleasesContent);
|
||||
await RetryAsync(async () => {
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Velopack.Core\Velopack.Core.csproj" />
|
||||
<ProjectReference Include="..\Velopack.Packaging\Velopack.Packaging.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using System.Text;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Velopack.Core;
|
||||
using Velopack.Packaging;
|
||||
using Velopack.Util;
|
||||
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
using System.Security.Cryptography;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Velopack.Packaging;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Velopack.Core;
|
||||
using Velopack.Packaging.Abstractions;
|
||||
using Velopack.Sources;
|
||||
using Velopack.Util;
|
||||
@@ -14,7 +13,7 @@ public class RepositoryOptions : IOutputOptions
|
||||
public RuntimeOs TargetOs { get; set; }
|
||||
|
||||
public string Channel {
|
||||
get => _channel ?? ReleaseEntryHelper.GetDefaultChannel(TargetOs);
|
||||
get => _channel ?? DefaultName.GetDefaultChannel(TargetOs);
|
||||
set => _channel = value;
|
||||
}
|
||||
|
||||
|
||||
25
src/vpk/Velopack.Flow/Commands/ApiCommandRunner.cs
Normal file
25
src/vpk/Velopack.Flow/Commands/ApiCommandRunner.cs
Normal file
@@ -0,0 +1,25 @@
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Velopack.Core.Abstractions;
|
||||
|
||||
namespace Velopack.Flow.Commands;
|
||||
|
||||
public class ApiCommandRunner(ILogger logger, IFancyConsole console) : ICommand<ApiOptions>
|
||||
{
|
||||
public async Task Run(ApiOptions options)
|
||||
{
|
||||
var loginOptions = new VelopackFlowLoginOptions() {
|
||||
AllowCacheCredentials = true,
|
||||
AllowDeviceCodeFlow = false,
|
||||
AllowInteractiveLogin = false,
|
||||
};
|
||||
|
||||
var client = new VelopackFlowServiceClient(options, logger, console);
|
||||
CancellationToken token = CancellationToken.None;
|
||||
if (!await client.LoginAsync(loginOptions, false, token)) {
|
||||
return;
|
||||
}
|
||||
|
||||
string response = await client.InvokeEndpointAsync(options, options.Endpoint, options.Method, options.Body, token);
|
||||
Console.WriteLine(response);
|
||||
}
|
||||
}
|
||||
8
src/vpk/Velopack.Flow/Commands/ApiOptions.cs
Normal file
8
src/vpk/Velopack.Flow/Commands/ApiOptions.cs
Normal file
@@ -0,0 +1,8 @@
|
||||
namespace Velopack.Flow.Commands;
|
||||
|
||||
public sealed class ApiOptions : VelopackFlowServiceOptions
|
||||
{
|
||||
public string Endpoint { get; set; } = "";
|
||||
public string Method { get; set; } = "";
|
||||
public string? Body { get; set; }
|
||||
}
|
||||
13
src/vpk/Velopack.Flow/Commands/LoginCommandRunner.cs
Normal file
13
src/vpk/Velopack.Flow/Commands/LoginCommandRunner.cs
Normal file
@@ -0,0 +1,13 @@
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Velopack.Core.Abstractions;
|
||||
|
||||
namespace Velopack.Flow.Commands;
|
||||
|
||||
public class LoginCommandRunner(ILogger logger, IFancyConsole console) : ICommand<LoginOptions>
|
||||
{
|
||||
public async Task Run(LoginOptions options)
|
||||
{
|
||||
var client = new VelopackFlowServiceClient(options, logger, console);
|
||||
await client.LoginAsync(null, false, CancellationToken.None);
|
||||
}
|
||||
}
|
||||
3
src/vpk/Velopack.Flow/Commands/LoginOptions.cs
Normal file
3
src/vpk/Velopack.Flow/Commands/LoginOptions.cs
Normal file
@@ -0,0 +1,3 @@
|
||||
namespace Velopack.Flow.Commands;
|
||||
|
||||
public sealed class LoginOptions : VelopackFlowServiceOptions;
|
||||
13
src/vpk/Velopack.Flow/Commands/LogoutCommandRunner.cs
Normal file
13
src/vpk/Velopack.Flow/Commands/LogoutCommandRunner.cs
Normal file
@@ -0,0 +1,13 @@
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Velopack.Core.Abstractions;
|
||||
|
||||
namespace Velopack.Flow.Commands;
|
||||
|
||||
public class LogoutCommandRunner(ILogger logger, IFancyConsole console) : ICommand<LogoutOptions>
|
||||
{
|
||||
public async Task Run(LogoutOptions options)
|
||||
{
|
||||
var client = new VelopackFlowServiceClient(options, logger, console);
|
||||
await client.LogoutAsync(CancellationToken.None);
|
||||
}
|
||||
}
|
||||
3
src/vpk/Velopack.Flow/Commands/LogoutOptions.cs
Normal file
3
src/vpk/Velopack.Flow/Commands/LogoutOptions.cs
Normal file
@@ -0,0 +1,3 @@
|
||||
namespace Velopack.Flow.Commands;
|
||||
|
||||
public sealed class LogoutOptions : VelopackFlowServiceOptions;
|
||||
29
src/vpk/Velopack.Flow/Commands/PublishCommandRunner.cs
Normal file
29
src/vpk/Velopack.Flow/Commands/PublishCommandRunner.cs
Normal file
@@ -0,0 +1,29 @@
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Velopack.Core.Abstractions;
|
||||
|
||||
namespace Velopack.Flow.Commands;
|
||||
|
||||
public class PublishCommandRunner(ILogger logger, IFancyConsole console) : ICommand<PublishOptions>
|
||||
{
|
||||
public async Task Run(PublishOptions options)
|
||||
{
|
||||
var loginOptions = new VelopackFlowLoginOptions() {
|
||||
AllowCacheCredentials = true,
|
||||
AllowDeviceCodeFlow = false,
|
||||
AllowInteractiveLogin = false,
|
||||
};
|
||||
|
||||
var client = new VelopackFlowServiceClient(options, logger, console);
|
||||
CancellationToken token = CancellationToken.None;
|
||||
if (!await client.LoginAsync(loginOptions, false, token)) {
|
||||
return;
|
||||
}
|
||||
|
||||
await client.UploadLatestReleaseAssetsAsync(
|
||||
options.Channel,
|
||||
options.ReleaseDirectory,
|
||||
options.TargetOs,
|
||||
options.WaitForLive,
|
||||
token);
|
||||
}
|
||||
}
|
||||
12
src/vpk/Velopack.Flow/Commands/PublishOptions.cs
Normal file
12
src/vpk/Velopack.Flow/Commands/PublishOptions.cs
Normal file
@@ -0,0 +1,12 @@
|
||||
namespace Velopack.Flow.Commands;
|
||||
|
||||
public sealed class PublishOptions : VelopackFlowServiceOptions
|
||||
{
|
||||
public RuntimeOs TargetOs { get; set; }
|
||||
|
||||
public string ReleaseDirectory { get; set; } = "";
|
||||
|
||||
public string? Channel { get; set; }
|
||||
|
||||
public bool WaitForLive { get; set; }
|
||||
}
|
||||
5486
src/vpk/Velopack.Flow/FlowApi.cs
Normal file
5486
src/vpk/Velopack.Flow/FlowApi.cs
Normal file
File diff suppressed because it is too large
Load Diff
2
src/vpk/Velopack.Flow/FlowApi.gen.bat
Normal file
2
src/vpk/Velopack.Flow/FlowApi.gen.bat
Normal file
@@ -0,0 +1,2 @@
|
||||
dotnet tool update --global NSwag.ConsoleCore
|
||||
nswag openapi2csclient /input:https://api.velopack.io/swagger/v1/swagger.json /output:FlowApi.cs /namespace:Velopack.Flow /classname:FlowApi
|
||||
98
src/vpk/Velopack.Flow/FlowApiExtensions.cs
Normal file
98
src/vpk/Velopack.Flow/FlowApiExtensions.cs
Normal file
@@ -0,0 +1,98 @@
|
||||
using System.Globalization;
|
||||
using System.Net.Http;
|
||||
using System.Text;
|
||||
|
||||
namespace Velopack.Flow;
|
||||
|
||||
public partial class Profile
|
||||
{
|
||||
public string GetDisplayName()
|
||||
{
|
||||
return DisplayName ?? Email ?? "<unknown>";
|
||||
}
|
||||
}
|
||||
|
||||
public partial class FlowApi
|
||||
{
|
||||
public virtual async Task DownloadInstallerLatestToFileAsync(string packageId, string channel, DownloadAssetType? assetType, string localFilePath,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
if (packageId == null)
|
||||
throw new ArgumentNullException("packageId");
|
||||
|
||||
if (channel == null)
|
||||
throw new ArgumentNullException("channel");
|
||||
|
||||
var client_ = _httpClient;
|
||||
var disposeClient_ = false;
|
||||
try {
|
||||
using (var request_ = new HttpRequestMessage()) {
|
||||
request_.Method = new HttpMethod("GET");
|
||||
|
||||
var urlBuilder_ = new StringBuilder();
|
||||
if (!string.IsNullOrEmpty(_baseUrl)) urlBuilder_.Append(_baseUrl);
|
||||
// Operation Path: "v1/download/{packageId}/{channel}"
|
||||
urlBuilder_.Append("v1/download/");
|
||||
urlBuilder_.Append(Uri.EscapeDataString(ConvertToString(packageId, CultureInfo.InvariantCulture)));
|
||||
urlBuilder_.Append('/');
|
||||
urlBuilder_.Append(Uri.EscapeDataString(ConvertToString(channel, CultureInfo.InvariantCulture)));
|
||||
urlBuilder_.Append('?');
|
||||
if (assetType != null) {
|
||||
urlBuilder_.Append(Uri.EscapeDataString("assetType")).Append('=')
|
||||
.Append(Uri.EscapeDataString(ConvertToString(assetType, CultureInfo.InvariantCulture))).Append('&');
|
||||
}
|
||||
|
||||
urlBuilder_.Length--;
|
||||
|
||||
PrepareRequest(client_, request_, urlBuilder_);
|
||||
|
||||
var url_ = urlBuilder_.ToString();
|
||||
request_.RequestUri = new Uri(url_, UriKind.RelativeOrAbsolute);
|
||||
|
||||
PrepareRequest(client_, request_, url_);
|
||||
|
||||
var response_ = await client_.SendAsync(request_, HttpCompletionOption.ResponseHeadersRead, cancellationToken).ConfigureAwait(false);
|
||||
var disposeResponse_ = true;
|
||||
try {
|
||||
var headers_ = new Dictionary<string, IEnumerable<string>>();
|
||||
foreach (var item_ in response_.Headers)
|
||||
headers_[item_.Key] = item_.Value;
|
||||
if (response_.Content != null && response_.Content.Headers != null) {
|
||||
foreach (var item_ in response_.Content.Headers)
|
||||
headers_[item_.Key] = item_.Value;
|
||||
}
|
||||
|
||||
ProcessResponse(client_, response_);
|
||||
|
||||
var status_ = (int) response_.StatusCode;
|
||||
if (status_ == 404) {
|
||||
string responseText_ = (response_.Content == null) ? string.Empty : await response_.Content.ReadAsStringAsync().ConfigureAwait(false);
|
||||
throw new ApiException("A server side error occurred.", status_, responseText_, headers_, null);
|
||||
} else if (status_ == 200 || status_ == 204) {
|
||||
using var fs = File.Create(localFilePath);
|
||||
#if NET6_0_OR_GREATER
|
||||
await response_.Content.CopyToAsync(fs, cancellationToken);
|
||||
#else
|
||||
await response_.Content.CopyToAsync(fs);
|
||||
#endif
|
||||
return;
|
||||
} else {
|
||||
var responseData_ = response_.Content == null ? null : await response_.Content.ReadAsStringAsync().ConfigureAwait(false);
|
||||
throw new ApiException(
|
||||
"The HTTP status code of the response was not expected (" + status_ + ").",
|
||||
status_,
|
||||
responseData_,
|
||||
headers_,
|
||||
null);
|
||||
}
|
||||
} finally {
|
||||
if (disposeResponse_)
|
||||
response_.Dispose();
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
if (disposeClient_)
|
||||
client_.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
using System.Net.Http;
|
||||
using System.Net.Http.Headers;
|
||||
|
||||
namespace Velopack.Packaging.Flow;
|
||||
namespace Velopack.Flow;
|
||||
|
||||
public class HmacAuthHttpClientHandler : DelegatingHandler
|
||||
{
|
||||
@@ -1,9 +1,7 @@
|
||||
using System.Security.Cryptography;
|
||||
|
||||
using System.Text;
|
||||
|
||||
#nullable enable
|
||||
namespace Velopack.Packaging.Flow;
|
||||
namespace Velopack.Flow;
|
||||
|
||||
public static class HmacHelper
|
||||
{
|
||||
@@ -11,7 +9,7 @@ public static class HmacHelper
|
||||
public static DateTime EpochStart { get; } = new(1970, 01, 01, 0, 0, 0, 0, DateTimeKind.Utc);
|
||||
|
||||
public static uint GetSecondsSinceEpoch()
|
||||
=> (uint)(DateTime.UtcNow - EpochStart).TotalSeconds;
|
||||
=> (uint) (DateTime.UtcNow - EpochStart).TotalSeconds;
|
||||
|
||||
public static string BuildSignature(string hashedId, string httpMethod, string requestUri, uint secondsSinceEpoch, string nonce)
|
||||
=> $"{hashedId}{httpMethod.ToUpperInvariant()}{requestUri.ToLowerInvariant()}{secondsSinceEpoch}{nonce}";
|
||||
@@ -45,6 +43,7 @@ public static class HmacHelper
|
||||
if (signatureData is null) {
|
||||
throw new ArgumentNullException(nameof(signatureData));
|
||||
}
|
||||
|
||||
using HMAC hmac = new HMACSHA256();
|
||||
hmac.Key = secret ?? throw new ArgumentNullException(nameof(secret));
|
||||
return Convert.ToBase64String(hmac.ComputeHash(Encoding.UTF8.GetBytes(signatureData)));
|
||||
24
src/vpk/Velopack.Flow/Velopack.Flow.csproj
Normal file
24
src/vpk/Velopack.Flow/Velopack.Flow.csproj
Normal file
@@ -0,0 +1,24 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFrameworks>net472;net6.0</TargetFrameworks>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<NoWarn>$(NoWarn);CA2007;CS8002</NoWarn>
|
||||
<Nullable>enable</Nullable>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.Identity.Client" Version="4.66.2" />
|
||||
<PackageReference Include="Microsoft.Identity.Client.Broker" Version="4.66.2" />
|
||||
<PackageReference Include="Microsoft.Identity.Client.Extensions.Msal" Version="4.66.2" />
|
||||
<PackageReference Include="Microsoft.Extensions.Http" Version="9.0.0" />
|
||||
<PackageReference Include="Microsoft.AspNet.WebApi.Client" Version="6.0.0" Aliases="HttpFormatting" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Velopack.Core\Velopack.Core.csproj" />
|
||||
<ProjectReference Include="..\Velopack.Packaging\Velopack.Packaging.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -1,9 +1,8 @@
|
||||
#nullable enable
|
||||
namespace Velopack.Packaging.Flow;
|
||||
namespace Velopack.Flow;
|
||||
|
||||
public class VelopackLoginOptions : VelopackServiceOptions
|
||||
public class VelopackFlowLoginOptions
|
||||
{
|
||||
public bool AllowCacheCredentials { get; set; } = true;
|
||||
public bool AllowInteractiveLogin { get; set; } = true;
|
||||
public bool AllowDeviceCodeFlow { get; set; } = true;
|
||||
}
|
||||
}
|
||||
@@ -5,8 +5,10 @@ using NuGet.Versioning;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using System.Text;
|
||||
using System.Net.Http.Headers;
|
||||
using Velopack.Core;
|
||||
using Velopack.Core.Abstractions;
|
||||
using Velopack.NuGet;
|
||||
using Velopack.Packaging.Abstractions;
|
||||
using Velopack.Packaging;
|
||||
using Velopack.Util;
|
||||
|
||||
#if NET6_0_OR_GREATER
|
||||
@@ -16,29 +18,12 @@ using System.Net.Http;
|
||||
#endif
|
||||
|
||||
#nullable enable
|
||||
namespace Velopack.Packaging.Flow;
|
||||
|
||||
public interface IVelopackFlowServiceClient
|
||||
{
|
||||
Task<bool> LoginAsync(VelopackLoginOptions? options, bool suppressOutput, CancellationToken cancellationToken);
|
||||
|
||||
Task LogoutAsync(VelopackServiceOptions? options, CancellationToken cancellationToken);
|
||||
|
||||
Task<Profile?> GetProfileAsync(VelopackServiceOptions? options, CancellationToken cancellationToken);
|
||||
|
||||
Task<string> InvokeEndpointAsync(VelopackServiceOptions? options, string endpointUri,
|
||||
string method,
|
||||
string? body,
|
||||
CancellationToken cancellationToken);
|
||||
|
||||
Task UploadLatestReleaseAssetsAsync(string? channel, string releaseDirectory, string? serviceUrl, RuntimeOs os,
|
||||
bool noWaitForLive, CancellationToken cancellationToken);
|
||||
}
|
||||
namespace Velopack.Flow;
|
||||
|
||||
public class VelopackFlowServiceClient(
|
||||
IHttpMessageHandlerFactory HttpMessageHandlerFactory,
|
||||
VelopackFlowServiceOptions Options,
|
||||
ILogger Logger,
|
||||
IFancyConsole Console) : IVelopackFlowServiceClient
|
||||
IFancyConsole Console)
|
||||
{
|
||||
private static readonly string[] Scopes = ["openid", "offline_access"];
|
||||
|
||||
@@ -48,7 +33,7 @@ public class VelopackFlowServiceClient(
|
||||
|
||||
private HttpClient GetHttpClient(Action<int>? progress = null)
|
||||
{
|
||||
HttpMessageHandler primaryHandler = HttpMessageHandlerFactory.CreateHandler("flow");
|
||||
HttpMessageHandler primaryHandler = new HmacAuthHttpClientHandler();
|
||||
|
||||
if (progress != null) {
|
||||
var ph = new HttpFormatting::System.Net.Http.Handlers.ProgressMessageHandler(primaryHandler);
|
||||
@@ -59,37 +44,51 @@ public class VelopackFlowServiceClient(
|
||||
ph.HttpReceiveProgress += (_, args) => {
|
||||
progress(args.ProgressPercentage);
|
||||
};
|
||||
|
||||
primaryHandler = ph;
|
||||
}
|
||||
|
||||
var client = new HttpClient(primaryHandler);
|
||||
client.DefaultRequestHeaders.Authorization = Authorization;
|
||||
client.Timeout = TimeSpan.FromMinutes(Options.Timeout);
|
||||
return client;
|
||||
}
|
||||
|
||||
public async Task<bool> LoginAsync(VelopackLoginOptions? options, bool suppressOutput, CancellationToken cancellationToken)
|
||||
private FlowApi GetFlowApi(Action<int>? progress = null)
|
||||
{
|
||||
options ??= new VelopackLoginOptions();
|
||||
if (!suppressOutput) {
|
||||
Logger.LogInformation("Preparing to login to Velopack ({ServiceUrl})", options.VelopackBaseUrl);
|
||||
var client = GetHttpClient(progress);
|
||||
var api = new FlowApi(client);
|
||||
if (!String.IsNullOrWhiteSpace(Options.VelopackBaseUrl)) {
|
||||
api.BaseUrl = Options.VelopackBaseUrl;
|
||||
}
|
||||
|
||||
var authConfiguration = await GetAuthConfigurationAsync(options, cancellationToken);
|
||||
return api;
|
||||
}
|
||||
|
||||
public async Task<bool> LoginAsync(VelopackFlowLoginOptions? loginOptions, bool suppressOutput, CancellationToken cancellationToken)
|
||||
{
|
||||
loginOptions ??= new VelopackFlowLoginOptions();
|
||||
var serviceUrl = Options.VelopackBaseUrl ?? GetFlowApi().BaseUrl;
|
||||
if (!suppressOutput) {
|
||||
Logger.LogInformation("Preparing to login to Velopack ({serviceUrl})", serviceUrl);
|
||||
}
|
||||
|
||||
var authConfiguration = await GetAuthConfigurationAsync(cancellationToken);
|
||||
var pca = await BuildPublicApplicationAsync(authConfiguration);
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(options.ApiKey)) {
|
||||
Authorization = new(HmacHelper.HmacScheme, options.ApiKey);
|
||||
if (!string.IsNullOrWhiteSpace(Options.ApiKey)) {
|
||||
Authorization = new(HmacHelper.HmacScheme, Options.ApiKey);
|
||||
} else {
|
||||
AuthenticationResult? rv = null;
|
||||
if (options.AllowCacheCredentials) {
|
||||
if (loginOptions.AllowCacheCredentials) {
|
||||
rv = await AcquireSilentlyAsync(pca, cancellationToken);
|
||||
}
|
||||
|
||||
if (rv is null && options.AllowInteractiveLogin) {
|
||||
if (rv is null && loginOptions.AllowInteractiveLogin) {
|
||||
rv = await AcquireInteractiveAsync(pca, authConfiguration, cancellationToken);
|
||||
}
|
||||
|
||||
if (rv is null && options.AllowDeviceCodeFlow) {
|
||||
if (rv is null && loginOptions.AllowDeviceCodeFlow) {
|
||||
rv = await AcquireByDeviceCodeAsync(pca, cancellationToken);
|
||||
}
|
||||
|
||||
@@ -101,7 +100,7 @@ public class VelopackFlowServiceClient(
|
||||
Authorization = new("Bearer", rv.IdToken ?? rv.AccessToken);
|
||||
}
|
||||
|
||||
var profile = await GetProfileAsync(options, cancellationToken);
|
||||
var profile = await GetProfileAsync(cancellationToken);
|
||||
|
||||
if (!suppressOutput) {
|
||||
Logger.LogInformation("{UserName} logged into Velopack", profile?.GetDisplayName());
|
||||
@@ -110,9 +109,9 @@ public class VelopackFlowServiceClient(
|
||||
return true;
|
||||
}
|
||||
|
||||
public async Task LogoutAsync(VelopackServiceOptions? options, CancellationToken cancellationToken)
|
||||
public async Task LogoutAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
var authConfiguration = await GetAuthConfigurationAsync(options, cancellationToken);
|
||||
var authConfiguration = await GetAuthConfigurationAsync(cancellationToken);
|
||||
|
||||
var pca = await BuildPublicApplicationAsync(authConfiguration);
|
||||
|
||||
@@ -125,31 +124,30 @@ public class VelopackFlowServiceClient(
|
||||
Logger.LogInformation("Cleared saved login(s) for Velopack");
|
||||
}
|
||||
|
||||
public async Task<Profile?> GetProfileAsync(VelopackServiceOptions? options, CancellationToken cancellationToken)
|
||||
public async Task<Profile?> GetProfileAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
AssertAuthenticated();
|
||||
var endpoint = GetEndpoint("v1/user/profile", options?.VelopackBaseUrl);
|
||||
|
||||
var client = GetHttpClient();
|
||||
return await client.GetFromJsonAsync<Profile>(endpoint, cancellationToken);
|
||||
var client = GetFlowApi();
|
||||
return await client.GetUserProfileAsync(cancellationToken);
|
||||
}
|
||||
|
||||
public async Task<string> InvokeEndpointAsync(
|
||||
VelopackServiceOptions? options,
|
||||
VelopackFlowServiceOptions? options,
|
||||
string endpointUri,
|
||||
string method,
|
||||
string? body,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
AssertAuthenticated();
|
||||
var endpoint = GetEndpoint(endpointUri, options?.VelopackBaseUrl);
|
||||
|
||||
var client = GetHttpClient();
|
||||
var endpoint = new FlowApi(client).BaseUrl;
|
||||
|
||||
HttpRequestMessage request = new(new HttpMethod(method), endpoint);
|
||||
if (body is not null) {
|
||||
request.Content = new StringContent(body, Encoding.UTF8, "application/json");
|
||||
}
|
||||
|
||||
var client = GetHttpClient();
|
||||
HttpResponseMessage response = await client.SendAsync(request, cancellationToken);
|
||||
|
||||
#if NET6_0_OR_GREATER
|
||||
@@ -166,14 +164,14 @@ public class VelopackFlowServiceClient(
|
||||
}
|
||||
}
|
||||
|
||||
public async Task UploadLatestReleaseAssetsAsync(string? channel, string releaseDirectory, string? serviceUrl,
|
||||
RuntimeOs os, bool noWaitForLive, CancellationToken cancellationToken)
|
||||
public async Task UploadLatestReleaseAssetsAsync(string? channel, string releaseDirectory,
|
||||
RuntimeOs os, bool waitForLive, CancellationToken cancellationToken)
|
||||
{
|
||||
AssertAuthenticated();
|
||||
|
||||
channel ??= ReleaseEntryHelper.GetDefaultChannel(os);
|
||||
channel ??= DefaultName.GetDefaultChannel(os);
|
||||
BuildAssets assets = BuildAssets.Read(releaseDirectory, channel);
|
||||
var fullAsset = assets.GetReleaseEntries().SingleOrDefault(a => a.Type == VelopackAssetType.Full);
|
||||
var fullAsset = assets.GetReleaseEntries().SingleOrDefault(a => a.Type == Velopack.VelopackAssetType.Full);
|
||||
|
||||
if (fullAsset is null) {
|
||||
Logger.LogError("No full asset found in release directory {ReleaseDirectory} (or it's missing from assets file)", releaseDirectory);
|
||||
@@ -188,7 +186,7 @@ public class VelopackFlowServiceClient(
|
||||
.Concat([(fullAssetPath, FileType.Release)])
|
||||
.ToArray();
|
||||
|
||||
Logger.LogInformation("Beginning upload to Velopack Flow (serviceUrl={ServiceUrl})", serviceUrl);
|
||||
Logger.LogInformation("Beginning upload to Velopack Flow");
|
||||
|
||||
await Console.ExecuteProgressAsync(
|
||||
async (progress) => {
|
||||
@@ -196,7 +194,7 @@ public class VelopackFlowServiceClient(
|
||||
$"Creating release {version}",
|
||||
async (report) => {
|
||||
report(-1);
|
||||
var result = await CreateReleaseGroupAsync(packageId, version, channel, serviceUrl, cancellationToken);
|
||||
var result = await CreateReleaseGroupAsync(packageId, version, channel, cancellationToken);
|
||||
report(100);
|
||||
return result;
|
||||
});
|
||||
@@ -209,7 +207,6 @@ public class VelopackFlowServiceClient(
|
||||
async (report) => {
|
||||
await UploadReleaseAssetAsync(
|
||||
assetTuple.Item1,
|
||||
serviceUrl,
|
||||
releaseGroup.Id,
|
||||
assetTuple.Item2,
|
||||
report,
|
||||
@@ -225,7 +222,7 @@ public class VelopackFlowServiceClient(
|
||||
var prevZip = await progress.RunTask(
|
||||
$"Downloading delta base for {version}",
|
||||
async (report) => {
|
||||
await DownloadLatestRelease(packageId, channel, serviceUrl, prevVersion, report, cancellationToken);
|
||||
await DownloadLatestRelease(packageId, channel, prevVersion, report, cancellationToken);
|
||||
return new ZipPackage(prevVersion);
|
||||
});
|
||||
|
||||
@@ -234,7 +231,7 @@ public class VelopackFlowServiceClient(
|
||||
$"Latest version in channel {channel} is greater than or equal to local (remote={prevZip.Version}, local={version})");
|
||||
}
|
||||
|
||||
var suggestedDeltaName = ReleaseEntryHelper.GetSuggestedReleaseName(packageId, version.ToFullString(), channel, true, RuntimeOs.Unknown);
|
||||
var suggestedDeltaName = DefaultName.GetSuggestedReleaseName(packageId, version.ToFullString(), channel, true, RuntimeOs.Unknown);
|
||||
var deltaPath = Path.Combine(releaseDirectory, suggestedDeltaName);
|
||||
|
||||
await progress.RunTask(
|
||||
@@ -254,7 +251,6 @@ public class VelopackFlowServiceClient(
|
||||
async (report) => {
|
||||
await UploadReleaseAssetAsync(
|
||||
deltaPath,
|
||||
serviceUrl,
|
||||
releaseGroup.Id,
|
||||
FileType.Release,
|
||||
report,
|
||||
@@ -269,50 +265,35 @@ public class VelopackFlowServiceClient(
|
||||
$"Publishing release {version}",
|
||||
async (report) => {
|
||||
report(-1);
|
||||
var result = await PublishReleaseGroupAsync(releaseGroup, serviceUrl, cancellationToken);
|
||||
var result = await PublishReleaseGroupAsync(releaseGroup.Id, cancellationToken);
|
||||
report(100);
|
||||
return result;
|
||||
});
|
||||
|
||||
if (!noWaitForLive) {
|
||||
if (waitForLive) {
|
||||
await progress.RunTask(
|
||||
"Waiting for release to go live",
|
||||
async (report) => {
|
||||
report(-1);
|
||||
await WaitUntilReleaseGroupLive(publishedGroup.Id, serviceUrl, cancellationToken);
|
||||
await WaitUntilReleaseGroupLive(publishedGroup.Id, cancellationToken);
|
||||
report(100);
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private async Task DownloadLatestRelease(string packageId, string channel, string? velopackBaseUrl, string localPath,
|
||||
Action<int> progress, CancellationToken cancellationToken)
|
||||
private async Task DownloadLatestRelease(string packageId, string channel, string localPath, Action<int> progress, CancellationToken cancellationToken)
|
||||
{
|
||||
var client = GetHttpClient(progress);
|
||||
var endpoint = GetEndpoint($"v1/download/{packageId}/{channel}", velopackBaseUrl) + $"?assetType=Full";
|
||||
|
||||
using var fs = File.Create(localPath);
|
||||
|
||||
var response = await client.GetAsync(endpoint, cancellationToken);
|
||||
response.EnsureSuccessStatusCode();
|
||||
|
||||
#if NET6_0_OR_GREATER
|
||||
await response.Content.CopyToAsync(fs, cancellationToken);
|
||||
#else
|
||||
await response.Content.CopyToAsync(fs);
|
||||
#endif
|
||||
var client = GetFlowApi(progress);
|
||||
await client.DownloadInstallerLatestToFileAsync(packageId, channel, DownloadAssetType.Full, localPath, cancellationToken);
|
||||
}
|
||||
|
||||
private async Task WaitUntilReleaseGroupLive(Guid releaseGroupId, string? velopackBaseUrl, CancellationToken cancellationToken)
|
||||
private async Task WaitUntilReleaseGroupLive(Guid releaseGroupId, CancellationToken cancellationToken)
|
||||
{
|
||||
var client = GetHttpClient();
|
||||
var endpoint = GetEndpoint($"v1/releaseGroups/{releaseGroupId}", velopackBaseUrl);
|
||||
var client = GetFlowApi();
|
||||
|
||||
for (int i = 0; i < 300; i++) {
|
||||
var response = await client.GetAsync(endpoint, cancellationToken);
|
||||
response.EnsureSuccessStatusCode();
|
||||
var releaseGroup = await response.Content.ReadFromJsonAsync<ReleaseGroup>(cancellationToken: cancellationToken);
|
||||
var releaseGroup = await client.GetReleaseGroupAsync(releaseGroupId, cancellationToken);
|
||||
if (releaseGroup?.FileUploads == null) {
|
||||
Logger.LogWarning("Failed to get release group status, it may not be live yet.");
|
||||
return;
|
||||
@@ -329,9 +310,7 @@ public class VelopackFlowServiceClient(
|
||||
Logger.LogWarning("Release did not go live within 5 minutes (timeout).");
|
||||
}
|
||||
|
||||
private async Task<ReleaseGroup> CreateReleaseGroupAsync(
|
||||
string packageId, SemanticVersion version, string channel,
|
||||
string? velopackBaseUrl, CancellationToken cancellationToken)
|
||||
private async Task<ReleaseGroup> CreateReleaseGroupAsync(string packageId, SemanticVersion version, string channel, CancellationToken cancellationToken)
|
||||
{
|
||||
CreateReleaseGroupRequest request = new() {
|
||||
ChannelIdentifier = channel,
|
||||
@@ -339,70 +318,37 @@ public class VelopackFlowServiceClient(
|
||||
Version = version.ToNormalizedString()
|
||||
};
|
||||
|
||||
var client = GetHttpClient();
|
||||
var endpoint = GetEndpoint("v1/releaseGroups/create", velopackBaseUrl);
|
||||
var response = await client.PostAsJsonAsync(endpoint, request, cancellationToken);
|
||||
|
||||
if (!response.IsSuccessStatusCode) {
|
||||
string content = await response.Content.ReadAsStringAsync(cancellationToken);
|
||||
throw new InvalidOperationException(
|
||||
$"Failed to create release group with version {version.ToNormalizedString()}" +
|
||||
$"{Environment.NewLine}Response status code: {response.StatusCode}{Environment.NewLine}{content}");
|
||||
}
|
||||
|
||||
return await response.Content.ReadFromJsonAsync<ReleaseGroup>(cancellationToken: cancellationToken)
|
||||
?? throw new InvalidOperationException($"Failed to create release group with version {version.ToNormalizedString()}");
|
||||
var client = GetFlowApi();
|
||||
return await client.CreateReleaseGroupAsync(request, cancellationToken);
|
||||
}
|
||||
|
||||
private async Task UploadReleaseAssetAsync(string filePath, string? velopackBaseUrl, Guid releaseGroupId,
|
||||
FileType fileType, Action<int> progress, CancellationToken cancellationToken)
|
||||
private async Task UploadReleaseAssetAsync(string filePath, Guid releaseGroupId, FileType fileType, Action<int> progress,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
using var formData = new MultipartFormDataContent();
|
||||
formData.Add(new StringContent(releaseGroupId.ToString()), "ReleaseGroupId");
|
||||
formData.Add(new StringContent(fileType.ToString()), "FileType");
|
||||
|
||||
using var fileStream = File.OpenRead(filePath);
|
||||
|
||||
using var fileContent = new StreamContent(fileStream);
|
||||
formData.Add(fileContent, "File", Path.GetFileName(filePath));
|
||||
|
||||
var endpoint = GetEndpoint("v1/releases/upload", velopackBaseUrl);
|
||||
|
||||
var client = GetHttpClient(progress);
|
||||
var response = await client.PostAsync(endpoint, formData, cancellationToken);
|
||||
response.EnsureSuccessStatusCode();
|
||||
using var stream = File.OpenRead(filePath);
|
||||
var file = new FileParameter(stream);
|
||||
var client = GetFlowApi(progress);
|
||||
await client.UploadReleaseAsync(releaseGroupId, fileType, file, cancellationToken);
|
||||
}
|
||||
|
||||
private async Task<ReleaseGroup> PublishReleaseGroupAsync(
|
||||
ReleaseGroup releaseGroup, string? velopackBaseUrl, CancellationToken cancellationToken)
|
||||
private async Task<ReleaseGroup> PublishReleaseGroupAsync(Guid releaseGroupId, CancellationToken cancellationToken)
|
||||
{
|
||||
UpdateReleaseGroupRequest request = new() {
|
||||
State = ReleaseGroupState.Published
|
||||
};
|
||||
|
||||
var client = GetHttpClient();
|
||||
var endpoint = GetEndpoint($"v1/releaseGroups/{releaseGroup.Id}", velopackBaseUrl);
|
||||
var response = await client.PutAsJsonAsync(endpoint, request, cancellationToken);
|
||||
|
||||
if (!response.IsSuccessStatusCode) {
|
||||
string content = await response.Content.ReadAsStringAsync(cancellationToken);
|
||||
throw new InvalidOperationException(
|
||||
$"Failed to publish release group with id {releaseGroup.Id}.{Environment.NewLine}Response status code: {response.StatusCode}{Environment.NewLine}{content}");
|
||||
}
|
||||
|
||||
return await response.Content.ReadFromJsonAsync<ReleaseGroup>(cancellationToken: cancellationToken)
|
||||
?? throw new InvalidOperationException($"Failed to publish release group with id {releaseGroup.Id}");
|
||||
var client = GetFlowApi();
|
||||
return await client.UpdateReleaseGroupAsync(releaseGroupId, request, cancellationToken);
|
||||
}
|
||||
|
||||
private async Task<AuthConfiguration> GetAuthConfigurationAsync(VelopackServiceOptions? options, CancellationToken cancellationToken)
|
||||
private async Task<AuthConfiguration> GetAuthConfigurationAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
if (AuthConfiguration is not null)
|
||||
return AuthConfiguration;
|
||||
|
||||
var endpoint = GetEndpoint("v1/auth/config", options);
|
||||
var client = GetFlowApi();
|
||||
var authConfig = await client.GetV1AuthConfigAsync(cancellationToken);
|
||||
|
||||
var client = GetHttpClient();
|
||||
var authConfig = await client.GetFromJsonAsync<AuthConfiguration>(endpoint, cancellationToken);
|
||||
if (authConfig is null)
|
||||
throw new Exception("Failed to get auth configuration.");
|
||||
if (authConfig.B2CAuthority is null)
|
||||
@@ -415,16 +361,6 @@ public class VelopackFlowServiceClient(
|
||||
return authConfig;
|
||||
}
|
||||
|
||||
private static Uri GetEndpoint(string relativePath, VelopackServiceOptions? options)
|
||||
=> GetEndpoint(relativePath, options?.VelopackBaseUrl);
|
||||
|
||||
private static Uri GetEndpoint(string relativePath, string? velopackBaseUrl)
|
||||
{
|
||||
var baseUrl = velopackBaseUrl ?? VelopackServiceOptions.DefaultBaseUrl;
|
||||
var endpoint = new Uri(relativePath, UriKind.Relative);
|
||||
return new(new Uri(baseUrl), endpoint);
|
||||
}
|
||||
|
||||
private void AssertAuthenticated()
|
||||
{
|
||||
if (Authorization is null) {
|
||||
@@ -516,7 +452,7 @@ public class VelopackFlowServiceClient(
|
||||
.WithB2CAuthority(authConfiguration.B2CAuthority)
|
||||
.WithRedirectUri(authConfiguration.RedirectUri)
|
||||
#if DEBUG
|
||||
.WithLogging((Microsoft.Identity.Client.LogLevel level, string message, bool containsPii) => System.Console.WriteLine($"[{level}]: {message}"), enablePiiLogging: true, enableDefaultPlatformLogging: true)
|
||||
.WithLogging((Microsoft.Identity.Client.LogLevel level, string message, bool containsPii) => System.Console.WriteLine($"[{level}]: {message}"), enablePiiLogging: true, enableDefaultPlatformLogging: true)
|
||||
#endif
|
||||
.WithClientName("velopack")
|
||||
.Build();
|
||||
@@ -524,11 +460,4 @@ public class VelopackFlowServiceClient(
|
||||
cacheHelper.RegisterCache(pca.UserTokenCache);
|
||||
return pca;
|
||||
}
|
||||
|
||||
private enum FileType
|
||||
{
|
||||
Unknown,
|
||||
Release,
|
||||
Installer,
|
||||
}
|
||||
}
|
||||
10
src/vpk/Velopack.Flow/VelopackFlowServiceOptions.cs
Normal file
10
src/vpk/Velopack.Flow/VelopackFlowServiceOptions.cs
Normal file
@@ -0,0 +1,10 @@
|
||||
namespace Velopack.Flow;
|
||||
|
||||
public class VelopackFlowServiceOptions
|
||||
{
|
||||
public string? VelopackBaseUrl { get; set; } = string.Empty;
|
||||
|
||||
public string? ApiKey { get; set; } = string.Empty;
|
||||
|
||||
public double Timeout { get; set; } = 30d;
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
using ELFSharp.ELF;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Velopack.Packaging.Abstractions;
|
||||
using Velopack.Core;
|
||||
using Velopack.Core.Abstractions;
|
||||
using Velopack.Util;
|
||||
|
||||
namespace Velopack.Packaging.Unix.Commands;
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
using System.Text.RegularExpressions;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using NuGet.Versioning;
|
||||
using Velopack.Packaging.Abstractions;
|
||||
using Velopack.Packaging.Exceptions;
|
||||
using Velopack.Core;
|
||||
using Velopack.Core.Abstractions;
|
||||
using Velopack.Util;
|
||||
|
||||
namespace Velopack.Packaging.Unix.Commands;
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
using System.Collections.Concurrent;
|
||||
using System.Runtime.Versioning;
|
||||
using System.Runtime.Versioning;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Velopack.Packaging.Abstractions;
|
||||
using Velopack.Core.Abstractions;
|
||||
using Velopack.Util;
|
||||
|
||||
namespace Velopack.Packaging.Unix.Commands;
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using System.Runtime.Versioning;
|
||||
using System.Security;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Velopack.Core;
|
||||
using Velopack.Util;
|
||||
|
||||
namespace Velopack.Packaging.Unix;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
using System.Diagnostics;
|
||||
using System.Text.RegularExpressions;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Velopack.Packaging.Exceptions;
|
||||
using Velopack.Core;
|
||||
using Velopack.Util;
|
||||
|
||||
namespace Velopack.Packaging.Windows;
|
||||
|
||||
@@ -1,11 +1,9 @@
|
||||
using System.IO.Compression;
|
||||
using System.Runtime.Versioning;
|
||||
using System.Runtime.Versioning;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Velopack.Compression;
|
||||
using Velopack.Core;
|
||||
using Velopack.Core.Abstractions;
|
||||
using Velopack.NuGet;
|
||||
using Velopack.Packaging.Abstractions;
|
||||
using Velopack.Packaging.Exceptions;
|
||||
using Velopack.Packaging.NuGet;
|
||||
using Velopack.Util;
|
||||
using Velopack.Windows;
|
||||
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
namespace Velopack.Packaging.Windows.Commands;
|
||||
using Velopack.Core;
|
||||
|
||||
namespace Velopack.Packaging.Windows.Commands;
|
||||
|
||||
public class WindowsReleasifyOptions : WindowsSigningOptions
|
||||
{
|
||||
|
||||
@@ -6,8 +6,8 @@ using AsmResolver.PE.DotNet.Cil;
|
||||
using AsmResolver.PE.Win32Resources.Version;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using NuGet.Versioning;
|
||||
using Velopack.Packaging.Abstractions;
|
||||
using Velopack.Packaging.Exceptions;
|
||||
using Velopack.Core;
|
||||
using Velopack.Core.Abstractions;
|
||||
using Velopack.Util;
|
||||
|
||||
namespace Velopack.Packaging.Windows;
|
||||
|
||||
@@ -2,5 +2,5 @@
|
||||
|
||||
public interface IPlatformOptions : IOutputOptions
|
||||
{
|
||||
RID TargetRuntime { get; set; }
|
||||
RID? TargetRuntime { get; set; }
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Velopack.Packaging.Abstractions;
|
||||
using Velopack.Core.Abstractions;
|
||||
|
||||
namespace Velopack.Packaging.Commands;
|
||||
|
||||
@@ -16,15 +16,18 @@ public class DeltaGenCommandRunner : ICommand<DeltaGenOptions>
|
||||
|
||||
public async Task Run(DeltaGenOptions options)
|
||||
{
|
||||
await _console.ExecuteProgressAsync(async (ctx) => {
|
||||
var pold = new ReleasePackage(options.BasePackage);
|
||||
var pnew = new ReleasePackage(options.NewPackage);
|
||||
await ctx.RunTask($"Building delta {pold.Version} -> {pnew.Version}", (progress) => {
|
||||
var delta = new DeltaPackageBuilder(_logger);
|
||||
delta.CreateDeltaPackage(pold, pnew, options.OutputFile, options.DeltaMode, progress);
|
||||
progress(100);
|
||||
return Task.CompletedTask;
|
||||
await _console.ExecuteProgressAsync(
|
||||
async (ctx) => {
|
||||
var pold = new ReleasePackage(options.BasePackage);
|
||||
var pnew = new ReleasePackage(options.NewPackage);
|
||||
await ctx.RunTask(
|
||||
$"Building delta {pold.Version} -> {pnew.Version}",
|
||||
(progress) => {
|
||||
var delta = new DeltaPackageBuilder(_logger);
|
||||
delta.CreateDeltaPackage(pold, pnew, options.OutputFile, options.DeltaMode, progress);
|
||||
progress(100);
|
||||
return Task.CompletedTask;
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,6 @@
|
||||
namespace Velopack.Packaging.Commands;
|
||||
using Velopack.Core;
|
||||
|
||||
namespace Velopack.Packaging.Commands;
|
||||
|
||||
public class DeltaGenOptions
|
||||
{
|
||||
@@ -9,4 +11,4 @@ public class DeltaGenOptions
|
||||
public string NewPackage { get; set; }
|
||||
|
||||
public string OutputFile { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Velopack.Compression;
|
||||
using Velopack.Packaging.Exceptions;
|
||||
using Velopack.Packaging.Abstractions;
|
||||
using Velopack.Core;
|
||||
using Velopack.Core.Abstractions;
|
||||
using Velopack.Util;
|
||||
|
||||
namespace Velopack.Packaging.Commands;
|
||||
@@ -35,18 +35,24 @@ public class DeltaPatchCommandRunner : ICommand<DeltaPatchOptions>
|
||||
var delta = new DeltaEmbedded(HelperFile.GetZstdPath(), _logger, tmp);
|
||||
EasyZip.ExtractZipToDirectory(_logger, options.BasePackage, workDir);
|
||||
|
||||
await _console.ExecuteProgressAsync(async (ctx) => {
|
||||
foreach (var f in options.PatchFiles) {
|
||||
await ctx.RunTask($"Applying {f.Name}", (progress) => {
|
||||
delta.ApplyDeltaPackageFast(workDir, f.FullName, progress);
|
||||
progress(100);
|
||||
return Task.CompletedTask;
|
||||
});
|
||||
}
|
||||
await ctx.RunTask($"Building {Path.GetFileName(options.OutputFile)}", async (progress) => {
|
||||
await EasyZip.CreateZipFromDirectoryAsync(_logger, options.OutputFile, workDir, progress);
|
||||
progress(100);
|
||||
await _console.ExecuteProgressAsync(
|
||||
async (ctx) => {
|
||||
foreach (var f in options.PatchFiles) {
|
||||
await ctx.RunTask(
|
||||
$"Applying {f.Name}",
|
||||
(progress) => {
|
||||
delta.ApplyDeltaPackageFast(workDir, f.FullName, progress);
|
||||
progress(100);
|
||||
return Task.CompletedTask;
|
||||
});
|
||||
}
|
||||
|
||||
await ctx.RunTask(
|
||||
$"Building {Path.GetFileName(options.OutputFile)}",
|
||||
async (progress) => {
|
||||
await EasyZip.CreateZipFromDirectoryAsync(_logger, options.OutputFile, workDir, progress);
|
||||
progress(100);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -7,4 +7,4 @@ public class DeltaPatchOptions
|
||||
public FileInfo[] PatchFiles { get; set; }
|
||||
|
||||
public string OutputFile { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,7 @@
|
||||
using System.Text;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Velopack.Compression;
|
||||
using Velopack.Core;
|
||||
using Velopack.Packaging.Exceptions;
|
||||
using Velopack.Util;
|
||||
|
||||
@@ -26,11 +27,13 @@ public class DeltaPackageBuilder
|
||||
public int Removed { get; set; }
|
||||
}
|
||||
|
||||
public (ReleasePackage package, DeltaStats stats) CreateDeltaPackage(ReleasePackage basePackage, ReleasePackage newPackage, string outputFile, DeltaMode mode, Action<int> progress)
|
||||
public (ReleasePackage package, DeltaStats stats) CreateDeltaPackage(ReleasePackage basePackage, ReleasePackage newPackage, string outputFile,
|
||||
DeltaMode mode, Action<int> progress)
|
||||
{
|
||||
if (basePackage == null) throw new ArgumentNullException(nameof(basePackage));
|
||||
if (newPackage == null) throw new ArgumentNullException(nameof(newPackage));
|
||||
if (String.IsNullOrEmpty(outputFile) || File.Exists(outputFile)) throw new ArgumentException("The output file is null or already exists", nameof(outputFile));
|
||||
if (String.IsNullOrEmpty(outputFile) || File.Exists(outputFile))
|
||||
throw new ArgumentException("The output file is null or already exists", nameof(outputFile));
|
||||
|
||||
Zstd zstd = null;
|
||||
try {
|
||||
@@ -137,6 +140,7 @@ public class DeltaPackageBuilder
|
||||
File.WriteAllText(targetFile.FullName + ".shasum", rl.EntryAsString, Encoding.UTF8);
|
||||
Interlocked.Increment(ref fChanged);
|
||||
}
|
||||
|
||||
targetFile.Delete();
|
||||
baseLibFiles.Remove(relativePath);
|
||||
var p = Interlocked.Increment(ref fProcessed);
|
||||
@@ -152,39 +156,47 @@ public class DeltaPackageBuilder
|
||||
}
|
||||
|
||||
try {
|
||||
Parallel.ForEach(newLibFiles, new ParallelOptions() { MaxDegreeOfParallelism = numParallel }, (f) => {
|
||||
// we try to use zstd first, if it fails we'll try bsdiff
|
||||
if (zstd != null) {
|
||||
Parallel.ForEach(
|
||||
newLibFiles,
|
||||
new ParallelOptions() { MaxDegreeOfParallelism = numParallel },
|
||||
(f) => {
|
||||
// we try to use zstd first, if it fails we'll try bsdiff
|
||||
if (zstd != null) {
|
||||
try {
|
||||
createDeltaForSingleFile(f, tempInfo, true);
|
||||
return; // success, so return from this function
|
||||
} catch (ProcessFailedException ex) {
|
||||
_logger.Error(
|
||||
$"Failed to create zstd diff for file '{f.FullName}' (will try to fallback to legacy bsdiff format - this will be much slower). " +
|
||||
Environment.NewLine + ex.Message);
|
||||
} catch (Exception ex) {
|
||||
_logger.Error($"Failed to create zstd diff for file '{f.FullName}'. " + Environment.NewLine + ex.Message);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
// if we're here, either zstd is not available or it failed
|
||||
try {
|
||||
createDeltaForSingleFile(f, tempInfo, true);
|
||||
return; // success, so return from this function
|
||||
} catch (ProcessFailedException ex) {
|
||||
_logger.Error($"Failed to create zstd diff for file '{f.FullName}' (will try to fallback to legacy bsdiff format - this will be much slower). " + Environment.NewLine + ex.Message);
|
||||
createDeltaForSingleFile(f, tempInfo, false);
|
||||
if (zstd != null) {
|
||||
_logger.Info($"Successfully created fallback bsdiff for file '{f.FullName}'.");
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
_logger.Error($"Failed to create zstd diff for file '{f.FullName}'. " + Environment.NewLine + ex.Message);
|
||||
_logger.Error($"Failed to create bsdiff for file '{f.FullName}'. " + Environment.NewLine + ex.Message);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
// if we're here, either zstd is not available or it failed
|
||||
try {
|
||||
createDeltaForSingleFile(f, tempInfo, false);
|
||||
if (zstd != null) {
|
||||
_logger.Info($"Successfully created fallback bsdiff for file '{f.FullName}'.");
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
_logger.Error($"Failed to create bsdiff for file '{f.FullName}'. " + Environment.NewLine + ex.Message);
|
||||
throw;
|
||||
}
|
||||
});
|
||||
});
|
||||
} catch {
|
||||
throw new UserInfoException("Delta creation failed for one or more files. See log for details. To skip delta generation, use the '--delta none' argument.");
|
||||
throw new UserInfoException(
|
||||
"Delta creation failed for one or more files. See log for details. To skip delta generation, use the '--delta none' argument.");
|
||||
}
|
||||
|
||||
EasyZip.CreateZipFromDirectoryAsync(_logger, outputFile, tempInfo.FullName, CoreUtil.CreateProgressDelegate(progress, 70, 100)).GetAwaiterResult();
|
||||
progress(100);
|
||||
fRemoved = baseLibFiles.Count;
|
||||
|
||||
_logger.Info($"Delta processed {fProcessed:D4} files. "
|
||||
_logger.Info(
|
||||
$"Delta processed {fProcessed:D4} files. "
|
||||
+ $"{fChanged:D4} patched, {fSame:D4} unchanged, {fNew:D4} new, {fRemoved:D4} removed");
|
||||
|
||||
_logger.Debug(
|
||||
@@ -241,4 +253,4 @@ public class DeltaPackageBuilder
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -9,7 +9,8 @@ public class ProcessFailedException : Exception
|
||||
public string StdOutput { get; }
|
||||
|
||||
public ProcessFailedException(string command, string stdOutput, string stdErr)
|
||||
: base($"Process failed: '{command}'{Environment.NewLine}Output was -{Environment.NewLine}{stdOutput}{Environment.NewLine}StdErr was -{Environment.NewLine}{stdErr}")
|
||||
: base(
|
||||
$"Process failed: '{command}'{Environment.NewLine}Output was -{Environment.NewLine}{stdOutput}{Environment.NewLine}StdErr was -{Environment.NewLine}{stdErr}")
|
||||
{
|
||||
Command = command;
|
||||
StdOutput = stdOutput;
|
||||
@@ -20,4 +21,4 @@ public class ProcessFailedException : Exception
|
||||
if (result.ExitCode != 0)
|
||||
throw new ProcessFailedException(result.Command, result.StdOutput, result.StdErr);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using Velopack.Core;
|
||||
|
||||
namespace Velopack.Packaging.Exceptions;
|
||||
|
||||
@@ -7,9 +8,9 @@ public class VelopackAppVerificationException : UserInfoException
|
||||
{
|
||||
public VelopackAppVerificationException(string message)
|
||||
: base(
|
||||
$"Failed to verify VelopackApp ({message}). " +
|
||||
$"Ensure you have added the startup code to the beginning of your Program.Main(): VelopackApp.Build().Run(); " +
|
||||
$"and then re-compile/re-publish your application.")
|
||||
$"Failed to verify VelopackApp ({message}). " +
|
||||
$"Ensure you have added the startup code to the beginning of your Program.Main(): VelopackApp.Build().Run(); " +
|
||||
$"and then re-compile/re-publish your application.")
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
using System.Diagnostics;
|
||||
using System.Text;
|
||||
using Velopack.Core;
|
||||
using Velopack.Packaging.Exceptions;
|
||||
using Velopack.Util;
|
||||
|
||||
@@ -134,14 +135,15 @@ public static class Exe
|
||||
process.Kill();
|
||||
ct.ThrowIfCancellationRequested();
|
||||
}
|
||||
|
||||
|
||||
// need to call this once more to wait for the streams to finish. if WaitForExit is called with a timeout, the streams will not be fully read.
|
||||
process.WaitForExit();
|
||||
process.WaitForExit();
|
||||
|
||||
return (process.ExitCode, sOut.ToString().Trim(), sErr.ToString().Trim());
|
||||
}
|
||||
|
||||
public static (int ExitCode, string StdOutput, string StdErr, string Command) InvokeProcess(string fileName, IEnumerable<string> args, string workingDirectory, CancellationToken ct = default,
|
||||
public static (int ExitCode, string StdOutput, string StdErr, string Command) InvokeProcess(string fileName, IEnumerable<string> args,
|
||||
string workingDirectory, CancellationToken ct = default,
|
||||
IDictionary<string, string> envVar = null)
|
||||
{
|
||||
var psi = CreateProcessStartInfo(fileName, workingDirectory);
|
||||
|
||||
@@ -1,9 +0,0 @@
|
||||
namespace Velopack.Packaging.Flow;
|
||||
|
||||
#nullable enable
|
||||
public class AuthConfiguration
|
||||
{
|
||||
public string? B2CAuthority { get; set; }
|
||||
public string? RedirectUri { get; set; }
|
||||
public string? ClientId { get; set; }
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
#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; }
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
namespace Velopack.Packaging.Flow;
|
||||
|
||||
#nullable enable
|
||||
public class Profile
|
||||
{
|
||||
public string? Id { get; set; }
|
||||
public string? DisplayName { get; set; }
|
||||
public string? Email { get; set; }
|
||||
|
||||
public string? GetDisplayName()
|
||||
{
|
||||
return DisplayName ?? Email ?? "<unknown>";
|
||||
}
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
#if NET6_0_OR_GREATER
|
||||
#else
|
||||
using System.Net.Http;
|
||||
#endif
|
||||
|
||||
#nullable enable
|
||||
namespace Velopack.Packaging.Flow;
|
||||
|
||||
internal sealed class FileUpload
|
||||
{
|
||||
public string? Status { get; set; }
|
||||
public string? Md5Hash { get; set; }
|
||||
}
|
||||
|
||||
internal sealed class ReleaseGroup
|
||||
{
|
||||
public Guid Id { get; set; }
|
||||
public string? Version { get; set; }
|
||||
public List<FileUpload>? FileUploads { get; set; }
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
#nullable enable
|
||||
using Newtonsoft.Json.Converters;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace Velopack.Packaging.Flow;
|
||||
|
||||
internal sealed class UpdateReleaseGroupRequest
|
||||
{
|
||||
public string? NotesHtml { get; set; }
|
||||
public string? NotesMarkdown { get; set; }
|
||||
public ReleaseGroupState? State { get; set; }
|
||||
}
|
||||
|
||||
[JsonConverter(typeof(StringEnumConverter))]
|
||||
internal enum ReleaseGroupState
|
||||
{
|
||||
Draft,
|
||||
Published,
|
||||
Unlisted
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
#nullable enable
|
||||
|
||||
namespace Velopack.Packaging.Flow;
|
||||
|
||||
public class UploadOptions(Stream releaseData, string fileName, string channel) : VelopackServiceOptions
|
||||
{
|
||||
public Stream ReleaseData { get; } = releaseData;
|
||||
public string FileName { get; } = fileName;
|
||||
public string Channel { get; } = channel;
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
namespace Velopack.Packaging.Flow;
|
||||
|
||||
public class VelopackServiceOptions
|
||||
{
|
||||
public const string DefaultBaseUrl = "https://api.velopack.io/";
|
||||
|
||||
public string VelopackBaseUrl { get; set; } = DefaultBaseUrl;
|
||||
|
||||
public string ApiKey { get; set; } = string.Empty;
|
||||
}
|
||||
@@ -5,9 +5,10 @@ using Markdig;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using NuGet.Versioning;
|
||||
using Velopack.Compression;
|
||||
using Velopack.Core;
|
||||
using Velopack.Core.Abstractions;
|
||||
using Velopack.NuGet;
|
||||
using Velopack.Packaging.Abstractions;
|
||||
using Velopack.Packaging.Exceptions;
|
||||
using Velopack.Util;
|
||||
|
||||
namespace Velopack.Packaging;
|
||||
@@ -44,8 +45,9 @@ public abstract class PackageBuilder<T> : ICommand<T>
|
||||
options.TargetRuntime = RID.Parse(TargetOs.GetOsShortName());
|
||||
}
|
||||
|
||||
if (options.TargetRuntime.BaseRID != TargetOs) {
|
||||
throw new UserInfoException($"To build packages for {TargetOs.GetOsLongName()}, " +
|
||||
if (options.TargetRuntime!.BaseRID != TargetOs) {
|
||||
throw new UserInfoException(
|
||||
$"To build packages for {TargetOs.GetOsLongName()}, " +
|
||||
$"the target rid must be {TargetOs} (actually was {options.TargetRuntime?.BaseRID}). " +
|
||||
$"If your real intention was to cross-compile a release for {options.TargetRuntime?.BaseRID} then you " +
|
||||
$"should provide an OS directive: eg. 'vpk [{options.TargetRuntime?.BaseRID.GetOsShortName()}] pack ...'");
|
||||
@@ -55,16 +57,19 @@ public abstract class PackageBuilder<T> : ICommand<T>
|
||||
Log.Info("Releases Directory: " + options.ReleaseDir.FullName);
|
||||
|
||||
var releaseDir = options.ReleaseDir;
|
||||
var channel = options.Channel?.ToLower() ?? ReleaseEntryHelper.GetDefaultChannel(TargetOs);
|
||||
var channel = options.Channel?.ToLower() ?? DefaultName.GetDefaultChannel(TargetOs);
|
||||
options.Channel = channel;
|
||||
|
||||
var entryHelper = new ReleaseEntryHelper(releaseDir.FullName, channel, Log, TargetOs);
|
||||
if (entryHelper.DoesSimilarVersionExist(SemanticVersion.Parse(options.PackVersion))) {
|
||||
if (await Console.PromptYesNo("A release in this channel with the same or greater version already exists. Do you want to continue and potentially overwrite files?") != true) {
|
||||
throw new UserInfoException($"There is a release in channel {channel} which is equal or greater to the current version {options.PackVersion}. Please increase the current package version or remove that release.");
|
||||
if (await Console.PromptYesNo(
|
||||
"A release in this channel with the same or greater version already exists. Do you want to continue and potentially overwrite files?") !=
|
||||
true) {
|
||||
throw new UserInfoException(
|
||||
$"There is a release in channel {channel} which is equal or greater to the current version {options.PackVersion}. Please increase the current package version or remove that release.");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
using var _1 = TempUtil.GetTempDirectory(out var pkgTempDir);
|
||||
TempDir = new DirectoryInfo(pkgTempDir);
|
||||
|
||||
@@ -85,10 +90,12 @@ public abstract class PackageBuilder<T> : ICommand<T>
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (mainExePath == null) {
|
||||
throw new UserInfoException(
|
||||
$"Could not find main application executable (the one that runs 'VelopackApp.Build().Run()'). " + Environment.NewLine +
|
||||
$"If your main binary is not named '{mainExeName}', please specify the name with the argument: --mainExe {{yourBinary.exe}}" + Environment.NewLine +
|
||||
$"If your main binary is not named '{mainExeName}', please specify the name with the argument: --mainExe {{yourBinary.exe}}" +
|
||||
Environment.NewLine +
|
||||
$"I searched the following paths and none exist: " + Environment.NewLine +
|
||||
String.Join(Environment.NewLine, mainSearchPaths)
|
||||
);
|
||||
@@ -105,72 +112,93 @@ public abstract class PackageBuilder<T> : ICommand<T>
|
||||
var incomplete = Path.Combine(pkgTempDir, fileName);
|
||||
var final = Path.Combine(releaseDir.FullName, fileName);
|
||||
try { File.Delete(incomplete); } catch { }
|
||||
|
||||
filesToCopy.Add((incomplete, final));
|
||||
return incomplete;
|
||||
}
|
||||
|
||||
await Console.ExecuteProgressAsync(async (ctx) => {
|
||||
ReleasePackage prev = null;
|
||||
await ctx.RunTask("Pre-process steps", async (progress) => {
|
||||
prev = entryHelper.GetPreviousFullRelease(NuGetVersion.Parse(packVersion));
|
||||
packDirectory = await PreprocessPackDir(progress, packDirectory);
|
||||
});
|
||||
await Console.ExecuteProgressAsync(
|
||||
async (ctx) => {
|
||||
ReleasePackage prev = null;
|
||||
await ctx.RunTask(
|
||||
"Pre-process steps",
|
||||
async (progress) => {
|
||||
prev = entryHelper.GetPreviousFullRelease(NuGetVersion.Parse(packVersion));
|
||||
packDirectory = await PreprocessPackDir(progress, packDirectory);
|
||||
});
|
||||
|
||||
if (TargetOs != RuntimeOs.Linux) {
|
||||
await ctx.RunTask("Code-sign application", async (progress) => {
|
||||
await CodeSign(progress, packDirectory);
|
||||
});
|
||||
}
|
||||
|
||||
Task portableTask = null;
|
||||
if (TargetOs == RuntimeOs.Linux || !Options.NoPortable) {
|
||||
portableTask = ctx.RunTask("Building portable package", async (progress) => {
|
||||
var suggestedName = ReleaseEntryHelper.GetSuggestedPortableName(packId, channel, TargetOs);
|
||||
var path = getIncompletePath(suggestedName);
|
||||
await CreatePortablePackage(progress, packDirectory, path);
|
||||
});
|
||||
}
|
||||
|
||||
// TODO: hack, this is a prerequisite for building full package but only on linux
|
||||
if (TargetOs == RuntimeOs.Linux) await portableTask;
|
||||
|
||||
string releasePath = null;
|
||||
await ctx.RunTask($"Building release {packVersion}", async (progress) => {
|
||||
var suggestedName = ReleaseEntryHelper.GetSuggestedReleaseName(packId, packVersion, channel, false, TargetOs);
|
||||
releasePath = getIncompletePath(suggestedName);
|
||||
await CreateReleasePackage(progress, packDirectory, releasePath);
|
||||
});
|
||||
|
||||
Task setupTask = null;
|
||||
if (!Options.NoInst && TargetOs != RuntimeOs.Linux) {
|
||||
setupTask = ctx.RunTask("Building setup package", async (progress) => {
|
||||
var suggestedName = ReleaseEntryHelper.GetSuggestedSetupName(packId, channel, TargetOs);
|
||||
var path = getIncompletePath(suggestedName);
|
||||
await CreateSetupPackage(progress, releasePath, packDirectory, path);
|
||||
});
|
||||
}
|
||||
|
||||
if (prev != null && options.DeltaMode != DeltaMode.None) {
|
||||
await ctx.RunTask($"Building delta {prev.Version} -> {packVersion}", async (progress) => {
|
||||
var suggestedName = ReleaseEntryHelper.GetSuggestedReleaseName(packId, packVersion, channel, true, TargetOs);
|
||||
var deltaPkg = await CreateDeltaPackage(progress, releasePath, prev.PackageFile, getIncompletePath(suggestedName), options.DeltaMode);
|
||||
});
|
||||
}
|
||||
|
||||
if (TargetOs != RuntimeOs.Linux && portableTask != null) await portableTask;
|
||||
if (setupTask != null) await setupTask;
|
||||
|
||||
await ctx.RunTask("Post-process steps", (progress) => {
|
||||
foreach (var f in filesToCopy) {
|
||||
IoUtil.MoveFile(f.from, f.to, true);
|
||||
if (TargetOs != RuntimeOs.Linux) {
|
||||
await ctx.RunTask(
|
||||
"Code-sign application",
|
||||
async (progress) => {
|
||||
await CodeSign(progress, packDirectory);
|
||||
});
|
||||
}
|
||||
|
||||
ReleaseEntryHelper.UpdateReleaseFiles(releaseDir.FullName, Log);
|
||||
BuildAssets.Write(releaseDir.FullName, channel, filesToCopy.Select(x => x.to));
|
||||
progress(100);
|
||||
return Task.CompletedTask;
|
||||
Task portableTask = null;
|
||||
if (TargetOs == RuntimeOs.Linux || !Options.NoPortable) {
|
||||
portableTask = ctx.RunTask(
|
||||
"Building portable package",
|
||||
async (progress) => {
|
||||
var suggestedName = DefaultName.GetSuggestedPortableName(packId, channel, TargetOs);
|
||||
var path = getIncompletePath(suggestedName);
|
||||
await CreatePortablePackage(progress, packDirectory, path);
|
||||
});
|
||||
}
|
||||
|
||||
// TODO: hack, this is a prerequisite for building full package but only on linux
|
||||
if (TargetOs == RuntimeOs.Linux) await portableTask!;
|
||||
|
||||
string releasePath = null;
|
||||
await ctx.RunTask(
|
||||
$"Building release {packVersion}",
|
||||
async (progress) => {
|
||||
var suggestedName = DefaultName.GetSuggestedReleaseName(packId, packVersion, channel, false, TargetOs);
|
||||
releasePath = getIncompletePath(suggestedName);
|
||||
await CreateReleasePackage(progress, packDirectory, releasePath);
|
||||
});
|
||||
|
||||
Task setupTask = null;
|
||||
if (!Options.NoInst && TargetOs != RuntimeOs.Linux) {
|
||||
setupTask = ctx.RunTask(
|
||||
"Building setup package",
|
||||
async (progress) => {
|
||||
var suggestedName = DefaultName.GetSuggestedSetupName(packId, channel, TargetOs);
|
||||
var path = getIncompletePath(suggestedName);
|
||||
await CreateSetupPackage(progress, releasePath, packDirectory, path);
|
||||
});
|
||||
}
|
||||
|
||||
if (prev != null && options.DeltaMode != DeltaMode.None) {
|
||||
await ctx.RunTask(
|
||||
$"Building delta {prev.Version} -> {packVersion}",
|
||||
async (progress) => {
|
||||
var suggestedName = DefaultName.GetSuggestedReleaseName(packId, packVersion, channel, true, TargetOs);
|
||||
var deltaPkg = await CreateDeltaPackage(
|
||||
progress,
|
||||
releasePath,
|
||||
prev.PackageFile,
|
||||
getIncompletePath(suggestedName),
|
||||
options.DeltaMode);
|
||||
});
|
||||
}
|
||||
|
||||
if (TargetOs != RuntimeOs.Linux && portableTask != null) await portableTask;
|
||||
if (setupTask != null) await setupTask;
|
||||
|
||||
await ctx.RunTask(
|
||||
"Post-process steps",
|
||||
(progress) => {
|
||||
foreach (var f in filesToCopy) {
|
||||
IoUtil.MoveFile(f.from, f.to, true);
|
||||
}
|
||||
|
||||
ReleaseEntryHelper.UpdateReleaseFiles(releaseDir.FullName, Log);
|
||||
BuildAssets.Write(releaseDir.FullName, channel, filesToCopy.Select(x => x.to));
|
||||
progress(100);
|
||||
return Task.CompletedTask;
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
protected virtual string ExtractPackDir(string packDirectory) => packDirectory;
|
||||
@@ -187,12 +215,14 @@ public abstract class PackageBuilder<T> : ICommand<T>
|
||||
var rid = Options.TargetRuntime;
|
||||
|
||||
string extraMetadata = "";
|
||||
|
||||
void addMetadata(string key, string value)
|
||||
{
|
||||
if (!String.IsNullOrEmpty(key) && !String.IsNullOrEmpty(value)) {
|
||||
if (!SecurityElement.IsValidText(value)) {
|
||||
value = $"""<![CDATA[{"\n"}{value}{"\n"}]]>""";
|
||||
}
|
||||
|
||||
extraMetadata += $"<{key}>{value}</{key}>{Environment.NewLine}";
|
||||
}
|
||||
}
|
||||
@@ -218,22 +248,22 @@ public abstract class PackageBuilder<T> : ICommand<T>
|
||||
}
|
||||
|
||||
string nuspec = $"""
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
|
||||
<metadata>
|
||||
<id>{packId}</id>
|
||||
<title>{packTitle ?? packId}</title>
|
||||
<description>{packTitle ?? packId}</description>
|
||||
<authors>{packAuthors ?? packId}</authors>
|
||||
<version>{packVersion}</version>
|
||||
<channel>{Options.Channel}</channel>
|
||||
<mainExe>{Options.EntryExecutableName}</mainExe>
|
||||
<os>{rid.BaseRID.GetOsShortName()}</os>
|
||||
<rid>{rid.ToDisplay(RidDisplayType.NoVersion)}</rid>
|
||||
{extraMetadata.Trim()}
|
||||
</metadata>
|
||||
</package>
|
||||
""".Trim();
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
|
||||
<metadata>
|
||||
<id>{packId}</id>
|
||||
<title>{packTitle ?? packId}</title>
|
||||
<description>{packTitle ?? packId}</description>
|
||||
<authors>{packAuthors ?? packId}</authors>
|
||||
<version>{packVersion}</version>
|
||||
<channel>{Options.Channel}</channel>
|
||||
<mainExe>{Options.EntryExecutableName}</mainExe>
|
||||
<os>{rid.BaseRID.GetOsShortName()}</os>
|
||||
<rid>{rid.ToDisplay(RidDisplayType.NoVersion)}</rid>
|
||||
{extraMetadata.Trim()}
|
||||
</metadata>
|
||||
</package>
|
||||
""".Trim();
|
||||
|
||||
return nuspec;
|
||||
}
|
||||
@@ -315,6 +345,7 @@ public abstract class PackageBuilder<T> : ICommand<T>
|
||||
Log.Debug("Skipping because matched exclude pattern: " + path);
|
||||
continue;
|
||||
}
|
||||
|
||||
fileInfo.CopyTo(path, true);
|
||||
}
|
||||
|
||||
@@ -356,12 +387,12 @@ public abstract class PackageBuilder<T> : ICommand<T>
|
||||
.ToArray();
|
||||
|
||||
var contentType = $"""
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Types xmlns="http://schemas.openxmlformats.org/package/2006/content-types">
|
||||
<Default Extension="rels" ContentType="application/vnd.openxmlformats-package.relationships+xml" />
|
||||
{String.Join(Environment.NewLine, extensions)}
|
||||
</Types>
|
||||
""";
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Types xmlns="http://schemas.openxmlformats.org/package/2006/content-types">
|
||||
<Default Extension="rels" ContentType="application/vnd.openxmlformats-package.relationships+xml" />
|
||||
{String.Join(Environment.NewLine, extensions)}
|
||||
</Types>
|
||||
""";
|
||||
|
||||
File.WriteAllText(Path.Combine(rootDirectory, NugetUtil.ContentTypeFileName), contentType);
|
||||
|
||||
@@ -369,11 +400,11 @@ public abstract class PackageBuilder<T> : ICommand<T>
|
||||
Directory.CreateDirectory(relsDir);
|
||||
|
||||
var rels = $"""
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">
|
||||
<Relationship Type="http://schemas.microsoft.com/packaging/2010/07/manifest" Target="/{Path.GetFileName(nuspecPath)}" Id="R1" />
|
||||
</Relationships>
|
||||
""";
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">
|
||||
<Relationship Type="http://schemas.microsoft.com/packaging/2010/07/manifest" Target="/{Path.GetFileName(nuspecPath)}" Id="R1" />
|
||||
</Relationships>
|
||||
""";
|
||||
File.WriteAllText(Path.Combine(relsDir, ".rels"), rels);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
using System.Text;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using NuGet.Versioning;
|
||||
using Velopack.Core;
|
||||
using Velopack.NuGet;
|
||||
using Velopack.Util;
|
||||
|
||||
@@ -17,7 +18,7 @@ public class ReleaseEntryHelper
|
||||
{
|
||||
_outputDir = outputDir;
|
||||
_logger = logger;
|
||||
_channel = channel ?? GetDefaultChannel(os);
|
||||
_channel = channel ?? DefaultName.GetDefaultChannel(os);
|
||||
_releases = GetReleasesFromDir(outputDir);
|
||||
}
|
||||
|
||||
@@ -26,11 +27,12 @@ public class ReleaseEntryHelper
|
||||
var rel = new Dictionary<string, List<VelopackAsset>>(StringComparer.OrdinalIgnoreCase);
|
||||
foreach (var releaseFile in Directory.EnumerateFiles(dir, "*.nupkg")) {
|
||||
var zip = new ZipPackage(releaseFile);
|
||||
var ch = zip.Channel ?? GetDefaultChannel(VelopackRuntimeInfo.SystemOs);
|
||||
var ch = zip.Channel ?? DefaultName.GetDefaultChannel(VelopackRuntimeInfo.SystemOs);
|
||||
if (!rel.ContainsKey(ch))
|
||||
rel[ch] = new List<VelopackAsset>();
|
||||
rel[ch].Add(VelopackAsset.FromZipPackage(zip));
|
||||
}
|
||||
|
||||
return rel;
|
||||
}
|
||||
|
||||
@@ -43,6 +45,7 @@ public class ReleaseEntryHelper
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -94,10 +97,12 @@ public class ReleaseEntryHelper
|
||||
foreach (var releaseFile in Directory.EnumerateFiles(outputDir, "RELEASES*")) {
|
||||
File.Delete(releaseFile);
|
||||
}
|
||||
|
||||
foreach (var kvp in releases) {
|
||||
var exclude = kvp.Value.Where(x => x.Version.ReleaseLabels.Any(r => r.Contains('.')) || x.Version.HasMetadata).ToArray();
|
||||
if (exclude.Any()) {
|
||||
log.Warn($"Excluding {exclude.Length} asset(s) from legacy RELEASES file, because they " +
|
||||
log.Warn(
|
||||
$"Excluding {exclude.Length} asset(s) from legacy RELEASES file, because they " +
|
||||
$"contain an invalid character in the version: {string.Join(", ", exclude.Select(x => x.FileName))}");
|
||||
}
|
||||
|
||||
@@ -151,54 +156,6 @@ public class ReleaseEntryHelper
|
||||
return Encoding.UTF8.GetString(ms.ToArray());
|
||||
}
|
||||
|
||||
public static string GetSuggestedReleaseName(string id, string version, string channel, bool delta, RuntimeOs os)
|
||||
{
|
||||
var suffix = GetUniqueAssetSuffix(channel);
|
||||
version = SemanticVersion.Parse(version).ToNormalizedString();
|
||||
if (os == RuntimeOs.Windows && channel == GetDefaultChannel(RuntimeOs.Windows)) {
|
||||
return $"{id}-{version}{(delta ? "-delta" : "-full")}.nupkg";
|
||||
}
|
||||
return $"{id}-{version}{suffix}{(delta ? "-delta" : "-full")}.nupkg";
|
||||
}
|
||||
|
||||
public static string GetSuggestedPortableName(string id, string channel, RuntimeOs os)
|
||||
{
|
||||
var suffix = GetUniqueAssetSuffix(channel);
|
||||
if (os == RuntimeOs.Linux) {
|
||||
if (channel == GetDefaultChannel(RuntimeOs.Linux)) {
|
||||
return $"{id}.AppImage";
|
||||
} else {
|
||||
return $"{id}{suffix}.AppImage";
|
||||
}
|
||||
} else {
|
||||
return $"{id}{suffix}-Portable.zip";
|
||||
}
|
||||
}
|
||||
|
||||
public static string GetSuggestedSetupName(string id, string channel, RuntimeOs os)
|
||||
{
|
||||
var suffix = GetUniqueAssetSuffix(channel);
|
||||
if (os == RuntimeOs.Windows)
|
||||
return $"{id}{suffix}-Setup.exe";
|
||||
else if (os == RuntimeOs.OSX)
|
||||
return $"{id}{suffix}-Setup.pkg";
|
||||
else
|
||||
throw new PlatformNotSupportedException("Platform not supported.");
|
||||
}
|
||||
|
||||
private static string GetUniqueAssetSuffix(string channel)
|
||||
{
|
||||
return "-" + channel;
|
||||
}
|
||||
|
||||
public static string GetDefaultChannel(RuntimeOs os)
|
||||
{
|
||||
if (os == RuntimeOs.Windows) return "win";
|
||||
if (os == RuntimeOs.OSX) return "osx";
|
||||
if (os == RuntimeOs.Linux) return "linux";
|
||||
throw new NotSupportedException("Unsupported OS: " + os);
|
||||
}
|
||||
|
||||
public enum AssetsMode
|
||||
{
|
||||
AllPackages,
|
||||
@@ -267,6 +224,4 @@ public class ReleaseEntryHelper
|
||||
|
||||
// return ret;
|
||||
//}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
@@ -16,4 +16,4 @@ public class ReleasePackage
|
||||
public string PackageFile { get; protected set; }
|
||||
|
||||
public SemanticVersion Version => _package.Value.Version;
|
||||
}
|
||||
}
|
||||
@@ -9,6 +9,7 @@
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\lib-csharp\Velopack.csproj" />
|
||||
<ProjectReference Include="..\Velopack.Core\Velopack.Core.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using Velopack.Packaging.Exceptions;
|
||||
using Velopack.Core;
|
||||
using Velopack.Packaging.Exceptions;
|
||||
|
||||
namespace Velopack.Packaging;
|
||||
|
||||
@@ -30,7 +31,8 @@ public class Zstd
|
||||
}
|
||||
|
||||
if (windowLog > 30) {
|
||||
throw new UserInfoException($"The file '{Path.GetFileName(oldFile)}' is too large for delta compression. You can disable delta generation using '--delta none'.");
|
||||
throw new UserInfoException(
|
||||
$"The file '{Path.GetFileName(oldFile)}' is too large for delta compression. You can disable delta generation using '--delta none'.");
|
||||
}
|
||||
|
||||
if (mode == DeltaMode.BestSize) {
|
||||
@@ -64,7 +66,11 @@ public class Zstd
|
||||
{
|
||||
int count = 0;
|
||||
v >>= 1;
|
||||
while (v > 0) { v >>= 1; count++; }
|
||||
while (v > 0) {
|
||||
v >>= 1;
|
||||
count++;
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
using Velopack.Packaging;
|
||||
using Velopack.Core;
|
||||
using Velopack.Packaging;
|
||||
|
||||
namespace Velopack.Vpk.Commands;
|
||||
|
||||
|
||||
@@ -1,26 +0,0 @@
|
||||
using System.Threading;
|
||||
using Serilog.Core;
|
||||
using Velopack.Packaging.Abstractions;
|
||||
using Velopack.Packaging.Flow;
|
||||
|
||||
namespace Velopack.Vpk.Commands.Flow;
|
||||
public class ApiCommandRunner(IVelopackFlowServiceClient Client) : ICommand<ApiOptions>
|
||||
{
|
||||
public async Task Run(ApiOptions options)
|
||||
{
|
||||
CancellationToken token = CancellationToken.None;
|
||||
if (!await Client.LoginAsync(new VelopackLoginOptions() {
|
||||
AllowCacheCredentials = true,
|
||||
AllowDeviceCodeFlow = false,
|
||||
AllowInteractiveLogin = false,
|
||||
ApiKey = options.ApiKey,
|
||||
VelopackBaseUrl = options.VelopackBaseUrl
|
||||
}, true, token)) {
|
||||
return;
|
||||
}
|
||||
|
||||
string response = await Client.InvokeEndpointAsync(options, options.Endpoint, options.Method, options.Body, token);
|
||||
Console.WriteLine(response);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +0,0 @@
|
||||
#nullable enable
|
||||
using Velopack.Packaging.Flow;
|
||||
|
||||
namespace Velopack.Vpk.Commands.Flow;
|
||||
|
||||
public sealed class ApiOptions : VelopackServiceOptions
|
||||
{
|
||||
public string Endpoint { get; set; } = "";
|
||||
public string Method { get; set; } = "";
|
||||
public string? Body { get; set; }
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
using System.Threading;
|
||||
using Velopack.Packaging.Abstractions;
|
||||
using Velopack.Packaging.Flow;
|
||||
|
||||
namespace Velopack.Vpk.Commands.Flow;
|
||||
#nullable enable
|
||||
|
||||
public class LoginCommandRunner(IVelopackFlowServiceClient Client) : ICommand<LoginOptions>
|
||||
{
|
||||
public async Task Run(LoginOptions options)
|
||||
{
|
||||
await Client.LoginAsync(new() {
|
||||
VelopackBaseUrl = options.VelopackBaseUrl,
|
||||
ApiKey = options.ApiKey,
|
||||
}, false, CancellationToken.None);
|
||||
}
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
using Velopack.Packaging.Flow;
|
||||
|
||||
namespace Velopack.Vpk.Commands.Flow;
|
||||
|
||||
public sealed class LoginOptions : VelopackServiceOptions;
|
||||
@@ -1,14 +0,0 @@
|
||||
using System.Threading;
|
||||
using Velopack.Packaging.Abstractions;
|
||||
using Velopack.Packaging.Flow;
|
||||
|
||||
#nullable enable
|
||||
namespace Velopack.Vpk.Commands.Flow;
|
||||
|
||||
internal class LogoutCommandRunner(IVelopackFlowServiceClient Client) : ICommand<LogoutOptions>
|
||||
{
|
||||
public async Task Run(LogoutOptions options)
|
||||
{
|
||||
await Client.LogoutAsync(options, CancellationToken.None);
|
||||
}
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
using Velopack.Packaging.Flow;
|
||||
|
||||
namespace Velopack.Vpk.Commands.Flow;
|
||||
|
||||
public sealed class LogoutOptions : VelopackServiceOptions;
|
||||
@@ -7,7 +7,7 @@ public class PublishCommand : VelopackServiceCommand
|
||||
|
||||
public string? Channel { get; set; }
|
||||
|
||||
public bool NoWaitForLive { get; set; }
|
||||
public bool WaitForLive { get; set; }
|
||||
|
||||
public PublishCommand()
|
||||
: base("publish", "Uploads a release to Velopack's hosted service")
|
||||
@@ -21,7 +21,7 @@ public class PublishCommand : VelopackServiceCommand
|
||||
.SetArgumentHelpName("NAME")
|
||||
.SetDescription("The channel used for the release.");
|
||||
|
||||
AddOption<bool>(v => NoWaitForLive = v, "--noWaitForLive")
|
||||
.SetDescription("Skip waiting for the release to finish processing and go live.");
|
||||
AddOption<bool>(v => WaitForLive = v, "--waitForLive")
|
||||
.SetDescription("Wait for the release to finish processing and go live.");
|
||||
}
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
using System.Threading;
|
||||
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)
|
||||
{
|
||||
CancellationToken token = CancellationToken.None;
|
||||
if (!await Client.LoginAsync(new VelopackLoginOptions() {
|
||||
AllowCacheCredentials = true,
|
||||
AllowDeviceCodeFlow = false,
|
||||
AllowInteractiveLogin = false,
|
||||
ApiKey = options.ApiKey,
|
||||
VelopackBaseUrl = options.VelopackBaseUrl
|
||||
}, false, token)) {
|
||||
return;
|
||||
}
|
||||
|
||||
await Client.UploadLatestReleaseAssetsAsync(options.Channel, options.ReleaseDirectory,
|
||||
options.VelopackBaseUrl, options.TargetOs, options.NoWaitForLive, token);
|
||||
}
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
using Velopack.Packaging.Flow;
|
||||
|
||||
namespace Velopack.Vpk.Commands.Flow;
|
||||
|
||||
#nullable enable
|
||||
public sealed class PublishOptions : VelopackServiceOptions
|
||||
{
|
||||
public RuntimeOs TargetOs { get; set; }
|
||||
|
||||
public string ReleaseDirectory { get; set; } = "";
|
||||
|
||||
public string? Channel { get; set; }
|
||||
|
||||
public bool NoWaitForLive { get; set; }
|
||||
}
|
||||
@@ -1,23 +1,27 @@
|
||||
using Velopack.Packaging.Flow;
|
||||
|
||||
namespace Velopack.Vpk.Commands.Flow;
|
||||
namespace Velopack.Vpk.Commands.Flow;
|
||||
|
||||
public abstract class VelopackServiceCommand : BaseCommand
|
||||
{
|
||||
public string VelopackBaseUrl { get; private set; }
|
||||
|
||||
public string ApiKey { get; private set; }
|
||||
|
||||
public double Timeout { get; private set; }
|
||||
|
||||
protected VelopackServiceCommand(string name, string description)
|
||||
: base(name, description)
|
||||
{
|
||||
AddOption<string>(v => VelopackBaseUrl = v, "--baseUrl")
|
||||
.SetDescription("The base Uri for the Velopack API service.")
|
||||
.SetArgumentHelpName("URI")
|
||||
.SetDefault(VelopackServiceOptions.DefaultBaseUrl);
|
||||
.SetArgumentHelpName("URI");
|
||||
|
||||
AddOption<string>(v => ApiKey = v, "--api-key")
|
||||
.SetDescription("The API key to use to authenticate with Velopack API service.")
|
||||
.SetArgumentHelpName("ApiKey");
|
||||
|
||||
AddOption<double>((v) => Timeout = v, "--timeout")
|
||||
.SetDescription("Network timeout in minutes.")
|
||||
.SetArgumentHelpName("MINUTES")
|
||||
.SetDefault(30);
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
using Velopack.Packaging;
|
||||
using Velopack.Core;
|
||||
using Velopack.Packaging;
|
||||
|
||||
namespace Velopack.Vpk.Commands;
|
||||
|
||||
@@ -24,7 +25,7 @@ public abstract class OutputCommand : BaseCommand
|
||||
.SetDescription("The channel to use for this release.")
|
||||
.RequiresValidNuGetId()
|
||||
.SetArgumentHelpName("NAME")
|
||||
.SetDefault(ReleaseEntryHelper.GetDefaultChannel(targetOs == RuntimeOs.Unknown ? VelopackRuntimeInfo.SystemOs : targetOs));
|
||||
.SetDefault(DefaultName.GetDefaultChannel(targetOs == RuntimeOs.Unknown ? VelopackRuntimeInfo.SystemOs : targetOs));
|
||||
}
|
||||
|
||||
public DirectoryInfo GetReleaseDirectory()
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using Velopack.Packaging.Abstractions;
|
||||
using Velopack.Core.Abstractions;
|
||||
using Velopack.Util;
|
||||
|
||||
namespace Velopack.Vpk.Logging;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
using System.Threading;
|
||||
using Spectre.Console;
|
||||
using Velopack.Packaging.Abstractions;
|
||||
using Velopack.Core.Abstractions;
|
||||
using Velopack.Util;
|
||||
|
||||
namespace Velopack.Vpk.Logging;
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using Riok.Mapperly.Abstractions;
|
||||
using Velopack.Deployment;
|
||||
using Velopack.Flow.Commands;
|
||||
using Velopack.Packaging.Commands;
|
||||
using Velopack.Packaging.Unix.Commands;
|
||||
using Velopack.Packaging.Windows.Commands;
|
||||
|
||||
@@ -5,11 +5,13 @@ using Microsoft.Extensions.Hosting;
|
||||
using Serilog;
|
||||
using Serilog.Core;
|
||||
using Serilog.Events;
|
||||
using Velopack.Core;
|
||||
using Velopack.Core.Abstractions;
|
||||
using Velopack.Deployment;
|
||||
using Velopack.Packaging.Abstractions;
|
||||
using Velopack.Flow;
|
||||
using Velopack.Flow.Commands;
|
||||
using Velopack.Packaging.Commands;
|
||||
using Velopack.Packaging.Exceptions;
|
||||
using Velopack.Packaging.Flow;
|
||||
using Velopack.Packaging.Unix.Commands;
|
||||
using Velopack.Packaging.Windows.Commands;
|
||||
using Velopack.Util;
|
||||
@@ -94,8 +96,7 @@ public class Program
|
||||
|
||||
SetupConfig(builder);
|
||||
SetupLogging(builder, verbose, legacyConsole);
|
||||
SetupVelopackService(builder.Services);
|
||||
|
||||
|
||||
RuntimeOs targetOs = VelopackRuntimeInfo.SystemOs;
|
||||
if (new bool[] { directiveWin, directiveLinux, directiveOsx }.Count(x => x) > 1) {
|
||||
throw new UserInfoException(
|
||||
@@ -208,15 +209,6 @@ public class Program
|
||||
Log.Logger = conf.CreateLogger();
|
||||
builder.Logging.AddSerilog();
|
||||
}
|
||||
|
||||
private static void SetupVelopackService(IServiceCollection services)
|
||||
{
|
||||
services.AddSingleton<IVelopackFlowServiceClient, VelopackFlowServiceClient>();
|
||||
services.AddSingleton<HmacAuthHttpClientHandler>();
|
||||
services.AddHttpClient().ConfigureHttpClientDefaults(x =>
|
||||
x.AddHttpMessageHandler<HmacAuthHttpClientHandler>()
|
||||
.ConfigureHttpClient(httpClient => httpClient.Timeout = TimeSpan.FromMinutes(60)));
|
||||
}
|
||||
}
|
||||
|
||||
public static class ProgramCommandExtensions
|
||||
|
||||
@@ -33,6 +33,7 @@
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Velopack.Deployment\Velopack.Deployment.csproj" />
|
||||
<ProjectReference Include="..\Velopack.Flow\Velopack.Flow.csproj" />
|
||||
<ProjectReference Include="..\Velopack.Packaging.Unix\Velopack.Packaging.Unix.csproj" />
|
||||
<ProjectReference Include="..\Velopack.Packaging.Windows\Velopack.Packaging.Windows.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using Neovolve.Logging.Xunit;
|
||||
using Velopack.Core;
|
||||
using Velopack.Packaging.Exceptions;
|
||||
using Velopack.Packaging.Windows;
|
||||
using Velopack.Util;
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using Velopack.Deployment;
|
||||
using Velopack.Sources;
|
||||
using Octokit;
|
||||
using Velopack.Core;
|
||||
using Velopack.Packaging.Exceptions;
|
||||
using Velopack.Util;
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@ using System.Xml.Linq;
|
||||
using Microsoft.Win32;
|
||||
using NuGet.Packaging;
|
||||
using Velopack.Compression;
|
||||
using Velopack.Core;
|
||||
using Velopack.Packaging.Commands;
|
||||
using Velopack.Packaging.Exceptions;
|
||||
using Velopack.Packaging.Windows.Commands;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#pragma warning disable CS0618 // Type or member is obsolete
|
||||
#pragma warning disable CS0612 // Type or member is obsolete
|
||||
using System.Text;
|
||||
using Velopack.Packaging;
|
||||
using Velopack.Core;
|
||||
using Velopack.Sources;
|
||||
using Velopack.Util;
|
||||
|
||||
@@ -39,13 +39,14 @@ internal class FakeFixtureRepository : Sources.IFileDownloader
|
||||
var name = new ReleaseEntryName(maxfullVer.PackageId, maxDeltaVer.Version, false);
|
||||
releases.Add(new ReleaseEntry("0000000000000000000000000000000000000000", name.ToFileName(), maxfullVer.Filesize));
|
||||
|
||||
releasesNew.Add(new VelopackAsset {
|
||||
PackageId = maxfullVer.PackageId,
|
||||
Version = maxDeltaVer.Version,
|
||||
Type = VelopackAssetType.Full,
|
||||
FileName = $"{maxfullVer.PackageId}-{maxDeltaVer.Version}-full.nupkg",
|
||||
Size = maxfullVer.Filesize,
|
||||
});
|
||||
releasesNew.Add(
|
||||
new VelopackAsset {
|
||||
PackageId = maxfullVer.PackageId,
|
||||
Version = maxDeltaVer.Version,
|
||||
Type = VelopackAssetType.Full,
|
||||
FileName = $"{maxfullVer.PackageId}-{maxDeltaVer.Version}-full.nupkg",
|
||||
Size = maxfullVer.Filesize,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -80,7 +81,8 @@ internal class FakeFixtureRepository : Sources.IFileDownloader
|
||||
return Task.FromResult(File.ReadAllBytes(filePath));
|
||||
}
|
||||
|
||||
public Task DownloadFile(string url, string targetFile, Action<int> progress, string authorization = null, string accept = null, double timeout = 30, CancellationToken token = default)
|
||||
public Task DownloadFile(string url, string targetFile, Action<int> progress, string authorization = null, string accept = null, double timeout = 30,
|
||||
CancellationToken token = default)
|
||||
{
|
||||
var rel = _releases.FirstOrDefault(r => url.EndsWith(r.OriginalFilename));
|
||||
var filePath = PathHelper.GetFixture(rel.OriginalFilename);
|
||||
@@ -111,4 +113,4 @@ internal class FakeFixtureRepository : Sources.IFileDownloader
|
||||
|
||||
throw new NotSupportedException("FakeFixtureRepository doesn't have: " + url);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
using System.Text;
|
||||
using NuGet.Versioning;
|
||||
using Velopack.Compression;
|
||||
using Velopack.Packaging;
|
||||
using Velopack.Core;
|
||||
using Velopack.Locators;
|
||||
using Velopack.Sources;
|
||||
using Velopack.Tests.TestHelpers;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
|
||||
<PropertyGroup>
|
||||
<NoWarn>$(NoWarn);CA1416</NoWarn>
|
||||
</PropertyGroup>
|
||||
@@ -19,29 +19,29 @@
|
||||
</Choose>
|
||||
|
||||
<ItemGroup>
|
||||
<Compile Include="..\..\src\vpk\Velopack.Packaging\SimpleJson.cs" Link="SimpleJson.cs" />
|
||||
<Compile Include="..\..\src\vpk\Velopack.Core\SimpleJson.cs" Link="SimpleJson.cs"/>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="System.IO.Packaging" Version="9.0.0" />
|
||||
<PackageReference Include="System.IO.Packaging" Version="9.0.0"/>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup Condition=" $(TargetFramework.StartsWith('net4')) ">
|
||||
<Reference Include="System.Web" />
|
||||
<Reference Include="System.Net.Http" />
|
||||
<Reference Include="System.IO.Compression" />
|
||||
<Reference Include="System.IO.Compression.FileSystem" />
|
||||
<Reference Include="System.Web"/>
|
||||
<Reference Include="System.Net.Http"/>
|
||||
<Reference Include="System.IO.Compression"/>
|
||||
<Reference Include="System.IO.Compression.FileSystem"/>
|
||||
</ItemGroup>
|
||||
|
||||
<Choose>
|
||||
<When Condition="'$(TargetFramework)' == 'net6.0'">
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\src\lib-csharp\Velopack.csproj" SetTargetFramework="TargetFramework=netstandard2.0" />
|
||||
<ProjectReference Include="..\..\src\lib-csharp\Velopack.csproj" SetTargetFramework="TargetFramework=netstandard2.0"/>
|
||||
</ItemGroup>
|
||||
</When>
|
||||
<Otherwise>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\src\lib-csharp\Velopack.csproj" />
|
||||
<ProjectReference Include="..\..\src\lib-csharp\Velopack.csproj"/>
|
||||
</ItemGroup>
|
||||
</Otherwise>
|
||||
</Choose>
|
||||
|
||||
Reference in New Issue
Block a user