Files
james.maesandCodex GPT-6 38dd5c1488 feat: implement Windows MarkdownViewr (#35)
This replaces the blocked history in PR #3 with one properly attributed,
signed/DCO commit. The owner personally reviewed and approved PR #3 in
the release session.

**No file changes from the approved candidate:** both
baee14f492 and
e605df62c4 have exact Git tree
a85912cbad. Original PR #3 and its commits
are preserved for review history. Historical commits were locally signed
but GitHub reported no_user; protected merge checks those introduced
commits even for squash merging. This commit resolves attribution
without bypassing or weakening signature rules.

The reader, Settings, themes, source highlighting/split, live reload,
file associations, Print/PDF, About credits, toolbar/footer/editor
fixes, user-first README/screenshots and developer guides are exactly
the approved implementation. Stable/dev separation and protected Azure
signing caller are unchanged.

Evidence:
- Approved tree baee14f / CI38285775463 passes all checks. Downloaded
focused29/29, published111/111, stable110/110 and dev110/110 TRXs have
zero failures/skips. Core coverage100% lines/98.86% branches; actual
update/restart/coexistence/default-preservation receipts verify.
- New single-commit hooks, online workflow security, signing and DCO
pass. GitHub must rerun all required checks on this commit; results are
not copied across SHAs.
- Live repository setup validates18 Actions-bound checks, with only
fuzzing added and all prior protections preserved.
- OpenSSF project15417 is now owner-reviewed and saved at70% under
owner-confirmed CDLA-Permissive2.0. This is an in-progress assessment,
not a passing badge.
- Production CI OIDC and signed1.0/dev publication remain required;
local representative signing is not CI proof.

Substantial AI assistance: Codex (GPT-6). Supersedes PR #3. Release work
remains tracked in #33.

Closes #4, #6, #7, #8, #9, #10, #11, #12, #13, #14, #15, #16, #17, #18,
#19, #20, #21, #22, #23, #24, #25, #26, #27, #28, #29, #32, #34.
Refs #1, #5, #30, #31, #33.

Signed-off-by: James Maes <james@kof22.com>
Co-authored-by: Codex GPT-6 <noreply@openai.com>
2026-10-12 14:01:42 -05:00

95 lines
3.5 KiB
C#

/*
* 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;
/// <summary>Exercises AppKit's public installed update flow with offline GitHub release fixtures.</summary>
internal static class Program
{
/// <summary>Records the installed channel, update offer and requests; optionally downloads and applies it.</summary>
private static async Task<int> 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 <fixture-directory> <result-json> <check|apply>", 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;
}
/// <summary>Records only the owned fixture's actual process arguments; it never opens or reads the document.</summary>
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;
}
}