Quick Tip: Easily Push Your NuGet Project Using MSBuild

Lately, I've been making some NuGet packages and I wanted a quicker way to build and deploy them. Today, I go over how to automatically build and push your assemblies to your own NuGet repository.

Written by Jonathan "JD" Danylko • Last Updated: • Develop •
Construction Man

If you build a lot of NuGet packages and a user is having an issue with a NuGet package you deployed, you know how fast you need to be when you have to deploy them in a pinch.

Of course, you can write a nuspec file and have it alway ready, but it's so much easier to generate the .nuspec file on the fly.

When you need to make a quick change, load the project, make your changes, create and run unit tests to make sure the change works, and rebuild to push your new assemblies out to your awaiting clients.

Quick Overview

So one quick tip I've recently learned (besides letting TFS do it for you) is to create a NuGet package on a "Release" build of your assembly project.

Here's how to achieve this:

  1. Load your Solution into Visual Studio.

  2. Create a .nuspec file in the root of your project:

    My.Library.nuspec

    <?xml version="1.0" encoding="utf-8"?>
    <package>
        <metadata>
            <id>$id$</id>
            <version>$version$</version>
            <title>$title$</title>
            <authors>$author$</authors>
            <owners>$author$</owners>
            <requireLicenseAcceptance>false</requireLicenseAcceptance>
            <description>$description$</description>
            <copyright>Copyright 2016</copyright>
            <references>
                <reference file="My.Library.dll"/>
            </references>
        </metadata>
        <files>
            <file src="bin\Release\My.Library.pdb" target="lib\net40"/>
        </files>
    </package>
    
    This is our template for when we perform the merging with the build.

  3. Create a folder at the root called NuGet and add a NuGet.exe located here.

  4. Unload your main project (.csproj, not your .sln) and right-click on it to edit the .csproj.

  5. Add these lines to the end before the </Project> end tag:
    <Target Name="AfterBuild">
        <MSBuild Condition="'$(Configuration)|$(Platform)' == 'Release|AnyCPU'" Projects="NuGet\NuGet.msbuild" />
    </Target>
    
    Make sure you don't have another <Target Name="AfterBuild"> in your project. All this is saying is that after a build happens in Visual Studio and it's in Release mode, this will run the NuGet.msbuild file in the NuGet directory to package up the assembly and push this to a NuGet Repository.

  6. Now, add this NuGet.msbuild file to your NuGet directory:

    NuGet\NuGet.msbuild

    <?xml version="1.0" encoding="utf-8" ?>
    <Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003"
             ToolsVersion="4.0"
             DefaultTargets="default">
        <Import Project="$(MSBuildExtensionsPath)\MSBuildCommunityTasks\MSBuild.Community.Tasks.Targets" />
        <PropertyGroup>
            <LibVersion>1.0.0.0</LibVersion>
            <ProjectName>My.Library</ProjectName>
            <BaseDir>$(MSBuildProjectDirectory)\..</BaseDir>
            <BuildDir>bin\Release</BuildDir>
            <PackageDir>$(BaseDir)\..\..\..\PackageSource</PackageDir>
            <NuGetApp>NuGet\NuGet.exe</NuGetApp>
            <NuSpecFile>$(BaseDir)\$(ProjectName).nuspec</NuSpecFile>
            <ProjectFile>$(ProjectName).csproj</ProjectFile>
            <ReferenceLib>$(ProjectName).dll</ReferenceLib>
            <SymbolDB>$(BuildDir)\$(ProjectName).pdb</SymbolDB>
        </PropertyGroup>
        <ProjectExtensions>
            <NuGetReferences>
                <references>
                    <reference file="$(ReferenceLib)" />
                </references>
            </NuGetReferences>
            <NuGetFiles>
                <files xmlns:xmu="urn:msbuildcommunitytasks-xmlmassupdate">
                    <file xmu:key="src"
                          src="Symbols"
                          target="lib\net40" />
                </files>
            </NuGetFiles>
        </ProjectExtensions>
        <Target Name="default" DependsOnTargets="StdSpec; StdPackage; Publish" />
        <Target Condition="!Exists($(NuSpecFile))" Name="StdSpec">
            <Exec Condition="!Exists($(NuSpecFile))"
                  WorkingDirectory="$(BaseDir)"
                  Command="$(NuGetApp) spec" />
            <XmlMassUpdate Condition="Exists($(NuSpecFile))"
                           ContentFile="$(NuSpecFile)"
                           ContentRoot="/package/metadata"
                           NamespaceDefinitions="msb=http://schemas.microsoft.com/developer/msbuild/2003"
                           SubstitutionsFile="$(MSBuildProjectFullPath)"
                           SubstitutionsRoot="/msb:Project/msb:ProjectExtensions/msb:NuGetReferences" />
            <XmlMassUpdate Condition="Exists($(NuSpecFile))"
                           ContentFile="$(NuSpecFile)"
                           ContentRoot="/package"
                           NamespaceDefinitions="msb=http://schemas.microsoft.com/developer/msbuild/2003"
                           SubstitutionsFile="$(MSBuildProjectFullPath)"
                           SubstitutionsRoot="/msb:Project/msb:ProjectExtensions/msb:NuGetFiles" />
            <XmlUpdate Condition="Exists($(NuSpecFile))"
                       XmlFileName="$(NuSpecFile)"
                       XPath="/package/metadata/licenseUrl"
                       Delete="true" />
            <XmlUpdate Condition="Exists($(NuSpecFile))"
                       XmlFileName="$(NuSpecFile)"
                       XPath="/package/metadata/projectUrl"
                       Delete="true" />
            <XmlUpdate Condition="Exists($(NuSpecFile))"
                       XmlFileName="$(NuSpecFile)"
                       XPath="/package/metadata/iconUrl"
                       Delete="true" />
            <XmlUpdate Condition="Exists($(NuSpecFile))"
                       XmlFileName="$(NuSpecFile)"
                       XPath="/package/metadata/releaseNotes"
                       Delete="true" />
            <XmlUpdate Condition="Exists($(NuSpecFile))"
                       XmlFileName="$(NuSpecFile)"
                       XPath="/package/metadata/tags"
                       Delete="true" />
            <XmlUpdate Condition="Exists($(NuSpecFile))"
                       XmlFileName="$(NuSpecFile)"
                       XPath="/package/metadata/references/reference/@file"
                       Value="$(ReferenceLib)" />
            <XmlUpdate Condition="Exists($(NuSpecFile))"
                       XmlFileName="$(NuSpecFile)"
                       XPath="/package/files/file[@@src='Symbols']/@@src"
                       Value="$(SymbolDB)" />
        </Target>
        <Target Name="StdPackage">
            <Exec WorkingDirectory="$(BaseDir)"
                  Command="$(NuGetApp) pack $(ProjectFile) -Verbose -Prop Configuration=Release" />
        </Target>
        <Target Name="Publish">
            <!-- Publish the nupkg to the well known NuGet server
            Note: "nuget setApiKey {put-your-api-key-here} -Source http://My_Nuget_Gallery_Service_Url/"
                must be run prior on the machine to ensure the api key is set correctly. -->
            <Exec WorkingDirectory="$(BaseDir)"
                  Command="$(NuGetApp) push &quot;$(ProjectName).$(LibVersion).nupkg&quot; -Source http://My_Nuget_Gallery_Service_Url/" />
        </Target>
    </Project>
    
    I'm also using the XmlUpdate from MSBuild Community Tasks. It makes your builds go so much smoother.
  7. Once you have this setup, you can now right-click on your project that's unloaded and reload it.

  8. To test it out, set your mode to Debug and run your Unit Tests. Once they pass, you are ready to change the project to Release mode and create your NuGet package.

If you have any questions about this quick tip, post your comments below. Enjoy!

Did you like this content? Show your support by buying me a coffee.

Buy me a coffee  Buy me a coffee
Picture of Jonathan "JD" Danylko

Jonathan Danylko is a web architect and entrepreneur who's been programming for over 25 years. He's developed websites for small, medium, and Fortune 500 companies since 1996.

He currently works at Insight Enterprises as an Principal Software Engineer Architect.

When asked what he likes to do in his spare time, he replies, "I like to write and I like to code. I also like to write about code."

comments powered by Disqus