Ruthlessly Helpful

Stephen Ritchie's offerings of ruthlessly helpful .NET practices.

NuGet Kickstart Package

I want to use NuGet to retrieve a set of content files that are needed for the build. For example, the TeamCity build configuration runs a runner.msbuild script, however, that script needs to import a Targets file, like this:

<Import Condition="$(BuildPath)\ImportTargets\MSBuild.Lender.Common.Targets"
        Project="$(BuildPath)\ImportTargets\MSBuild.Lender.Common.Targets"
        />

The plan is to create a local NuGet feed that has all the prerequisite files for the build script. Using the local NuGet feed, install the “global build” package as the first build task. After that, the primary build script can find the import file and proceed normally. Here is the basic solution strategy that I came up with.

To see an example, follow these steps:

1. Create a local NuGet feed. Read more information here: http://docs.nuget.org/docs/creating-packages/hosting-your-own-nuget-feeds

2. Write a NuGet spec file and name it Lender.Build.nuspec. This is simply an XML file. The schema is described here: http://docs.nuget.org/docs/reference/nuspec-reference

<?xml version="1.0"?>
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
  <metadata>
    <id>_globalBuild</id>
    <version>1.0.0</version>
    <authors>Lender Development</authors>
    <requireLicenseAcceptance>false</requireLicenseAcceptance>
    <description>Lender Build</description>
  </metadata>
  <files>
    <file src="ImportTargets\**" target="ImportTargets" />
  </files>
</package>

Notice the “file” element. It specifies the source files, which includes in the MSBuild.Lender.Common.Targets file when the ImportTargets folder is added.

3. Using the NuGet Package Explorer, I opened the Lender.Build.nuspec file and saved the package in the LocalNuGetFeed folder. Here’s how that looks:

NuGet_Package_Explorer_1211-12-21

4. Save the package to the local NuGet feeds folder. In this case, it is the C:\LocalNuGetFeeds folder.

5. Now let’s move on over to where this “_globalBuild” dependency is going to be used. For example, the C:\projects\Lender.Slos folder.  In that folder, create a packages.config file and add it to version control. That config file looks like this:

<?xml version="1.0" encoding="utf-8"?>
<packages>
  <package id="_globalBuild" version="1.0.0" />
</packages>

This references the package with the id of “_globalBuild”, which is found in the LocalNuGetFeeds package. It is one of the available package sources because it was added through Visual Studio, under Tools >> Library Package Manager >> Package Manager Settings.

Library_Package_Manager_settings_2011-12-21

6. From MSBuild, the CI server calls the “Kickstart” target before running the default script target. The Kickstart target uses the NuGet.exe command line to install the global build package. Here is the MSBuild script:

<Project DefaultTargets="Default"
         xmlns="http://schemas.microsoft.com/developer/msbuild/2003"
         ToolsVersion="4.0"
         >
  <PropertyGroup>
    <RootPath>.</RootPath>
    <BuildPath>$(RootPath)\_globalBuild.1.0.0\ImportTargets</BuildPath>
    <CommonImportFile>$(BuildPath)\MSBuild.Lender.Common.Targets</CommonImportFile>
  </PropertyGroup>

  <Import Condition="Exists('$(CommonImportFile)')"
          Project="$(CommonImportFile)"
          />

  <Target Name="Kickstart" >
    <PropertyGroup>
      <PackagesConfigFile>packages.config</PackagesConfigFile>
      <ReferencesPath>.</ReferencesPath>
    </PropertyGroup>
    <Exec Command="$(NuGetRoot)\nuget.exe i $(PackagesConfigFile) -o $(ReferencesPath)" />
  </Target>

  <!-- The Rebuild or other targets belong here -->
  <Target Name="Default" >
    <PropertyGroup>
      <ProjectFullName Condition="$(ProjectFullName)==''">(undefined)</ProjectFullName>
    </PropertyGroup>

    <Message Text="Project name: '$(ProjectFullName)'"
             Importance="High"
             />
  </Target>

</Project>

7. In this way, the MSBuild script uses NuGet to bring down the ImportTargets files and places them under the _globalBuild.1.0.0 folder. This can happen on the CI server with multiple build steps. For the sake of simplicity here are the lines in a batch file that simulates these steps:

%MSBuildRoot%\msbuild.exe "runner.msbuild" /t:Kickstart
%MSBuildRoot%\msbuild.exe "runner.msbuild"

With the kickstart bringing down the prerequisite files, the rest of the build script performs the automated build using the common Targets properly imported.

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: