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>
|
/// <summary> A full update package. </summary>
|
||||||
Full = 1,
|
Full = 1,
|
||||||
/// <summary> A delta update package. </summary>
|
/// <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>
|
/// <summary>
|
||||||
|
|||||||
@@ -1,47 +1,60 @@
|
|||||||
using Newtonsoft.Json;
|
using System.Collections.Concurrent;
|
||||||
using Velopack.Util;
|
using Velopack.Util;
|
||||||
|
|
||||||
namespace Velopack.Core;
|
namespace Velopack.Core;
|
||||||
|
|
||||||
public class BuildAssets
|
public class BuildAssets(string outputDir, string channel)
|
||||||
{
|
{
|
||||||
[JsonIgnore]
|
public int Count => Assets.Count;
|
||||||
private string? _outputDir;
|
|
||||||
|
|
||||||
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()
|
public List<VelopackAsset> GetReleaseEntries()
|
||||||
{
|
{
|
||||||
return GetFilePaths()
|
return GetAssets()
|
||||||
.Where(x => x.EndsWith(".nupkg", StringComparison.OrdinalIgnoreCase))
|
.Where(x => x.Type is VelopackAssetType.Delta or VelopackAssetType.Full)
|
||||||
.Select(VelopackAsset.FromNupkg)
|
.Select(x => VelopackAsset.FromNupkg(x.Path))
|
||||||
.ToList();
|
.ToList();
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<string> GetNonReleaseAssetPaths()
|
public IEnumerable<(string Path, VelopackAssetType Type)> GetAssets()
|
||||||
{
|
{
|
||||||
return GetFilePaths()
|
return Assets.Select(asset => (Path.Combine(outputDir, asset.RelativeFileName), asset.Type));
|
||||||
.Where(x => !x.EndsWith(".nupkg", StringComparison.OrdinalIgnoreCase))
|
|
||||||
.ToList();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public IEnumerable<string> GetFilePaths()
|
public IEnumerable<string> GetFilePaths()
|
||||||
{
|
{
|
||||||
if (RelativeFileNames is { } relativeFileNames && _outputDir is { } outputDir) {
|
return Assets.Select(x => Path.Combine(outputDir, x.RelativeFileName));
|
||||||
return relativeFileNames.Select(f => Path.GetFullPath(Path.Combine(outputDir, f))).ToList();
|
|
||||||
}
|
|
||||||
return [];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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 path = Path.Combine(outputDir, $"assets.{channel}.json");
|
||||||
var json = SimpleJson.SerializeObject(assets);
|
var json = SimpleJson.SerializeObject(Assets);
|
||||||
File.WriteAllText(path, json);
|
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'.");
|
$"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();
|
var me = new BuildAssets(outputDir, channel) {
|
||||||
assets._outputDir = outputDir;
|
Assets = SimpleJson.DeserializeObject<ConcurrentBag<Asset>>(File.ReadAllText(path)) ?? []
|
||||||
return assets;
|
};
|
||||||
|
return me;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -62,7 +62,7 @@ public class GitHubRepository(ILogger logger) : SourceRepository<GitHubDownloadO
|
|||||||
var semVer = options.TagName ?? latest.Version.ToString();
|
var semVer = options.TagName ?? latest.Version.ToString();
|
||||||
var releaseName = string.IsNullOrWhiteSpace(options.ReleaseName) ? semVer.ToString() : options.ReleaseName;
|
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")) {
|
var client = new GitHubClient(new ProductHeaderValue("Velopack")) {
|
||||||
Credentials = new Credentials(options.Token)
|
Credentials = new Credentials(options.Token)
|
||||||
|
|||||||
@@ -82,7 +82,7 @@ public class GiteaRepository : SourceRepository<GiteaDownloadOptions, GiteaSourc
|
|||||||
config.BasePath = baseUri + "/api/v1";
|
config.BasePath = baseUri + "/api/v1";
|
||||||
config.Timeout = (int)TimeSpan.FromMinutes(options.Timeout).TotalMilliseconds;
|
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
|
// Set token if provided
|
||||||
if (!string.IsNullOrWhiteSpace(options.Token)) {
|
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 build = BuildAssets.Read(options.ReleaseDir.FullName, options.Channel);
|
||||||
var client = CreateClient(options);
|
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);
|
var remoteReleases = await GetReleasesAsync(options);
|
||||||
Log.Info($"There are {remoteReleases.Assets.Length} asset(s) in remote releases file.");
|
Log.Info($"There are {remoteReleases.Assets.Length} asset(s) in remote releases file.");
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ public class ApiErrorResult
|
|||||||
public UserInfoException? ToUserInfoException()
|
public UserInfoException? ToUserInfoException()
|
||||||
{
|
{
|
||||||
if (!String.IsNullOrWhiteSpace(Detail)) {
|
if (!String.IsNullOrWhiteSpace(Detail)) {
|
||||||
return new UserInfoException(Detail);
|
return new UserInfoException(Detail!);
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
@@ -37,4 +37,20 @@ public static class FlowApiExtensions
|
|||||||
|
|
||||||
return null;
|
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);
|
channel ??= DefaultName.GetDefaultChannel(os);
|
||||||
BuildAssets assets = BuildAssets.Read(releaseDirectory, channel);
|
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) {
|
if (fullAsset is null) {
|
||||||
Logger.LogError("No full asset found in release directory {ReleaseDirectory} (or it's missing from assets file)", releaseDirectory);
|
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 packageId = fullAsset.PackageId;
|
||||||
var version = fullAsset.Version;
|
var version = fullAsset.Version;
|
||||||
|
|
||||||
var filesToUpload = assets.GetNonReleaseAssetPaths()
|
var filesToUpload = assets.GetAssets()
|
||||||
.Select(p => (p, FileType.Installer))
|
.Where(p => p.Type is not VelopackAssetType.Delta)
|
||||||
.Concat([(fullAssetPath, FileType.Release)])
|
.Select(p => (p.Path, p.Type.ToFileType()))
|
||||||
.Where(kvp => !kvp.Item1.Contains("-Portable.zip"))
|
|
||||||
.ToArray();
|
.ToArray();
|
||||||
|
|
||||||
Logger.LogInformation("Beginning upload to Velopack Flow");
|
Logger.LogInformation("Beginning upload to Velopack Flow");
|
||||||
@@ -161,6 +160,7 @@ public class VelopackFlowServiceClient(
|
|||||||
await CreateChannelIfNotExists(client, packageId, channel, cancellationToken);
|
await CreateChannelIfNotExists(client, packageId, channel, cancellationToken);
|
||||||
report(50);
|
report(50);
|
||||||
var result = await CreateReleaseGroupAsync(client, packageId, version, channel, cancellationToken);
|
var result = await CreateReleaseGroupAsync(client, packageId, version, channel, cancellationToken);
|
||||||
|
Logger.LogInformation("Created release {Version} ({ReleaseGroupId})", version, result.Id);
|
||||||
report(100);
|
report(100);
|
||||||
return result;
|
return result;
|
||||||
});
|
});
|
||||||
@@ -218,7 +218,7 @@ public class VelopackFlowServiceClient(
|
|||||||
await UploadReleaseAssetAsync(
|
await UploadReleaseAssetAsync(
|
||||||
deltaPath,
|
deltaPath,
|
||||||
releaseGroup.Id,
|
releaseGroup.Id,
|
||||||
FileType.Release,
|
FileType.Delta,
|
||||||
report,
|
report,
|
||||||
cancellationToken);
|
cancellationToken);
|
||||||
report(100);
|
report(100);
|
||||||
@@ -283,14 +283,20 @@ public class VelopackFlowServiceClient(
|
|||||||
{
|
{
|
||||||
for (int i = 0; i < 300; i++) {
|
for (int i = 0; i < 300; i++) {
|
||||||
var releaseGroup = await client.GetReleaseGroupAsync(releaseGroupId, 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.");
|
if (releaseGroup.ProcessingState is ReleaseGroupProcessingState.Completed) {
|
||||||
|
Logger.LogInformation("Release is now live.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (releaseGroup.FileUploads.All(f => f.Status?.ToLowerInvariant().Equals("processed") == true)) {
|
if (releaseGroup.ProcessingState is ReleaseGroupProcessingState.Failed) {
|
||||||
Logger.LogInformation("Release is now live.");
|
foreach (var file in releaseGroup.FileUploads) {
|
||||||
return;
|
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);
|
await Task.Delay(1000, cancellationToken);
|
||||||
@@ -325,14 +331,14 @@ public class VelopackFlowServiceClient(
|
|||||||
{
|
{
|
||||||
var client = GetFlowApi(progress);
|
var client = GetFlowApi(progress);
|
||||||
using var stream = File.OpenRead(filePath);
|
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);
|
await client.UploadReleaseAsync(releaseGroupId, fileType, file, cancellationToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static async Task<ReleaseGroup> PublishReleaseGroupAsync(FlowApi client, Guid releaseGroupId, CancellationToken cancellationToken)
|
private static async Task<ReleaseGroup> PublishReleaseGroupAsync(FlowApi client, Guid releaseGroupId, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
UpdateReleaseGroupRequest request = new() {
|
UpdateReleaseGroupRequest request = new() {
|
||||||
State = ReleaseGroupState.Published
|
State = ReleaseGroupPublishState.Published,
|
||||||
};
|
};
|
||||||
|
|
||||||
return await client.UpdateReleaseGroupAsync(releaseGroupId, request, cancellationToken);
|
return await client.UpdateReleaseGroupAsync(releaseGroupId, request, cancellationToken);
|
||||||
|
|||||||
@@ -105,17 +105,7 @@ public abstract class PackageBuilder<T> : ICommand<T>
|
|||||||
options.EntryExecutableName = Path.GetFileName(mainExePath);
|
options.EntryExecutableName = Path.GetFileName(mainExePath);
|
||||||
Options = options;
|
Options = options;
|
||||||
|
|
||||||
ConcurrentBag<(string from, string to)> filesToCopy = new();
|
var assetCache = new BuildAssets(pkgTempDir, channel);
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
await Console.ExecuteProgressAsync(
|
await Console.ExecuteProgressAsync(
|
||||||
async (ctx) => {
|
async (ctx) => {
|
||||||
@@ -141,7 +131,7 @@ public abstract class PackageBuilder<T> : ICommand<T>
|
|||||||
"Building portable package",
|
"Building portable package",
|
||||||
async (progress) => {
|
async (progress) => {
|
||||||
var suggestedName = DefaultName.GetSuggestedPortableName(packId, channel, TargetOs);
|
var suggestedName = DefaultName.GetSuggestedPortableName(packId, channel, TargetOs);
|
||||||
var path = getIncompletePath(suggestedName);
|
var path = assetCache.MakeAssetPath(suggestedName, VelopackAssetType.Portable);
|
||||||
await CreatePortablePackage(progress, packDirectory, path);
|
await CreatePortablePackage(progress, packDirectory, path);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -154,7 +144,7 @@ public abstract class PackageBuilder<T> : ICommand<T>
|
|||||||
$"Building release {packVersion}",
|
$"Building release {packVersion}",
|
||||||
async (progress) => {
|
async (progress) => {
|
||||||
var suggestedName = DefaultName.GetSuggestedReleaseName(packId, packVersion, channel, false, TargetOs);
|
var suggestedName = DefaultName.GetSuggestedReleaseName(packId, packVersion, channel, false, TargetOs);
|
||||||
releasePath = getIncompletePath(suggestedName);
|
releasePath = assetCache.MakeAssetPath(suggestedName, VelopackAssetType.Full);
|
||||||
await CreateReleasePackage(progress, packDirectory, releasePath);
|
await CreateReleasePackage(progress, packDirectory, releasePath);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -164,7 +154,7 @@ public abstract class PackageBuilder<T> : ICommand<T>
|
|||||||
"Building setup package",
|
"Building setup package",
|
||||||
async (progress) => {
|
async (progress) => {
|
||||||
var suggestedName = DefaultName.GetSuggestedSetupName(packId, channel, TargetOs);
|
var suggestedName = DefaultName.GetSuggestedSetupName(packId, channel, TargetOs);
|
||||||
var path = getIncompletePath(suggestedName);
|
var path = assetCache.MakeAssetPath(suggestedName, VelopackAssetType.Installer);
|
||||||
await CreateSetupPackage(progress, releasePath, packDirectory, path);
|
await CreateSetupPackage(progress, releasePath, packDirectory, path);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -174,11 +164,12 @@ public abstract class PackageBuilder<T> : ICommand<T>
|
|||||||
$"Building delta {prev.Version} -> {packVersion}",
|
$"Building delta {prev.Version} -> {packVersion}",
|
||||||
async (progress) => {
|
async (progress) => {
|
||||||
var suggestedName = DefaultName.GetSuggestedReleaseName(packId, packVersion, channel, true, TargetOs);
|
var suggestedName = DefaultName.GetSuggestedReleaseName(packId, packVersion, channel, true, TargetOs);
|
||||||
|
var path = assetCache.MakeAssetPath(suggestedName, VelopackAssetType.Delta);
|
||||||
var deltaPkg = await CreateDeltaPackage(
|
var deltaPkg = await CreateDeltaPackage(
|
||||||
progress,
|
progress,
|
||||||
releasePath,
|
releasePath,
|
||||||
prev.PackageFile,
|
prev.PackageFile,
|
||||||
getIncompletePath(suggestedName),
|
path,
|
||||||
options.DeltaMode);
|
options.DeltaMode);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -189,12 +180,9 @@ public abstract class PackageBuilder<T> : ICommand<T>
|
|||||||
await ctx.RunTask(
|
await ctx.RunTask(
|
||||||
"Post-process steps",
|
"Post-process steps",
|
||||||
(progress) => {
|
(progress) => {
|
||||||
foreach (var f in filesToCopy) {
|
assetCache.MoveBagTo(releaseDir.FullName);
|
||||||
IoUtil.MoveFile(f.from, f.to, true);
|
assetCache.Write();
|
||||||
}
|
|
||||||
|
|
||||||
ReleaseEntryHelper.UpdateReleaseFiles(releaseDir.FullName, Log);
|
ReleaseEntryHelper.UpdateReleaseFiles(releaseDir.FullName, Log);
|
||||||
BuildAssets.Write(releaseDir.FullName, channel, filesToCopy.Select(x => x.to));
|
|
||||||
progress(100);
|
progress(100);
|
||||||
return Task.CompletedTask;
|
return Task.CompletedTask;
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user