Browse Source

Updater now uses SemVersion for prerelease version support

pull/55/head
Ionite 1 year ago
parent
commit
875460ae9b
No known key found for this signature in database
  1. 28
      StabilityMatrix.Avalonia/App.axaml.cs
  2. 2
      StabilityMatrix.Avalonia/Helpers/UnixPrerequisiteHelper.cs
  3. 6
      StabilityMatrix.Avalonia/Program.cs
  4. 10
      StabilityMatrix.Avalonia/StabilityMatrix.Avalonia.csproj
  5. 89
      StabilityMatrix.Avalonia/ViewModels/SettingsViewModel.cs
  6. 4
      StabilityMatrix.Avalonia/Views/SettingsPage.axaml
  7. 20
      StabilityMatrix.Core/Converters/Json/SemVersionJsonConverter.cs
  8. 11
      StabilityMatrix.Core/Helper/Compat.cs
  9. 10
      StabilityMatrix.Core/Models/Update/UpdateInfo.cs
  10. 1
      StabilityMatrix.Core/StabilityMatrix.Core.csproj
  11. 19
      StabilityMatrix.Core/Updater/UpdateHelper.cs

28
StabilityMatrix.Avalonia/App.axaml.cs

@ -1,6 +1,7 @@
using System;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Http;
@ -15,6 +16,7 @@ using Avalonia.Markup.Xaml;
using Avalonia.Platform.Storage;
using Avalonia.Styling;
using FluentAvalonia.UI.Controls;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using NLog;
@ -43,6 +45,7 @@ using StabilityMatrix.Core.Helper;
using StabilityMatrix.Core.Helper.Cache;
using StabilityMatrix.Core.Helper.Factory;
using StabilityMatrix.Core.Models.Api;
using StabilityMatrix.Core.Models.Configs;
using StabilityMatrix.Core.Models.Packages;
using StabilityMatrix.Core.Models.Settings;
using StabilityMatrix.Core.Python;
@ -58,7 +61,8 @@ public sealed class App : Application
[NotNull] public static IServiceProvider? Services { get; private set; }
[NotNull] public static Visual? VisualRoot { get; private set; }
[NotNull] public static IStorageProvider? StorageProvider { get; private set; }
[NotNull] public static IConfiguration? Config { get; private set; }
// ReSharper disable once MemberCanBePrivate.Global
public IClassicDesktopStyleApplicationLifetime? DesktopLifetime =>
ApplicationLifetime as IClassicDesktopStyleApplicationLifetime;
@ -150,6 +154,15 @@ public sealed class App : Application
mainWindow.WindowStartupLocation = WindowStartupLocation.CenterScreen;
}
mainWindow.Closing += (_, _) =>
{
settingsManager.Transaction(s =>
{
s.WindowSettings = new WindowSettings(
mainWindow.Width, mainWindow.Height,
mainWindow.Position.X, mainWindow.Position.Y);
}, ignoreMissingLibraryDir: true);
};
mainWindow.Closed += (_, _) => Shutdown();
VisualRoot = mainWindow;
@ -292,11 +305,18 @@ public sealed class App : Application
services.AddSingleton<IPyRunner, PyRunner>();
services.AddSingleton<IUpdateHelper, UpdateHelper>();
Config = new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
.Build();
services.Configure<DebugOptions>(Config.GetSection(nameof(DebugOptions)));
if (Compat.IsWindows)
{
services.AddSingleton<IPrerequisiteHelper, WindowsPrerequisiteHelper>();
}
else if (Compat.IsLinux)
else
{
services.AddSingleton<IPrerequisiteHelper, UnixPrerequisiteHelper>();
}
@ -440,10 +460,6 @@ public sealed class App : Application
sharedFolders.RemoveLinksForAllPackages();
}
var mainWindow = Services.GetRequiredService<MainWindow>();
settingsManager.Transaction(s => s.WindowSettings = new WindowSettings(mainWindow.Width,
mainWindow.Height, mainWindow.Position.X, mainWindow.Position.Y));
Debug.WriteLine("Start OnExit: Disposing services");
// Dispose all services
foreach (var disposable in Services.GetServices<IDisposable>())

2
StabilityMatrix.Avalonia/Helpers/UnixPrerequisiteHelper.cs

