1
0
mirror of https://github.com/Artemis-RGB/Artemis synced 2025-12-13 05:48:35 +00:00

Added update history

This commit is contained in:
Robert 2023-03-04 16:30:41 +01:00
parent ede29aa9f9
commit 5d01665d6e
11 changed files with 195 additions and 74 deletions

View File

@ -70,7 +70,7 @@ public static class Constants
/// </summary>
public static readonly string CurrentVersion = CoreAssembly.GetCustomAttribute<AssemblyInformationalVersionAttribute>()!.InformationalVersion != "1.0.0"
? CoreAssembly.GetCustomAttribute<AssemblyInformationalVersionAttribute>()!.InformationalVersion
: "1.2023.0212.2-feature-gh-actions";
: "local";
/// <summary>
/// The plugin info used by core components of Artemis

View File

@ -0,0 +1,19 @@
using System;
namespace Artemis.Storage.Entities.General;
public class ReleaseEntity
{
public Guid Id { get; set; }
public string Version { get; set; }
public ReleaseEntityStatus Status { get; set; }
public DateTimeOffset? InstalledAt { get; set; }
}
public enum ReleaseEntityStatus
{
Queued,
Installed,
Historical
}

View File

@ -0,0 +1,76 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Artemis.Storage.Entities.General;
using Artemis.Storage.Repositories.Interfaces;
using LiteDB;
namespace Artemis.Storage.Repositories;
public class ReleaseRepository : IReleaseRepository
{
private readonly LiteRepository _repository;
public ReleaseRepository(LiteRepository repository)
{
_repository = repository;
_repository.Database.GetCollection<ReleaseEntity>().EnsureIndex(s => s.Version, true);
_repository.Database.GetCollection<ReleaseEntity>().EnsureIndex(s => s.Status);
}
public string GetQueuedVersion()
{
return _repository.Query<ReleaseEntity>().Where(r => r.Status == ReleaseEntityStatus.Queued).FirstOrDefault()?.Version;
}
public string GetInstalledVersion()
{
return _repository.Query<ReleaseEntity>().Where(r => r.Status == ReleaseEntityStatus.Installed).FirstOrDefault()?.Version;
}
public string GetPreviousInstalledVersion()
{
return _repository.Query<ReleaseEntity>().Where(r => r.Status == ReleaseEntityStatus.Historical).OrderByDescending(r => r.InstalledAt).FirstOrDefault()?.Version;
}
public void QueueInstallation(string version)
{
// Mark release as queued and add if missing
ReleaseEntity release = _repository.Query<ReleaseEntity>().Where(r => r.Version == version).FirstOrDefault() ?? new ReleaseEntity {Version = version};
release.Status = ReleaseEntityStatus.Queued;
_repository.Upsert(release);
}
public void FinishInstallation(string version)
{
// Mark release as installed and add if missing
ReleaseEntity release = _repository.Query<ReleaseEntity>().Where(r => r.Version == version).FirstOrDefault() ?? new ReleaseEntity {Version = version};
release.Status = ReleaseEntityStatus.Installed;
release.InstalledAt = DateTimeOffset.UtcNow;
_repository.Upsert(release);
// Mark other releases as historical
List<ReleaseEntity> oldReleases = _repository.Query<ReleaseEntity>().Where(r => r.Version != version && r.Status == ReleaseEntityStatus.Installed).ToList();
if (!oldReleases.Any())
return;
foreach (ReleaseEntity oldRelease in oldReleases)
oldRelease.Status = ReleaseEntityStatus.Historical;
_repository.Update<ReleaseEntity>(oldReleases);
}
public void DequeueInstallation()
{
_repository.DeleteMany<ReleaseEntity>(r => r.Status == ReleaseEntityStatus.Queued);
}
}
public interface IReleaseRepository : IRepository
{
string GetQueuedVersion();
string GetInstalledVersion();
string GetPreviousInstalledVersion();
void QueueInstallation(string version);
void FinishInstallation(string version);
void DequeueInstallation();
}

View File

