From cbfd5f8da8f9539fd3d0a7dfc94fe265e08603b5 Mon Sep 17 00:00:00 2001 From: Timerix Date: Mon, 6 Jan 2025 01:01:34 +0500 Subject: [PATCH] Updater --- Mlaumcherb.Client.Avalonia/Config.cs | 8 ++ .../Mlaumcherb.Client.Avalonia.csproj | 5 ++ .../зримое/LauncherApp.axaml.cs | 26 +++++++ .../зримое/MainWindow.axaml | 1 + .../зримое/MainWindow.axaml.cs | 2 + .../сеть/Update/Gitea.cs | 38 ++++++++++ .../сеть/Update/UpdateHelper.cs | 74 +++++++++++++++++++ 7 files changed, 154 insertions(+) create mode 100644 Mlaumcherb.Client.Avalonia/сеть/Update/Gitea.cs create mode 100644 Mlaumcherb.Client.Avalonia/сеть/Update/UpdateHelper.cs diff --git a/Mlaumcherb.Client.Avalonia/Config.cs b/Mlaumcherb.Client.Avalonia/Config.cs index bc1e5e3..7f5b03d 100644 --- a/Mlaumcherb.Client.Avalonia/Config.cs +++ b/Mlaumcherb.Client.Avalonia/Config.cs @@ -1,5 +1,6 @@ using Mlaumcherb.Client.Avalonia.зримое; using Mlaumcherb.Client.Avalonia.классы; +using Mlaumcherb.Client.Avalonia.сеть.Update; using Mlaumcherb.Client.Avalonia.холопы; namespace Mlaumcherb.Client.Avalonia; @@ -25,6 +26,13 @@ public record Config new() { name = "Тимериховое", url = "https://timerix.ddns.net/minecraft/catalog.json" }, new() { name = "Mojang", url = "https://piston-meta.mojang.com/mc/game/version_manifest_v2.json" }, ]; + + public UpdateHelper.GiteaConfig gitea { get; set; } = new UpdateHelper.GiteaConfig + { + serverUrl = "https://timerix.ddns.net:3322", + user = "Timerix", + repo = "mlaumcherb", + }; [JsonIgnore] private static IOPath _filePath = "млаумчерб.json"; diff --git a/Mlaumcherb.Client.Avalonia/Mlaumcherb.Client.Avalonia.csproj b/Mlaumcherb.Client.Avalonia/Mlaumcherb.Client.Avalonia.csproj index 4dd3ad2..979ef3f 100644 --- a/Mlaumcherb.Client.Avalonia/Mlaumcherb.Client.Avalonia.csproj +++ b/Mlaumcherb.Client.Avalonia/Mlaumcherb.Client.Avalonia.csproj @@ -1,5 +1,6 @@  + 1.1.0 Exe WinExe net8.0 @@ -34,5 +35,9 @@ + + + + diff --git a/Mlaumcherb.Client.Avalonia/зримое/LauncherApp.axaml.cs b/Mlaumcherb.Client.Avalonia/зримое/LauncherApp.axaml.cs index 655c4c4..2a1f31b 100644 --- a/Mlaumcherb.Client.Avalonia/зримое/LauncherApp.axaml.cs +++ b/Mlaumcherb.Client.Avalonia/зримое/LauncherApp.axaml.cs @@ -2,6 +2,9 @@ using Avalonia; using Avalonia.Controls.ApplicationLifetimes; using Avalonia.Markup.Xaml; using Mlaumcherb.Client.Avalonia.классы; +using Mlaumcherb.Client.Avalonia.сеть; +using Mlaumcherb.Client.Avalonia.сеть.Update; +using Mlaumcherb.Client.Avalonia.холопы; namespace Mlaumcherb.Client.Avalonia.зримое; @@ -17,6 +20,8 @@ public class LauncherApp : Application Config = Config.LoadFromFile(); Logger.DebugLogEnabled = Config.debug; AvaloniaXamlLoader.Load(this); + + Update(); // some file required by forge installer if (!File.Exists("launcher_profiles.json")) @@ -27,6 +32,27 @@ public class LauncherApp : Application InstalledVersionCatalog = InstalledVersionCatalog.Load(); } + private async void Update() + { + try + { + Logger.LogInfo(nameof(LauncherApp), "checking for updates..."); + var upd = new UpdateHelper(Config.gitea); + upd.DeleteBackupFiles(); + if (await upd.IsUpdateAvailable()) + { + Logger.LogInfo(nameof(LauncherApp), "downloading update..."); + await upd.UpdateSelf(); + Logger.LogInfo(nameof(LauncherApp), "restarting..."); + upd.RestartSelf(); + } + } + catch (Exception e) + { + ErrorHelper.ShowMessageBox(nameof(LauncherApp), e); + } + } + public override void OnFrameworkInitializationCompleted() { if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop) diff --git a/Mlaumcherb.Client.Avalonia/зримое/MainWindow.axaml b/Mlaumcherb.Client.Avalonia/зримое/MainWindow.axaml index b2619b8..8aee368 100644 --- a/Mlaumcherb.Client.Avalonia/зримое/MainWindow.axaml +++ b/Mlaumcherb.Client.Avalonia/зримое/MainWindow.axaml @@ -178,6 +178,7 @@ + assets { get; set; } = new(); + + public class Asset + { + [JsonRequired] public int id { get; set; } + [JsonRequired] public string name { get; set; } = ""; + [JsonRequired] public int size { get; set; } + [JsonRequired] public DateTime created_at { get; set; } + [JsonRequired] public string browser_download_url { get; set; } = ""; + + public async Task Download(IOPath localPath) + { + await NetworkHelper.DownloadFile(browser_download_url, localPath); + } + } + + public Asset? FindAssetByName(string name) => + assets.FirstOrDefault(a => a.name == name); +} + +public class GiteaClient(string ServerUrl) +{ + public async Task GetLatestRelease(string user, string repo) + { + string url = $"{ServerUrl}//api/v1/repos/{user}/{repo}/releases/latest"; + return await NetworkHelper.DownloadStringAndDeserialize(url); + } +} \ No newline at end of file diff --git a/Mlaumcherb.Client.Avalonia/сеть/Update/UpdateHelper.cs b/Mlaumcherb.Client.Avalonia/сеть/Update/UpdateHelper.cs new file mode 100644 index 0000000..6278a98 --- /dev/null +++ b/Mlaumcherb.Client.Avalonia/сеть/Update/UpdateHelper.cs @@ -0,0 +1,74 @@ +using System.Diagnostics; +using System.Reflection; + +namespace Mlaumcherb.Client.Avalonia.сеть.Update; + +public class UpdateHelper +{ + private readonly string _executableName = "млаумчерб.exe"; + private readonly IOPath executablePathActual; + private readonly IOPath executablePathOld; + private readonly IOPath executablePathNew; + + + public class GiteaConfig + { + [JsonRequired] public required string serverUrl { get; set; } + [JsonRequired] public required string user { get; set; } + [JsonRequired] public required string repo { get; set; } + } + + private GiteaConfig _giteaConfig; + + public UpdateHelper(GiteaConfig giteaConfig) + { + _giteaConfig = giteaConfig; + executablePathActual = _executableName; + executablePathOld = _executableName + ".old"; + executablePathNew = _executableName + ".new"; + Gitea = new GiteaClient(giteaConfig.serverUrl); + } + + public GiteaClient Gitea { get; } + + public void DeleteBackupFiles() + { + if(File.Exists(executablePathOld)) + File.Delete(executablePathOld); + } + + public async Task IsUpdateAvailable() + { + var latest = await Gitea.GetLatestRelease(_giteaConfig.user, _giteaConfig.repo); + if (!Version.TryParse(latest.tag_name, out var latestVersion)) + throw new Exception($"Can't parse version of latest release: {latest.tag_name}"); + + Version? currentVersion = Assembly.GetExecutingAssembly().GetName().Version; + if(currentVersion is null) + throw new Exception($"Can't get current version from {Assembly.GetExecutingAssembly().GetName()}"); + + return currentVersion < latestVersion; + } + + public async Task UpdateSelf() + { + if(File.Exists(executablePathNew)) + File.Delete(executablePathNew); + + var latest = await Gitea.GetLatestRelease(_giteaConfig.user, _giteaConfig.repo); + var asset = latest.FindAssetByName(_executableName); + if(asset == null) + throw new Exception($"Can't find updated executable on gitea: {_executableName}"); + await asset.Download(executablePathNew); + + File.Move(executablePathActual, executablePathOld, true); + File.Move(executablePathNew, executablePathActual, true); + } + + public void RestartSelf() + { + Process.Start(executablePathActual.ToString()); + Thread.Sleep(500); + Environment.Exit(0); + } +} \ No newline at end of file