Welcome to another part of the ReSharper SDK Adventures! One of the problems that many plugin developers face is in being able to update their plugins after they are released, so in this post we’ll take a look at how you can do that with simple update notifications.
Update Notifications are already used by at least two ReSharper plugins – the NuGet plugin and ForTea.
The user gets informed about plugin updates in two ways. First, there is the Updates tab in ReSharper Options that shows a listing of all the plugins that have pending updates:
And then there’s a periodic check that ReSharper does. If a plugin has been updated, it will pop up a dialog box to let you know:
At the time of writing, the notification mechanisms are different, so we’re going to consider Nuget’s implementation here. Essentially, the mechanism for update notification is as follows:
Once every 24 hours, a special XSLT file is downloaded from a location of the plugin developer’s choosing.
This XSLT file is applied to certain data taken from the environment in order to determine if anything has changed.
If something has in fact changed, an update location is acquired and a corresponding update dialog is displayed.
Please note: update notifications are not the same as automatic updates. Even though the user is informed about the new version being available, it is their responsibility to download and install the updated plugin – you are simply providing the download link.
Let’s go through each of the files in turn and discuss what’s going on. I’m going to keep the links to the NuGet project here so that you can quickly see the whole file if you need to.
The update notification shell component (source) is the component responsible for providing the data from the updates. All of its operations happen in the constructor. At least two things need to be injected into the constructor — the
Lifetime of whoever instantiated the component, and an
UpdatesManager, which is the ReSharper component responsible for handling updates.
Inside the constructor, we first create a URI where our magic XSLT file is stored. We’ll discuss the contents of the XSLT file soon, but for now it’s just important to remember that this XSLT is appled to (an XML representation of) a set of variables to determine whether a change is required:
The above file must obviously be accessible on the web. Note the use of
raw.github.com above – the path might be useful if you’re also hosting a project on GitHub.
Now that we have the URI, we ask the
UpdatesManager that we injected to give us a category that’s related to our plugin:
With the category acquired, we can now proceed to customize the local environment settings for the duration of the lifetime of our component (which is equivalent to the lifetime of the shell). Specifically, what we want to do is customize the local environment information that the XSLT is applied to:
Let’s discuss the above code. Two things are happening:
First, we check that the type of argument that we are customizing is indeed of type
Second, if it is, we overwrite it with our own definition, which incorporates both the original environment and information about our plugin.
PluginLocalEnvironmentInfo from above is also of our own making, and is simply an aggregation of environment info and our plugin info. The class is decorated with appropriate metadata so as to allow subsequent serialization and application of the XSLT file:
We store the plugin version in a special structure called
VersionSubInfo, and it’s acquired in our case via the
GetThisVersion() call which simply takes the version of our plugin assembly:
Finally, the constructor is finished off with the following call:
This requires a bit of an explanation. ReSharper typically downloads and evaluates the XSLT on a regular basis, but doesn’t re-evaluate it after an install. As a result, if there is a reminder out there to download this or older version of the plugin, we remove it:
And that’s it for the update notification component!
The second piece of the puzzle is the update XSLT file. Behind the scenes, the data that we provide ReSharper via
PluginLocalEnvironmentInfo gets serialized into XML, and then this XSLT is applied to it. If the latest plugin version number has changed, the XSLT provides ReSharper with new data regarding where the updated version lives, who makes it, how much it costs, and so on.
Rather than show the whole thing, let’s discuss all the important bits. First of all, the XSLT contains version numbers for the latest version that’s available currently. You update this number when you release a new version of your plugin.
The XSLT is solely responsible for telling ReSharper that an update is needed. To determine this, it queries the current plugin versions:
It then does a simple
if check to see if something has changed and, if it has, it needs to provide ReSharper with a new
<UpdateInfo> structure that contains all the relevant information about the new version.
I won’t list the contents of the
<UpdateInfo> structure here — if you’re interested, please take a look at the updates.xslt file of the NuGet plugin. The only thing I want to point out is that the icon specified in the
<IconData> element is simply a base64 representation of a PNG file. Keep in mind that
UpdateInfo is, in its original form, a .NET class located in the
JetBrains.UI.Updates namespace — you can use this class to figure out which XML elements can be provided, and what they are used for (the class has extensive documentation).
Hint: to find out more about what the update manager is doing behind the scenes, you can peek at the
%LOCALAPPDATA%\JetBrains\ReSharper\v7.1\UpdatesManager.xml file. This file contains information about all the plugins that ReSharper tries to update, and ReSharper itself, of course!
Implementing update notifications for ReSharper plugins is not that difficult — simply take the existing files, tweak them a little and you’ve got a mechanism for automatically letting your users know that an update is available. So go ahead, implement this in your plugin right now — your users will appreciate it! ■