update buildassets and fix publish after api update

This commit is contained in:
Caelan Sayler
2024-12-28 15:59:43 +00:00
committed by Caelan
parent 066d43c50e
commit 863e9a16ba
8 changed files with 92 additions and 64 deletions

View File

@@ -15,7 +15,11 @@ namespace Velopack
/// <summary> A full update package. </summary>
Full = 1,
/// <summary> A delta update package. </summary>
Delta,
Delta = 2,
/// <summary> A portable application zip archive. </summary>
Portable = 3,
/// <summary> An application installer archive. </summary>
Installer = 4,
}
/// <summary>

View File

@@ -1,47 +1,60 @@
using Newtonsoft.Json;
using System.Collections.Concurrent;
using Velopack.Util;
namespace Velopack.Core;
public class BuildAssets
public class BuildAssets(string outputDir, string channel)
{
[JsonIgnore]
private string? _outputDir;
public int Count => Assets.Count;
public List<string>? RelativeFileNames { get; set; } = [];
class Asset
{
public string RelativeFileName { get; set; } = string.Empty;
public VelopackAssetType Type { get; set; }
}
private ConcurrentBag<Asset> Assets { get; set; } = [];
public List<VelopackAsset> GetReleaseEntries()
{
return GetFilePaths()
.Where(x => x.EndsWith(".nupkg", StringComparison.OrdinalIgnoreCase))
.Select(VelopackAsset.FromNupkg)
return GetAssets()
.Where(x => x.Type is VelopackAssetType.Delta or VelopackAssetType.Full)
.Select(x => VelopackAsset.FromNupkg(x.Path))
.ToList();
}
public List<string> GetNonReleaseAssetPaths()
public IEnumerable<(string Path, VelopackAssetType Type)> GetAssets()
{
return GetFilePaths()
.Where(x => !x.EndsWith(".nupkg", StringComparison.OrdinalIgnoreCase))
.ToList();
return Assets.Select(asset => (Path.Combine(outputDir, asset.RelativeFileName), asset.Type));
}
public IEnumerable<string> GetFilePaths()
{
if (RelativeFileNames is { } relativeFileNames && _outputDir is { } outputDir) {
return relativeFileNames.Select(f => Path.GetFullPath(Path.Combine(outputDir, f))).ToList();
}
return [];
return Assets.Select(x => Path.Combine(outputDir, x.RelativeFileName));
}
public static void Write(string outputDir, string channel, IEnumerable<string> files)
public string MakeAssetPath(string relativePath, VelopackAssetType type)
{
// var relativeFileName = PathUtil.MakePathRelativeTo(outputDir, fullPath);
Assets.Add(new Asset { RelativeFileName = relativePath, Type = type });
return Path.Combine(outputDir, relativePath);
}
public void MoveBagTo(string newOutputDir)
{
foreach (var asset in Assets) {
var from = Path.Combine(outputDir, asset.RelativeFileName);
var to = Path.Combine(newOutputDir, asset.RelativeFileName);
IoUtil.MoveFile(from, to, true);
}
outputDir = newOutputDir;
}
public void Write()
{
var assets = new BuildAssets {
RelativeFileNames = files.OrderBy(f => f)
.Select(f => PathUtil.MakePathRelativeTo(outputDir, f))
.ToList(),
};
var path = Path.Combine(outputDir, $"assets.{channel}.json");
var json = SimpleJson.SerializeObject(assets);
var json = SimpleJson.SerializeObject(Assets);
File.WriteAllText(path, json);
}
@@ -54,8 +67,9 @@ public class BuildAssets
$"If you've just created a Velopack release, verify you're calling this command with the same '--channel' as you did with 'pack'.");
}
var assets = SimpleJson.DeserializeObject<BuildAssets>(File.ReadAllText(path)) ?? new();
assets._outputDir = outputDir;
return assets;
var me = new BuildAssets(outputDir, channel) {
Assets = SimpleJson.DeserializeObject<ConcurrentBag<Asset>>(File.ReadAllText(path)) ?? []
};
return me;
}
}

View File

