Skip to content

feat: surface app version and update awareness#223

Merged
fuzzzerd merged 2 commits into
masterfrom
fuzzz/update-check
May 20, 2026
Merged

feat: surface app version and update awareness#223
fuzzzerd merged 2 commits into
masterfrom
fuzzz/update-check

Conversation

@fuzzzerd
Copy link
Copy Markdown
Owner

Summary

Adds a Help → About dialog showing the host version and a row per loaded plugin. Plugins that opt in via a new capability interface get a per-row "Check for updates" affordance; the host has its own check against the public releases endpoint. The host never hardcodes a plugin's update channel.

Design

A new capability interface in SharpFM.Plugin:

public interface IUpdateCheckable
{
    Task<UpdateCheckResult> CheckForUpdatesAsync(CancellationToken ct);
}

public sealed record UpdateCheckResult(
    bool UpdateAvailable,
    string? LatestVersion,
    Uri? ReleaseUrl,
    string? Notes);

Opt-in, not a member of IPlugin:

  • Bundled plugins ship with the host binary and have no independent update channel — forcing them to implement an empty check would be ceremony.
  • IPlugin.Version already covers "what am I running?" — every plugin reports it. Check-for-updates is a separate concern.
  • The pattern matches the existing IPanelPlugin : IPlugin opt-in extension in SharpFM.Plugin.UI.
  • No breaking change to existing plugins.
  • HttpClient / transport concerns stay out of the headless plugin contract.

Each plugin decides where its updates live. The host iterates [host, ...plugins] uniformly and runs CheckForUpdatesAsync on entries that opted in. The host has no compile-time knowledge of any specific plugin's release source.

Host self-check

HostUpdateCheck implements IUpdateCheckable against api.github.com/repos/fuzzzerd/SharpFM/releases/latest:

  • Anonymous (no auth header) — anonymous requests are rate-limited to 60/hour per IP, fine for explicit user action.
  • The /releases/latest endpoint already excludes pre-release tags, so beta-channel filtering is the endpoint's responsibility, not ours.
  • Strips v prefix from tags and +commit-hash build metadata from the running AssemblyInformationalVersion before comparing.
  • Catches every exception except OperationCanceledException — fail silently if GitHub is unreachable.
  • Adds a User-Agent header (GitHub rejects requests without one).

About dialog UX

  • Title + host version at top.
  • "Check for updates" / "View release" / "GitHub" buttons for the host. "View release" appears only after a successful check that found an update.
  • Plugin list below: each row shows name + version. Plugins that opted in get a "Check for updates" button; plugins that didn't, don't.
  • Status text appears inline per row after the check resolves ("Update available: 2.0.0", "Up to date.", or "Could not check for updates.").
  • Dialog is [ExcludeFromCodeCoverage] (Avalonia plumbing); the view models behind it are fully unit-tested.

Tests

  • Plugin contract (6 new) — interface shape, capability is standalone (not IPlugin-derived), record equality.
  • HostUpdateCheck (11 new) — fake HttpMessageHandler for newer/equal/older versions, 404, network exception, malformed JSON, cancellation propagation, v-prefix and +commit stripping, release-notes capture.
  • AboutEntryViewModel (7 new) — CanCheckForUpdates gating, status transitions, fail-silent on exceptions, INPC on Status, cancellation propagation.
  • AboutViewModel (4 new) — host always checkable, plugins checkable iff they implement IUpdateCheckable, homepage URL exposed.

All 1454 tests pass (+28 new).

Test plan

  • Manually launch the app and open Help → About; confirm host version renders and plugin rows appear.
  • Click host "Check for updates" with network available — confirm result text appears.
  • Click "GitHub" — confirm the public repo opens in the system browser.

@github-actions
Copy link
Copy Markdown

Test Results

✔️ Tests 1454 / 1454 - passed in 11.3s
✔️ Coverage 79.51% - passed with 70% threshold
📏 15390 / 17814 lines covered 🌿 5223 / 8110 branches covered
🔍 click here for more details

✏️ updated for commit 6a41645

@fuzzzerd fuzzzerd merged commit 99416a0 into master May 20, 2026
6 checks passed
@fuzzzerd fuzzzerd deleted the fuzzz/update-check branch May 20, 2026 04:12
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant