StageKit.Updatum 0.3.0

dotnet add package StageKit.Updatum --version 0.3.0
                    
NuGet\Install-Package StageKit.Updatum -Version 0.3.0
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="StageKit.Updatum" Version="0.3.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="StageKit.Updatum" Version="0.3.0" />
                    
Directory.Packages.props
<PackageReference Include="StageKit.Updatum" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add StageKit.Updatum --version 0.3.0
                    
#r "nuget: StageKit.Updatum, 0.3.0"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package StageKit.Updatum@0.3.0
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=StageKit.Updatum&version=0.3.0
                    
Install as a Cake Addin
#tool nuget:?package=StageKit.Updatum&version=0.3.0
                    
Install as a Cake Tool

StageKit.Updatum

Logo

License GitHub repo size Code size Nuget GitHub Sponsors

StageKit.Updatum is a lightweight, easy-to-integrate C# library that automates application updates through GitHub Releases. It checks for new versions, retrieves release notes, discovers the right asset for the current runtime, downloads it with progress reporting and optional verification, then prepares and runs a cross-platform update β€” installer, portable archive, single-file executable, AppImage, Flatpak, Debian, RPM, Arch Linux, Snap, macOS app bundle, PKG, or DMG.

Features

  • πŸ’» Cross-platform: Works on Windows, Linux, and macOS.
  • βš™οΈ Flexible integration: Embeds into WPF, WinForms, Avalonia, or console applications.
  • πŸ” Update checker: Checks GitHub for the latest release manually or on a timer.
  • πŸ“¦ Asset selection: Picks the release asset for the current platform, architecture, and packaging type via regex.
  • πŸ“„ Changelog support: Builds a formatted changelog from the releases ahead of the current version.
  • ⬇️ Download with progress tracking: Progress is exposed through INotifyPropertyChanged properties and events.
  • πŸ”’ Download verification: GitHub-native sha256: digest with <asset>.sha256 sidecar fallback, plus a pluggable signature verifier.
  • πŸ”„ Auto-upgrade: Generates and runs platform scripts to replace portable and single-file installs, or defers to the system installer.
  • πŸ“¦ Minimal dependencies: Only Octokit; no external update infrastructure required.

Install

dotnet add package StageKit.Updatum

Or add the package reference directly to your project:

<PackageReference Include="StageKit.Updatum" Version="*" />

The package targets .NET 8 and .NET 10.

Requirements

  1. Publish your application to GitHub Releases.
  2. Name the assets so the platform and architecture are matchable, for example:
    • Windows: MyApp_win-x64_v1.0.0.exe, MyApp_win-x64_v1.0.0.msi, MyApp_win-x64_v1.0.0.zip
    • Linux: MyApp_linux-x64_v1.0.0.AppImage, .flatpak, .deb, .rpm, .pkg.tar.zst, .snap, or .zip
    • macOS: MyApp_osx-arm64_v1.0.0.zip, .pkg, or .dmg
    • See the UVtools release assets for a real-world naming example.
    • Asset matching is configurable via regex (AssetRegexPattern) and an optional extension filter (AssetExtensionFilter).

The selected asset must be compatible with the application’s runtime identifier and packaging type. For example, a portable Windows application might publish MyApp_win-x64_v1.0.0.zip, while an installed build might publish an .msi with the same runtime identifier.

Basic usage

using StageKit.Updatum;

// Create one instance and keep it global. The current version defaults to the entry assembly version;
// pass it explicitly to be strict.
using var updater = new UpdatumManager("owner", "repository")
{
    AssetRegexPattern = "win-x64",
    AssetExtensionFilter = ".zip"
};

// Returns true when a newer release with a compatible asset is available.
if (!await updater.CheckForUpdatesAsync(cancellationToken)) return;

// Optionally show the changelog for every release ahead of the current version.
Console.WriteLine(updater.GetChangelog());

var download = await updater.DownloadUpdateAsync(cancellationToken);
if (download is null) return;

await updater.InstallUpdateAsync(
    download,
    forceTerminate: true,
    runArguments: null,
    cancellationToken);

DownloadAndInstallUpdateAsync(...) runs the download and install in one call and deletes the download if the install does not start.

Only one check, download, or install operation runs on a manager at a time. A competing operation returns false or null; State and IsBusy stay active until the accepted operation completes. Cancellation and manager disposal restore the idle state.

Asset selection

