Commit, Push, Consume: continuous delivery of ReSharper extensions

5 minute read

I've recently announced my ReSharper Razor plugin and published the EAP package to the brand new ReSharper Plugin feed. I've originally built it against the ReSharper 7 SDK and only recently added support for the ReSharper 8 EAP SDK. One of the questions I asked myself when I built the R# 7 version was: how do I ship it? I was quite thrilled to receive an email from Matt Ellis (@citizenmatt) inviting me to take a look at the latest SDK bits which included the new NuGet-based Extension Manager! In this post I want to show you how I'm using free tools to develop, build, package, test & publish a ReSharper plugin.

The missing link?

The plugin obviously is open source. The source repository for this plugin can be found on GitHub: https://github.com/xavierdecoster/ReSharper.RazorExtensions.

One of the first things I do when starting a new project is setting up Continuous Integration. Yes! Even for a little OSS pet project! In this case, the output of my build should be a NuGet package containing the plugin, so I need a repository to serve these CI packages for consumption to facilitate testing. Each GitHub repository has this great feature called Post-Receive Hooks which allows you to trigger another service whenever someone commits to the repository. Sounds useful, right?

Let's rephrase that: whenever GitHub triggers a service, it should build the sources, package the plugin and publish it on a NuGet feed for consumption. I know another free service for that: MyGet!

MyGet "Wonka" Build Services allows you to connect your GitHub repository to a MyGet feed. It is designed to take care of building, testing, packaging and publishing consumables. I like to think in terms of consumables and consumers: don't try to build and package your enterprise web sites on there. We're likely missing some fancy SDKs anyway and we refuse to install Visual Studio J For this scenario however it's perfect! But I'm biased J

Connecting the services

To develop a ReSharper plugin you need to reference the ReSharper SDK. It's not available on NuGet, so you'll need to download the MSI. It comes with some nice project and item templates for Visual Studio, and the SDK is quite large. My plugin only used a limited set of binaries from this SDK so I decided to commit them into Git (yeah, I know, bite me…). It's for the greater good, as the SDK won't be installed on the build agent and isn't available as a NuGet package to restore.

When you're a MyGet feed owner, you can add and configure a build source for your feed. Click on the Add from GitHub button and select the repository you want.

You'll need to authorize MyGet to get access to your GitHub repository, and you'll be prompted with a dialog to select the repository you want to connect to.

We'll select the master branch by default, but you can obviously change that if you want. You don't need to edit anything else on this build source for now as you're already presented with a HTTP POST hook URL.

Next up, connect your build source to GitHub. Browse your GitHub repository settings and look for the Service Hooks tab. There you're able to enter a WebHook URL as shown below. This URL will be hit with a POST request whenever someone pushes to your GitHub repository and passes some information about the push event.

This provides enough information to MyGet in order to clone this repository and run a build. MyGet Build Services uses a conventional approach and selects one of the following artifacts in order of precedence:

  1. Build.bat (or build.cmd or build.ps1)
  2. MyGet.sln
  3. Any other .sln
  4. All .csproj files (or other project types)
  5. All .nuspec files

It is up to you to ensure your repository is in a suitable format for an automated build. If you like full control over the build process, then you'll be happy to find a build.bat file in my repository. That's it, whenever I push a commit to my GitHub repository, a MyGet build will be triggered: it will compile my sources, run any tests available, create a NuGet package for the plugin in the ReSharper plugin package format, and push the package on my MyGet feed for consumption.

Consuming the plugin from a MyGet feed

Open the ReSharper Extension Manager (of all places, it's available in ReSharper > Extension Manager), and click on Settings. Just as with the NuGet Visual Studio extension, the ReSharper Extension Manager allows you to configure any NuGet feed for consumption. Simply add your MyGet feed url into the list:

Save your settings and reopen the Extension Manager: you'll find your newly added feed in there. All CI packages you publish on that feed will be available for consumption.

Even better: ReSharper will notify you about available updates for your plugins!

Release some!

As soon as you're satisfied with your plugin, you can release it to the masses. Here I'm going to use MyGet's package sources feature: any MyGet feed can have one or more upstream NuGet feeds. This allows for various scenarios such as aggregating feeds, filtering, proxying & mirroring, but also package promotion.

In order release a CI package, you need to promote a package to the ReSharper Plugin feed (and optionally change the package version, e.g. strip the pre-release tag). To be able to do this, you first have to configure the ReSharper plugin feed as an upstream package source. We just announced the availability of a new preset for this on the MyGet blog.

Browse to your feed settings and select the Package Sources tab. Select Add Package Source and click on the Preset button in the top-left of the dialog. You'll find a new ReSharper extension gallery preset. Don't forget to configure your API key for the ReSharper Gallery or your package can't be pushed upstream as the request would be unauthorized.

Whenever you want to release a new version for consumption, simply promote your package to the release repository with a click of a button.

Conclusion

Matt and the ReSharper team have really done a great job by adopting NuGet as a protocol for distributing plugins. It makes these plugins more discoverable and it's a breeze to install them or keep them up to date. As a plugin author (actually, any OSS developer), you should really leverage all the (often free for OSS!) tooling available: many organizations can only dream of such a streamlined development workflow. Releasing a new version of my plugin is really just a commit (or a pull request) away! Commit, push & consume!

Leave a Comment