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

Output install log to file

This commit is contained in:
Robert 2023-03-09 21:35:12 +01:00
parent 148eb99870
commit 0e32bb6b61
9 changed files with 139 additions and 73 deletions

View File

@ -15,13 +15,14 @@ public class ReleaseRepository : IReleaseRepository
_repository.Database.GetCollection<ReleaseEntity>().EnsureIndex(s => s.Version, true); _repository.Database.GetCollection<ReleaseEntity>().EnsureIndex(s => s.Version, true);
} }
public void SaveVersionInstallDate(string version) public bool SaveVersionInstallDate(string version)
{ {
ReleaseEntity release = _repository.Query<ReleaseEntity>().Where(r => r.Version == version).FirstOrDefault(); ReleaseEntity release = _repository.Query<ReleaseEntity>().Where(r => r.Version == version).FirstOrDefault();
if (release != null) if (release != null)
return; return false;
_repository.Insert(new ReleaseEntity {Version = version, InstalledAt = DateTimeOffset.UtcNow}); _repository.Insert(new ReleaseEntity {Version = version, InstalledAt = DateTimeOffset.UtcNow});
return true;
} }
public ReleaseEntity GetPreviousInstalledVersion() public ReleaseEntity GetPreviousInstalledVersion()
@ -32,6 +33,6 @@ public class ReleaseRepository : IReleaseRepository
public interface IReleaseRepository : IRepository public interface IReleaseRepository : IRepository
{ {
void SaveVersionInstallDate(string version); bool SaveVersionInstallDate(string version);
ReleaseEntity GetPreviousInstalledVersion(); ReleaseEntity GetPreviousInstalledVersion();
} }

View File

@ -99,18 +99,12 @@ public class ApplicationStateManager
argsList.Add("--autorun"); argsList.Add("--autorun");
// Retain startup arguments after update by providing them to the script // Retain startup arguments after update by providing them to the script
string script = $"\"{Path.Combine(Constants.UpdatingFolder, "installing", "scripts", "update.ps1")}\""; string script = Path.Combine(Constants.UpdatingFolder, "installing", "scripts", "update.ps1");
string source = $"-sourceDirectory \"{Path.Combine(Constants.UpdatingFolder, "installing")}\""; string source = $"-sourceDirectory \"{Path.Combine(Constants.UpdatingFolder, "installing")}\"";
string destination = $"-destinationDirectory \"{Constants.ApplicationFolder}\""; string destination = $"-destinationDirectory \"{Constants.ApplicationFolder}\"";
string args = argsList.Any() ? $"-artemisArgs \"{string.Join(',', argsList)}\"" : ""; 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 RunScriptWithOutputFile(script, $"{source} {destination} {args}", Path.Combine(Constants.DataFolder, "update-log.txt"));
ProcessStartInfo info = new()
{
Arguments = $"-File {script} {source} {destination} {args}",
FileName = "PowerShell.exe"
};
Process.Start(info);
// Lets try a graceful shutdown, PowerShell will kill if needed // Lets try a graceful shutdown, PowerShell will kill if needed
if (Application.Current?.ApplicationLifetime is IControlledApplicationLifetime controlledApplicationLifetime) if (Application.Current?.ApplicationLifetime is IControlledApplicationLifetime controlledApplicationLifetime)
@ -142,6 +136,20 @@ public class ApplicationStateManager
Process.Start(info); Process.Start(info);
} }
private void RunScriptWithOutputFile(string script, string arguments, string outputFile)
{
// Use > for files that are bigger than 200kb to start fresh, otherwise use >> to append
string redirectSymbol = File.Exists(outputFile) && new FileInfo(outputFile).Length > 200000 ? ">" : ">>";
ProcessStartInfo info = new()
{
Arguments = $"PowerShell -File \"{script}\" {arguments} {redirectSymbol} \"{outputFile}\"",
FileName = "PowerShell.exe",
WindowStyle = ProcessWindowStyle.Hidden,
CreateNoWindow = true,
};
Process.Start(info);
}
[System.Runtime.InteropServices.DllImport("user32.dll")] [System.Runtime.InteropServices.DllImport("user32.dll")]
private static extern int GetSystemMetrics(int nIndex); private static extern int GetSystemMetrics(int nIndex);
} }

View File

