In-app updates
Avalloy.Updates is the runtime half of an app's update story: is there a newer release, download it, and either restart into it or stage it for the next quit, narrating through a notifier. Velopack does the packaging and the install hooks; this package holds the policy. Its own package so Core stays free of Velopack.
dotnet add package Avalloy.UpdatesWiring
The install and update hooks are a separate, earlier concern: VelopackApp.Build().Run() must be the first line of Main. Then register the pieces:
services.AddSingleton(new AppUpdateOptions
{
AppName = "MyApp",
RepoUrl = "https://github.com/me/myapp", // GitHub Releases
Prerelease = false,
});
services.AddSingleton<IUpdateSource, VelopackUpdateSource>();
services.AddSingleton<IUpdateNotifier, ToastUpdateNotifier>();
services.AddSingleton<UpdateService>();And call it: silently at startup, and loudly when the user asks.
await updates.CheckAndApplyAsync(userInitiated: false); // quiet unless it is actually updating
await updates.CheckAndApplyAsync(userInitiated: true); // always answersIsSupported is false in dev runs and non-packaged builds, and the service no-ops there, so rig run and loose builds are unaffected.
The seams
IUpdateSource is where updates come from and how they get applied. It exists so the actual logic, the overlap guard, the silent-versus-asked-for split, stage-versus-restart, and failure reporting, is testable without a packaged build, a release feed or a process restart. VelopackUpdateSource is the real one, GitHub Releases via Velopack, and deliberately a thin adapter holding no policy.
IUpdateNotifier is how the service talks to the user, with three levels because updating has three things to say: something is happening, it worked, it did not. What gets said is the behaviour worth pinning: a silent startup check stays quiet unless it is actually updating, while a user who asked gets an answer either way. ToastUpdateNotifier is the production one.
AppUpdateOptions carries the app name, the repository URL, an optional access token for a private repository, whether to take prereleases, and an optional channel.