Browse Source

DIY auto-update implemented I think? And fix venv paths and upgrade nugets

pull/14/head
JT 1 year ago
parent
commit
d1f2f034c0
  1. 17
      StabilityMatrix.Tests/StabilityMatrix.Tests.csproj
  2. 6
      StabilityMatrix/App.xaml.cs
  3. 5
      StabilityMatrix/Helper/EventManager.cs
  4. 13
      StabilityMatrix/Helper/IUpdateHelper.cs
  5. 101
      StabilityMatrix/Helper/UpdateHelper.cs
  6. 15
      StabilityMatrix/Helper/Utilities.cs
  7. 4
      StabilityMatrix/Models/FileInterfaces/DirectoryPath.cs
  8. 15
      StabilityMatrix/Models/UpdateInfo.cs
  9. 4
      StabilityMatrix/Python/PyVenvRunner.cs
  10. 9
      StabilityMatrix/Services/DownloadService.cs
  11. 2
      StabilityMatrix/Services/IDownloadService.cs
  12. 18
      StabilityMatrix/StabilityMatrix.csproj
  13. 37
      StabilityMatrix/UpdateWindow.xaml
  14. 4
      StabilityMatrix/ViewModels/InstallerViewModel.cs
  15. 3
      StabilityMatrix/ViewModels/MainWindowViewModel.cs
  16. 11
      StabilityMatrix/ViewModels/SettingsViewModel.cs
  17. 48
      StabilityMatrix/ViewModels/UpdateWindowViewModel.cs

17
StabilityMatrix.Tests/StabilityMatrix.Tests.csproj

@ -10,15 +10,18 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="DotNext" Version="4.12.0" />
<PackageReference Include="DotNext" Version="4.12.4" />
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="7.0.0" />
<PackageReference Include="Microsoft.Extensions.Http.Polly" Version="6.0.16" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.5.0" />
<PackageReference Include="Microsoft.Extensions.Http.Polly" Version="7.0.8" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.6.3" />
<PackageReference Include="Moq" Version="4.18.4" />
<PackageReference Include="MSTest.TestAdapter" Version="2.2.10" />
<PackageReference Include="MSTest.TestFramework" Version="2.2.10" />
<PackageReference Include="coverlet.collector" Version="3.2.0" />
<PackageReference Include="Polly" Version="7.2.3" />
<PackageReference Include="MSTest.TestAdapter" Version="3.0.4" />
<PackageReference Include="MSTest.TestFramework" Version="3.0.4" />
<PackageReference Include="coverlet.collector" Version="6.0.0">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Polly" Version="7.2.4" />
<PackageReference Include="Polly.Contrib.WaitAndRetry" Version="1.1.1" />
</ItemGroup>

6
StabilityMatrix/App.xaml.cs

@ -271,6 +271,7 @@ namespace StabilityMatrix
.Or<TimeoutRejectedException>()
.OrResult(r => retryStatusCodes.Contains(r.StatusCode))
.WaitAndRetryAsync(delay);
// Shorter timeout for local requests
var localTimeout = Policy.TimeoutAsync<HttpResponseMessage>(TimeSpan.FromSeconds(3));
var localDelay = Backoff
@ -284,6 +285,10 @@ namespace StabilityMatrix
Debug.WriteLine("Retrying local request...");
return Task.CompletedTask;
});
// named client for update
serviceCollection.AddHttpClient("UpdateClient")
.AddPolicyHandler(retryPolicy);
// Add Refit clients
serviceCollection.AddRefitClient<ICivitApi>(defaultRefitSettings)
@ -297,6 +302,7 @@ namespace StabilityMatrix
// Add Refit client managers
serviceCollection.AddHttpClient("A3Client")
.AddPolicyHandler(localTimeout.WrapAsync(localRetryPolicy));
serviceCollection.AddSingleton<IA3WebApiManager>(services =>
new A3WebApiManager(services.GetRequiredService<ISettingsManager>(),
services.GetRequiredService<IHttpClientFactory>())

5
StabilityMatrix/Helper/EventManager.cs

@ -1,5 +1,6 @@
using System;
using AutoUpdaterDotNET;
using StabilityMatrix.Models;
namespace StabilityMatrix.Helper;
@ -18,12 +19,12 @@ public class EventManager
public event EventHandler? OneClickInstallFinished;
public event EventHandler? TeachingTooltipNeeded;
public event EventHandler<bool>? DevModeSettingChanged;
public event EventHandler<UpdateInfoEventArgs>? UpdateAvailable;
public event EventHandler<UpdateInfo>? UpdateAvailable;
public void OnGlobalProgressChanged(int progress) => GlobalProgressChanged?.Invoke(this, progress);
public void RequestPageChange(Type pageType) => PageChangeRequested?.Invoke(this, pageType);
public void OnInstalledPackagesChanged() => InstalledPackagesChanged?.Invoke(this, EventArgs.Empty);
public void OnOneClickInstallFinished() => OneClickInstallFinished?.Invoke(this, EventArgs.Empty);
public void OnTeachingTooltipNeeded() => TeachingTooltipNeeded?.Invoke(this, EventArgs.Empty);
public void OnDevModeSettingChanged(bool value) => DevModeSettingChanged?.Invoke(this, value);
public void OnUpdateAvailable(UpdateInfoEventArgs args) => UpdateAvailable?.Invoke(this, args);
public void OnUpdateAvailable(UpdateInfo args) => UpdateAvailable?.Invoke(this, args);
}