@ -35,30 +35,12 @@ public class WindowsUpdateNotificationProvider : IUpdateNotificationProvider
ToastNotificationManagerCompat.OnActivated += ToastNotificationManagerCompatOnOnActivated; ToastNotificationManagerCompat.OnActivated += ToastNotificationManagerCompatOnOnActivated;
} }
private async void ToastNotificationManagerCompatOnOnActivated(ToastNotificationActivatedEventArgsCompat e) /// <inheritdoc />
{
ToastArguments args = ToastArguments.Parse(e.Argument);
Guid releaseId = Guid.Parse(args.Get("releaseId"));
string releaseVersion = args.Get("releaseVersion");
string action = "view-changes";
if (args.Contains("action"))
action = args.Get("action");
if (action == "install")
await InstallRelease(releaseId, releaseVersion);
else if (action == "view-changes")
ViewRelease(releaseId);
else if (action == "cancel")
_cancellationTokenSource?.Cancel();
else if (action == "restart-for-update")
_updateService.RestartForUpdate(false);
}
public void ShowNotification(Guid releaseId, string releaseVersion) public void ShowNotification(Guid releaseId, string releaseVersion)
{ {
GetBuilderForRelease(releaseId, releaseVersion) GetBuilderForRelease(releaseId, releaseVersion)
.AddText("Update available") .AddText("Update available")
.AddText($"Artemis version {releaseVersion} has been released") .AddText($"Artemis {releaseVersion} has been released")
.AddButton(new ToastButton() .AddButton(new ToastButton()
.SetContent("Install") .SetContent("Install")
.AddArgument("action", "install").SetAfterActivationBehavior(ToastAfterActivationBehavior.PendingUpdate)) .AddArgument("action", "install").SetAfterActivationBehavior(ToastAfterActivationBehavior.PendingUpdate))
@ -66,7 +48,17 @@ public class WindowsUpdateNotificationProvider : IUpdateNotificationProvider
.Show(t => t.Tag = releaseId.ToString()); .Show(t => t.Tag = releaseId.ToString());
} }
private void ViewRelease(Guid releaseId) /// <inheritdoc />
public void ShowInstalledNotification(string installedVersion)
{
new ToastContentBuilder().AddArgument("releaseVersion", installedVersion)
.AddText("Update installed")
.AddText($"Artemis {installedVersion} has been installed")
.AddButton(new ToastButton().SetContent("View changes").AddArgument("action", "view-changes"))
.Show();
}
private void ViewRelease(string releaseVersion)
{ {
Dispatcher.UIThread.Post(() => Dispatcher.UIThread.Post(() =>
{ {
@ -83,7 +75,7 @@ public class WindowsUpdateNotificationProvider : IUpdateNotificationProvider
// Navigate to the settings VM // Navigate to the settings VM
_mainWindowService.HostScreen.Router.Navigate.Execute(settingsViewModel); _mainWindowService.HostScreen.Router.Navigate.Execute(settingsViewModel);
// Navigate to the release tab // Navigate to the release tab
releaseTabViewModel.PreselectId = releaseId; releaseTabViewModel.PreselectVersion = releaseVersion;
settingsViewModel.SelectedTab = releaseTabViewModel; settingsViewModel.SelectedTab = releaseTabViewModel;
}); });
} }
@ -128,10 +120,18 @@ public class WindowsUpdateNotificationProvider : IUpdateNotificationProvider
installer.PropertyChanged -= InstallerOnPropertyChanged; installer.PropertyChanged -= InstallerOnPropertyChanged;
} }
// If the main window is not open the user isn't busy, restart straight away
if (!_mainWindowService.IsMainWindowOpen)
{
_updateService.RestartForUpdate(true);
return;
}
// Ask for a restart because the user is actively using Artemis
GetBuilderForRelease(releaseId, releaseVersion) GetBuilderForRelease(releaseId, releaseVersion)
.AddAudio(new ToastAudio {Silent = true}) .AddAudio(new ToastAudio {Silent = true})
.AddText("Update ready") .AddText("Update ready")
.AddText($"Artemis version {releaseVersion} is ready to be applied") .AddText("Artemis must restart to finish the update")
.AddButton(new ToastButton().SetContent("Restart Artemis").AddArgument("action", "restart-for-update")) .AddButton(new ToastButton().SetContent("Restart Artemis").AddArgument("action", "restart-for-update"))
.AddButton(new ToastButton().SetContent("Later").AddArgument("action", "postpone-update")) .AddButton(new ToastButton().SetContent("Later").AddArgument("action", "postpone-update"))
.Show(t => t.Tag = releaseId.ToString()); .Show(t => t.Tag = releaseId.ToString());
@ -160,4 +160,24 @@ public class WindowsUpdateNotificationProvider : IUpdateNotificationProvider
return data; return data;
} }
private async void ToastNotificationManagerCompatOnOnActivated(ToastNotificationActivatedEventArgsCompat e)
{
ToastArguments args = ToastArguments.Parse(e.Argument);
Guid releaseId = args.Contains("releaseId") ? Guid.Parse(args.Get("releaseId")) : Guid.Empty;
string releaseVersion = args.Get("releaseVersion");
string action = "view-changes";
if (args.Contains("action"))
action = args.Get("action");
if (action == "install")
await InstallRelease(releaseId, releaseVersion);
else if (action == "view-changes")
ViewRelease(releaseVersion);
else if (action == "cancel")
_cancellationTokenSource?.Cancel();
else if (action == "restart-for-update")
_updateService.RestartForUpdate(false);
}
} }

