MyGet: Continuous Integration for the Rest of Us
By this time, most .NET developers are familiar with NuGet. It used to be that if you wanted to use some part of .NET, such as Entity Framework, you would add a reference to your project pointing to some DLL in the .NET Framework Class Library (FCL). Those days are loooong gone. Nowadays, if you want to use Entity Framework you must get it from NuGet, because it’s not even included in the FCL anymore. The reason is simple: product teams at Microsoft can push out updates in much shorter cycles using NuGet than they ever could do with the .NET Framework itself.
So why not use NuGet to distribute libraries to other parts of your organization? This process is straightforward and well-documented. But what is not so well documented is how to generate NuGet packages and push them up to a feed whenever you check code into your repository and kick off a Continuous Integration build. Fundamentally it’s not that difficult. The process entails writing a build script that executes pack and push commands using the NuGet command line tool, which you can install using either a bootstrapper or Chocolatey. If you are using Team Foundation Build Server, you can create a build definition to execute these NuGet commands, or for smaller projects you can use a cloud-hosted build controller with Visual Studio Online.
But what if you’re authoring an open-source project on GitHub or BitBucket and want the benefits of a build server with NuGet integration? That’s where MyGet comes in! For example, my Trackable Entities project is hosted on GitHub and whenever I push to the repo, MyGet kicks off a build and publishes all the packages to a CI NuGet feed. To get those packages, you need to add a NuGet package source pointing to the feed (in Visual Studio select Tools, Options, NuGet Package Manager, Package Sources).
After adding the CI package feed from MyGet, you’ll see the latest packages in the dialog that appears when you select Manage NuGet Packages after right-clicking on the project in the Visual Studio solution explorer. These will show up when you select “Include Prerelease” from the drop down.
MyGet provides a hosted NuGet server with continuous integration build services that hook into several popular online Git repositories, including GitHub, BitBucket, CodePlex, and Visual Studio Online. You can even configure builds to run unit tests and push releases to the public NuGet package gallery. And for public feeds using less than 500 MB, the service is free.
I implemented continuous integration for my Trackable Entities open-source project using MyGet and thought it might be useful to share my experience here. MyGet’s build services, while still in beta, are well documented, and excellent tech support is also provided. The most reliable path for me was to write a custom build script containing NuGet commands for generating NuGet packages. Along the way, I discovered some things about NuGet I didn’t know before. In particular, I found out how to use tokenized nuspec files in conjunction with csproj files, which allows setting the NuGet package version based on the build version. What you want to do is create a nuspec file with the same name and in the same directory as a csproj file. The path to the csproj file is passed to the NuGet pack command, but the $PackageVersion$ token is used in the nuspec file to pull in the NuGet PackageVersion parameter. For example, here is the nuspec file for my TrackableEntities.Client NuGet package. Notice how the $PackageVersion$ token is also used for the dependency on TrackableEntities.Common. This way, all the package versions for a single build stay in sync, which is nice.
<?xml version="1.0" encoding="utf-8"?>
<title>Trackable Entities Client</title>
<description>Change-tracking utility for client applications that wish to transmit entities to a web service for batch updates.</description>
<tags>change-tracking entity-framework n-tier wcf web-api</tags>
<dependency id="Newtonsoft.Json" version="6.0.3" />
<dependency id="TrackableEntities.Common" version="$PackageVersion$" />
Here is the build script with an install command for restoring NuGet packages and compiling the project, followed by the pack command for generating the NuGet package and setting the PackageVersion.
REM Set Variables:
if %config% == "" (set config=Release)
Source\.nuget\nuget.exe install Source\TrackableEntities.Client\packages.config -OutputDirectory Source\packages -NonInteractive
%WINDIR%\Microsoft.NET\Framework\v4.0.30319\msbuild Source\TrackableEntities.Client\TrackableEntities.Client.csproj /p:Configuration="%config%" /m /v:M /fl /flp:LogFile=Build\Source\Output\TrackableEntities.Client\portable-net45+sl5+win8+windowsphone8\msbuild.log;Verbosity=Normal /nr:false
Source\.nuget\nuget.exe pack "Source\TrackableEntities.Client\TrackableEntities.Client.csproj" -symbols -o Build\Source\Output\TrackableEntities.Client\portable-net45+sl5+win8+windowsphone8 -p Configuration=%config%;PackageVersion=%version%
Notice the presence of the –symbols parameter, which produces a NuGet package containing just the source and symbols (pdf files) for the project, which are then pushed by MyGet to the symbols server at SymbolSource.org, provided you set up MyGet to push packages to SymbolSource. This allows developers using the CI NuGet packages to debug the library and step into code. To get this to work, you’ll need to add the correct symbol file location (http://srv.symbolsource.org/pdb/MyGet/tonysneed/f190ef34-3196-4bc2-b3a1-61b2172064e3) and specify a local symbols cache on your machine. In addition you’ll need to go to Tools, Options, Debugging, General, then check “Enable source server support” and uncheck “Require source files to exactly match the original version” (which is needed because the checksums will differ).
What’s nice about this story is that it enables developers using a NuGet package to easily step through the source code while debugging, while matching it to a specific NuGet package version and commit to a Git repository. You’ll want to pay close attention to the Modules window, which you can view while debugging by selecting Debug, Windows, Modules. Make sure that the debug symbols are loaded from the correct location set for the symbols cache. If not, you can disable automatic symbol loading, then load the symbols manually.
Thanks to MyGet, connecting CI builds to a NuGet feed and a symbol server is now attainable for the ordinary developer. Enjoy!
|Reference:||MyGet: Continuous Integration for the Rest of Us from our NCG partner Tony Sneed at the Tony Sneed’s Blog blog.|