289 lines
9.0 KiB
C#
289 lines
9.0 KiB
C#
using System;
|
|
using System.Linq;
|
|
using System.Text.RegularExpressions;
|
|
using System.Threading.Tasks;
|
|
using Filtration.Enums;
|
|
using Filtration.Properties;
|
|
using NLog;
|
|
using Squirrel;
|
|
|
|
namespace Filtration.Services
|
|
{
|
|
public enum UpdateStatus
|
|
{
|
|
NoUpdateAvailable,
|
|
CheckingForUpdate,
|
|
UpdateAvailable,
|
|
Downloading,
|
|
ReadyToApplyUpdate,
|
|
Updating,
|
|
UpdateComplete,
|
|
Error
|
|
}
|
|
|
|
internal class UpdateStatusChangedEventArgs : EventArgs
|
|
{
|
|
public UpdateStatusChangedEventArgs(UpdateStatus updateStatus)
|
|
{
|
|
UpdateStatus = updateStatus;
|
|
}
|
|
|
|
public UpdateStatus UpdateStatus { get; }
|
|
}
|
|
|
|
internal class UpdateProgressChangedEventArgs : EventArgs
|
|
{
|
|
public UpdateProgressChangedEventArgs(int progress)
|
|
{
|
|
Progress = progress;
|
|
}
|
|
|
|
public int Progress { get; }
|
|
}
|
|
|
|
internal interface IUpdateService
|
|
{
|
|
event EventHandler<UpdateStatusChangedEventArgs> UpdateStatusChanged;
|
|
|
|
event EventHandler<UpdateProgressChangedEventArgs> UpdateProgressChanged;
|
|
|
|
UpdateStatus UpdateStatus { get; }
|
|
|
|
string LatestReleaseNotes { get; }
|
|
|
|
string LatestReleaseVersion { get; }
|
|
|
|
Task CheckForUpdatesAsync();
|
|
|
|
Task DownloadUpdatesAsync();
|
|
|
|
Task ApplyUpdatesAsync();
|
|
|
|
void RestartAfterUpdate();
|
|
}
|
|
|
|
internal class UpdateService : IUpdateService
|
|
{
|
|
private static readonly Logger Logger = LogManager.GetCurrentClassLogger();
|
|
|
|
private const string _localUpdatePath = @"C:\Repos\Filtration\Releases";
|
|
|
|
private readonly ISettingsService _settingsService;
|
|
private readonly UpdateSource _updateSource = UpdateSource.GitHub;
|
|
|
|
private ReleaseEntry _latestRelease;
|
|
private UpdateInfo _updates;
|
|
private bool _downloadPrereleaseUpdates;
|
|
private UpdateStatus _updateStatus;
|
|
|
|
public UpdateService(ISettingsService settingsService)
|
|
{
|
|
_settingsService = settingsService;
|
|
|
|
UpdateStatus = UpdateStatus.NoUpdateAvailable;
|
|
}
|
|
|
|
public event EventHandler<UpdateStatusChangedEventArgs> UpdateStatusChanged;
|
|
public event EventHandler<UpdateProgressChangedEventArgs> UpdateProgressChanged;
|
|
|
|
public UpdateStatus UpdateStatus
|
|
{
|
|
get => _updateStatus;
|
|
private set
|
|
{
|
|
_updateStatus = value;
|
|
UpdateStatusChanged?.Invoke(this, new UpdateStatusChangedEventArgs(value));
|
|
}
|
|
}
|
|
|
|
public string LatestReleaseNotes { get; private set; }
|
|
|
|
public string LatestReleaseVersion { get; private set; }
|
|
|
|
public async Task CheckForUpdatesAsync()
|
|
{
|
|
if (UpdateStatus != UpdateStatus.NoUpdateAvailable)
|
|
{
|
|
throw new InvalidOperationException();
|
|
}
|
|
|
|
Logger.Debug("Checking for update...");
|
|
UpdateStatus = UpdateStatus.CheckingForUpdate;
|
|
|
|
try
|
|
{
|
|
_downloadPrereleaseUpdates = Settings.Default.DownloadPrereleaseUpdates;
|
|
#if DEBUG
|
|
_downloadPrereleaseUpdates = true;
|
|
#endif
|
|
|
|
async Task CheckForUpdatesAsync(IUpdateManager updateManager)
|
|
{
|
|
_updates = await updateManager.CheckForUpdate(progress: progress => UpdateProgressChanged?.Invoke(this, new UpdateProgressChangedEventArgs(progress)));
|
|
}
|
|
|
|
if (_updateSource == UpdateSource.GitHub)
|
|
{
|
|
using (var updateManager = await UpdateManager.GitHubUpdateManager("https://github.com/ben-wallis/Filtration", prerelease: _downloadPrereleaseUpdates))
|
|
{
|
|
await CheckForUpdatesAsync(updateManager);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
using (var updateManager = new UpdateManager(_localUpdatePath))
|
|
{
|
|
await CheckForUpdatesAsync(updateManager);
|
|
}
|
|
}
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
Logger.Error(e);
|
|
UpdateStatus = UpdateStatus.Error;
|
|
return;
|
|
}
|
|
|
|
if (_updates.ReleasesToApply.Any())
|
|
{
|
|
_latestRelease = _updates.ReleasesToApply.OrderBy(x => x.Version).Last();
|
|
LatestReleaseVersion = _latestRelease.Version.ToString();
|
|
|
|
Logger.Debug($"Update found ({LatestReleaseVersion})");
|
|
|
|
UpdateStatus = UpdateStatus.UpdateAvailable;
|
|
}
|
|
else
|
|
{
|
|
UpdateStatus = UpdateStatus.NoUpdateAvailable;
|
|
Logger.Debug("No update available");
|
|
}
|
|
}
|
|
|
|
private static string ProcessReleaseNotes(string rawReleaseNotes)
|
|
{
|
|
var regex = new Regex(@"<!\[CDATA\[(.*)]]>", RegexOptions.Singleline);
|
|
var matches = regex.Match(rawReleaseNotes);
|
|
if (matches.Success)
|
|
{
|
|
var releaseNotes = matches.Groups[1].Value;
|
|
return "<font face=\"Segoe UI\" size=\"2\">" + releaseNotes + "</font>";
|
|
}
|
|
|
|
return string.Empty;
|
|
}
|
|
|
|
public async Task DownloadUpdatesAsync()
|
|
{
|
|
if (_updates == null || UpdateStatus != UpdateStatus.UpdateAvailable)
|
|
{
|
|
throw new InvalidOperationException();
|
|
}
|
|
|
|
UpdateStatus = UpdateStatus.Downloading;
|
|
|
|
async Task DownloadUpdatesAsync(IUpdateManager updateManager)
|
|
{
|
|
Logger.Debug("Downloading update...");
|
|
await updateManager.DownloadReleases(_updates.ReleasesToApply, OnProgressChanged);
|
|
|
|
Logger.Debug("Fetching release notes...");
|
|
var releaseNotes = _updates.FetchReleaseNotes();
|
|
LatestReleaseNotes = ProcessReleaseNotes(releaseNotes[_latestRelease]);
|
|
}
|
|
|
|
try
|
|
{
|
|
if (_updateSource == UpdateSource.GitHub)
|
|
{
|
|
using (var updateManager = await UpdateManager.GitHubUpdateManager("https://github.com/ben-wallis/Filtration", prerelease: _downloadPrereleaseUpdates))
|
|
{
|
|
await DownloadUpdatesAsync(updateManager);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
using (var updateManager = new UpdateManager(_localUpdatePath))
|
|
{
|
|
await DownloadUpdatesAsync(updateManager);
|
|
}
|
|
}
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
Logger.Error(e);
|
|
UpdateStatus = UpdateStatus.Error;
|
|
return;
|
|
}
|
|
|
|
UpdateStatus = UpdateStatus.ReadyToApplyUpdate;
|
|
}
|
|
|
|
|
|
public async Task ApplyUpdatesAsync()
|
|
{
|
|
if (UpdateStatus != UpdateStatus.ReadyToApplyUpdate)
|
|
{
|
|
throw new InvalidOperationException();
|
|
}
|
|
|
|
UpdateStatus = UpdateStatus.Updating;
|
|
|
|
// Back up the current user settings before updating as updating using Squirrel
|
|
// wipes out user settings due to the application directory changing with each update
|
|
_settingsService.BackupSettings();
|
|
|
|
Logger.Debug("Applying update...");
|
|
try
|
|
{
|
|
if (_updateSource == UpdateSource.GitHub)
|
|
{
|
|
using (var mgr = await UpdateManager.GitHubUpdateManager("https://github.com/ben-wallis/Filtration", prerelease: _downloadPrereleaseUpdates))
|
|
{
|
|
await mgr.ApplyReleases(_updates, OnProgressChanged);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
using (var updateManager = new UpdateManager(_localUpdatePath))
|
|
{
|
|
await updateManager.ApplyReleases(_updates, OnProgressChanged);
|
|
}
|
|
}
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
Logger.Error(e);
|
|
UpdateStatus = UpdateStatus.Error;
|
|
return;
|
|
}
|
|
|
|
Logger.Debug("Update complete");
|
|
UpdateStatus = UpdateStatus.UpdateComplete;
|
|
}
|
|
|
|
public void RestartAfterUpdate()
|
|
{
|
|
if (UpdateStatus != UpdateStatus.UpdateComplete)
|
|
{
|
|
throw new InvalidOperationException();
|
|
}
|
|
|
|
try
|
|
{
|
|
UpdateManager.RestartApp();
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
Logger.Error(e);
|
|
UpdateStatus = UpdateStatus.Error;
|
|
}
|
|
}
|
|
|
|
private void OnProgressChanged(int progress)
|
|
{
|
|
UpdateProgressChanged?.Invoke(this, new UpdateProgressChangedEventArgs(progress));
|
|
}
|
|
}
|
|
}
|