/* * Copyright (c) 2026 James Maes * SPDX-License-Identifier: MIT */ using System.Text.Json; using KofTwentyTwo.AppKit.Updates; using Velopack; using Velopack.Locators; using Velopack.Sources; namespace MarkdownViewr.Windows.UpdateProbe; /// Exercises AppKit's public installed update flow with offline GitHub release fixtures. internal static class Program { /// Records the installed channel, update offer and requests; optionally downloads and applies it. private static async Task Main(string[] args) { if(Environment.GetEnvironmentVariable("MARKDOWNVIEWR_EDITOR_PROBE_RESULT") is { Length: > 0 } editorResult) { return WriteEditorResult(editorResult, args); } VelopackStartup.Run(); if(args.Length != 3 || args[2] is not ("check" or "apply")) { throw new ArgumentException("Usage: UpdateProbe ", nameof(args)); } var repository = new Uri("https://github.com/Example/InstalledProbe"); var publicService = new VelopackUpdateService(repository); var downloader = new FixtureDownloader(args[0]); (UpdateManager manager, GithubSource source) = OfflineService.Attach(publicService, downloader); UpdateCheckResult update = await publicService.CheckAsync().ConfigureAwait(false); if(update.Error is not null && (manager.IsInstalled || !string.Equals(update.Error, UpdateText.NotSupported, StringComparison.Ordinal))) { throw new InvalidOperationException(update.Error); } bool applyCalled = false; string? applyError = null; // Apply exits this process. Record every download request synchronously before that happens. Action saveResult = () => File.WriteAllText(args[1], JsonSerializer.Serialize(new { UpdateDriver = "AppKit", Installed = manager.IsInstalled, AppKitSupported = publicService.IsSupported, AppKitVersion = publicService.CurrentVersion, Version = manager.CurrentVersion?.ToString(), VelopackLocator.Current.Channel, manager.IsPortable, IncludePrereleases = source.Prerelease, UpdateVersion = update.AvailableVersion, AppKitCheckError = update.Error, AppKitApplyCalled = applyCalled, AppKitApplyError = applyError, downloader.Requests })); downloader.RequestRecorded = saveResult; saveResult(); if(string.Equals(args[2], "apply", StringComparison.Ordinal)) { if(!update.IsUpdateAvailable) { throw new InvalidOperationException("No update was offered to apply."); } applyCalled = true; saveResult(); applyError = await publicService.DownloadAndApplyAsync().ConfigureAwait(false); saveResult(); if(applyError is not null) { throw new InvalidOperationException(applyError); } } return 0; } /// Records only the owned fixture's actual process arguments; it never opens or reads the document. private static int WriteEditorResult(string resultPath, string[] args) { if(args.Length != 1) { throw new ArgumentException("The editor fixture expects one literal document argument.", nameof(args)); } File.WriteAllText(resultPath + ".tmp", JsonSerializer.Serialize(new { Arguments = args, WorkingDirectory = Environment.CurrentDirectory })); File.Move(resultPath + ".tmp", resultPath, overwrite: true); return 0; } }