NuGet is great. So great, in fact, that I’ve set up my own NuGet server in Azure to use during my personal development to share Common projects, etc. It’s insanely nice as now I can do my builds using packages from other VSO projects without any hassle.
But for my own personal development, and likely any private NuGet dev that my readers might be doing, it makes some sense to not only deploy libs as NuGet projects, but also configure the applications that use them to pull the latest version of the libs at build time. But NuGet doesn’t do this. By default, anyway… ;)
When you configure NuGet package restore for your project, what happens at build time is simply a download of the version specified in packages.config to the build location to be used to resolve references. It doesn’t grab any version other than that specified in that file. If you want to get a new version, you have to run the equivalent of ‘nuget update <package id>’ before doing the build.
But it’s not as simple as putting ‘nuget update <package id>’ in the pre-build steps. In fact that’s actually too late.
So how can we make this happen? Sure you can add a new build step to your process – if you’re doing a big fat build server build configuration with TFS, etc. But what about on my local machine? I want the update to take place on its own without me having to think about it, and also to find out if anything breaks when I get this new version – at a time when I’m able to fix it, not on the build server.
Let’s have a look at a few snippets from the NuGet.Targets file that’s put in place when you configure package restore:
<!-- Commands -->
<RestoreCommand>$(NuGetCommand) install "$(PackagesConfig)" -source "$(PackageSources)" $(NonInteractiveSwitch) $(RequireConsentSwitch) -solutionDir $(PaddedSolutionDir)</RestoreCommand>
<BuildCommand>$(NuGetCommand) pack "$(ProjectPath)" -Properties "Configuration=$(Configuration);Platform=$(Platform)" $(NonInteractiveSwitch) -OutputDirectory "$(PackageOutputDir)" -symbols</BuildCommand>
<!-- We need to ensure packages are restored prior to assembly resolve -->
<BuildDependsOn Condition="$(RestorePackages) == 'true'">
RestorePackages;
$(BuildDependsOn);
</BuildDependsOn>
<!-- Make the build depend on restore packages -->
<BuildDependsOn Condition="$(BuildPackage) == 'true'">
$(BuildDependsOn);
BuildPackage;
</BuildDependsOn>
<Target Name="RestorePackages" DependsOnTargets="CheckPrerequisites">
<Exec Command="$(RestoreCommand)"
Condition="'$(OS)' != 'Windows_NT' And Exists('$(PackagesConfig)')" />
<Exec Command="$(RestoreCommand)"
LogStandardErrorAsError="true"
Condition="'$(OS)' == 'Windows_NT' And Exists('$(PackagesConfig)')" />
</Target>
<Target Name="BuildPackage" DependsOnTargets="CheckPrerequisites">
<Exec Command="$(BuildCommand)"
Condition=" '$(OS)' != 'Windows_NT' " />
<Exec Command="$(BuildCommand)"
LogStandardErrorAsError="true"
Condition=" '$(OS)' == 'Windows_NT' " />
</Target>
Seeing how this works? We’ve got a set of ‘Commands’ we’re going to run. We define what they actually do, define the order of execution, then wrap that up in a ‘<Target>’ tag to be used by the MSBuild engine.
So have a look at these apples:
<!-- Commands -->
<UpdateCommand>$(NuGetCommand) update "$(PackagesConfig)" -source "$(PackageSources)" -id AutoUpdater $(NonInteractiveSwitch)</UpdateCommand>
<RestoreCommand>$(NuGetCommand) install "$(PackagesConfig)" -source "$(PackageSources)" $(NonInteractiveSwitch) $(RequireConsentSwitch) -solutionDir $(PaddedSolutionDir)</RestoreCommand>
<BuildCommand>$(NuGetCommand) pack "$(ProjectPath)" -Properties "Configuration=$(Configuration);Platform=$(Platform)" $(NonInteractiveSwitch) -OutputDirectory "$(PackageOutputDir)" -symbols</BuildCommand>
<!-- We need to ensure packages are restored prior to assembly resolve -->
<BuildDependsOn Condition="$(RestorePackages) == 'true'">
RestorePackages;
UpdatePackages;
$(BuildDependsOn);
</BuildDependsOn>
<Target Name="UpdatePackages" DependsOnTargets="CheckPrerequisites">
<Exec Command="$(UpdateCommand)"
Condition="'$(OS)' != 'Windows_NT' And Exists('$(PackagesConfig)')" />
<Exec Command="$(UpdateCommand)"
LogStandardErrorAsError="true"
Condition="'$(OS)' == 'Windows_NT' And Exists('$(PackagesConfig)')" />
</Target>
Define an UpdateCommand and wire it in to the MSBuild processing and voila. What you’ll see happen is the originally-specified package (eg: v1.0) will be “restored” at build time, then the update takes place (to v1.1) and that’s what gets referenced by the parent Project file. On your physical disk you’ll see both packages show up in SolutionDir\packages, but the end result is what you want. I tried ordering UpdatePackages before RestorePackages to avoid this quirk but I was met with errors, unfortunately.
Best part is this works locally in your VS instance and on the build server when the build gets run.
Enjoy!