ClickOnce, Squirrel and Nuts

Background

In a post two years ago I expressed my admiration of ClickOnce. At the time we'd experienced years of tremendous success using it as our client application's primary deployment mechanism and since that time it has not faltered.

I now regret to say that over the last year I've seen evidence that makes me question continued reliance on ClickOnce. I think its life-cycle may be coming to end. Though that saddens me I understand that in the tech world all technology is transient and the best ideas don't always last as long as they should (see Silverlight). Data, however, must continue flowing.

About eight months ago I started searching for alternative deployment mechanism which might provide some of the benefits we'd leveraged with ClickOnce. That search came up mostly wanting. However, one candidate identified was the open source project Squirrel.Windows. The moniker on the GitHub project page says "Squirrel: It's like ClickOnce but Works". I took exception to that statement since for us ClickOnce has provided nearly flawless performance over a decade of heavy use but it got my attention none the less. After a little investigation I decided it might be worth a deep dive into Squirrel. For that purpose I created the Nuts application.

Overview of Nuts

My goal in creating the Nuts project was to explore Squirrel deployments and gain practical experience with the library through testing of deployment scenarios. Specifically, I was interested in how Squirrel handled updates. ClickOnce updates work beautifully, only downloading files newer than those currently installed. I wanted to ascertain whether Squirrel's delta packages provided similar behavior.

Nuts is a .NET application consisting of three projects, Nuts (Windows Forms), Nut.Acorn (class library) and Nut.Walnut (class-library). All logic is contained in the Nuts project. Class libraries exist only to explore versioning scenarios.

Figure 1. Nuts on first run after clicking the List Versions button.

The List Versions button displays basic information about the assemblies found in the current run-time environment, Clear Messages is self evident and Debugger calls System.Diagnostics.Debugger.Launch(). The Check Nuts and Update Nuts buttons both instantiate a new instance of Squirrel.UpdateManager. Check Nuts stops after checking for updates while Update Nuts continues to apply available updates.

Procedure

In my exploration I modified Nuts multiple times to create four separate Squirrel deployment versions. I initially ran Setup.exe from version 1.0 and from then ran Nuts using the shortcut created by that initial setup. For each new version I used the Update Nuts button to download and install. The four versions were as follows.

  • 1.0 - Initial deployment generated following the Quick Start from Nuts project README
  • 1.1 - Changed the version in the Nut.Acorn library.
  • 1.2 - Added a large file to the deployment package.
  • 1.3 - Changed the version in the Nut.Walnut library.

Results

When the Setup.exe file was first run it "installed" Nuts in the user's AppData folder (Figure 2). The initial install created the app-1.0.0 folder, packages folder and Update.exe file in the installation directory. In addition a desktop shortcut was created as was an entry in the Windows Programs and Features list. Examination of the shortcut showed its target was the Update.exe file with an argument named processStart which had a value of Nuts.exe. The start-in for the shortcut was set to the app-1.0.0 folder. Each successive application update would add a new app-{version} directory and modify the start in directory of the shortcut to reflect the new version folder, e.g. app-1.1.0 for the first update.

Figure 2. Nuts install directory after four version installs.

Figure 3 shows the Releases folder (created by Squirrel) after the Squirrel command releasify has been applied on each of the four versions of the Nuts NuGet package.

Figure 3. Releases folder contents after all four version packages have been created.

Discussion

Initial installation of Nuts was flawless. Squirrel provides a mechanism to include an animated .gif to display during initial deployment but I didn't experiment with that. Successive updates were equally solid however, the mechanism of updating the shortcut's start in directory with a new version's installation path does present one possible annoyance to the user. When a desktop shortcut is updated in this manner is can re-position the icons on the user's desktop. For users who maintain a significant number of desktop shortcuts this can be problematic.

The generation of the deployment packages through the Squirrel releasify command worked fairly well though I did encounter issues attempting to call releasify more than once in a single Visual Studio session. Searching through Squirrel issues on GitHub I found it suggested this may be related to a bug in the Package Manager Console and not Squirrel itself.

The releasify command relies on the existence of the output from its previous executions in order to generate delta packages. This means the historical contents of the Releases folder must be kept in some type of repository to facilitate delta package deployments. This type of binary package tracking is not a typical development pattern and thus could necessitate modifications to build/release methodologies for teams that wish to utilize Squirrel deployments.

Comparison of the final .nupkg file sizes (Figure 3) with the four exploratory versions clearly indicated that Squirrel created deltas that were appropriately sized. Network monitoring further confirmed that delta download size on clients was as expected. How Squirrel handled deltas was one of my primary concerns and it appears that it handles them very sensibly.

Conclusions

Squirrel's performance was exemplary throughout my testing. It appears to be an admirable substitute for ClickOnce in the realm of Windows desktop application deployment. One lamentable difference is that ClickOnce installations (when deployed in online only mode) are guaranteed to run the latest deployment at each startup while Squirrel will not utilize the latest bits until the run after an update. The only other consideration I encountered was that while ClickOnce only retains two installations (current and previous) it appears Squirrel does not perform clean-up on previously installed versions so some sort of application level clean-up procedure would probably be necessary.