View File

@ -1,17 +1,16 @@
param ( param (
[Parameter(Mandatory=$true)][string]$sourceDirectory, [Parameter(Mandatory = $true)][string]$sourceDirectory,
[Parameter(Mandatory=$true)][string]$destinationDirectory, [Parameter(Mandatory = $true)][string]$destinationDirectory,
[Parameter(Mandatory=$false)][string]$artemisArgs [Parameter(Mandatory = $false)][string]$artemisArgs
) )
Write-Host "Artemis update script v1" Write-Host "Artemis update script v1"
Write-Host "Please do not close this window, this should not take long"
Write-Host ""
# Wait up to 10 seconds for the process to shut down # Wait up to 10 seconds for the process to shut down
for ($i=1; $i -le 10; $i++) { for ($i = 1; $i -le 10; $i++) {
$process = Get-Process -Name Artemis.UI.Windows -ErrorAction SilentlyContinue $process = Get-Process -Name Artemis.UI.Windows -ErrorAction SilentlyContinue
if (!$process) { if (!$process)
{
break break
} }
Write-Host "Waiting for Artemis to shut down ($i / 10)" Write-Host "Waiting for Artemis to shut down ($i / 10)"
@ -20,13 +19,15 @@ for ($i=1; $i -le 10; $i++) {
# If the process is still running, kill it # If the process is still running, kill it
$process = Get-Process -Name Artemis.UI.Windows -ErrorAction SilentlyContinue $process = Get-Process -Name Artemis.UI.Windows -ErrorAction SilentlyContinue
if ($process) { if ($process)
{
Stop-Process -Id $process.Id -Force Stop-Process -Id $process.Id -Force
Start-Sleep -Seconds 1 Start-Sleep -Seconds 1
} }
# Check if the destination directory exists # Check if the destination directory exists
if (!(Test-Path $destinationDirectory)) { if (!(Test-Path $destinationDirectory))
{
Write-Error "The destination directory does not exist" Write-Error "The destination directory does not exist"
} }
@ -44,8 +45,11 @@ Write-Host "Finished! Restarting Artemis"
Start-Sleep -Seconds 1 Start-Sleep -Seconds 1
# When finished, run the updated version # When finished, run the updated version
if ($artemisArgs) { if ($artemisArgs)
{
Start-Process -FilePath "$destinationDirectory\Artemis.UI.Windows.exe" -WorkingDirectory $destinationDirectory -ArgumentList $artemisArgs Start-Process -FilePath "$destinationDirectory\Artemis.UI.Windows.exe" -WorkingDirectory $destinationDirectory -ArgumentList $artemisArgs
} else { }
else
{
Start-Process -FilePath "$destinationDirectory\Artemis.UI.Windows.exe" -WorkingDirectory $destinationDirectory Start-Process -FilePath "$destinationDirectory\Artemis.UI.Windows.exe" -WorkingDirectory $destinationDirectory
} }

View File

@ -36,7 +36,7 @@ public static class ContainerExtensions
container.Register<NodeScriptWindowViewModelBase, NodeScriptWindowViewModel>(Reuse.Singleton); container.Register<NodeScriptWindowViewModelBase, NodeScriptWindowViewModel>(Reuse.Singleton);
container.Register<IPropertyVmFactory, PropertyVmFactory>(Reuse.Singleton); container.Register<IPropertyVmFactory, PropertyVmFactory>(Reuse.Singleton);
container.Register<IUpdateNotificationProvider, InAppUpdateNotificationProvider>(); container.Register<IUpdateNotificationProvider, BasicUpdateNotificationProvider>();
container.RegisterMany(thisAssembly, type => type.IsAssignableTo<IArtemisUIService>(), Reuse.Singleton); container.RegisterMany(thisAssembly, type => type.IsAssignableTo<IArtemisUIService>(), Reuse.Singleton);
} }

View File

@ -54,14 +54,14 @@ public class ReleasesTabViewModel : ActivatableViewModelBase
{ {
await _updateService.CacheLatestRelease(); await _updateService.CacheLatestRelease();
await GetMoreReleases(d.AsCancellationToken()); await GetMoreReleases(d.AsCancellationToken());
SelectedReleaseViewModel = ReleaseViewModels.FirstOrDefault(r => r.ReleaseId == PreselectId) ?? ReleaseViewModels.FirstOrDefault(); SelectedReleaseViewModel = ReleaseViewModels.FirstOrDefault(r => r.Version == PreselectVersion) ?? ReleaseViewModels.FirstOrDefault();
}); });
} }
public ReadOnlyObservableCollection<ReleaseViewModel> ReleaseViewModels { get; } public ReadOnlyObservableCollection<ReleaseViewModel> ReleaseViewModels { get; }
public string Channel { get; } public string Channel { get; }
public Guid? PreselectId { get; set; } public string? PreselectVersion { get; set; }
public ReleaseViewModel? SelectedReleaseViewModel public ReleaseViewModel? SelectedReleaseViewModel
{ {

View File

@ -1,6 +1,5 @@
using System; using System;
using System.Linq; using System.Linq;
using System.Threading.Tasks;
using Artemis.UI.Screens.Settings; using Artemis.UI.Screens.Settings;
using Artemis.UI.Shared.Services; using Artemis.UI.Shared.Services;
using Artemis.UI.Shared.Services.Builders; using Artemis.UI.Shared.Services.Builders;
@ -9,35 +8,67 @@ using ReactiveUI;
namespace Artemis.UI.Services.Updating; namespace Artemis.UI.Services.Updating;
public class InAppUpdateNotificationProvider : IUpdateNotificationProvider public class BasicUpdateNotificationProvider : IUpdateNotificationProvider
{ {
private readonly Func<IScreen, SettingsViewModel> _getSettingsViewModel; private readonly Func<IScreen, SettingsViewModel> _getSettingsViewModel;
private readonly IMainWindowService _mainWindowService; private readonly IMainWindowService _mainWindowService;
private readonly INotificationService _notificationService; private readonly INotificationService _notificationService;
private Action? _notification; private Action? _available;
private Action? _installed;
public InAppUpdateNotificationProvider(INotificationService notificationService, IMainWindowService mainWindowService, Func<IScreen, SettingsViewModel> getSettingsViewModel) public BasicUpdateNotificationProvider(INotificationService notificationService, IMainWindowService mainWindowService, Func<IScreen, SettingsViewModel> getSettingsViewModel)
{ {
_notificationService = notificationService; _notificationService = notificationService;
_mainWindowService = mainWindowService; _mainWindowService = mainWindowService;
_getSettingsViewModel = getSettingsViewModel; _getSettingsViewModel = getSettingsViewModel;
} }
private void ShowInAppNotification(Guid releaseId, string releaseVersion) /// <inheritdoc />
public void ShowNotification(Guid releaseId, string releaseVersion)
{ {
_notification?.Invoke(); if (_mainWindowService.IsMainWindowOpen)
_notification = _notificationService.CreateNotification() ShowAvailable(releaseVersion);
else
_mainWindowService.MainWindowOpened += (_, _) => ShowAvailable(releaseVersion);
}
/// <inheritdoc />
public void ShowInstalledNotification(string installedVersion)
{
if (_mainWindowService.IsMainWindowOpen)
ShowInstalled(installedVersion);
else
_mainWindowService.MainWindowOpened += (_, _) => ShowInstalled(installedVersion);
}
private void ShowAvailable(string releaseVersion)
{
_available?.Invoke();
_available = _notificationService.CreateNotification()
.WithTitle("Update available") .WithTitle("Update available")
.WithMessage($"Artemis version {releaseVersion} has been released") .WithMessage($"Artemis {releaseVersion} has been released")
.WithSeverity(NotificationSeverity.Success) .WithSeverity(NotificationSeverity.Success)
.WithTimeout(TimeSpan.FromSeconds(15)) .WithTimeout(TimeSpan.FromSeconds(15))
.HavingButton(b => b.WithText("View release").WithAction(() => ViewRelease(releaseId))) .HavingButton(b => b.WithText("View release").WithAction(() => ViewRelease(releaseVersion)))
.Show(); .Show();
} }
private void ViewRelease(Guid releaseId) private void ShowInstalled(string installedVersion)
{ {
_notification?.Invoke(); _installed?.Invoke();
_installed = _notificationService.CreateNotification()
.WithTitle("Update installed")
.WithMessage($"Artemis {installedVersion} has been installed.")
.WithSeverity(NotificationSeverity.Success)
.WithTimeout(TimeSpan.FromSeconds(15))
.HavingButton(b => b.WithText("View release").WithAction(() => ViewRelease(installedVersion)))
.Show();
}
private void ViewRelease(string version)
{
_installed?.Invoke();
_available?.Invoke();
if (_mainWindowService.HostScreen == null) if (_mainWindowService.HostScreen == null)
return; return;
@ -51,16 +82,7 @@ public class InAppUpdateNotificationProvider : IUpdateNotificationProvider
// Navigate to the settings VM // Navigate to the settings VM
_mainWindowService.HostScreen.Router.Navigate.Execute(settingsViewModel); _mainWindowService.HostScreen.Router.Navigate.Execute(settingsViewModel);
// Navigate to the release tab // Navigate to the release tab
releaseTabViewModel.PreselectId = releaseId; releaseTabViewModel.PreselectVersion = version;
settingsViewModel.SelectedTab = releaseTabViewModel; settingsViewModel.SelectedTab = releaseTabViewModel;
} }
/// <inheritdoc />
public void ShowNotification(Guid releaseId, string releaseVersion)
{
if (_mainWindowService.IsMainWindowOpen)
ShowInAppNotification(releaseId, releaseVersion);
else
_mainWindowService.MainWindowOpened += (_, _) => ShowInAppNotification(releaseId, releaseVersion);
}
} }

View File

@ -1,9 +1,9 @@
using System; using System;
using System.Threading.Tasks;
namespace Artemis.UI.Services.Updating; namespace Artemis.UI.Services.Updating;
public interface IUpdateNotificationProvider public interface IUpdateNotificationProvider
{ {
void ShowNotification(Guid releaseId, string releaseVersion); void ShowNotification(Guid releaseId, string releaseVersion);
void ShowInstalledNotification(string installedVersion);
} }

View File

@ -66,7 +66,7 @@ public class UpdateService : IUpdateService
private void ProcessReleaseStatus() private void ProcessReleaseStatus()
{ {
string currentVersion = Constants.CurrentVersion; string currentVersion = Constants.CurrentVersion;
_releaseRepository.SaveVersionInstallDate(currentVersion); bool updated = _releaseRepository.SaveVersionInstallDate(currentVersion);
PreviousVersion = _releaseRepository.GetPreviousInstalledVersion()?.Version; PreviousVersion = _releaseRepository.GetPreviousInstalledVersion()?.Version;
if (!Directory.Exists(Constants.UpdatingFolder)) if (!Directory.Exists(Constants.UpdatingFolder))
@ -88,6 +88,9 @@ public class UpdateService : IUpdateService
_logger.Warning(e, "Failed to clean up old update file at {FilePath}", file); _logger.Warning(e, "Failed to clean up old update file at {FilePath}", file);
} }
} }
// if (updated)
_updateNotificationProvider.Value.ShowInstalledNotification(currentVersion);
} }
private void ShowUpdateNotification(IGetNextRelease_NextPublishedRelease release) private void ShowUpdateNotification(IGetNextRelease_NextPublishedRelease release)
@ -194,7 +197,15 @@ public class UpdateService : IUpdateService
if (Directory.Exists(Path.Combine(Constants.UpdatingFolder, "installing"))) if (Directory.Exists(Path.Combine(Constants.UpdatingFolder, "installing")))
{ {
_logger.Warning("Cleaning up leftover installing folder, did an update go wrong?"); _logger.Warning("Cleaning up leftover installing folder, did an update go wrong?");
Directory.Delete(Path.Combine(Constants.UpdatingFolder, "installing")); try
{
Directory.Delete(Path.Combine(Constants.UpdatingFolder, "installing"), true);
}
catch (Exception e)
{
_logger.Error(e, "Failed to delete leftover installing folder");
}
} }
// If an update is pending, don't bother with anything else // If an update is pending, don't bother with anything else