Skip to content

Implement Update Mechanism #139

@boexler

Description

@boexler

Implement Update Mechanism

Description

Implement an automatic update mechanism that checks for new versions on GitHub Releases when the application starts. When a new version is available, display a popup dialog allowing users to download and install the update. The mechanism should automatically detect whether the application is running as a SingleFile deployment or as an installed application (MSI), and provide the appropriate update method.

Requirements

Core Functionality

  • Asynchronous Update Check: Check for updates in the background when the application starts (non-blocking)
  • GitHub Releases Integration: Query GitHub Releases API to check for new versions
  • Version Comparison: Compare current application version with latest release version
  • Deployment Type Detection: Automatically detect if running as SingleFile or installed (MSI) version
  • Update Dialog: Display a popup window when a new version is available
  • SingleFile Update: Download and replace the single executable file for portable deployments
  • MSI Update: Download and install MSI package for installed applications

User Experience

  • Update check should not block application startup
  • User must manually confirm download and installation (not fully automatic)
  • Provide options to skip or postpone the update
  • Show download progress
  • Display current and new version information
  • Automatically detect deployment type and show appropriate update method

Implementation Details

1. UpdateService

File: app/Sentinel.NLogViewer.App/Services/UpdateService.cs

  • Check GitHub Releases API: https://api.github.com/repos/boexler/NLogViewer/releases/latest
  • Compare versions using semantic versioning
  • Deployment Type Detection:
    • SingleFile: Check if Assembly.GetEntryAssembly()?.Location is empty or if running from non-ProgramFiles location
    • Installed: Check if application is in Program Files or has MSI installation registry entry
  • SingleFile Update:
    • Download single executable from release assets (e.g., NLogViewer-SelfContained-win-x64-{version}.zip)
    • Extract executable to temporary location
    • Replace current executable on next restart (or prompt user to restart)
  • MSI Update:
    • Download MSI file from release assets
    • Launch MSI installer process
  • Use System.Net.Http.HttpClient for API calls and downloads

2. UpdateWindow

Files:

  • app/Sentinel.NLogViewer.App/UpdateWindow.xaml
  • app/Sentinel.NLogViewer.App/UpdateWindow.xaml.cs

Features:

  • Display current version and new version
  • Display deployment type (SingleFile or Installed)
  • Download button
  • Install/Update button (enabled after download completes)
    • For SingleFile: "Update" button (replaces executable)
    • For MSI: "Install" button (launches MSI installer)
  • "Skip" / "Later" options
  • Progress bar for download status

3. UpdateViewModel

File: app/Sentinel.NLogViewer.App/ViewModels/UpdateViewModel.cs

  • Bind UpdateWindow to UpdateService
  • Commands for Download/Install/Skip actions
  • Progress tracking for download

4. Integration

File: app/Sentinel.NLogViewer.App/App.xaml.cs

  • Register UpdateService as singleton in DI container
  • Call async update check in OnStartup method (non-blocking)
  • Show UpdateWindow when new version is detected

5. Deployment Type Detection

File: app/Sentinel.NLogViewer.App/Services/UpdateService.cs

Methods to detect deployment type:

SingleFile Detection:

  • Check if Assembly.GetEntryAssembly()?.Location is empty or null
  • Check if AppContext.GetData("TRUSTED_PLATFORM_ASSEMBLIES") is null
  • Check if executable path is NOT in Program Files or Program Files (x86)
  • Check if executable is in a user-writable location

Installed (MSI) Detection:

  • Check if executable path is in Program Files or Program Files (x86)
  • Check Windows Registry for MSI installation entry:
    • HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall
    • Look for product name matching application
  • Check if Assembly.GetEntryAssembly()?.Location points to Program Files

Implementation:

public enum DeploymentType
{
    SingleFile,
    Installed
}

public DeploymentType DetectDeploymentType()
{
    var entryAssembly = Assembly.GetEntryAssembly();
    var location = entryAssembly?.Location;
    var exePath = Environment.GetCommandLineArgs()[0];
    
    // SingleFile: Location is typically empty
    if (string.IsNullOrEmpty(location))
        return DeploymentType.SingleFile;
    
    // Check if running from Program Files (likely installed)
    if (exePath.Contains("Program Files", StringComparison.OrdinalIgnoreCase))
        return DeploymentType.Installed;
    
    // Default to SingleFile for portable deployments
    return DeploymentType.SingleFile;
}

6. Version Management

  • Current version: Read from AssemblyInformationalVersionAttribute (already implemented in MainWindow.xaml.cs)
  • Latest version: Extract from GitHub Release tag_name or name
  • Implement semantic version comparison

7. Update Package Handling

SingleFile Deployment:

  • Download ZIP file containing the single executable (e.g., NLogViewer-SelfContained-win-x64-{version}.zip)
  • Extract executable to %TEMP%\NLogViewer\ directory
  • Replace current executable:
    • Option 1: Copy new executable to same directory with .new extension, then on restart replace
    • Option 2: Use updater process that replaces executable after application closes
  • Filter release assets by .zip extension for SelfContained packages

MSI Installation:

  • Download MSI file from release assets
  • Save to %TEMP%\NLogViewer\ directory
  • Launch MSI using Process.Start() with appropriate arguments
  • Filter release assets by .msi extension

Configuration

  • Repository owner/name: boexler/NLogViewer (can be configured in appsettings.json or as constant)
  • Make update check optional/configurable

Dependencies

  • System.Net.Http (included in .NET)
  • Consider SemanticVersioning NuGet package for robust version comparison

GitHub Workflow Integration

  • Ensure GitHub Releases workflow uploads both:
    • MSI packages as assets (e.g., NLogViewer-1.2.3.msi)
    • SingleFile ZIP packages as assets (e.g., NLogViewer-SelfContained-win-x64-1.2.3.zip)
  • Package filenames should include version for easy identification

Acceptance Criteria

  • UpdateService checks GitHub Releases on application startup
  • Update check runs asynchronously without blocking UI
  • Deployment type is correctly detected (SingleFile vs Installed)
  • UpdateWindow displays when new version is available
  • For SingleFile: User can download and update the executable
  • For MSI: User can download and install MSI package
  • Appropriate update method is shown based on deployment type
  • User can skip or postpone update
  • Download progress is displayed
  • Version comparison works correctly with semantic versioning
  • Error handling for network issues, missing releases, etc.
  • SingleFile update handles executable replacement correctly (restart mechanism)

Related

  • Current version display: MainWindow.xaml.cs (line 36-46)
  • GitHub Releases workflow: .github/workflows/release.yml
  • Version management: GitVersion (configured in GitVersion.yml)

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