Note: This post is now also part of the official NuGet documentation.
In this post, I’ll provide a walkthrough on how to use package restore with Team Foundation Build with both, git as well TF version control.
In our previous blog post on how we improved package restore, I showed that NuGet 2.7 and Microsoft.Bcl.Build made restoring packages a lot easier. However, I didn’t explain in great detail on how you can restore packages during the build. In this post, I’ll close that gap by providing a more detailed walkthrough.
Although this walkthrough is specific for the scenario of using Team Foundation Service, the concepts also apply to other version control- and build systems.
The General Approach
An advantage of using a package manager like NuGet is that you can use it to avoid checking in binaries to your version control system.
This is especially interesting if you are using a distributed version control system like git because developers need to clone the entire repository, including the full history, before they can start working locally. Checking in binaries can cause significant repository bloat as binary files are typically stored without delta compression.
NuGet has supported restoring packages as part of the build for a long time now. The previous implementation had a chicken-and-egg problem for packages that want to extend the build process because NuGet restored packages while building the project. However, MSBuild doesn’t allow extending the build during the build; one could argue that this an issue in MSBuild but I would argue that this an inherent problem. Depending on which aspect you need to extend it might be too late to register by the time your package is restored.
The cure to this problem is making sure that packages are restored as the first step in the build process. NuGet 2.7 makes this super easy via a simplified command line. In order to restore packages for an entire solution all you need is to execute a command line like this:
nuget.exe restore pathtosolution.sln
When your build process restores packages before building the code, you don’t need to check-in .targets files.
Note: Packages must be authored to allow loading in Visual Studio (our packages now all behave like this). Otherwise, you may still want to check in .targets files so that other developers can simply open the solution without having to restore packages first.
The following demo project shows how to set up the build in such a way that the packages folders and .targets files don’t need to be checked-in. Finally, I’ll show how you can setup an automated build on the Team Foundation Service for this sample project.
Repository Structure
Our demo project is a simple command line tool that uses the command line argument to query Bing. It targets the .NET Framework 4 and uses many of our packages (Microsoft.Net.Http, Microsoft.Bcl, Microsoft.Bcl.Async, and Microsoft.Bcl.Build).
The structure of the repository looks as follows:
<Project> │ .gitignore │ .tfignore │ build.proj │ ├───src │ │ BingSearcher.sln │ │ │ └───BingSearcher │ │ App.config │ │ BingSearcher.csproj │ │ packages.config │ │ Program.cs │ │ │ └───Properties │ AssemblyInfo.cs │ └───tools └───NuGet NuGet.exe
You can see that we haven’t checked-in the packages
folder nor any .targets files.
We have, however, checked-in the nuget.exe
as it’s needed during the build. Following widely used conventions we’ve checked it in under a shared tools
folder.
The source code is under the src
folder. Although our demo only uses a single solution, you can easily imagine that this folder contains more than one solution.
In order to communicate to the version control that we don’t intent to check-in the packages
folders, we’ve also added ignore files for both git (.gitignore
) as well as TF version control (.tfignore
). These files describes patterns of files you don’t want to check-in.
The .gitignore
file looks as follows:
syntax: glob *.user *.suo bin obj packages
The .gitignore
file is quite powerful. For example, if you want to generally not check-in the contents of the packages
folder but want to go with our previous guidance of checking in the .targets files you could have the following rule instead:
packages !packages/**/*.targets
This will exclude all packages
folders but will re-include all contained .targets files. By the way, you can find a template for .gitignore
files that is specifically tailored for the needs of Visual Studio developers here.
TF version control supports a very similar mechanism via the .tfignore file. The syntax is virtually the same:
*.user *.suo bin obj packages
build.proj
For our demo, we keep the build process fairly simple. We’ll create an MSBuild project that builds all solutions while making sure that packages are restored before building the solutions.
This project will have the three conventional targets Clean
, Build
and Rebuild
as well as a new target RestorePackages
.
- The
Build
andRebuild
targets both depend onRestorePackages
. This makes sure that you can both runBuild
andRebuild
and rely on packages being restored. Clean
,Build
andRebuild
invoke the corresponding MSBuild target on all solution files.- The
RestorePackages
target invokesnuget.exe
for each solution file. This is accomplished by using MSBuild’s batching functionality.
The result looks as follows:
<?xml version=”1.0″ encoding=”utf-8″?> <Project ToolsVersion=”4.0″ DefaultTargets=”Build” xmlns=”http://schemas.microsoft.com/developer/msbuild/2003“>
<PropertyGroup> <OutDir>$(MSBuildThisFileDirectory)bin</OutDir> <Configuration>Release</Configuration> <ProjectProperties> OutDir=$(OutDir); Configuration=$(Configuration); </ProjectProperties> </PropertyGroup>
<ItemGroup> <Solution Include=”$(MSBuildThisFileDirectory)src*.sln” /> </ItemGroup>
<Target Name=”RestorePackages”> <Exec Command=”"$(MSBuildThisFileDirectory)toolsNuGetNuGet.exe" restore "%(Solution.Identity)"” /> </Target>
<Target Name=”Clean”> <MSBuild Targets=”Clean” Projects=”@(Solution)” Properties=”$(ProjectProperties)” /> </Target>
<Target Name=”Build” DependsOnTargets=”RestorePackages”> <MSBuild Targets=”Build” Projects=”@(Solution)” Properties=”$(ProjectProperties)” /> </Target>
<Target Name=”Rebuild” DependsOnTargets=”RestorePackages”> <MSBuild Targets=”Rebuild” Projects=”@(Solution)” Properties=”$(ProjectProperties)” /> </Target>
</Project>
Configuring Team Build
Team Build offers various process templates. For this demonstration, I’m using the Team Foundation Service. On premise installations of TFS will be very similar though.
Git and TF Version Control have different Team Build templates, so the following steps will vary depending on which version control system you are using. In both cases, all you need is selecting the build.proj as the project you want to build.
First, let’s look at the process template for git. In the git based template the build is selected via the property 1. Solution to build:
Please note that this property is a location in your repository. Since our build.proj is in the root, we simply used build.proj. If you place the build file under a folder called tools, the value would be toolsbuild.proj.
In the TF version control template the project is selected via the property 1. Projects:
In contrast to the git based template the TF version control supports pickers (the button on the right hand side with the three dots). So in order to avoid any typing errors I suggest you use them to select the project.
Summary
Using NuGet package restore is a great way to avoid checked-in libraries. In this walkthrough I’ve demonstrated how you can set up a repository structure and a build process for a project that avoids this.
Please let us know what you think!
0 comments