mirror of
https://github.com/velopack/velopack.git
synced 2025-10-25 15:19:22 +00:00
146 lines
4.6 KiB
C#
146 lines
4.6 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using System.Text;
|
|
using System.Threading.Tasks;
|
|
using Microsoft.Extensions.Logging;
|
|
using Velopack.Sources;
|
|
using Velopack.Packaging;
|
|
|
|
namespace Velopack.Deployment;
|
|
|
|
public class RepositoryOptions
|
|
{
|
|
public string Channel { get; set; }
|
|
|
|
public DirectoryInfo ReleaseDir { get; set; }
|
|
}
|
|
|
|
public interface IRepositoryCanUpload<TUp> where TUp : RepositoryOptions
|
|
{
|
|
Task UploadMissingAssetsAsync(TUp options);
|
|
}
|
|
|
|
public interface IRepositoryCanDownload<TDown> where TDown : RepositoryOptions
|
|
{
|
|
Task DownloadLatestFullPackageAsync(TDown options);
|
|
}
|
|
|
|
public abstract class SourceRepository<TDown, TSource> : DownRepository<TDown>
|
|
where TDown : RepositoryOptions
|
|
where TSource : IUpdateSource
|
|
{
|
|
public SourceRepository(ILogger logger)
|
|
: base(logger)
|
|
{ }
|
|
|
|
protected override Task<ReleaseEntry[]> GetReleasesAsync(TDown options)
|
|
{
|
|
var source = CreateSource(options);
|
|
return source.GetReleaseFeed();
|
|
}
|
|
|
|
protected override Task SaveEntryToFileAsync(TDown options, ReleaseEntry entry, string filePath)
|
|
{
|
|
var source = CreateSource(options);
|
|
return source.DownloadReleaseEntry(entry, filePath, (i) => { });
|
|
}
|
|
|
|
public abstract TSource CreateSource(TDown options);
|
|
}
|
|
|
|
public abstract class DownRepository<TDown> : IRepositoryCanDownload<TDown>
|
|
where TDown : RepositoryOptions
|
|
{
|
|
protected ILogger Log { get; }
|
|
|
|
public DownRepository(ILogger logger)
|
|
{
|
|
Log = logger;
|
|
}
|
|
|
|
public virtual async Task DownloadLatestFullPackageAsync(TDown options)
|
|
{
|
|
var releasesName = SourceBase.GetReleasesFileNameImpl(options.Channel);
|
|
|
|
ReleaseEntry[] releases = await RetryAsyncRet(() => GetReleasesAsync(options), $"Fetching {releasesName}...");
|
|
|
|
Log.Info($"Found {releases.Length} release in remote file");
|
|
|
|
var latest = releases.Where(r => !r.IsDelta).OrderByDescending(r => r.Version).FirstOrDefault();
|
|
if (latest == null) {
|
|
Log.Warn("No full / applicible release was found to download. Aborting.");
|
|
return;
|
|
}
|
|
|
|
var path = Path.Combine(options.ReleaseDir.FullName, latest.OriginalFilename);
|
|
var incomplete = Path.Combine(options.ReleaseDir.FullName, latest.OriginalFilename + ".incomplete");
|
|
|
|
if (File.Exists(path)) {
|
|
Log.Warn($"File '{path}' already exists on disk. Verifying checksum...");
|
|
var hash = Utility.CalculateFileSHA1(path);
|
|
if (hash == latest.SHA1) {
|
|
Log.Info("Checksum matches. Finished.");
|
|
return;
|
|
} else {
|
|
Log.Info($"Checksum mismatch, re-downloading...");
|
|
}
|
|
}
|
|
|
|
await RetryAsync(() => SaveEntryToFileAsync(options, latest, incomplete), $"Downloading {latest.OriginalFilename}...");
|
|
|
|
Log.Info("Verifying checksum...");
|
|
var newHash = Utility.CalculateFileSHA1(incomplete);
|
|
if (newHash != latest.SHA1) {
|
|
Log.Error($"Checksum mismatch, expected {latest.SHA1}, got {newHash}");
|
|
return;
|
|
}
|
|
|
|
File.Move(incomplete, path, true);
|
|
Log.Info("Finished.");
|
|
}
|
|
|
|
protected abstract Task<ReleaseEntry[]> GetReleasesAsync(TDown options);
|
|
|
|
protected abstract Task SaveEntryToFileAsync(TDown options, ReleaseEntry entry, string filePath);
|
|
|
|
protected async Task<T> RetryAsyncRet<T>(Func<Task<T>> block, string message, int maxRetries = 3)
|
|
{
|
|
int ctry = 0;
|
|
while (true) {
|
|
try {
|
|
Log.Info((ctry > 0 ? $"(retry {ctry}) " : "") + message);
|
|
return await block().ConfigureAwait(false);
|
|
} catch (Exception ex) {
|
|
if (ctry++ > maxRetries) {
|
|
Log.Error(ex.Message + ", will not try again.");
|
|
throw;
|
|
}
|
|
|
|
Log.Error($"{ex.Message}, retrying in 1 second.");
|
|
await Task.Delay(1000).ConfigureAwait(false);
|
|
}
|
|
}
|
|
}
|
|
|
|
protected async Task RetryAsync(Func<Task> block, string message, int maxRetries = 3)
|
|
{
|
|
int ctry = 0;
|
|
while (true) {
|
|
try {
|
|
Log.Info((ctry > 0 ? $"(retry {ctry}) " : "") + message);
|
|
await block().ConfigureAwait(false);
|
|
return;
|
|
} catch (Exception ex) {
|
|
if (ctry++ > maxRetries) {
|
|
Log.Error(ex.Message + ", will not try again.");
|
|
throw;
|
|
}
|
|
|
|
Log.Error($"{ex.Message}, retrying in 1 second.");
|
|
await Task.Delay(1000).ConfigureAwait(false);
|
|
}
|
|
}
|
|
}
|
|
}
|