@ -2,7 +2,7 @@
"profiles": {
"Artemis.UI.Windows": {
"commandName": "Project",
"commandLineArgs": "--force-elevation --disable-forced-shutdown --pcmr --channel=feature/lawn-mowing"
"commandLineArgs": "--force-elevation --disable-forced-shutdown --pcmr --channel=feature/gh-actions"
}
}
}

View File

@ -128,7 +128,7 @@ public class WindowsUpdateNotificationProvider : IUpdateNotificationProvider
}
// Queue an update in case the user interrupts the process after everything has been prepared
_updateService.QueueUpdate();
_updateService.QueueUpdate(releaseVersion);
GetBuilderForRelease(releaseId, releaseVersion)
.AddAudio(new ToastAudio {Silent = true})

View File

@ -16,6 +16,7 @@ using Artemis.UI.Services.Updating;
using Artemis.UI.Shared;
using Artemis.UI.Shared.Providers;
using Artemis.UI.Shared.Services;
using Artemis.UI.Shared.Services.Builders;
using Avalonia.Threading;
using DryIoc;
using DynamicData;
@ -162,14 +163,25 @@ public class GeneralTabViewModel : ActivatableViewModelBase
private async Task ExecuteCheckForUpdate(CancellationToken cancellationToken)
{
// If an update was available a popup was shown, no need to continue
if (await _updateService.CheckForUpdate())
return;
try
{
// If an update was available a popup was shown, no need to continue
if (await _updateService.CheckForUpdate())
return;
_notificationService.CreateNotification()
.WithTitle("No update available")
.WithMessage("You are running the latest version in your current channel")
.Show();
_notificationService.CreateNotification()
.WithTitle("No update available")
.WithMessage("You are running the latest version in your current channel")
.Show();
}
catch (Exception e)
{
_notificationService.CreateNotification()
.WithTitle("Failed to check for update")
.WithMessage(e.Message)
.WithSeverity(NotificationSeverity.Warning)
.Show();
}
}
private async Task ExecuteShowSetupWizard()

View File

@ -9,6 +9,14 @@
mc:Ignorable="d" d:DesignWidth="1000" d:DesignHeight="1400"
x:Class="Artemis.UI.Screens.Settings.ReleasesTabView"
x:DataType="settings:ReleasesTabViewModel">
<UserControl.Styles>
<Style Selector="avalonia|MaterialIcon.status-icon">
<Setter Property="Width" Value="20" />
<Setter Property="Height" Value="20" />
<Setter Property="Background" Value="Transparent" />
<Setter Property="Foreground" Value="{DynamicResource SystemAccentColorLight1}" />
</Style>
</UserControl.Styles>
<Panel>
<StackPanel VerticalAlignment="Center" MaxWidth="300" Spacing="15" IsVisible="{CompiledBinding Loading}">
<TextBlock TextAlignment="Center">Loading releases...</TextBlock>
@ -16,9 +24,10 @@
</StackPanel>
<Panel IsVisible="{CompiledBinding !Loading}">
<StackPanel VerticalAlignment="Center" Spacing="15" IsVisible="{CompiledBinding !ReleaseViewModels.Count}">
<TextBlock TextAlignment="Center"
TextWrapping="Wrap"
Text="{CompiledBinding Channel, StringFormat='Found no releases for the \'{0}\' channel.'}"></TextBlock>
<TextBlock TextAlignment="Center"
TextWrapping="Wrap"
Text="{CompiledBinding Channel, StringFormat='Found no releases for the \'{0}\' channel.'}">
</TextBlock>
<controls:HyperlinkButton NavigateUri="https://wiki.artemis-rgb.com/en/channels"
HorizontalAlignment="Center">
Learn more about channels on the wiki
@ -31,20 +40,15 @@
<ListBox.ItemTemplate>
<DataTemplate x:DataType="updating:ReleaseViewModel">
<Panel>
<Grid Margin="4" IsVisible="{CompiledBinding IsCurrentVersion}" RowDefinitions="*,*" ColumnDefinitions="*,Auto">
<TextBlock Grid.Row="0" Grid.Column="0" Text="{CompiledBinding Version}" VerticalAlignment="Center" IsVisible="{CompiledBinding IsCurrentVersion}"
FontWeight="SemiBold" />
<Grid Margin="4" IsVisible="{CompiledBinding ShowStatusIndicator}" RowDefinitions="*,*" ColumnDefinitions="*,Auto">
<TextBlock Grid.Row="0" Grid.Column="0" Text="{CompiledBinding Version}" VerticalAlignment="Center" FontWeight="SemiBold" />
<TextBlock Grid.Row="1" Grid.Column="0" Text="{CompiledBinding CreatedAt, StringFormat={}{0:g}}" VerticalAlignment="Center" Classes="subtitle" FontSize="13" />
<avalonia:MaterialIcon Grid.Row="0"
Grid.Column="1"
Grid.RowSpan="2"
Kind="CheckCircle"
ToolTip.Tip="Currently installed"
Foreground="{DynamicResource SystemAccentColorLight3}"
Width="20"
Height="20" />
<avalonia:MaterialIcon Classes="status-icon" Grid.Row="0" Grid.Column="1" Grid.RowSpan="2" Kind="CheckCircle" ToolTip.Tip="Current version"
IsVisible="{CompiledBinding IsCurrentVersion}" />
<avalonia:MaterialIcon Classes="status-icon" Grid.Row="0" Grid.Column="1" Grid.RowSpan="2" Kind="History" ToolTip.Tip="Previous version"
IsVisible="{CompiledBinding IsPreviousVersion}" />
</Grid>
<StackPanel Margin="4" IsVisible="{CompiledBinding !IsCurrentVersion}">
<StackPanel Margin="4" IsVisible="{CompiledBinding !ShowStatusIndicator}">
<TextBlock Text="{CompiledBinding Version}" VerticalAlignment="Center" />
<TextBlock Text="{CompiledBinding CreatedAt, StringFormat={}{0:g}}" VerticalAlignment="Center" Classes="subtitle" FontSize="13" />
</StackPanel>

