Skip to content

Check for updates via GitHub Releases API #81

@TimGels

Description

@TimGels

Description of the feature

Add the ability for the application to check for new versions via the GitHub Releases API and inform the user when an update is available. No auto-update — the user is directed to the GitHub release page to download manually.

The feature is completely disabled for Microsoft Store installs, as the Store handles updates natively. For all other distributions (portable executable, self-signed MSIX, developer builds), the update checker is active and includes both stable and pre-release versions.

Use case

Users of the portable (non-Store) distribution have no way to know when a new version is available. They must manually check the GitHub releases page. This feature provides a lightweight, non-intrusive notification mechanism so users stay informed about new releases without leaving the app.

Proposed solution

API Integration

  • Endpoint: GET https://api.github.com/repos/TimGels/Matroska-Batch-Flow/releases?per_page=20
  • Unauthenticated, public repo (rate limit: 60 requests/hour/IP)
  • Uses the list endpoint (not /releases/latest) so that pre-releases are included
  • The service iterates the response to find the newest release (stable or pre-release) with a tag_name newer than the current app version, skipping drafts
  • Relevant fields per release: tag_name, html_url, published_at, prerelease, draft

Store Detection

Disable the feature for Store installs using PackageSignatureKind:

#if WINDOWS10_0_19041_0_OR_GREATER
bool isStore = Package.Current.SignatureKind == PackageSignatureKind.Store;
#endif

This distinguishes between:

  • Store-signed MSIX → update check disabled
  • Self-signed / developer MSIX → update check enabled
  • Unpackaged portable app → update check enabled

Add AppEnvironmentHelper.IsStoreDistribution() helper method.

Version Comparison

Use NuGet.Versioning.SemanticVersion for correct pre-release ordering (1.0.0-alpha < 1.0.0-beta < 1.0.0). Parse tag_name by stripping the v prefix and compare against the current app version.

Caching — UpdateState.json

Cached API state is stored separately from UserSettings.json via WritableJsonSettings<UpdateState>:

{
  "LastSuccessfulCheck": "2026-02-20T14:30:00Z",
  "LatestKnownVersion": "1.3.0",
  "IsPreRelease": false,
  "ReleaseUrl": "https://github.com/TimGels/Matroska-Batch-Flow/releases/tag/v1.3.0",
  "PublishedAt": "2026-02-19T10:00:00Z",
  "SkippedVersion": null
}

Startup cache logic:

  1. Read UpdateState.json
  2. If LatestKnownVersion > currentAppVersion AND LatestKnownVersion != SkippedVersion → show notification immediately from cache (no API call)
  3. If LastSuccessfulCheck is null OR (UtcNow - LastSuccessfulCheck) >= 1 hour → fetch from GitHub API, update UpdateState.json → show notification if newer version found
  4. Otherwise: cache is fresh, no action needed

Service Architecture

The service lives in Core (pure HTTP + JSON deserialization + version comparison, no UI dependency):

Core:
  Models/UpdateInfo.cs                        — record with version, URL, date, prerelease flag
  Services/IUpdateCheckService.cs             — interface: CheckForUpdateAsync(CancellationToken)
  Services/GitHubUpdateCheckService.cs        — HttpClient implementation
  Logging/GitHubUpdateCheckService.Logging.cs — LoggerMessage source generators

Store detection, caching decisions, and opt-out checks are the caller's responsibility (ViewModel / App startup in Uno).

UX — Notification Patterns

Several patterns can be combined (exact selection to be decided during implementation):

  1. InfoBar on the Settings pageInfoBar showing "Version X.Y.Z is available" with a "View Release" action button that opens the GitHub release page in the default browser
  2. Dialog on startup — Content dialog shown once after startup with version, date, and a "View Release" button
  3. Badge on the Settings nav itemInfoBadge dot on the Settings icon for passive awareness

UX — Manual Check

  • "Check for Updates" button on the Settings page (About section)
  • Bypasses the 1-hour cache
  • Rate-limited (e.g., button disabled for 30 seconds after a check) to be a good citizen of the GitHub API
  • Shows a brief loading state, then the result (update available or up-to-date)

UX — User Preferences

  • "Check for updates on startup" toggle in Settings (default: enabled, only shown for non-Store installs)
  • Skip version — "Don't remind me about this version" stored in UpdateState.json as SkippedVersion

Error Handling & Logging

  • Timeout: 5 seconds — fail fast for offline scenarios
  • Catch all exceptions, log at Debug level, silently return "no update available"
  • Never block app startup for the update check (fire-and-forget async after activation)
  • LoggerMessage source generators in a separate .Logging.cs partial class

Alternatives considered

  • In-app release notes rendering: GitHub release notes are Markdown; WinUI 3 / Uno Platform has no reliable cross-platform Markdown rendering control (the Community Toolkit MarkdownTextBlock is not reliable on Skia). Instead, the user is directed to the GitHub release page via a link/button.

Raw HttpClient with the GitHub Releases API was chosen: ~50 lines of service code, zero external dependencies (beyond NuGet.Versioning), full UX control.

Additional information

Startup integration

The update check runs as a fire-and-forget async operation after activation completes. It only runs if:

  • User hasn't opted out (CheckForUpdatesOnStartup in UserSettings)
  • App is not a Store install (!IsStoreDistribution())
  • Cache TTL (1 hour) has expired

UserSettings.UISettings addition

Only the user preference toggle:

[JsonPropertyName("CheckForUpdatesOnStartup")]
public bool CheckForUpdatesOnStartup { get; set; } = true;

Open design questions

  • Should the HttpClient be configured via IHttpClientFactory (recommended for resilience) or a simple singleton?
  • What is the exact badge/indicator UX for the navigation item? (InfoBadge with attention dot vs. numeric badge vs. icon overlay)
  • Should the skipped version persist across app restarts, or reset each session?
  • Should the UI differentiate between stable and pre-release updates (e.g., different InfoBar severity, a "Pre-release" label)?
  • When multiple newer releases exist (e.g., a stable and a pre-release), should the notification show the newest overall, or the newest stable?

Scope exclusions

  • No auto-download or auto-install
  • No GitHub Actions release workflow (separate concern)
  • No update checking for Microsoft Store installs
  • No in-app display of release notes

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions