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
Related
- Current version display:
MainWindow.xaml.cs (line 36-46)
- GitHub Releases workflow:
.github/workflows/release.yml
- Version management: GitVersion (configured in
GitVersion.yml)
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
User Experience
Implementation Details
1. UpdateService
File:
app/Sentinel.NLogViewer.App/Services/UpdateService.cshttps://api.github.com/repos/boexler/NLogViewer/releases/latestAssembly.GetEntryAssembly()?.Locationis empty or if running from non-ProgramFiles locationNLogViewer-SelfContained-win-x64-{version}.zip)System.Net.Http.HttpClientfor API calls and downloads2. UpdateWindow
Files:
app/Sentinel.NLogViewer.App/UpdateWindow.xamlapp/Sentinel.NLogViewer.App/UpdateWindow.xaml.csFeatures:
3. UpdateViewModel
File:
app/Sentinel.NLogViewer.App/ViewModels/UpdateViewModel.cs4. Integration
File:
app/Sentinel.NLogViewer.App/App.xaml.csOnStartupmethod (non-blocking)5. Deployment Type Detection
File:
app/Sentinel.NLogViewer.App/Services/UpdateService.csMethods to detect deployment type:
SingleFile Detection:
Assembly.GetEntryAssembly()?.Locationis empty or nullAppContext.GetData("TRUSTED_PLATFORM_ASSEMBLIES")is nullProgram FilesorProgram Files (x86)Installed (MSI) Detection:
Program FilesorProgram Files (x86)HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\UninstallAssembly.GetEntryAssembly()?.Locationpoints to Program FilesImplementation:
6. Version Management
AssemblyInformationalVersionAttribute(already implemented inMainWindow.xaml.cs)tag_nameorname7. Update Package Handling
SingleFile Deployment:
NLogViewer-SelfContained-win-x64-{version}.zip)%TEMP%\NLogViewer\directory.newextension, then on restart replace.zipextension for SelfContained packagesMSI Installation:
%TEMP%\NLogViewer\directoryProcess.Start()with appropriate arguments.msiextensionConfiguration
boexler/NLogViewer(can be configured inappsettings.jsonor as constant)Dependencies
System.Net.Http(included in .NET)SemanticVersioningNuGet package for robust version comparisonGitHub Workflow Integration
NLogViewer-1.2.3.msi)NLogViewer-SelfContained-win-x64-1.2.3.zip)Acceptance Criteria
Related
MainWindow.xaml.cs(line 36-46).github/workflows/release.ymlGitVersion.yml)