Implemented Artemis.Web.Update client

This commit is contained in:
Robert committed 2023-02-12 16:48:16 +01:00
1 parent a5d2249593
commit 0cd65a2ebf
36 files changed
+1022 -359

No files matched your search

@@ -17,7 +17,7 @@ namespace Artemis.UI.Windows;
public class ApplicationStateManager
{
private const int SM_SHUTTINGDOWN = 0x2000;
public ApplicationStateManager(IContainer container, string[] startupArguments)
{
StartupArguments = startupArguments;
@@ -25,6 +25,7 @@ public class ApplicationStateManager
Core.Utilities.ShutdownRequested += UtilitiesOnShutdownRequested;
Core.Utilities.RestartRequested += UtilitiesOnRestartRequested;
Core.Utilities.UpdateRequested += UtilitiesOnUpdateRequested;
// On Windows shutdown dispose the IOC container just so device providers get a chance to clean up
if (Application.Current?.ApplicationLifetime is IControlledApplicationLifetime controlledApplicationLifetime)
@@ -91,6 +92,33 @@ public class ApplicationStateManager
Dispatcher.UIThread.Post(() => controlledApplicationLifetime.Shutdown());
}
private void UtilitiesOnUpdateRequested(object? sender, UpdateEventArgs e)
{
List<string> argsList = new(StartupArguments);
if (e.Silent)
argsList.Add("--autorun");
// Retain startup arguments after update by providing them to the script
string script = $"\"{Path.Combine(Constants.DataFolder, "updating", "pending", "scripts", "update.ps1")}\"";
string source = $"-sourceDirectory \"{Path.Combine(Constants.DataFolder, "updating", "pending")}\"";
string destination = $"-destinationDirectory \"{Constants.ApplicationFolder}\"";
string args = argsList.Any() ? $"-artemisArgs \"{string.Join(',', argsList)}\"" : "";
// Run the PowerShell script included in the new version, that way any changes made to the script are used
ProcessStartInfo info = new()
{
Arguments = $"-File {script} {source} {destination} {args}",
WindowStyle = ProcessWindowStyle.Hidden,
CreateNoWindow = true,
FileName = "PowerShell.exe"
};
Process.Start(info);
// Lets try a graceful shutdown, PowerShell will kill if needed
if (Application.Current?.ApplicationLifetime is IControlledApplicationLifetime controlledApplicationLifetime)
Dispatcher.UIThread.Post(() => controlledApplicationLifetime.Shutdown());
}
private void UtilitiesOnShutdownRequested(object? sender, EventArgs e)
{
// Use PowerShell to kill the process after 8 sec just in case
@@ -115,7 +143,7 @@ public class ApplicationStateManager
};
Process.Start(info);
}
[System.Runtime.InteropServices.DllImport("user32.dll")]
private static extern int GetSystemMetrics(int nIndex);
}
@@ -12,20 +12,10 @@
</PropertyGroup>
<ItemGroup>
<AvaloniaResource Include="Assets\**" />
<None Remove=".gitignore" />
<None Remove="Assets\Cursors\aero_crosshair.cur" />
<None Remove="Assets\Cursors\aero_crosshair_minus.cur" />
<None Remove="Assets\Cursors\aero_crosshair_plus.cur" />
<None Remove="Assets\Cursors\aero_drag.cur" />
<None Remove="Assets\Cursors\aero_drag_ew.cur" />
<None Remove="Assets\Cursors\aero_fill.cur" />
<None Remove="Assets\Cursors\aero_pen_min.cur" />
<None Remove="Assets\Cursors\aero_pen_plus.cur" />
<None Remove="Assets\Cursors\aero_rotate.cur" />
<None Remove="Assets\Cursors\aero_rotate_bl.cur" />
<None Remove="Assets\Cursors\aero_rotate_br.cur" />
<None Remove="Assets\Cursors\aero_rotate_tl.cur" />
<None Remove="Assets\Cursors\aero_rotate_tr.cur" />
<Content Include="Scripts\**">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<None Include="..\Artemis.UI\Assets\Images\Logo\application.ico">
<Link>application.ico</Link>
</None>
@@ -20,7 +20,6 @@ public static class UIContainerExtensions
{
container.Register<ICursorProvider, CursorProvider>(Reuse.Singleton);
container.Register<IGraphicsContextProvider, GraphicsContextProvider>(Reuse.Singleton);
container.Register<IUpdateProvider, UpdateProvider>(Reuse.Singleton);
container.Register<IAutoRunProvider, AutoRunProvider>();
container.Register<InputProvider, WindowsInputProvider>(serviceKey: WindowsInputProvider.Id);
}
@@ -1,70 +0,0 @@
<Window xmlns="https://github.com/avaloniaui"
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:update="clr-namespace:Artemis.UI.Windows.Screens.Update"
xmlns:controls="clr-namespace:FluentAvalonia.UI.Controls;assembly=FluentAvalonia"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="Artemis.UI.Windows.Screens.Update.UpdateDialogView"
x:DataType="update:UpdateDialogViewModel"
Title="Artemis | Update available"
Width="750"
MinWidth="750"
Height="500"
WindowStartupLocation="CenterOwner">
<Grid Margin="15" RowDefinitions="Auto,*,Auto">
<TextBlock Grid.Row="0" Classes="h4">
A new Artemis update is available! 🥳
</TextBlock>
<StackPanel Grid.Row="1" IsVisible="{CompiledBinding RetrievingChanges}" VerticalAlignment="Center" HorizontalAlignment="Center">
<TextBlock>Retrieving changes...</TextBlock>
<ProgressBar IsIndeterminate="True"></ProgressBar>
</StackPanel>
<Border Grid.Row="1" Classes="card" IsVisible="{CompiledBinding !RetrievingChanges}">
<Grid RowDefinitions="Auto,*">
<StackPanel Grid.Row="0">
<StackPanel Orientation="Horizontal">
<TextBlock Text="You are currently running build " />
<TextBlock Text="{CompiledBinding CurrentBuild, Mode=OneWay}"></TextBlock>
<TextBlock Text=" while the latest build is " />
<TextBlock Text="{CompiledBinding LatestBuild, Mode=OneWay}"></TextBlock>
<TextBlock Text="." />
</StackPanel>
<TextBlock Text="Updating Artemis will give you the latest bug(fixes), features and improvements." />
<Separator Classes="card-separator" />
<TextBlock Classes="h5" IsVisible="{CompiledBinding HasChanges}">
Changelog (auto-generated)
</TextBlock>
</StackPanel>
<ScrollViewer Grid.Row="1" VerticalAlignment="Top" HorizontalScrollBarVisibility="Disabled" VerticalScrollBarVisibility="Auto" IsVisible="{CompiledBinding HasChanges}">
<ItemsControl Items="{CompiledBinding Changes}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Grid ColumnDefinitions="Auto,*">
<TextBlock Text="•" Margin="10 0" />
<TextBlock Grid.Column="1" Text="{Binding}" TextWrapping="Wrap" />
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</ScrollViewer>
<StackPanel Grid.Row="1" IsVisible="{CompiledBinding !HasChanges}" VerticalAlignment="Center" HorizontalAlignment="Center">
<TextBlock HorizontalAlignment="Center">We couldn't retrieve any changes</TextBlock>
<controls:HyperlinkButton NavigateUri="https://github.com/Artemis-RGB/Artemis/commits/master" HorizontalAlignment="Center">View online</controls:HyperlinkButton>
</StackPanel>
</Grid>
</Border>
<StackPanel HorizontalAlignment="Right" Orientation="Horizontal" Spacing="5" Grid.Row="2" Margin="0 15 0 0">
<Button Classes="accent" Command="{CompiledBinding Install}">Install update</Button>
<Button Command="{CompiledBinding AskLater}">Ask later</Button>
</StackPanel>
</Grid>
</Window>
@@ -1,21 +0,0 @@
using Artemis.UI.Shared;
using Avalonia;
using Avalonia.Markup.Xaml;
namespace Artemis.UI.Windows.Screens.Update;
public class UpdateDialogView : ReactiveCoreWindow<UpdateDialogViewModel>
{
public UpdateDialogView()
{
InitializeComponent();
#if DEBUG
this.AttachDevTools();
#endif
}
private void InitializeComponent()
{
AvaloniaXamlLoader.Load(this);
}
}
@@ -1,120 +0,0 @@
using System;
using System.Collections.ObjectModel;
using System.Linq;
using System.Reactive;
using System.Reactive.Disposables;
using System.Threading.Tasks;
using Artemis.Core;
using Artemis.UI.Shared;
using Artemis.UI.Shared.Providers;
using Artemis.UI.Shared.Services;
using Artemis.UI.Shared.Services.Builders;
using Artemis.UI.Windows.Models;
using Artemis.UI.Windows.Providers;
using Avalonia.Threading;
using DynamicData;
using ReactiveUI;
namespace Artemis.UI.Windows.Screens.Update;
public class UpdateDialogViewModel : DialogViewModelBase<bool>
{
// Based on https://docs.microsoft.com/en-us/azure/devops/pipelines/repos/github?view=azure-devops&tabs=yaml#skipping-ci-for-individual-commits
private readonly string[] _excludedCommitMessages =
{
"[skip ci]",
"[ci skip]",
"skip-checks: true",
"skip-checks:true",
"[skip azurepipelines]",
"[azurepipelines skip]",
"[skip azpipelines]",
"[azpipelines skip]",
"[skip azp]",
"[azp skip]",
"***NO_CI***"
};
private readonly INotificationService _notificationService;
private readonly UpdateProvider _updateProvider;
private bool _hasChanges;
private string? _latestBuild;
private bool _retrievingChanges;
public UpdateDialogViewModel(string channel, IUpdateProvider updateProvider, INotificationService notificationService)
{
_updateProvider = (UpdateProvider) updateProvider;
_notificationService = notificationService;
Channel = channel;
CurrentBuild = Constants.BuildInfo.BuildNumberDisplay;
this.WhenActivated((CompositeDisposable _) => Dispatcher.UIThread.InvokeAsync(GetBuildChanges));
Install = ReactiveCommand.Create(() => Close(true));
AskLater = ReactiveCommand.Create(() => Close(false));
}
public ReactiveCommand<Unit, Unit> Install { get; }
public ReactiveCommand<Unit, Unit> AskLater { get; }
public string Channel { get; }
public string CurrentBuild { get; }
public ObservableCollection<string> Changes { get; } = new();
public bool RetrievingChanges
{
get => _retrievingChanges;
set => RaiseAndSetIfChanged(ref _retrievingChanges, value);
}
public bool HasChanges
{
get => _hasChanges;
set => RaiseAndSetIfChanged(ref _hasChanges, value);
}
public string? LatestBuild
{
get => _latestBuild;
set => RaiseAndSetIfChanged(ref _latestBuild, value);
}
private async Task GetBuildChanges()
{
try
{
RetrievingChanges = true;
Task<DevOpsBuild?> currentTask = _updateProvider.GetBuildInfo(1, CurrentBuild);
Task<DevOpsBuild?> latestTask = _updateProvider.GetBuildInfo(1);
DevOpsBuild? current = await currentTask;
DevOpsBuild? latest = await latestTask;
LatestBuild = latest?.BuildNumber;
if (current != null && latest != null)
{
GitHubDifference difference = await _updateProvider.GetBuildDifferences(current, latest);
// Only take commits with one parents (no merges)
Changes.Clear();
Changes.AddRange(difference.Commits.Where(c => c.Parents.Count == 1)
.SelectMany(c => c.Commit.Message.Split("\n"))
.Select(m => m.Trim())
.Where(m => !string.IsNullOrWhiteSpace(m) && !_excludedCommitMessages.Contains(m))
.OrderBy(m => m)
);
HasChanges = Changes.Any();
}
}
catch (Exception e)
{
_notificationService.CreateNotification().WithTitle("Failed to retrieve build changes").WithMessage(e.Message).WithSeverity(NotificationSeverity.Error).Show();
}
finally
{
RetrievingChanges = false;
}
}
}
+42
View File
@@ -0,0 +1,42 @@
param (
[Parameter(Mandatory=$true)][string]$sourceDirectory,
[Parameter(Mandatory=$true)][string]$destinationDirectory,
[Parameter(Mandatory=$false)][string]$artemisArgs
)
# Wait up to 10 seconds for the process to shut down
for ($i=1; $i -le 10; $i++) {
$process = Get-Process -Name Artemis.UI.Windows -ErrorAction SilentlyContinue
if (!$process) {
break
}
Write-Host "Waiting for Artemis to shut down ($i / 10)"
Start-Sleep -Seconds 1
}
# If the process is still running, kill it
$process = Get-Process -Name Artemis.UI.Windows -ErrorAction SilentlyContinue
if ($process) {
Stop-Process -Id $process.Id -Force
Start-Sleep -Seconds 1
}
# Check if the destination directory exists
if (!(Test-Path $destinationDirectory)) {
Write-Error "The destination directory does not exist"
}
# If the destination directory exists, clear it
Get-ChildItem $destinationDirectory | Remove-Item -Recurse -Force
# Move the contents of the source directory to the destination directory
Get-ChildItem $sourceDirectory | Move-Item -Destination $destinationDirectory
Start-Sleep -Seconds 1
# When finished, run the updated version
if ($artemisArgs) {
Start-Process -FilePath "$destinationDirectory\Artemis.UI.Windows.exe" -WorkingDirectory $destinationDirectory -ArgumentList $artemisArgs
} else {
Start-Process -FilePath "$destinationDirectory\Artemis.UI.Windows.exe" -WorkingDirectory $destinationDirectory
}