13
StabilityMatrix/Helper/IUpdateHelper.cs

@ -1,6 +1,13 @@
namespace StabilityMatrix.Helper;
using System;
using System.Threading.Tasks;
using StabilityMatrix.Models;
namespace StabilityMatrix.Helper;
public interface IUpdateHelper
{
void StartCheckingForUpdates();
}
Task StartCheckingForUpdates();
Task DownloadUpdate(UpdateInfo updateInfo,
IProgress<ProgressReport> progress);
}

101
StabilityMatrix/Helper/UpdateHelper.cs

@ -1,51 +1,114 @@
using System;
using System.IO;
using System.Net.Http;
using System.Reflection;
using System.Text.Json;
using System.Threading.Tasks;
using System.Windows.Threading;
using AutoUpdaterDotNET;
using Microsoft.Extensions.Logging;
using StabilityMatrix.Models;
using StabilityMatrix.Services;
namespace StabilityMatrix.Helper;
public class UpdateHelper : IUpdateHelper
{
private readonly ILogger<UpdateHelper> logger;
private readonly IHttpClientFactory httpClientFactory;
private readonly IDownloadService downloadService;
private readonly DispatcherTimer timer = new();
private static readonly string UpdateFolder =
Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Update");
public UpdateHelper(ILogger<UpdateHelper> logger)
public static readonly string ExecutablePath =
Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Update", "StabilityMatrix.exe");
public UpdateHelper(ILogger<UpdateHelper> logger, IHttpClientFactory httpClientFactory,
IDownloadService downloadService)
{
this.logger = logger;
this.httpClientFactory = httpClientFactory;
this.downloadService = downloadService;
timer.Interval = TimeSpan.FromMinutes(5);
timer.Tick += (_, _) =>
{
CheckForUpdate();
};
AutoUpdater.CheckForUpdateEvent += AutoUpdaterOnCheckForUpdateEvent;
timer.Tick += async (_, _) => { await CheckForUpdate(); };
}
public void StartCheckingForUpdates()
public async Task StartCheckingForUpdates()
{
timer.IsEnabled = true;
timer.Start();
CheckForUpdate();
await CheckForUpdate();
}
private void CheckForUpdate()
public async Task DownloadUpdate(UpdateInfo updateInfo,
IProgress<ProgressReport> progress)
{
AutoUpdater.DownloadPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Update");
AutoUpdater.ExecutablePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Update", "StabilityMatrix.exe");
// TODO: make this github url?
AutoUpdater.Start("https://cdn.lykos.ai/update.xml");
var downloadUrl = updateInfo.DownloadUrl;
Directory.CreateDirectory(UpdateFolder);
// download the file from URL
await downloadService.DownloadToFileAsync(downloadUrl, ExecutablePath, progress: progress,
httpClientName: "UpdateClient");
}
private void AutoUpdaterOnCheckForUpdateEvent(UpdateInfoEventArgs args)
private async Task CheckForUpdate()
{
if (args.Error == null && args.IsUpdateAvailable)
var httpClient = httpClientFactory.CreateClient("UpdateClient");
var response = await httpClient.GetAsync("https://cdn.lykos.ai/update.json");
if (!response.IsSuccessStatusCode)
{
logger.LogError("Error while checking for update");
return;
}
var updateInfo =
await JsonSerializer.DeserializeAsync<UpdateInfo>(
await response.Content.ReadAsStreamAsync());
if (updateInfo == null)
{
logger.LogError("UpdateInfo is null");
return;
}
if (updateInfo.Version == Utilities.GetAppVersion())
{
logger.LogInformation("No update available");
return;
}
// check if update is newer
var updateVersion = updateInfo.Version.Split('.');
var currentVersion = Utilities.GetAppVersion().Split('.');
if (updateVersion.Length != 4 || currentVersion.Length != 4)
{
logger.LogError("Invalid version format");
return;
}
var updateVersionInt = new int[4];
var currentVersionInt = new int[4];
for (var i = 0; i < 4; i++)
{
EventManager.Instance.OnUpdateAvailable(args);
if (int.TryParse(updateVersion[i], out updateVersionInt[i]) &&
int.TryParse(currentVersion[i], out currentVersionInt[i])) continue;
logger.LogError("Invalid version format");
return;
}
else if (args.Error != null)
// check if update is newer
for (var i = 0; i < 4; i++)
{
logger.LogError(args.Error, "Error while checking for update");
if (updateVersionInt[i] <= currentVersionInt[i]) continue;
logger.LogInformation("Update available");
EventManager.Instance.OnUpdateAvailable(updateInfo);
return;
}
}
}

