Skip to main content

Integrating Overview

Applies to
Windows
MacOS
Linux

To integrate Velopack into your application, you must initialise the Velopack as early as possible in app startup, and you should add update checking code somewhere.

For .NET applications, you should first install the Velopack NuGet Package.

Application Startup

Velopack requires you add some code to your application startup to handle hooks. This is because Velopack will run your main binary at certain stages of the install/update process with special arguments, to allow you to customise behavior. It expects your app to respond to these arguments in the right way and then exit as soon as possible.

The simplest/minimal way to handle this properly is to add the SDK startup code to your Main() method. This should be in the "main" binary (the one specified when packaging with --mainExe {exeName}).

static void Main(string[] args)
{
VelopackApp.Build().Run();
// ... your other startup code below
}

There are a variety of options / callbacks you can specify here to customise Velopack, for example:

static void Main(string[] args)
{
IVelopackLogger log = CreateLogger();
VelopackApp.Build()
.SetLogger(log)
.OnBeforeUninstallFastCallback((v) => {
// delete / clean up some files before uninstallation
})
.OnFirstRun((v) => {
MessageBox.Show("Thanks for installing my application!");
})
.Run();
}

The full list of options for VelopackApp is available here. You can also read more about how hooks work.

VelopackApp options

The builder exposes several methods to customise startup behaviour:

  • SetArgs(string[]) — override the command-line arguments used to detect which hook to run. If not set, the process arguments are used.
  • SetLogger(IVelopackLogger) — route Velopack's diagnostic messages to your own logger (in addition to the default log file). Ignored if you also provide a custom locator.
  • SetLocator(IVelopackLocator) — override how Velopack finds application paths. Mostly useful for testing.
  • SetAutoApplyOnStartup(bool) — whether to automatically apply a pending downloaded update on startup. On by default; see Apply updates below.
  • SetAppUserModelId(string) — override the Windows Application User Model ID (used for taskbar grouping and toast notifications). By default this is read from the package manifest, falling back to velopack.{AppId}.
  • OnFirstRun(VelopackHook) — runs the first time the app is launched after installation.
  • OnRestarted(VelopackHook) — runs when the app is restarted by Velopack after applying an update. See hooks.
warning

A "FastCallback" (the OnBefore*/OnAfter*FastCallback methods) requires that your application show no UI and exit quickly. When the callback returns, your application will exit. These callbacks are Windows-only (they are no-ops on macOS and Linux) and each has a timeout — install/uninstall callbacks have 30 seconds, update callbacks have 15 seconds. If you do not exit the callback quickly enough your process will be killed. See App Hooks for details.

The Run() contract

Run() must be called exactly once, and should be the first code in your Main(). When Velopack runs your app with a fast-exit hook argument (during install, update or uninstall), the hook executes and then the process exits from inside Run() — so any code placed before Run() will be re-run during those operations. Keeping Run() first ensures your normal startup logic only executes during a normal launch.

Configuring Updates

Updates can be accomplished by adding UpdateManager to your app:

private static async Task UpdateMyApp()
{
var mgr = new UpdateManager("https://the.place/you-host/updates");

// check for new version
var newVersion = await mgr.CheckForUpdatesAsync();
if (newVersion == null)
return; // no update available

// download new version
await mgr.DownloadUpdatesAsync(newVersion);

// install new version and restart app
mgr.ApplyUpdatesAndRestart(newVersion);
}
tip

Updates can be done silently in the background, or integrated into your application UI. It's always up to you.

You can host your update packages basically anywhere, here are a few examples:

  • Local directory:
    new UpdateManager("C:\Updates")
  • HTTP server, or S3, Azure Storage, etc:
    new UpdateManager("https://the.place/you-host/updates")
  • GitHub Releases:
    new UpdateManager(new GithubSource("https://github.com/yourName/yourRepo", null, false))

There are a variety of built-in sources (eg. GithubSource, SimpleWebSource) you can use when checking for updates, but you can also build your own by deriving from IUpdateSource. See Update sources for the full list and how to construct each.

Check for updates

CheckForUpdatesAsync will read the provided update source for a releases.{channel}.json file to retrieve available updates (Read about channels). If there is an update available, a non-null UpdateInfo will be returned with some details about the update. You can also retrieve any release notes which were provided when the update was packaged.

