Squirrel - The Basics

Squirrel is like ClickOnce, but works.

This intriguing thesis made me wanna know more. By coincidence, I got the opportunity to spend the last few days fiddling with Squirrel. Squirrel is an install and update framework. It comes in a few flavours, for Mac, Windows, and iOS. In this post we’ll focus on Squirrel.Windows, the Windows flavour.

If you’ve ever had the pleasure of working with ClickOnce, you know that it has its flaws and that it can really be a pain to work with and maintain.

This post takes you through all steps of packing your application for release, distribution, installation, and updating.

Packaging

Squirrel is based upon NuGet. Your application is therefore wrapped into a NuGet package before Squirrel can make an installer.

I would recommend creating a .nuspec file and use the nuget pack command to create the .nupkg file. For a quick demo you can create the .nupkg using a tool like NuGet Package Explorer.

You .nupkg file should contain a single lib folder with a net45 folder inside. .Net 4.5 is a requirement for Squirrel, but it is not restrained to only be used with .Net 4.5 projects. Actually, you can use Squirrel to distribute and install any sort of desktop application on Windows and Mac.

Then inside your net45 folder you put the content you want to install. For .Net projects, this is often the output of a Release build. You can remove all IDE related files and debugging symbols, such as .pdb, .vshost etc. Set the appropriate metadata on the .nupkg file. The icon will be used on the application, and the package summary will be used as Name in the Programs and Features list.

Once you have a nice .nupkg file, you use Squirrel to Releasify the application:

> squirrel.exe --releasify myapp.1.0.0.nupkg

This means that Squirrel wraps it into a folder, a Releases folder per default, next to the .nupkg file. The Squirrel.exe is part of the squirrel.windows NuGet package. This means that an easy way to access it, is through the NuGet Package Manager Console in Visual Studio. Squirrel.exe comes with a number of command line options, which are worth looking into.

Inside the Releases folder is the .nupkg file, a Setup.exe, a Setup.msi, and a RELEASES file. The two Setup files are used for installation. The RELEASES file contains information, a SHA, name, and size of the releases in the folder.

The content of the Releases folder is your installation pieces. Just run the Setup.exe or the Setup.msi to install your application.

Distribution

One of the key features of Squirrel is that you can distribute your application from almost anywhere.

You can copy the content of the Releases folder to a local path, a Dropbox folder, Amazon S3, Azure Blob Storage, Microsoft IIS, etc. There are no requirements what so ever. You can even change the path or url and move the installation files. If you wanna use the background updating mechanism, you just have to update the path or url in your application.

Updating

To get started with updating your application you would need to pick up the Squirrel.Windows NuGet package:

> install-package squirrel.windows

This will give you the Squirrel namespace. Now you have two options, you can take the Easy-Mode or the Step-Wise path.

Easy Mode

When you have referenced the NuGet package you use the ‘Easy Mode’, which will run all the tasks necessary to check, download and apply new releases.

You can use the ‘Easy Mode’ with the following snippet:

using (var mgr = new UpdateManager("C:\\Projects\\MyApp\\Releases"))
{
    await mgr.UpdateApp();
}

You can place this snippet somewhere that makes sense. Some applications checks for updates on startup others have a separate UI for this.

If there is a new release for the application it will be installed silently in the background, and when the user restarts the application he will be using the new version.

Step-Wise

Opposite to the Easy-Mode, the Step-Wise updating mechanism is the solution if you would like to notify the user that there is a new release, and ask the user for permission to install.

As with Easy-Mode it is good practice to put the UpdateManager into a using statement. It is required that the UpdateManager is disposed prior to application termination. You can either handle this manually or use a using statement.

CheckForUpdate()

First step is to call the CheckForUpdate() method on the UpdateManager object:

using (var mgr = new UpdateManager("C:\\Projects\\MyApp\\Releases"))
{
    var updateInfo = await mgr.CheckForUpdate();
}

You can then use the updateInfo object to check the installed version and whether there is any new releases available. Using the FetchReleaseNotes() method you can retrieve the release notes from the .nupkg, display them to the users and let them decide whether they’d like to install the update.

DownloadReleases()

So if you want to install the release, we can extend our small sample with the following:

if (updateInfo.ReleasesToApply.Any())
{
    newVersion = updateInfo.FutureReleaseEntry.Version;

    Console.WriteLine("New version: {0}", newVersion);

    Console.Write("New version available. Install? (y/n) ");
    if (Console.ReadKey().Key == ConsoleKey.Y)
    {
        Console.WriteLine("Beginning to download...");

        mgr.DownloadReleases(updateInfo.ReleasesToApply).Wait();
        Console.WriteLine("... done downloading.");
    }
} What happens is that we check the `updateInfo` for any releases to apply. This would be any new releases. If this is the case we ask the user whether to install the latest release. If this is the case to call the `DownloadReleases()` method on the `UpdateManager` object. Upon downloaded, we can apply the release.

The downloaded release will be put into a folder next to the current.

ApplyReleases()

Once we have downloaded our releases, we can apply the releases using the ApplyReleases() method. This would extend our sample with this:

Console.WriteLine("Beginning to apply release...");
mgr.ApplyReleases(updateInfo).Wait();
Console.WriteLine("... done applying."); This step will change shortcut pointers from the current installed release to the newly downloaded.

CreateUninstallerRegistryEntry()

It is by all good practice to create an uninstaller for the user to uninstall the application in the Programs and Features page. This is done with the following:

Console.WriteLine("Creating uninstaller...");
mgr.CreateUninstallerRegistryEntry().Wait();
Console.WriteLine("... done creating uninstaller.");

Restarting Your Application

Whether you use the easy-mode or step-wise, you can restart your application after applying a new release. Squirrel has a buit-in feature for this. It is a static method on the UpdateManager:

UpdateManager.RestartApp();

This is the final step in creating a step-wise updating process for your application.

Gotchas

During my experimentation with Squirrel I came across some gotchas, which could be of interest to others.

There is an ArgumentException with a message saying, You must dispose UpdateManager!

You will run into this issue if you wrap your UpdateManager into a using statement and awaits the any of the async methods on UpdateManager, like this:

using (var mgr = new UpdateManager("C:\\Projects\\MyApp\\Releases"))
{
    var updateInfo = await mgr.CheckForUpdates();
}

The runtime will not be able to dispose your UpdateManager object before the application terminates.

To resolve this you can either make the CheckForUpdate() call synchronous, applying .Result to CheckForUpdate or make sure to dispose the UpdateManager when finalizing the AppDomain, like this:

AppDomain.CurrentDomain.ProcessExit += () => DisposeUpdateManager(); 

The other gotcha I ran into was a AbandonedMutexException with a message saying, Leaked a Mutex!

Again this is related to the locking mechanism of the UpdateManager. I got this exception when I tried to call the UpdateManager.RestartApp() method inside my using statement. This would close the running application before the UpdateManager’s mutex is released.

To resolve this, move the UpdateManager.RestartApp() outside the using statement.

Conclusion

Coming with some experience using ClickOnce, it is a relief to use Squirrel. It is much more lean, and requires so much less effort. Both in maintenance and development.

I have put my sample application on GitHub.

If you have any comments or feedback on this post, throw them into the comments.


comments powered by Disqus