15
StabilityMatrix/Helper/Utilities.cs

@ -0,0 +1,15 @@
using System.Reflection;
namespace StabilityMatrix.Helper;
public static class Utilities
{
public static string GetAppVersion()
{
var assembly = Assembly.GetExecutingAssembly();
var version = assembly.GetName().Version;
return version == null
? "(Unknown)"
: $"{version.Major}.{version.Minor}.{version.Build}.{version.Revision}";
}
}

4
StabilityMatrix/Models/FileInterfaces/DirectoryPath.cs

@ -74,7 +74,9 @@ public class DirectoryPath : FileSystemPath, IPathObject
/// <summary> Deletes the directory asynchronously. </summary>
public Task DeleteAsync(bool recursive) => Task.Run(() => Delete(recursive));
public override string ToString() => FullPath;
// DirectoryPath + DirectoryPath = DirectoryPath
public static DirectoryPath operator +(DirectoryPath path, DirectoryPath other) => new(Path.Combine(path, other.FullPath));

15
StabilityMatrix/Models/UpdateInfo.cs

@ -0,0 +1,15 @@
using System.Text.Json.Serialization;
namespace StabilityMatrix.Models;
public class UpdateInfo
{
[JsonPropertyName("version")]
public string Version { get; set; }
[JsonPropertyName("url")]
public string DownloadUrl { get; set; }
[JsonPropertyName("changelog")]
public string ChangelogUrl { get; set; }
}

4
StabilityMatrix/Python/PyVenvRunner.cs

@ -31,12 +31,12 @@ public class PyVenvRunner : IDisposable
/// <summary>
/// The path to the python executable.
/// </summary>
public FilePath PythonPath => RootPath + @"\Scripts\python.exe";
public FilePath PythonPath => RootPath + @"Scripts\python.exe";
/// <summary>
/// The path to the pip executable.
/// </summary>
public FilePath PipPath => RootPath + @"\Scripts\pip.exe";
public FilePath PipPath => RootPath + @"Scripts\pip.exe";
/// <summary>
/// List of substrings to suppress from the output.

9
StabilityMatrix/Services/DownloadService.cs

@ -21,10 +21,13 @@ public class DownloadService : IDownloadService
}
public async Task DownloadToFileAsync(string downloadUrl, string downloadLocation, int bufferSize = ushort.MaxValue,
IProgress<ProgressReport>? progress = null)
IProgress<ProgressReport>? progress = null, string? httpClientName = null)
{
using var client = httpClientFactory.CreateClient();
client.Timeout = TimeSpan.FromMinutes(5);
using var client = string.IsNullOrWhiteSpace(httpClientName)
? httpClientFactory.CreateClient()
: httpClientFactory.CreateClient(httpClientName);
client.Timeout = TimeSpan.FromMinutes(10);
client.DefaultRequestHeaders.UserAgent.Add(new ProductInfoHeaderValue("StabilityMatrix", "1.0"));
await using var file = new FileStream(downloadLocation, FileMode.Create, FileAccess.Write, FileShare.None);

2
StabilityMatrix/Services/IDownloadService.cs

@ -7,5 +7,5 @@ namespace StabilityMatrix.Services;
public interface IDownloadService
{
Task DownloadToFileAsync(string downloadUrl, string downloadLocation, int bufferSize = ushort.MaxValue,
IProgress<ProgressReport>? progress = null);
IProgress<ProgressReport>? progress = null, string? httpClientName = null);
}

18
StabilityMatrix/StabilityMatrix.csproj

