feat(launcher): implement verified atomic updates

This commit is contained in:
Erik 2026-08-14 22:09:34 +02:00
parent 2198a0cc8e
commit 2d2a5b5046
34 changed files with 6755 additions and 61 deletions

View file

@ -1,7 +1,9 @@
using System.Reflection;
using AcDream.Launcher.Core.Installation;
using AcDream.Launcher.Core.Launching;
using AcDream.Launcher.Core.Orchestration;
using AcDream.Launcher.Core.Profiles;
using AcDream.Launcher.Core.Updates;
using AcDream.Launcher.ViewModels;
using AcDream.Platform;
using Avalonia;
@ -14,6 +16,8 @@ public sealed partial class App : Application
{
private LauncherOrchestrator? _orchestrator;
private LauncherWindowViewModel? _viewModel;
private HttpClient? _updateHttpClient;
private ReleaseManifestClient? _manifestClient;
public override void Initialize() => AvaloniaXamlLoader.Load(this);
@ -23,6 +27,7 @@ public sealed partial class App : Application
{
ApplicationPathSet paths = ApplicationPathSet.Resolve();
LauncherProfileStore profiles = LauncherProfileStore.ForApplicationPaths(paths);
string rid = LauncherRuntimeIdentity.DetectRid();
string executableSuffix = OperatingSystem.IsWindows() ? ".exe" : string.Empty;
var installer = new LauncherInstaller(
paths,
@ -47,16 +52,38 @@ public sealed partial class App : Application
$"Client content verification failed: {ex.Message}");
}
var clientVersions = new ClientVersionStore(paths);
_ = clientVersions.LoadAndRecoverAsync(rid)
.GetAwaiter()
.GetResult();
_orchestrator = new LauncherOrchestrator(
profiles,
paths,
LauncherExecutableSet.FromDirectory(AppContext.BaseDirectory),
LauncherExecutableSet.FromCurrentVersionStore(clientVersions),
verification.Record,
installationStatus: verification.Status);
installationStatus: verification.Status,
updateSessionBarrier: clientVersions.Barrier);
_updateHttpClient = new HttpClient();
_updateHttpClient.Timeout = TimeSpan.FromSeconds(15);
_updateHttpClient.DefaultRequestHeaders.UserAgent.ParseAdd(
"acdream-launcher/1");
_manifestClient = new ReleaseManifestClient(_updateHttpClient);
var selfUpdates = new LauncherSelfUpdateManager(paths, _updateHttpClient);
var updater = new LauncherUpdater(
_manifestClient,
_updateHttpClient,
clientVersions,
selfUpdates,
GetLauncherVersion(),
rid,
AppContext.BaseDirectory,
() => _orchestrator.GetSnapshot().Sessions.Any(session => session.IsActive));
_viewModel = new LauncherWindowViewModel(
_orchestrator,
new AvaloniaUiDispatcher(),
installer);
installer,
updater);
_viewModel.Initialize();
desktop.MainWindow = new MainWindow
@ -73,7 +100,25 @@ public sealed partial class App : Application
{
_viewModel?.Dispose();
_orchestrator?.Dispose();
_manifestClient?.Dispose();
_updateHttpClient?.Dispose();
_viewModel = null;
_orchestrator = null;
_manifestClient = null;
_updateHttpClient = null;
}
private static LauncherVersion GetLauncherVersion()
{
string? informationalVersion = typeof(App).Assembly
.GetCustomAttribute<AssemblyInformationalVersionAttribute>()?
.InformationalVersion;
if (!LauncherVersion.TryParse(informationalVersion, out LauncherVersion? version))
{
throw new InvalidOperationException(
$"Launcher informational version '{informationalVersion}' is not SemVer 2.0.");
}
return version;
}
}