AssetRegexPattern filters release asset names and defaults to EntryApplication.GenericRuntimeIdentifier. When a release has multiple matching assets and no AssetExtensionFilter is set, Updatum infers the best asset from the running EntryApplication bundle type:

  • Windows: .exe when running as a .NET single-file app, otherwise .msi
  • Linux: .AppImage, .flatpak, .deb, .rpm, .pkg.tar.zst, or .snap for the matching runtime package type; otherwise .zip
  • If nothing matches, the first matching asset is used

Set AssetExtensionFilter (for example .zip, .msi, .AppImage) to force a package type. You typically need a marker file in the application folder to know whether the user runs the portable or the installer build.

Prereleases are excluded unless AllowPreReleases is enabled. Set FetchOnlyLatestRelease to request only GitHub's latest release instead of scanning the configured release page (up to 30 releases, 1 page β€” tune with GitHubApiOptions).

Auto-updater strategy

When you call the install path, Updatum:

  • If the asset is a zip with a single entry, extracts it to a temporary folder and continues the other checks.
    • If the zip contains multiple files, it is treated as a portable application. A generated script performs checks, kills running instances, merges files, renames the version in the folder name, and starts the new instance.
  • If the asset is a single-file application (.NET single-file executable or Linux AppImage), it is moved into the current folder and renamed to the current name and version.
  • If the asset is an installer, it is executed and follows its normal installation process.

Supported targets:

  • Portable applications (zip)
  • .NET single-file publishes
  • Windows installers (.exe and .msi)
  • Linux AppImage
  • Linux Flatpak
  • Linux Debian (.deb), RPM (.rpm), Arch Linux (.pkg.tar.zst), and Snap (.snap) packages
  • macOS app bundle
  • macOS PKG (.pkg) and disk image (.dmg) packages

Installing updates

Portable archives and single-file applications use generated platform scripts. Replacement content is copied to a sibling staging path first; the current installation is moved to a backup only after staging succeeds, and is restored when the final swap fails.

forceTerminate: true lets generated scripts stop running application processes and terminates the current process after the script starts. With forceTerminate: false, Updatum does not kill or exit the current application; the caller is responsible for arranging a safe shutdown when replacing locked files.

Pass UpdatumManager.NoRunAfterUpgradeToken as runArguments to suppress relaunch. Flatpak updates first identify whether the current application is installed in the user or system scope. System Flatpak updates and Debian, RPM, Arch Linux, and Snap packages request elevation through ProcessHelper; Updatum waits for a zero exit code before reporting package-install completion, relaunching, or terminating the current process. Denied elevation, timeout, cancellation, or installer failure stops that continuation.

macOS PKG and DMG installation uses the native administrator prompt when privileges are actually required. PKG assets run through /usr/sbin/installer, which always installs into the system domain and therefore always prompts. DMG assets are mounted read-only and always detached; an embedded PKG is installed with installer, while an embedded app bundle is staged and atomically replaced in its current location (or /Applications when no current bundle is known), with rollback on failure. A DMG runs unprivileged first and only re-runs with the administrator prompt when it wraps a PKG or targets a directory the current user cannot write, so instance termination and InstallUpdateInjectCustomScript still run exactly once.

Call SafeDeleteFile() on a downloaded asset when installation is not attempted. It removes the downloaded file and its empty managed workspace on a best-effort basis.

For single-file updates, the default EntryApplicationName strategy preserves the current executable name and updates a version embedded in that name when possible. Choose a custom or downloaded name when release assets use a different convention:

updater.InstallUpdateSingleFileExecutableNameStrategy =
    UpdatumSingleFileExecutableNameStrategy.CustomName;
updater.InstallUpdateSingleFileExecutableName = "MyApp_v{0}";

{0} is replaced with the downloaded tag version. CustomName falls back to the current executable name and then the downloaded asset name; DownloadName always keeps the downloaded asset name.

Download verification

Downloads are placed in unique directories below the operating system temporary directory. Asset names containing path components are rejected.

To require SHA-256 verification:

updater.RequireAssetChecksum = true;

Updatum first retrieves the release asset's native sha256: digest through Octokit's authenticated GitHub API connection. The metadata URL must match the configured GitHub API origin so credentials cannot be forwarded to an untrusted host. If GitHub does not return a supported digest, Updatum falls back to a checksum release asset named exactly <asset-name>.sha256 (suffix configurable via AssetChecksumSuffix).

