17

We have two separate .NET solutions:

  • Running a build for the first solution produces our end product: a bunch of DLLs. These DLLs are delivered to our customers via a NuGet package.
  • The second solution serves as a product-test solution: the NuGet package is installed to it, and it is built and executed - thus it makes use of our product exactly the same way as our customers would do.

The challenge here is that there should be a way our latest NuGet package gets installed automatically to the product-test solution, preferably during the build of this product-test solution.

Based on the ideas from a similar question, I got this far with configuring the product-test solution:

  • First I enabled NuGet Package Restore. This lets me get rid of the "packages" directory completely from VCS since the package with the version defined in packages.config file would be downloaded automatically by NuGet before build.
  • Then I added the following pre-build event in Visual Studio: $(SolutionDir).nuget\nuget update -prerelease $(ProjectDir)packages.config. This lets me pull in the latest version of our NuGet package during build.

I currently use the above scenario to run local builds using Visual Studio and unattended builds using TeamCity. The solution seems to work for both scenarios on the first sight, but actually it does not produce the expected result: when the product-test solution is built, in the bin directory I don't get the latest version of the DLLs, only the latest-1 version.

The problem is that although the nuget update command updates everything as expected, including the packages.config and the .csproj file, their new content is not picked up by the build, therefore - as my guess goes - the HintPath settings from the .csproj file still reflect a "before build" state, therefore old DLLs are copied to the bin directory. I assume the .csproj file is processed only once: before the pre-build event is triggered, and the changes made by the pre-build event are ignored until the next build.

I considered the following solutions:

  • Apparently pre-build is not "pre" enough. If there was an even earlier point I could insert the nuget update command, my above solution would probably work.
  • I read that I could override the HintPath-s in the .csproj file by defining a ReferencePath. But I doubt I could easily figure out the right path or I could set it early enough so the build picks it up.
  • As a workaround I could run the builds twice: duplicate the build step for the product-test solution in TeamCity and I could always build the solution twice locally in Visual Studio.

Has someone figured out how to automatically update a NuGet package to the latest version during build?

Community
  • 1
  • 1
remark
  • 331
  • 1
  • 3
  • 10
  • @DavidBrabant - That's a long read. Are you suggesting to move the package update to a separate build step? – remark Feb 22 '13 at 16:03

3 Answers3

7

Have a look at a blog post I just did regarding this process. Instead of configuring stuff on the server, I did it by augmenting the Nuget.Targets file that's put in place with the Nuget Package Restore option. Beauty of this approach is it executes locally AND on the server (so you get to see any possible side-effects before breaking the build)

Posted details here: http://netitude.bc3tech.net/2014/11/28/auto-update-your-nuget-packages-at-build-time/

bc3tech
  • 1,228
  • 14
  • 27
  • Nice work. I would have commented on your blog but Disqus.. urggh. I used a modified version of your approach. I created a separate itemgroup called 'InternalPackageSources' and put my internal feed in that, and supplied that as a parameter to UpdateCommand so that I could do a restore for all package sources and an update for the internal feed. I left out the -id because I have dozens of internal packages and didn't want to create a command per package – user381624 Aug 05 '15 at 06:57
  • @ZachLeighton honestly I doubt it. they changed *a lot* of how nuget runs, etc w/ 3.x, unfortunately. :( – bc3tech Oct 02 '15 at 01:16
  • How come I can't find this Nuget.Targets file? – Vin Shahrdar Mar 13 '18 at 11:23
4

I think that put automatic update to pre-build step it's not NuGet style. You can understand that you need this operation every time when you do it. Mostly because it can increase build time. For example when you use TDD and often rebuild project and run tests, you expect that it will be done quickly. Additionally it can update unexpected packages and break something, after that you can spend a lot of time to find the problem.
I suggest to do update as the separate step. On TeamCity you can use NuGet installer build step. For performing update just check two checkboxes in the bottom area of step configuration: enter image description here

Additionally if you want keep result of update after successful build and passing test, you can add later build step which commits this changes to VCS (using cmd or PowerShell for example).
When you are working locally, I thing the better way run update packages once before you start working with project. You can use Package Manager Console for this, with command Update-Package -IncludePrerelease.

Pavel Bakshy
  • 7,697
  • 3
  • 37
  • 22
  • Thank you for your answer. As you recommended, I created a NuGet Installer build step. In my opinion this is in essence the same sort of workaround as the #3 "considered solution" in my original question, but obviously much more elegant and appropriate, so I will stick with it for a while. – remark Feb 25 '13 at 12:14
  • Note: I think a build-time NuGet package update is still a valid idea: 1) being similar to the "official" Package Restore it is not far from NuGet principles 2) increased build time only bad if the update was not needed and in case of our demo/test application it would rarely happen + you can still "turn off" the pre-build event 3) it is easier to support a build process having automatic package update if it is a must to build the solution against the latest NuGet package, especially if a customer is going to be confronted with this requirement ("build is not-working" vs. "build is slow"). – remark Feb 25 '13 at 12:38
  • I'm sure manually checking for updates every time you open the solution does work. But it would be really neat if there was a nice automated way of doing this. – Craig Brett May 06 '14 at 08:04
-1

The MSBuild-solution I figured out is to override the BuildDependsOn property. Therefore, I created an UpdateNugetPackages.target which looks like this:

<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <PropertyGroup>
    <BuildDependsOn>
      UpdateNugetPackages;
      $(BuildDependsOn);
    </BuildDependsOn>
    <UpdateCommand>"$(SolutionDir)pathToYourNugetExe.exe" update "$(SolutionDir)NameOfYourSolution.sln"</UpdateCommand>
  </PropertyGroup>
  <Target Name="UpdateNugetPackages">
    <Exec Command="$(UpdateCommand)"></Exec>
  </Target>
</Project>

The UpdateCommand defines where, and with which arguments the nuget.exe gets called. Feel free to adopt this for your own needs.

This target has then be referenced in your .csproj File. As simple as that:

<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
 ...
    <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
    <!-- After the initial decleration of the BuildDependsOn-Property. -->        
    <Import Project="UpdateNugetPackages.target" Condition="Exists('UpdateNugetPackages.target')" />

Keep in mind, that the import order matters. You have to import your target-file (UpdateNugetPackages.targets), with overrides (or actually decorates) the BuildDependsOn property, after the target-file Microsoft.Common.targets which defines it. Otherwise, the property will be redefined and will remove your changes, as the initial definition in Microsoft.Common.targets does not include any existing value in BuildDependsOn. Microsoft.Common.targets is imported by Microsoft.CSharp.targets for a C# project. Thus, your import has to go after the import of Microsoft.CSharp.targets.

David Leitner
  • 3,312
  • 1
  • 18
  • 27