View File

@ -2,7 +2,6 @@
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:settings="clr-namespace:Artemis.UI.Screens.Settings"
xmlns:updating="clr-namespace:Artemis.UI.Screens.Settings.Updating"
xmlns:avalonia="clr-namespace:Markdown.Avalonia;assembly=Markdown.Avalonia"
xmlns:mdc="clr-namespace:Markdown.Avalonia.Controls;assembly=Markdown.Avalonia"
@ -55,7 +54,7 @@
</Style>
</UserControl.Styles>
<Grid RowDefinitions="Auto,*" IsVisible="{CompiledBinding !Loading}" Classes="fade-in">
<Grid RowDefinitions="Auto,*" IsVisible="{CompiledBinding Commit, Converter={x:Static StringConverters.IsNotNullOrEmpty}}" Classes="fade-in">
<Border Grid.Row="0" Classes="card" Margin="0 0 0 10">
<StackPanel>
<Grid ColumnDefinitions="*,Auto">

View File

@ -25,11 +25,10 @@ public class ReleaseViewModel : ActivatableViewModelBase
private readonly IUpdateService _updateService;
private readonly Platform _updatePlatform;
private readonly IUpdatingClient _updatingClient;
private readonly IWindowService _windowService;
private CancellationTokenSource? _installerCts;
private string _changelog = string.Empty;
private string _commit = string.Empty;
private string _shortCommit = string.Empty;
private string? _changelog;
private string? _commit;
private string? _shortCommit;
private long _fileSize;
private bool _installationAvailable;
private bool _installationFinished;
@ -43,14 +42,12 @@ public class ReleaseViewModel : ActivatableViewModelBase
ILogger logger,
IUpdatingClient updatingClient,
INotificationService notificationService,
IUpdateService updateService,
IWindowService windowService)
IUpdateService updateService)
{
_logger = logger;
_updatingClient = updatingClient;
_notificationService = notificationService;
_updateService = updateService;
_windowService = windowService;
if (OperatingSystem.IsWindows())
_updatePlatform = Platform.Windows;
@ -97,19 +94,19 @@ public class ReleaseViewModel : ActivatableViewModelBase
public DateTimeOffset CreatedAt { get; }
public ReleaseInstaller ReleaseInstaller { get; }
public string Changelog
public string? Changelog
{
get => _changelog;
set => RaiseAndSetIfChanged(ref _changelog, value);
}
public string Commit
public string? Commit
{
get => _commit;
set => RaiseAndSetIfChanged(ref _commit, value);
}
public string ShortCommit
public string? ShortCommit
{
get => _shortCommit;
set => RaiseAndSetIfChanged(ref _shortCommit, value);
@ -146,7 +143,9 @@ public class ReleaseViewModel : ActivatableViewModelBase
}
public bool IsCurrentVersion => Version == Constants.CurrentVersion;
public bool IsPreviousVersion => Version == _updateService.PreviousVersion;
public bool ShowStatusIndicator => IsCurrentVersion || IsPreviousVersion;
public void NavigateToSource()
{
Utilities.OpenUrl($"https://github.com/Artemis-RGB/Artemis/commit/{Commit}");
@ -159,14 +158,20 @@ public class ReleaseViewModel : ActivatableViewModelBase
{
InstallationInProgress = true;
await ReleaseInstaller.InstallAsync(_installerCts.Token);
_updateService.QueueUpdate();
_updateService.QueueUpdate(Version);
InstallationFinished = true;
}
catch (Exception e)
{
if (_installerCts.IsCancellationRequested)
return;
_windowService.ShowExceptionDialog("Failed to install update", e);
_logger.Warning(e, "Failed to install update through UI");
_notificationService.CreateNotification()
.WithTitle("Failed to install update")
.WithMessage(e.Message)
.WithSeverity(NotificationSeverity.Warning)
.Show();
}
finally
{

View File

@ -7,11 +7,12 @@ namespace Artemis.UI.Services.Updating;
public interface IUpdateService : IArtemisUIService
{
string Channel { get; }
string? PreviousVersion { get; }
IGetNextRelease_NextPublishedRelease? CachedLatestRelease { get; }
Task CacheLatestRelease();
Task<bool> CheckForUpdate();
void QueueUpdate();
void QueueUpdate(string version);
ReleaseInstaller GetReleaseInstaller(string releaseId);
void RestartForUpdate(bool silent);

View File

@ -4,8 +4,7 @@ using System.Threading;
using System.Threading.Tasks;
using Artemis.Core;
using Artemis.Core.Services;
using Artemis.Storage.Entities.General;
using Artemis.Storage.Repositories.Interfaces;
using Artemis.Storage.Repositories;
using Artemis.UI.Shared.Services.MainWindow;
using Artemis.WebClient.Updating;
using Serilog;
@ -22,10 +21,9 @@ public class UpdateService : IUpdateService
private readonly Platform _updatePlatform;
private readonly ILogger _logger;
private readonly IMainWindowService _mainWindowService;
private readonly IQueuedActionRepository _queuedActionRepository;
private readonly Lazy<IUpdateNotificationProvider> _updateNotificationProvider;
private readonly IUpdatingClient _updatingClient;
private readonly IReleaseRepository _releaseRepository;
private readonly Lazy<IUpdateNotificationProvider> _updateNotificationProvider;
private readonly Func<string, ReleaseInstaller> _getReleaseInstaller;
private bool _suspendAutoCheck;
@ -33,15 +31,14 @@ public class UpdateService : IUpdateService
public UpdateService(ILogger logger,
ISettingsService settingsService,
IMainWindowService mainWindowService,
IQueuedActionRepository queuedActionRepository,
IUpdatingClient updatingClient,
IReleaseRepository releaseRepository,
Lazy<IUpdateNotificationProvider> updateNotificationProvider,
Func<string, ReleaseInstaller> getReleaseInstaller)
{
_logger = logger;
_mainWindowService = mainWindowService;
_queuedActionRepository = queuedActionRepository;
_updatingClient = updatingClient;
_releaseRepository = releaseRepository;
_updateNotificationProvider = updateNotificationProvider;
_getReleaseInstaller = getReleaseInstaller;
@ -59,33 +56,41 @@ public class UpdateService : IUpdateService
_updatePlatform = Platform.Osx;
else
throw new PlatformNotSupportedException("Cannot auto update on the current platform");
_autoCheck = settingsService.GetSetting("UI.Updating.AutoCheck", true);
_autoInstall = settingsService.GetSetting("UI.Updating.AutoInstall", false);
_autoCheck.SettingChanged += HandleAutoUpdateEvent;
_mainWindowService.MainWindowOpened += HandleAutoUpdateEvent;
mainWindowService.MainWindowOpened += HandleAutoUpdateEvent;
Timer timer = new(UPDATE_CHECK_INTERVAL);
timer.Elapsed += HandleAutoUpdateEvent;
timer.Start();
InstallQueuedUpdate();
_logger.Information("Update service initialized for {Channel} channel", Channel);
ProcessReleaseStatus();
}
public string Channel { get; }
public string? PreviousVersion { get; set; }
public IGetNextRelease_NextPublishedRelease? CachedLatestRelease { get; private set; }
private void InstallQueuedUpdate()
private void ProcessReleaseStatus()
{
if (!_queuedActionRepository.IsTypeQueued("InstallUpdate"))
// If an update is queued, don't bother with anything else
string? queued = _releaseRepository.GetQueuedVersion();
if (queued != null)
{
// Remove the queued installation, in case something goes wrong then at least we don't end up in a loop
_logger.Information("Installing queued version {Version}", queued);
RestartForUpdate(true);
return;
}
// If a different version was installed, mark it as such
string? installed = _releaseRepository.GetInstalledVersion();
if (installed != Constants.CurrentVersion)
_releaseRepository.FinishInstallation(Constants.CurrentVersion);
// Remove the queued action, in case something goes wrong then at least we don't end up in a loop
_queuedActionRepository.ClearByType("InstallUpdate");
_logger.Information("Installing queued update");
Utilities.ApplyUpdate(false);
PreviousVersion = _releaseRepository.GetPreviousInstalledVersion();
}
private void ShowUpdateNotification(IGetNextRelease_NextPublishedRelease release)
@ -111,15 +116,22 @@ public class UpdateService : IUpdateService
}
catch (Exception ex)
{
_logger.Warning(ex, "Auto update failed");
_logger.Warning(ex, "Auto update-check failed");
}
}
/// <inheritdoc />
public async Task CacheLatestRelease()
{
IOperationResult<IGetNextReleaseResult> result = await _updatingClient.GetNextRelease.ExecuteAsync(Constants.CurrentVersion, Channel, _updatePlatform);
CachedLatestRelease = result.Data?.NextPublishedRelease;
try
{
IOperationResult<IGetNextReleaseResult> result = await _updatingClient.GetNextRelease.ExecuteAsync(Constants.CurrentVersion, Channel, _updatePlatform);
CachedLatestRelease = result.Data?.NextPublishedRelease;
}
catch (Exception e)
{
_logger.Warning(e, "Failed to cache latest release");
}
}
public async Task<bool> CheckForUpdate()
@ -147,18 +159,11 @@ public class UpdateService : IUpdateService
}
/// <inheritdoc />
public void QueueUpdate()
public void QueueUpdate(string version)
{
if (!_queuedActionRepository.IsTypeQueued("InstallUpdate"))
_queuedActionRepository.Add(new QueuedActionEntity {Type = "InstallUpdate"});
_releaseRepository.QueueInstallation(version);
}
/// <inheritdoc />
public void DequeueUpdate()
{
_queuedActionRepository.ClearByType("InstallUpdate");
}
/// <inheritdoc />
public ReleaseInstaller GetReleaseInstaller(string releaseId)
{
@ -168,7 +173,7 @@ public class UpdateService : IUpdateService
/// <inheritdoc />
public void RestartForUpdate(bool silent)
{
DequeueUpdate();
_releaseRepository.DequeueInstallation();
Utilities.ApplyUpdate(silent);
}
}