@ -15,7 +15,7 @@ using StabilityMatrix.Core.Services;
namespace StabilityMatrix.Avalonia.Helpers;
[SupportedOSPlatform("linux")]
[UnsupportedOSPlatform("windows")]
public class UnixPrerequisiteHelper : IPrerequisiteHelper
{
private static readonly Logger Logger = LogManager.GetCurrentClassLogger();

6
StabilityMatrix.Avalonia/Program.cs

@ -2,6 +2,7 @@
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Threading;
using System.Threading.Tasks;
@ -13,6 +14,7 @@ using NLog;
using Polly.Contrib.WaitAndRetry;
using Projektanker.Icons.Avalonia;
using Projektanker.Icons.Avalonia.FontAwesome;
using Semver;
using Sentry;
using StabilityMatrix.Avalonia.ViewModels.Dialogs;
using StabilityMatrix.Avalonia.Views.Dialogs;
@ -35,6 +37,10 @@ public class Program
{
HandleUpdateReplacement();
var infoVersion = Assembly.GetExecutingAssembly()
.GetCustomAttribute<AssemblyInformationalVersionAttribute>()?.InformationalVersion;
Compat.AppVersion = SemVersion.Parse(infoVersion ?? "0.0.0", SemVersionStyles.Strict);
// Configure exception dialog for unhandled exceptions
if (!Debugger.IsAttached || args.Contains("--debug-exception-dialog"))
{

10
StabilityMatrix.Avalonia/StabilityMatrix.Avalonia.csproj

@ -8,8 +8,11 @@
<ApplicationManifest>app.manifest</ApplicationManifest>
<AvaloniaUseCompiledBindingsByDefault>true</AvaloniaUseCompiledBindingsByDefault>
<TrimMode>partial</TrimMode>
<EnableTrimAnalyzer>true</EnableTrimAnalyzer>
<PublishTrimmed>true</PublishTrimmed>
<ApplicationIcon>./Assets/Icon.ico</ApplicationIcon>
<VersionPrefix>2.0.0</VersionPrefix>
<VersionSuffix>dev.1</VersionSuffix>
<InformationalVersion>$(Version)</InformationalVersion>
</PropertyGroup>
<ItemGroup>
@ -28,8 +31,12 @@
<PackageReference Include="FluentIcons.FluentAvalonia" Version="1.1.207" />
<PackageReference Include="FuzzySharp" Version="2.0.2" />
<PackageReference Include="Markdown.Avalonia" Version="11.0.0" />
<PackageReference Include="Microsoft.Extensions.Configuration" Version="7.0.0" />
<PackageReference Include="Microsoft.Extensions.Configuration.FileExtensions" Version="7.0.0" />
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="7.0.0" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="7.0.0" />
<PackageReference Include="Microsoft.Extensions.Http.Polly" Version="7.0.8" />
<PackageReference Include="Microsoft.Extensions.Options.ConfigurationExtensions" Version="7.0.0" />
<PackageReference Include="Nito.AsyncEx" Version="5.1.2" />
<PackageReference Include="NLog" Version="5.2.2" />
<PackageReference Include="NLog.Extensions.Logging" Version="5.3.2" />
@ -50,7 +57,6 @@
<ItemGroup>
<ProjectReference Include="..\StabilityMatrix.Core\StabilityMatrix.Core.csproj" />
<TrimmerRootAssembly Include="StabilityMatrix.Core" />
</ItemGroup>
<ItemGroup>

89
StabilityMatrix.Avalonia/ViewModels/SettingsViewModel.cs

@ -1,5 +1,5 @@
using System;
using System.Collections.ObjectModel;
using System.Collections.Generic;
using System.IO;
using System.Reflection;
using System.Threading.Tasks;
@ -34,19 +34,22 @@ public partial class SettingsViewModel : PageViewModelBase
public override string Title => "Settings";
public override IconSource IconSource => new SymbolIconSource {Symbol = Symbol.Settings, IsFilled = true};
public string AppVersion => GetAppVersion();
// Theme panel
// ReSharper disable once MemberCanBeMadeStatic.Global
public string AppVersion => $"Version {Compat.AppVersion}";
// Theme section
[ObservableProperty] private string? selectedTheme;
// Debug info
// Shared folder options
[ObservableProperty] private bool removeSymlinksOnShutdown;
// Debug section
[ObservableProperty] private string? debugPaths;
[ObservableProperty] private string? debugCompatInfo;
[ObservableProperty] private string? debugGpuInfo;
[ObservableProperty] private bool removeSymlinksOnShutdown;
public ObservableCollection<string> AvailableThemes { get; } = new()
public IReadOnlyList<string> AvailableThemes { get; } = new[]
{
"Light",
"Dark",
@ -81,7 +84,39 @@ public partial class SettingsViewModel : PageViewModelBase
};
}
[RelayCommand]
private async Task CheckPythonVersion()
{
var isInstalled = prerequisiteHelper.IsPythonInstalled;
Logger.Debug($"Check python installed: {isInstalled}");
// Ensure python installed
if (!prerequisiteHelper.IsPythonInstalled)
{
// Need 7z as well for site packages repack
Logger.Debug("Python not installed, unpacking resources...");
await prerequisiteHelper.UnpackResourcesIfNecessary();
Logger.Debug("Unpacked resources, installing python...");
await prerequisiteHelper.InstallPythonIfNecessary();
}
// Get python version
await pyRunner.Initialize();
var result = await pyRunner.GetVersionInfo();
// Show dialog box
var dialog = new ContentDialog
{
Title = "Python version info",
Content = result,
PrimaryButtonText = "Ok",
IsPrimaryButtonEnabled = true
};
dialog.Title = "Python version info";
dialog.Content = result;
dialog.PrimaryButtonText = "Ok";
await dialog.ShowAsync();
}
#region Debug Section
public void LoadDebugInfo()
{
var assembly = Assembly.GetExecutingAssembly();
@ -156,45 +191,7 @@ public partial class SettingsViewModel : PageViewModelBase
// Use try-catch to generate traceback information
throw new OperationCanceledException("Example Message");
}
#endregion
[RelayCommand]
private async Task CheckPythonVersion()
{
var isInstalled = prerequisiteHelper.IsPythonInstalled;
Logger.Debug($"Check python installed: {isInstalled}");
// Ensure python installed
if (!prerequisiteHelper.IsPythonInstalled)
{
// Need 7z as well for site packages repack
Logger.Debug("Python not installed, unpacking resources...");
await prerequisiteHelper.UnpackResourcesIfNecessary();
Logger.Debug("Unpacked resources, installing python...");
await prerequisiteHelper.InstallPythonIfNecessary();
}
// Get python version
await pyRunner.Initialize();
var result = await pyRunner.GetVersionInfo();
// Show dialog box
var dialog = new ContentDialog
{
Title = "Python version info",
Content = result,
PrimaryButtonText = "Ok",
IsPrimaryButtonEnabled = true
};
dialog.Title = "Python version info";
dialog.Content = result;
dialog.PrimaryButtonText = "Ok";
await dialog.ShowAsync();
}
private static string GetAppVersion()
{
var assembly = Assembly.GetExecutingAssembly();
var version = assembly.GetName().Version;
return version == null
? "(Unknown)"
: $"Version {version.Major}.{version.Minor}.{version.Build}";
}
}

4
StabilityMatrix.Avalonia/Views/SettingsPage.axaml

@ -6,7 +6,6 @@
xmlns:ui="using:FluentAvalonia.UI.Controls"
xmlns:mocks="clr-namespace:StabilityMatrix.Avalonia.DesignData"
xmlns:controls="clr-namespace:StabilityMatrix.Avalonia.Controls"
xmlns:icons="clr-namespace:Projektanker.Icons.Avalonia;assembly=Projektanker.Icons.Avalonia"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:DataType="vm:SettingsViewModel"
x:CompileBindings="True"
@ -28,7 +27,6 @@
</ui:SettingsExpander>
<!-- TODO: Text2Image host port settings -->
<!-- TODO: Keep folder links on shutdown -->
<!-- Checkpoints Manager Options -->
<Grid RowDefinitions="auto,*">
@ -163,7 +161,7 @@
Text="Stability Matrix" />
<Grid>
<Button
Background="Transparent"
Classes="transparent"
BorderThickness="0"
Content="{Binding AppVersion}"
Margin="8,0,8,8"

20
StabilityMatrix.Core/Converters/Json/SemVersionJsonConverter.cs

@ -0,0 +1,20 @@
using System.Text.Json;
using System.Text.Json.Serialization;
using Semver;
namespace StabilityMatrix.Core.Converters.Json;
public class SemVersionJsonConverter : JsonConverter<SemVersion>
{
public override SemVersion Read(
ref Utf8JsonReader reader,
Type typeToConvert,
JsonSerializerOptions options) =>
SemVersion.Parse(reader.GetString()!, SemVersionStyles.Strict);
public override void Write(
Utf8JsonWriter writer,
SemVersion value,
JsonSerializerOptions options) =>
writer.WriteStringValue(value.ToString());
}

11
StabilityMatrix.Core/Helper/Compat.cs

@ -1,7 +1,9 @@
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using Semver;
using StabilityMatrix.Core.Models.FileInterfaces;
namespace StabilityMatrix.Core.Helper;
@ -14,6 +16,8 @@ public static class Compat
{
private const string AppName = "StabilityMatrix";
public static SemVersion AppVersion { get; set; }
// OS Platform
public static PlatformKind Platform { get; }
@ -25,6 +29,8 @@ public static class Compat
[SupportedOSPlatformGuard("macos")]
public static bool IsMacOS => Platform.HasFlag(PlatformKind.MacOS);
[UnsupportedOSPlatformGuard("windows")]
public static bool IsUnix => Platform.HasFlag(PlatformKind.Unix);
public static bool IsArm => Platform.HasFlag(PlatformKind.Arm);
@ -67,6 +73,11 @@ public static class Compat
static Compat()
{
var infoVersion = Assembly.GetCallingAssembly()
.GetCustomAttribute<AssemblyInformationalVersionAttribute>()?.InformationalVersion;
AppVersion = SemVersion.Parse(infoVersion ?? "0.0.0", SemVersionStyles.Strict);
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
Platform = PlatformKind.Windows;

10
StabilityMatrix.Core/Models/Update/UpdateInfo.cs

@ -1,10 +1,14 @@
using System.Text.Json.Serialization;
using System.Diagnostics.CodeAnalysis;
using System.Text.Json.Serialization;
using Semver;
using StabilityMatrix.Core.Converters.Json;
namespace StabilityMatrix.Core.Models.Update;
[SuppressMessage("ReSharper", "ClassNeverInstantiated.Global")]
public record UpdateInfo(
[property: JsonPropertyName("version")]
Version Version,
[property: JsonPropertyName("version"), JsonConverter(typeof(SemVersionJsonConverter))]
SemVersion Version,
[property: JsonPropertyName("releaseDate")]
DateTimeOffset ReleaseDate,

1
StabilityMatrix.Core/StabilityMatrix.Core.csproj

@ -32,6 +32,7 @@
<PackageReference Include="Refit" Version="7.0.0" />
<PackageReference Include="Refit.HttpClientFactory" Version="7.0.0" />
<PackageReference Include="Salaros.ConfigParser" Version="0.3.8" />
<PackageReference Include="Semver" Version="3.0.0-beta.0" />
<PackageReference Include="Sentry.NLog" Version="3.33.1" />
<PackageReference Include="SharpCompress" Version="0.33.0" />
</ItemGroup>

19
StabilityMatrix.Core/Updater/UpdateHelper.cs

@ -1,5 +1,4 @@
using System.Reflection;
using System.Text.Json;
using System.Text.Json;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using StabilityMatrix.Core.Extensions;
@ -26,7 +25,7 @@ public class UpdateHelper : IUpdateHelper
public const string UpdateFolderName = ".StabilityMatrixUpdate";
public static DirectoryPath UpdateFolder => Compat.AppCurrentDir.JoinDir(UpdateFolderName);
private static FilePath ExecutablePath => UpdateFolder.JoinFile(Compat.GetExecutableName());
public static FilePath ExecutablePath => UpdateFolder.JoinFile(Compat.GetExecutableName());
public UpdateHelper(ILogger<UpdateHelper> logger, IHttpClientFactory httpClientFactory,
IDownloadService downloadService, IOptions<DebugOptions> debugOptions)
@ -81,7 +80,8 @@ public class UpdateHelper : IUpdateHelper
var response = await httpClient.GetAsync(UpdateManifestUrl);
if (!response.IsSuccessStatusCode)
{
logger.LogError("Error while checking for update");
logger.LogWarning("Error while checking for update {StatusCode} - {Content}",
response.StatusCode, await response.Content.ReadAsStringAsync());
return;
}
@ -123,16 +123,15 @@ public class UpdateHelper : IUpdateHelper
}
logger.LogInformation("UpdateInfo signature verified");
var currentVersion = Assembly.GetExecutingAssembly().GetName().Version;
if (updateInfo.Version <= currentVersion)
if (updateInfo.Version.ComparePrecedenceTo(Compat.AppVersion) > 0)
{
logger.LogInformation("No update available");
logger.LogInformation("Update available {AppVer} -> {UpdateVer}",
Compat.AppVersion, updateInfo.Version);
EventManager.Instance.OnUpdateAvailable(updateInfo);
return;
}
logger.LogInformation("Update available");
EventManager.Instance.OnUpdateAvailable(updateInfo);
logger.LogInformation("No update available");
}
catch (Exception e)
{

Loading…
Cancel
Save