@ -23,26 +23,26 @@
<PackageReference Include="FuzzySharp" Version="2.0.2" />
<PackageReference Include="LiteDB" Version="5.0.16" />
<PackageReference Include="LiteDB.Async" Version="0.1.6" />
<PackageReference Include="MdXaml" Version="1.19.2" />
<PackageReference Include="MdXaml" Version="1.20.1" />
<PackageReference Include="Microsoft.Extensions.Caching.Memory" Version="7.0.0" />
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="7.0.0" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="6.0.1" />
<PackageReference Include="Microsoft.Extensions.Http.Polly" Version="6.0.16" />
<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="Microsoft.Toolkit.Uwp.Notifications" Version="7.1.3" />
<PackageReference Include="Microsoft.Xaml.Behaviors.Wpf" Version="1.1.39" />
<PackageReference Include="NCode.ReparsePoints" Version="1.0.2" />
<PackageReference Include="NLog" Version="5.1.4" />
<PackageReference Include="NLog.Extensions.Logging" Version="5.2.3" />
<PackageReference Include="Octokit" Version="6.0.0" />
<PackageReference Include="NLog" Version="5.2.0" />
<PackageReference Include="NLog.Extensions.Logging" Version="5.3.0" />
<PackageReference Include="Octokit" Version="6.2.1" />
<PackageReference Include="Ookii.Dialogs.Wpf" Version="5.0.1" />
<PackageReference Include="Polly" Version="7.2.3" />
<PackageReference Include="Polly" Version="7.2.4" />
<PackageReference Include="Polly.Contrib.WaitAndRetry" Version="1.1.1" />
<PackageReference Include="Refit" Version="6.3.2" />
<PackageReference Include="Refit.HttpClientFactory" Version="6.3.2" />
<PackageReference Include="Salaros.ConfigParser" Version="0.3.8" />
<PackageReference Include="Sentry" Version="3.33.0" />
<PackageReference Include="Sentry.NLog" Version="3.33.0" />
<PackageReference Include="Sentry" Version="3.33.1" />
<PackageReference Include="Sentry.NLog" Version="3.33.1" />
<PackageReference Include="SharpCompress" Version="0.33.0" />
<PackageReference Include="WPF-UI" Version="3.0.0-preview.2" />
<PackageReference Include="pythonnet" Version="3.0.1" />

37
StabilityMatrix/UpdateWindow.xaml

@ -77,30 +77,33 @@
FontSize="18"
TextWrapping="Wrap"
TextAlignment="Center"
Margin="16,32,16,0">
<Run Text="Stability Matrix"/>
<Run Text="v"/><Run Text="{Binding UpdateInfo.CurrentVersion, FallbackValue=0.0.0.0}"/>
<Run Text="is now available. You have version"/>
<Run Text="v"/><Run Text="{Binding UpdateInfo.InstalledVersion, FallbackValue=0.0.0.0}"/>
<Run Text="installed. Would you like to download it now?"/>
</TextBlock>
Text="{Binding UpdateText, FallbackValue=Update available and stuff}"
Margin="16,32,16,0"/>
<TextBlock Grid.Row="3"
Text="Release Notes"
FontSize="16"
Visibility="{Binding ShowProgressBar, Converter={StaticResource InvertAndVisibilitate}}"
Margin="32,16,32,0"/>
<Border Grid.Row="4"
Margin="32, 16"
CornerRadius="16"
Background="#66000000"/>
<FlowDocumentScrollViewer
Grid.Row="4"
Margin="32,16"
Document="{Binding ReleaseNotes, Converter={StaticResource TextToFlowDocumentConverter}}"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch" />
<ProgressBar Grid.Row="4"
Height="200"
Value="{Binding ProgressValue}"
Visibility="{Binding ShowProgressBar, Converter={StaticResource BoolToVisConverter}}"
Margin="32"/>
<Grid Grid.Row="4"
Visibility="{Binding ShowProgressBar, Converter={StaticResource InvertAndVisibilitate}}">
<Border Margin="32, 16"
CornerRadius="16"
Background="#66000000"/>
<FlowDocumentScrollViewer
Margin="32,16"
Document="{Binding ReleaseNotes, Converter={StaticResource TextToFlowDocumentConverter}}"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch" />
</Grid>
<StackPanel Grid.Row="5" Orientation="Horizontal" HorizontalAlignment="Center" Margin="0,0,0,16">
<ui:Button Content="Remind Me Later"
Margin="0,0,8,0"

4
StabilityMatrix/ViewModels/InstallerViewModel.cs

@ -154,6 +154,10 @@ public partial class InstallerViewModel : ObservableObject
}
ReleaseNotes = SelectedVersion.ReleaseNotesMarkdown;
ShowDuplicateWarning =
settingsManager.Settings.InstalledPackages.Any(p =>
p.LibraryPath.Equals($"Packages\\{InstallName}"));
}
[RelayCommand]