@@ -62,7 +62,7 @@ public class GitHubRepository(ILogger logger) : SourceRepository<GitHubDownloadO
var semVer = options.TagName ?? latest.Version.ToString();
var releaseName = string.IsNullOrWhiteSpace(options.ReleaseName) ? semVer.ToString() : options.ReleaseName;
Log.Info($"Preparing to upload {build.RelativeFileNames.Count} asset(s) to GitHub");
Log.Info($"Preparing to upload {build.Count} asset(s) to GitHub");
var client = new GitHubClient(new ProductHeaderValue("Velopack")) {
Credentials = new Credentials(options.Token)

View File

@@ -82,7 +82,7 @@ public class GiteaRepository : SourceRepository<GiteaDownloadOptions, GiteaSourc
config.BasePath = baseUri + "/api/v1";
config.Timeout = (int)TimeSpan.FromMinutes(options.Timeout).TotalMilliseconds;
Log.Info($"Preparing to upload {build.RelativeFileNames.Count} asset(s) to Gitea");
Log.Info($"Preparing to upload {build.Count} asset(s) to Gitea");
// Set token if provided
if (!string.IsNullOrWhiteSpace(options.Token)) {

View File

@@ -53,7 +53,7 @@ public abstract class ObjectRepository<TDown, TUp, TClient> : DownRepository<TDo
var build = BuildAssets.Read(options.ReleaseDir.FullName, options.Channel);
var client = CreateClient(options);
Log.Info($"Preparing to upload {build.RelativeFileNames.Count} local asset(s).");
Log.Info($"Preparing to upload {build.Count} local asset(s).");
var remoteReleases = await GetReleasesAsync(options);
Log.Info($"There are {remoteReleases.Assets.Length} asset(s) in remote releases file.");

View File

@@ -20,7 +20,7 @@ public class ApiErrorResult
public UserInfoException? ToUserInfoException()
{
if (!String.IsNullOrWhiteSpace(Detail)) {
return new UserInfoException(Detail);
return new UserInfoException(Detail!);
}
return null;
@@ -37,4 +37,20 @@ public static class FlowApiExtensions
return null;
}
public static FileType ToFileType(this VelopackAssetType type)
{
switch (type) {
case VelopackAssetType.Full:
return FileType.Full;
case VelopackAssetType.Delta:
return FileType.Delta;
case VelopackAssetType.Portable:
return FileType.Portable;
case VelopackAssetType.Installer:
return FileType.Setup;
default:
throw new ArgumentOutOfRangeException(nameof(type), type, null);
}
}
}

View File

@@ -130,7 +130,7 @@ public class VelopackFlowServiceClient(
channel ??= DefaultName.GetDefaultChannel(os);
BuildAssets assets = BuildAssets.Read(releaseDirectory, channel);
var fullAsset = assets.GetReleaseEntries().SingleOrDefault(a => a.Type == Velopack.VelopackAssetType.Full);
var fullAsset = assets.GetReleaseEntries().SingleOrDefault(a => a.Type == VelopackAssetType.Full);
if (fullAsset is null) {
Logger.LogError("No full asset found in release directory {ReleaseDirectory} (or it's missing from assets file)", releaseDirectory);
@@ -141,10 +141,9 @@ public class VelopackFlowServiceClient(
var packageId = fullAsset.PackageId;
var version = fullAsset.Version;
var filesToUpload = assets.GetNonReleaseAssetPaths()
.Select(p => (p, FileType.Installer))
.Concat([(fullAssetPath, FileType.Release)])
.Where(kvp => !kvp.Item1.Contains("-Portable.zip"))
var filesToUpload = assets.GetAssets()
.Where(p => p.Type is not VelopackAssetType.Delta)
.Select(p => (p.Path, p.Type.ToFileType()))
.ToArray();
Logger.LogInformation("Beginning upload to Velopack Flow");
@@ -161,6 +160,7 @@ public class VelopackFlowServiceClient(
await CreateChannelIfNotExists(client, packageId, channel, cancellationToken);
report(50);
var result = await CreateReleaseGroupAsync(client, packageId, version, channel, cancellationToken);
Logger.LogInformation("Created release {Version} ({ReleaseGroupId})", version, result.Id);
report(100);
return result;
});
@@ -218,7 +218,7 @@ public class VelopackFlowServiceClient(
await UploadReleaseAssetAsync(
deltaPath,
releaseGroup.Id,
FileType.Release,
FileType.Delta,
report,
cancellationToken);
report(100);
@@ -283,14 +283,20 @@ public class VelopackFlowServiceClient(
{
for (int i = 0; i < 300; i++) {
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.");
if (releaseGroup.ProcessingState is ReleaseGroupProcessingState.Completed) {
Logger.LogInformation("Release is now live.");
return;
}
if (releaseGroup.FileUploads.All(f => f.Status?.ToLowerInvariant().Equals("processed") == true)) {
Logger.LogInformation("Release is now live.");
return;
if (releaseGroup.ProcessingState is ReleaseGroupProcessingState.Failed) {
foreach (var file in releaseGroup.FileUploads) {
if (file.State == FileUploadState.Failed) {
Logger.LogError("File {FileName} failed to upload: {Error}", file.FileName, file.StateMessage);
}
}
throw new UserInfoException("There were one or more errors publishing this release.");
}
await Task.Delay(1000, cancellationToken);
@@ -325,14 +331,14 @@ public class VelopackFlowServiceClient(
{
var client = GetFlowApi(progress);
using var stream = File.OpenRead(filePath);
var file = new FileParameter(stream);
var file = new FileParameter(stream, Path.GetFileName(filePath));
await client.UploadReleaseAsync(releaseGroupId, fileType, file, cancellationToken);
}
private static async Task<ReleaseGroup> PublishReleaseGroupAsync(FlowApi client, Guid releaseGroupId, CancellationToken cancellationToken)
{
UpdateReleaseGroupRequest request = new() {
State = ReleaseGroupState.Published
State = ReleaseGroupPublishState.Published,
};
return await client.UpdateReleaseGroupAsync(releaseGroupId, request, cancellationToken);

View File

@@ -105,17 +105,7 @@ public abstract class PackageBuilder<T> : ICommand<T>
options.EntryExecutableName = Path.GetFileName(mainExePath);
Options = options;
ConcurrentBag<(string from, string to)> filesToCopy = new();
string getIncompletePath(string fileName)
{
var incomplete = Path.Combine(pkgTempDir, fileName);
var final = Path.Combine(releaseDir.FullName, fileName);
try { File.Delete(incomplete); } catch { }
filesToCopy.Add((incomplete, final));
return incomplete;
}
var assetCache = new BuildAssets(pkgTempDir, channel);
await Console.ExecuteProgressAsync(
async (ctx) => {
@@ -141,7 +131,7 @@ public abstract class PackageBuilder<T> : ICommand<T>
"Building portable package",
async (progress) => {
var suggestedName = DefaultName.GetSuggestedPortableName(packId, channel, TargetOs);
var path = getIncompletePath(suggestedName);
var path = assetCache.MakeAssetPath(suggestedName, VelopackAssetType.Portable);
await CreatePortablePackage(progress, packDirectory, path);
});
}
@@ -154,7 +144,7 @@ public abstract class PackageBuilder<T> : ICommand<T>
$"Building release {packVersion}",
async (progress) => {
var suggestedName = DefaultName.GetSuggestedReleaseName(packId, packVersion, channel, false, TargetOs);
releasePath = getIncompletePath(suggestedName);
releasePath = assetCache.MakeAssetPath(suggestedName, VelopackAssetType.Full);
await CreateReleasePackage(progress, packDirectory, releasePath);
});
@@ -164,7 +154,7 @@ public abstract class PackageBuilder<T> : ICommand<T>
"Building setup package",
async (progress) => {
var suggestedName = DefaultName.GetSuggestedSetupName(packId, channel, TargetOs);
var path = getIncompletePath(suggestedName);
var path = assetCache.MakeAssetPath(suggestedName, VelopackAssetType.Installer);
await CreateSetupPackage(progress, releasePath, packDirectory, path);
});
}
@@ -174,11 +164,12 @@ public abstract class PackageBuilder<T> : ICommand<T>
$"Building delta {prev.Version} -> {packVersion}",
async (progress) => {
var suggestedName = DefaultName.GetSuggestedReleaseName(packId, packVersion, channel, true, TargetOs);
var path = assetCache.MakeAssetPath(suggestedName, VelopackAssetType.Delta);
var deltaPkg = await CreateDeltaPackage(
progress,
releasePath,
prev.PackageFile,
getIncompletePath(suggestedName),
path,
options.DeltaMode);
});
}
@@ -189,12 +180,9 @@ public abstract class PackageBuilder<T> : ICommand<T>
await ctx.RunTask(
"Post-process steps",
(progress) => {
foreach (var f in filesToCopy) {
IoUtil.MoveFile(f.from, f.to, true);
}
assetCache.MoveBagTo(releaseDir.FullName);
assetCache.Write();
ReleaseEntryHelper.UpdateReleaseFiles(releaseDir.FullName, Log);
BuildAssets.Write(releaseDir.FullName, channel, filesToCopy.Select(x => x.to));
progress(100);
return Task.CompletedTask;
});