mirror of
https://github.com/velopack/velopack.git
synced 2025-10-25 15:19:22 +00:00
update buildassets and fix publish after api update
This commit is contained in:
@@ -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>
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
|
||||
@@ -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)) {
|
||||
|
||||
@@ -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.");
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user