Last Updated Feb 04, 2013 — Enterprise Agile Planning expert
How NuGet is Helping Us Realize a New Level of Continuous Integration
Enterprise Agile PlanningAs I was working on cleaning up build scripts and Jenkins build projects this week, I was considering a blog post on how we use NuGet and the benefit it brings for us. I hadn't seen anything around about the internet that resembled our approach. Then this morning, I noticed Douglas Rathbone explaining why NuGet Package Restore is not for him. So, I'll take the opposite position and explain why NuGet Package Restore (and then some) is for us.
We Used to Check/in LibrariesMy context may be different from Douglas's. I mainly work with many independent, open/source products that represent tools and integrations built to work with the main VersionOne application. Historically, these projects were checked into Subversion and the dependencies were managed by having a special directory structure for external libraries. Groups of projects could depend on a peer directory that held shared libraries. In this model, we could update a dependency in one place and all the projects would get the new library. We have recently begun moving these projects to GitHub, which changes a number of things. First on my mind is that we want to avoid checking in libraries. For Git, this is helpful to keep repositories small and fast without having to do maintenance. This also helps us reduce the risk that we break the terms of an open/source or commercial license for a library we consume. In Git, the equivalent feature for managing shared libraries is the submodule. While capable of recreating the relationships we had, we felt submodules were a little too obscure and could be a barrier to external participants who might be new to Git.
NuGet Package RestoreIt isn't perfect, but NuGet Package Restore is hardly as bad as Douglas claims. Douglas suggests build speed suffers due to NuGet restore. This might be true when you have fast local network speeds to retrieve files from version control, but slow Internet bandwidth with many, large NuGet packages. That doesn't fit for us, since both GitHub and NuGet are hosted, libraries are going to be about as fast from either. Moreover, if there are binary deltas, Git will actually loose the speed contests. Douglas also overlooks NuGet caching and the option to run your own, internal NuGet feed, which both solve for both performance and reliability. In our experience, the pain comes from other places. We wanted to keep binaries out of the Git repo, but Package Restore wants to put the NuGet.exe inside. We have tried some other approaches here but nothing is quite satisfying. Also, as NuGet changed versions, we found changes in command/line switches and the API meant some reconfiguration. Most of these changes came with 2.0, which is to be expected on a major version change.
Controlling Change vs Embracing ChangeDouglas also claims that NuGet reduces control over dependency versioning. His logic is hard to refute. After all, how can you prove a copy is really a copy? However, I see no reason to trust that your version control system gives you a better copy than NuGet. The dependencies in the NuGet packages.config file are quite explicit and that file should be under version control. The packages.config file also remembers the specific version that is required. Package restore does not automatically update dependency versions all on its own. We like explicit, versioned control over our dependencies. We also want to know as soon as possible when a change in an external library may break our code. The XP description of continuous integration tells us:
Continuous integration avoids or detects compatibility problems early. Integration is a "pay me now or pay me more later" kind of activity. That is, if you integrate throughout the project in small amounts you will not find your self trying to integrate the system for weeks at the project's end while the deadline slips by. Always work in the context of the latest version of the system.We take that to mean integration with all of the components, regardless of whether they are internal or external. NuGet gives us a means to automatically integrate new versions of libraries with the command/line nuget.exe update. In addition to restoring packages, we have Jenkins update them automatically so that we keep up with changes. The only pain around this is that we manually edit the packages.config to include allowedVersions. Following the rules of semantic versioning, we expect to work with the current version up to the next major version (i.e. the next breaking change). For example, for NUnit, we specify:
<package id="NUnit" version="2.6.2" allowedVersions="[2.6,3)" targetFramework="net40" />