diff --git a/src/Artemis.UI/Screens/Settings/Dialogs/UpdateDialogView.xaml b/src/Artemis.UI/Screens/Settings/Dialogs/UpdateDialogView.xaml
new file mode 100644
index 000000000..be9bacf79
--- /dev/null
+++ b/src/Artemis.UI/Screens/Settings/Dialogs/UpdateDialogView.xaml
@@ -0,0 +1,37 @@
+
+
+
+ Update available
+
+
+ A new Artemis update is available! 🥳
+ You are currently running build while the latest build is .
+ Updating Artemis will give you the latest bug(fixes), features and improvements.
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/Artemis.UI/Screens/Settings/Dialogs/UpdateDialogViewModel.cs b/src/Artemis.UI/Screens/Settings/Dialogs/UpdateDialogViewModel.cs
new file mode 100644
index 000000000..a048ee1f8
--- /dev/null
+++ b/src/Artemis.UI/Screens/Settings/Dialogs/UpdateDialogViewModel.cs
@@ -0,0 +1,55 @@
+using System;
+using System.Threading.Tasks;
+using Artemis.Core;
+using Artemis.UI.Services;
+using Artemis.UI.Shared.Services;
+using Newtonsoft.Json.Linq;
+
+namespace Artemis.UI.Screens.Settings.Dialogs
+{
+ public class UpdateDialogViewModel : DialogViewModelBase
+ {
+ private readonly JToken _buildInfo;
+ private readonly IDialogService _dialogService;
+ private readonly IUpdateService _updateService;
+ private bool _canUpdate = true;
+
+ public UpdateDialogViewModel(JToken buildInfo, IUpdateService updateService, IDialogService dialogService)
+ {
+ _buildInfo = buildInfo;
+ _updateService = updateService;
+ _dialogService = dialogService;
+
+ CurrentBuild = Constants.BuildInfo.BuildNumberDisplay;
+ LatestBuild = buildInfo?.SelectToken("value[0].buildNumber")?.Value();
+ }
+
+ public string CurrentBuild { get; }
+ public string LatestBuild { get; }
+
+ public bool CanUpdate
+ {
+ get => _canUpdate;
+ set => SetAndNotify(ref _canUpdate, value);
+ }
+
+ public async Task Update()
+ {
+ try
+ {
+ CanUpdate = false;
+ await _updateService.ApplyUpdate();
+ }
+ catch (Exception e)
+ {
+ _dialogService.ShowExceptionDialog("An exception occurred while applying the update", e);
+ }
+ finally
+ {
+ CanUpdate = true;
+ }
+
+ Session.Close();
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Artemis.UI/Screens/Settings/Tabs/General/GeneralSettingsTabViewModel.cs b/src/Artemis.UI/Screens/Settings/Tabs/General/GeneralSettingsTabViewModel.cs
index 3b852d910..d6ccc65b3 100644
--- a/src/Artemis.UI/Screens/Settings/Tabs/General/GeneralSettingsTabViewModel.cs
+++ b/src/Artemis.UI/Screens/Settings/Tabs/General/GeneralSettingsTabViewModel.cs
@@ -276,7 +276,7 @@ namespace Artemis.UI.Screens.Settings.Tabs.General
return;
CanOfferUpdatesIfFound = false;
- bool updateFound = await _updateService.OfferUpdatesIfFound();
+ bool updateFound = await _updateService.OfferUpdateIfFound();
if (!updateFound)
_messageService.ShowMessage("You are already running the latest Artemis build. (☞゚ヮ゚)☞");
CanOfferUpdatesIfFound = true;
diff --git a/src/Artemis.UI/Services/UpdateService.cs b/src/Artemis.UI/Services/UpdateService.cs
index fbabfaa8f..00ebcceba 100644
--- a/src/Artemis.UI/Services/UpdateService.cs
+++ b/src/Artemis.UI/Services/UpdateService.cs
@@ -1,9 +1,15 @@
using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Diagnostics;
using System.Globalization;
+using System.IO;
using System.Net.Http;
using System.Threading.Tasks;
using Artemis.Core;
using Artemis.Core.Services;
+using Artemis.UI.Exceptions;
+using Artemis.UI.Screens.Settings.Dialogs;
using Artemis.UI.Services.Interfaces;
using Artemis.UI.Shared.Services;
using MaterialDesignThemes.Wpf;
@@ -36,46 +42,25 @@ namespace Artemis.UI.Services
_checkForUpdates.SettingChanged += CheckForUpdatesOnSettingChanged;
}
- public async Task GetLatestBuildNumber()
+ public async Task AutoUpdate()
{
- // TODO: The URL is hardcoded, that should change in the future
- string latestBuildUrl = ApiUrl + "build/builds?api-version=6.1-preview.6&branchName=refs/heads/master&resultFilter=succeeded&$top=1";
- _logger.Debug("Getting latest build number from {latestBuildUrl}", latestBuildUrl);
+ if (!_checkForUpdates.Value)
+ return false;
- // Make the request
- using HttpClient client = new();
- HttpResponseMessage httpResponseMessage = await client.GetAsync(latestBuildUrl);
-
- // Ensure it returned correctly
- if (!httpResponseMessage.IsSuccessStatusCode)
- {
- _logger.Warning("Failed to check for updates, request returned {statusCode}", httpResponseMessage.StatusCode);
- return 0;
- }
-
- // Parse the response
- string response = await httpResponseMessage.Content.ReadAsStringAsync();
- try
- {
- JToken buildNumberToken = JObject.Parse(response).SelectToken("value[0].buildNumber");
- if (buildNumberToken != null)
- return buildNumberToken.Value();
-
- _logger.Warning("Failed to find build number at \"value[0].buildNumber\"");
- return 0;
- }
- catch (Exception e)
- {
- _logger.Warning(e, "Failed to retrieve build info JSON");
- return 0;
- }
+ return await OfferUpdateIfFound();
}
- public async Task OfferUpdatesIfFound()
+ public async Task OfferUpdateIfFound()
{
_logger.Information("Checking for updates");
- double buildNumber = await GetLatestBuildNumber();
+ JToken buildInfo = await GetBuildInfo(1);
+ JToken buildNumberToken = buildInfo?.SelectToken("value[0].buildNumber");
+
+ if (buildNumberToken == null)
+ throw new ArtemisUIException("Failed to find build number at \"value[0].buildNumber\"");
+ double buildNumber = buildNumberToken.Value();
+
string buildNumberDisplay = buildNumber.ToString(CultureInfo.InvariantCulture);
_logger.Information("Latest build is {buildNumber}, we're running {localBuildNumber}", buildNumberDisplay, Constants.BuildInfo.BuildNumberDisplay);
@@ -83,8 +68,7 @@ namespace Artemis.UI.Services
return false;
if (_windowService.IsMainWindowOpen)
- {
- }
+ await OfferUpdate(buildInfo);
else if (_autoInstallUpdates.Value)
{
// Lets go
@@ -93,6 +77,7 @@ namespace Artemis.UI.Services
$"Build {buildNumberDisplay} is available, currently on {Constants.BuildInfo.BuildNumberDisplay}.",
PackIconKind.Update
);
+ await ApplyUpdate();
}
else
{
@@ -107,23 +92,112 @@ namespace Artemis.UI.Services
return true;
}
+ private async Task OfferUpdate(JToken buildInfo)
+ {
+ await _dialogService.ShowDialog(new Dictionary {{"buildInfo", buildInfo}});
+ }
+
public async Task IsUpdateAvailable()
{
- double buildNumber = await GetLatestBuildNumber();
- return buildNumber > Constants.BuildInfo.BuildNumber;
+ JToken buildInfo = await GetBuildInfo(1);
+ JToken buildNumberToken = buildInfo?.SelectToken("value[0].buildNumber");
+
+ if (buildNumberToken != null)
+ return buildNumberToken.Value() > Constants.BuildInfo.BuildNumber;
+
+ _logger.Warning("IsUpdateAvailable: Failed to find build number at \"value[0].buildNumber\"");
+ return false;
}
- public void ApplyUpdate()
+ public async Task ApplyUpdate()
{
- throw new NotImplementedException();
+ _logger.Information("ApplyUpdate: Applying update");
+
+ // Ensure the installer is up-to-date, get installer build info
+ JToken buildInfo = await GetBuildInfo(6);
+ JToken finishTimeToken = buildInfo?.SelectToken("value[0].finishTime");
+ string installerPath = Path.Combine(Constants.ApplicationFolder, "Installer", "Artemis.Installer.exe");
+
+ // Always update installer if it is missing ^^
+ if (!File.Exists(installerPath))
+ await UpdateInstaller();
+ // Compare the creation date of the installer with the build date and update if needed
+ else
+ {
+ if (finishTimeToken == null)
+ _logger.Warning("ApplyUpdate: Failed to find build finish time at \"value[0].finishTime\", not updating the installer.");
+ else if (File.GetLastWriteTime(installerPath) < finishTimeToken.Value())
+ await UpdateInstaller();
+ }
+
+ _logger.Information("ApplyUpdate: Running installer at {installerPath}", installerPath);
+
+ try
+ {
+ Process.Start(new ProcessStartInfo(installerPath, "-autoupdate")
+ {
+ UseShellExecute = true,
+ Verb = "runas"
+ });
+ }
+ catch (Win32Exception e)
+ {
+ if (e.NativeErrorCode == 0x4c7)
+ _logger.Warning("ApplyUpdate: Operation was cancelled, user likely clicked No in UAC dialog.");
+ else
+ throw;
+ }
+
}
- public async Task AutoUpdate()
+ private async Task UpdateInstaller()
{
- if (!_checkForUpdates.Value)
- return false;
+ string downloadUrl = "https://builds.artemis-rgb.com/binaries/Artemis.Installer.exe";
+ string installerDirectory = Path.Combine(Constants.ApplicationFolder, "Installer");
+ string installerPath = Path.Combine(installerDirectory, "Artemis.Installer.exe");
- return await OfferUpdatesIfFound();
+ _logger.Information("UpdateInstaller: Downloading installer from {downloadUrl}", downloadUrl);
+ using HttpClient client = new();
+ HttpResponseMessage httpResponseMessage = await client.GetAsync(downloadUrl);
+ if (!httpResponseMessage.IsSuccessStatusCode)
+ throw new ArtemisUIException($"Failed to download installer, status code {httpResponseMessage.StatusCode}");
+
+ _logger.Information("UpdateInstaller: Writing installer file to {installerPath}", installerPath);
+ if (File.Exists(installerPath))
+ File.Delete(installerPath);
+ else if (!Directory.Exists(installerDirectory))
+ Directory.CreateDirectory(installerDirectory);
+ await using FileStream fs = new(installerPath, FileMode.Create, FileAccess.Write, FileShare.None);
+ await httpResponseMessage.Content.CopyToAsync(fs);
+ }
+
+ private async Task GetBuildInfo(int buildDefinition)
+ {
+ string latestBuildUrl = ApiUrl + $"build/builds?definitions=6&resultFilter=succeeded&$top={buildDefinition}&api-version=6.1-preview.6";
+ _logger.Debug("GetBuildInfo: Getting build info from {latestBuildUrl}", latestBuildUrl);
+
+ // Make the request
+ using HttpClient client = new();
+ HttpResponseMessage httpResponseMessage = await client.GetAsync(latestBuildUrl);
+
+ // Ensure it returned correctly
+ if (!httpResponseMessage.IsSuccessStatusCode)
+ {
+ _logger.Warning("GetBuildInfo: Getting build info, request returned {statusCode}", httpResponseMessage.StatusCode);
+ return null;
+ }
+
+ // Parse the response
+ string response = await httpResponseMessage.Content.ReadAsStringAsync();
+ try
+ {
+ return JObject.Parse(response);
+ }
+ catch (Exception e)
+ {
+ _logger.Warning(e, "GetBuildInfo: Failed to retrieve build info JSON");
+ return null;
+ }
}
#region Event handlers
@@ -145,13 +219,13 @@ namespace Artemis.UI.Services
public interface IUpdateService : IArtemisUIService
{
- Task OfferUpdatesIfFound();
- Task IsUpdateAvailable();
- void ApplyUpdate();
-
///
/// If auto-update is enabled this will offer updates if found
///
Task AutoUpdate();
+
+ Task OfferUpdateIfFound();
+ Task IsUpdateAvailable();
+ Task ApplyUpdate();
}
}
\ No newline at end of file