3
StabilityMatrix/ViewModels/MainWindowViewModel.cs

@ -10,6 +10,7 @@ using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using Microsoft.Extensions.Options;
using StabilityMatrix.Helper;
using StabilityMatrix.Models;
using StabilityMatrix.Models.Configs;
using StabilityMatrix.Services;
using Wpf.Ui.Appearance;
@ -27,7 +28,7 @@ public partial class MainWindowViewModel : ObservableObject
private readonly UpdateWindowViewModel updateWindowViewModel;
private readonly DebugOptions debugOptions;
private UpdateInfoEventArgs? updateInfo;
private UpdateInfo? updateInfo;
public MainWindowViewModel(
ISettingsManager settingsManager,

11
StabilityMatrix/ViewModels/SettingsViewModel.cs

@ -126,15 +126,8 @@ public partial class SettingsViewModel : ObservableObject
[ObservableProperty]
private WindowBackdropType windowBackdropType;
public string AppVersion => $"Version {GetAppVersion()}";
private string GetAppVersion()
{
var assembly = Assembly.GetExecutingAssembly();
var version = assembly.GetName().Version;
return version == null ? "(Unknown)" : $"{version.Major}.{version.Minor}.{version.Build}";
}
public string AppVersion => $"Version {Utilities.GetAppVersion()}";
partial void OnSelectedThemeChanged(string value)
{
settingsManager.SetTheme(value);

48
StabilityMatrix/ViewModels/UpdateWindowViewModel.cs

@ -1,9 +1,13 @@
using System.Net.Http;
using System;
using System.Diagnostics;
using System.Net.Http;
using System.Threading.Tasks;
using AutoUpdaterDotNET;
using System.Windows;
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using StabilityMatrix.Helper;
using StabilityMatrix.Models;
using StabilityMatrix.Services;
using Wpf.Ui.Controls.Window;
namespace StabilityMatrix.ViewModels;
@ -12,23 +16,32 @@ public partial class UpdateWindowViewModel : ObservableObject
{
private readonly ISettingsManager settingsManager;
private readonly IHttpClientFactory httpClientFactory;
private readonly IUpdateHelper updateHelper;
public UpdateWindowViewModel(ISettingsManager settingsManager, IHttpClientFactory httpClientFactory)
public UpdateWindowViewModel(ISettingsManager settingsManager,
IHttpClientFactory httpClientFactory, IUpdateHelper updateHelper)
{
this.settingsManager = settingsManager;
this.httpClientFactory = httpClientFactory;
this.updateHelper = updateHelper;
}
[ObservableProperty] private string? releaseNotes;
[ObservableProperty] private string? updateText;
[ObservableProperty] private int progressValue;
[ObservableProperty] private bool showProgressBar;
public UpdateInfoEventArgs? UpdateInfo { get; set; }
public UpdateInfo? UpdateInfo { get; set; }
public WindowBackdropType WindowBackdropType => settingsManager.Settings.WindowBackdropType ??
WindowBackdropType.Mica;
public async Task OnLoaded()
{
using var client = httpClientFactory.CreateClient();
var response = await client.GetAsync(UpdateInfo?.ChangelogURL);
UpdateText = $"Stability Matrix v{UpdateInfo?.Version} is now available! You currently have v{Utilities.GetAppVersion()}. Would you like to update now?";
var client = httpClientFactory.CreateClient();
var response = await client.GetAsync(UpdateInfo?.ChangelogUrl);
if (response.IsSuccessStatusCode)
{
ReleaseNotes = await response.Content.ReadAsStringAsync();
@ -40,11 +53,28 @@ public partial class UpdateWindowViewModel : ObservableObject
}
[RelayCommand]
private void InstallUpdate()
private async Task InstallUpdate()
{
if (AutoUpdater.DownloadUpdate(UpdateInfo))
if (UpdateInfo == null)
{
System.Windows.Application.Current.Shutdown();
return;
}
ShowProgressBar = true;
UpdateText = $"Downloading update v{UpdateInfo.Version}...";
await updateHelper.DownloadUpdate(UpdateInfo, new Progress<ProgressReport>(report =>
{
ProgressValue = Convert.ToInt32(report.Percentage);
}));
UpdateText = "Update complete. Restarting Stability Matrix in 3 seconds...";
await Task.Delay(1000);
UpdateText = "Update complete. Restarting Stability Matrix in 2 seconds...";
await Task.Delay(1000);
UpdateText = "Update complete. Restarting Stability Matrix in 1 second...";
await Task.Delay(1000);
Process.Start(UpdateHelper.ExecutablePath);
Application.Current.Shutdown();
}
}

Loading…
Cancel
Save