The fallback checksum file may contain a bare 64-character hexadecimal digest or the common <digest> <filename> format. When RequireAssetChecksum is enabled, the download fails if neither source is available. A verified download exposes Sha256.

Platform package trust is application-specific. Provide a verifier for Authenticode, Apple code signing, or another package policy:

updater.RequireAssetSignatureVerification = true;
updater.AssetSignatureVerifier = async (filePath, cancellationToken) =>
{
    return await VerifyPackageTrustAsync(filePath, cancellationToken);
};

The download fails and its temporary workspace is removed if checksum or signature verification fails. Successful custom verification is exposed through IsSignatureVerified.

UI notifications

EventSynchronizationContext controls dispatch for update events and INotifyPropertyChanged notifications. The manager captures SynchronizationContext.Current when constructed; set the property explicitly when construction happens away from the UI thread (for Avalonia, AvaloniaSynchronizationContext.Current), or set it to null for direct invocation.

Bindable progress properties, all raising change notifications:

  • DownloadedMegabytes / DownloadSizeMegabytes β€” text for a progress bar
  • DownloadedPercentage β€” value for a progress bar (0–100)

Subscribe to PropertyChanged to redirect progress:

updater.PropertyChanged += (_, e) =>
{
    if (e.PropertyName == nameof(UpdatumManager.DownloadedPercentage))
    {
        Console.WriteLine($"Downloaded: {updater.DownloadedMegabytes} MB / {updater.DownloadSizeMegabytes} MB ({updater.DownloadedPercentage} %)");
    }
};

Adjust the progress notification frequency with DownloadProgressUpdateFrequencySeconds (default 0.1; 0 reports every chunk).

For periodic checks, subscribe to UpdateFound and configure AutoUpdateCheckTimer. The timer uses the same manager and does not start until Start() is called:

updater.UpdateFound += (_, _) => Console.WriteLine("An update is available.");
updater.AutoUpdateCheckTimer.Interval = TimeSpan.FromHours(1).TotalMilliseconds;
updater.AutoUpdateCheckTimer.Start();

FAQs

Custom asset pattern

Your naming convention may differ from the default. Set AssetRegexPattern to a regex that matches your assets:

// Expect assets named like: MyApp_winx64_v1.0.0
updater.AssetRegexPattern = RuntimeInformation.RuntimeIdentifier.Replace("-", string.Empty);

Multiple assets with the same name but different extension

For MyApp_win-x64_v1.0.0.zip (portable) and MyApp_win-x64_v1.0.0.msi (installer), use AssetExtensionFilter. If omitted, Updatum first applies its packaging-based extension preference and falls back to the first matching asset only when the preferred extension is unavailable.

if (IsPortableApp) updater.AssetExtensionFilter = "zip";

Check for updates on a timer

Use the built-in AutoUpdateCheckTimer (default interval 12 hours) and listen for UpdateFound:

updater.AutoUpdateCheckTimer.Interval = TimeSpan.FromHours(1).TotalMilliseconds;
updater.AutoUpdateCheckTimer.Start();

Force an update

Pass an explicit base version to compare against the latest release. An empty version always triggers the update-found path:

var updateFound = await updater.CheckForUpdatesAsync(new Version());

My installer (.exe) or single-file app (.exe) does not upgrade

A Windows .exe can be an installer or a single-file application, and Updatum must pick the right strategy. By default it infers from file signatures (Inno Setup, NSIS, Nullsoft, InstallShield, Windows Installer, …), which can produce false positives. Set InstallUpdateWindowsExeType explicitly:

using var updater = new UpdatumManager(owner, repository)
{
    // Auto           - infer from the asset file signature (use when assets contain both types)
    // Installer      - the .exe is an installer
    // SingleFileApp  - the .exe is a single-file executable
    InstallUpdateWindowsExeType = UpdatumWindowsExeType.Installer,

    // Arguments passed to msi/exe installers, e.g. "/qb" for a basic MSI UI
    InstallUpdateWindowsInstallerArguments = "/qb"
};

Example

See demo/StageKit.Demo in the repository for a complete Avalonia example. Its MainWindowViewModel demonstrates the full check, changelog, compatible-asset selection, checksum-verified download, cancellation, progress binding, manual installation, automatic installation, and temporary-asset cleanup flow.

License

StageKit.Updatum is licensed under the MIT License.

Product Compatible and additional computed target framework versions.
.NET net8.0 is compatible.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed.  net9.0 was computed.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  net10.0 is compatible.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
0.3.0 0 9/3/2026