There are also some options which can be passed in to UpdateManager to customise how updates are handled, eg. to allow things like switching channels.

Download updates

DownloadUpdatesAsync will attempt to download deltas (if available) and re-construct the latest full release. If there are no deltas available, or the delta reconstruction fails, the latest full release package will be downloaded instead. Note that if an option like AllowVersionDowngrade is specified, the downloaded version might be older than the currently executing version. See Targeting a specific version for details, including how to install an exact version.

Delta fallback rules

Deltas keep downloads small, but Velopack will fall back to a full download when it would be faster or when deltas can't be used:

  • If there are more deltas to apply than UpdateOptions.MaximumDeltasBeforeFallback (default 10), the full package is downloaded instead. Set this to a negative number to disable deltas entirely.
  • If the combined size of the deltas is larger than the full package, the full package is downloaded instead.
  • If there is no local base package to apply deltas on top of, deltas are disabled. Because only Windows (installer) installs ship with a complete .nupkg, the first update after a non-Windows install is always a full download.

Concurrency & resuming

DownloadUpdatesAsync acquires a global per-app lock (a .velopack_lock file in the packages directory) so only one download/apply operation runs at a time. If another operation is already in progress it throws AcquireLockFailedException. If the full package already exists on disk the download is skipped, and in-progress downloads are written to .partial files.

Apply updates

Once the update has downloaded, you have a few options available. Calling ApplyUpdatesAndRestart or ApplyUpdatesAndExit will exit your app, install any bootstrap prerequisites, install the update, and then optionally restart your app right away.

If you do not want to exit your app immediately, you can call WaitExitThenApplyUpdates instead, which will launch Update.exe and wait for 60 seconds before proceeding. If your app has not exited within 60 seconds it will be killed.

Lastly, if you do not call any of these "Apply" methods, when you re-launch your app, by default, Velopack will detect that there is a pending update and install it then. If you wish to disable this, you should call VelopackApp.Build().SetAutoApplyOnStartup(false).

You can detect whether there is a downloaded-but-unapplied update at any time via UpdateManager.UpdatePendingRestart, which returns the pending VelopackAsset (or null if there is none).

tip

It is recommended that you use one of the functions which explicitly applies a package (eg. ApplyUpdatesAndRestart), and do not rely on the AutoApply behavior as a rule of thumb. The auto behavior will only apply a downloaded version if it is > the currently installed version, so will not work if trying to downgrade or switch channels, and if more than one instance of your process is running it could result in the update failing or those other processes being terminated.

Auto-apply & startup cleanup

On every launch, VelopackApp.Build().Run() performs two related housekeeping steps:

  • Auto-apply: if a newer full package exists locally (latestLocal > current), the app was not just restarted by Velopack, and auto-apply is enabled, it applies that update and restarts. This is why it only ever moves forward — it cannot downgrade or switch channels (see Targeting a specific version).
  • Cleanup: it deletes old local packages from the packages directory, keeping only the currently installed version and the latest local version.

Handling errors

The update methods can throw, and a robust integration should catch at least these:

  • NotInstalledException — thrown by any check/download call when the app is not a real Velopack install. This is the most common source of first-run confusion: running your app directly from bin/Debug (rather than from an installed copy) is not an install, so update checks will throw. See Testing for how to test without a full install.
  • ChecksumFailedException — a downloaded package failed size or hash verification.
  • AcquireLockFailedException — another download/apply operation is already in progress (see Concurrency & resuming above).

How updates work

On Windows

In a typical Windows install the application structure will look like this:

%LocalAppData%
└── {packId}
├── current
│ ├── YourFile.dll
│ ├── sq.version
│ └── YourApp.exe
└── Update.exe

sq.version is a special file created by Velopack which contains some metadata about your currently installed application. During install/uninstall, the entire {packId} folder is replaced or removed (see Uninstalling). During updates, only the current folder is replaced. If you store settings in the same folder as your main binary, they will be erased during updates.

warning

Since current is replaced with the new version during updates, it's not safe to store settings, logs, etc in the current dir where your app lives. See Preserving Files for more info.

On Linux & Mac

On these platforms, the app is stored as a single (typically read-only) bundle like .app or .AppImage. The bundle is replaced during updates in a single atomic operation. If you have any files you wish to persist (settings, logs, etc) you must find a directory elsewhere on the filesystem to store these files.