When you are building nuget packages that are not directly using the AssemblyInfo.cs for the version number, you need to make sure to increment the nuget version number before building the package. Inevitable though you will forget to increment the version number and have to build the package a 2nd time. Wouldn’t it be great if you could automatically increment the version number before calling nuget pack. Well I have written a powershell script to do just this. Below are the details out the script.

  1. In your project directory, create a file called IncrementNugetVersionNumber.ps1
  2. Add the following parameters to the top of the file to take in the nuget spec file location and any version parameters.
param($NuSpecFile = $(throw "Mandatory parameter -NuSpecFile not supplied"),
    $MajorVersion,
    $MinorVersion,
    $RevisionVersion)

Next we are going to add a bunch of helper functions right under the param code from above. These helper functions will make it easier to deal with the xml queries of the nuget spec document.

function AssignVersionValue(
    [string]$oldValue,
    [string]$newValue)
{
    if ($newValue -eq $null -or $newValue -eq "") {
        $oldValue
    } else {
        $newValue
    }
}
function Get-XmlNamespaceManager(
    [xml]$XmlDocument,
    [string]$NamespaceURI = "")
{
    if ([string]::IsNullOrEmpty($NamespaceURI))
    {
        $NamespaceURI = $XmlDocument.DocumentElement.NamespaceURI
     }

    [System.Xml.XmlNamespaceManager]$xmlNsManager = New-Object System.Xml.XmlNamespaceManager($XmlDocument.NameTable)
    $xmlNsManager.AddNamespace("ns", $NamespaceURI)
    return ,$xmlNsManager
}
function Get-FullyQualifiedXmlNodePath(
    [string]$NodePath,
    [string]$NodeSeparatorCharacter = '.')
{
    return "/ns:$($NodePath.Replace($($NodeSeparatorCharacter), '/ns:'))"
}
function Get-XmlNode(
    [xml]$XmlDocument,
    [string]$NodePath,
    [string]$NamespaceURI = "",
    [string]$NodeSeparatorCharacter = '.')
{
    $xmlNsManager = Get-XmlNamespaceManager -XmlDocument $XmlDocument -NamespaceURI $NamespaceURI
    [string]$fullyQualifiedNodePath = Get-FullyQualifiedXmlNodePath -NodePath $NodePath -NodeSeparatorCharacter $NodeSeparatorCharacter

    $node = $XmlDocument.SelectSingleNode($fullyQualifiedNodePath, $xmlNsManager)
    return $node
}
function Get-XmlNodes(
    [xml]$XmlDocument,
    [string]$NodePath,
    [string]$NamespaceURI = "",
    [string]$NodeSeparatorCharacter = '.')
{
    $xmlNsManager = Get-XmlNamespaceManager -XmlDocument $XmlDocument -NamespaceURI $NamespaceURI
    [string]$fullyQualifiedNodePath = Get-FullyQualifiedXmlNodePath -NodePath $NodePath -NodeSeparatorCharacter $NodeSeparatorCharacter

    $nodes = $XmlDocument.SelectNodes($fullyQualifiedNodePath, $xmlNsManager)
    return $nodes
}
function Get-XmlElementsTextValue(
    [xml]$XmlDocument,
    [string]$ElementPath,
    [string]$NamespaceURI = "",
    [string]$NodeSeparatorCharacter = '.')
{
    $node = Get-XmlNode -XmlDocument $XmlDocument -NodePath $ElementPath -NamespaceURI $NamespaceURI -NodeSeparatorCharacter $NodeSeparatorCharacter

    if ($node) {
        return $node.InnerText
    } else {
        return $null
    }
}
function Set-XmlElementsTextValue(
    [xml]$XmlDocument,
    [string]$ElementPath,
    [string]$TextValue,
    [string]$NamespaceURI = "",
    [string]$NodeSeparatorCharacter = '.')
{
    $node = Get-XmlNode -XmlDocument $XmlDocument -NodePath $ElementPath -NamespaceURI $NamespaceURI -NodeSeparatorCharacter $NodeSeparatorCharacter

    if ($node)
    {
        $node.InnerText = $TextValue
    }
    else
    {
        $elementName = $ElementPath.Substring($ElementPath.LastIndexOf($NodeSeparatorCharacter) + 1)
        $element = $XmlDocument.CreateElement($elementName, $XmlDocument.DocumentElement.NamespaceURI)
        $textNode = $XmlDocument.CreateTextNode($TextValue)
        $element.AppendChild($textNode) > $null

        $parentNodePath = $ElementPath.Substring(0, $ElementPath.LastIndexOf($NodeSeparatorCharacter))
        $parentNode = Get-XmlNode -XmlDocument $XmlDocument -NodePath $parentNodePath -NamespaceURI $NamespaceURI -NodeSeparatorCharacter $NodeSeparatorCharacter

        if ($parentNode)
        {
            $parentNode.AppendChild($element) > $null
        }
        else
        {
            throw "$parentNodePath does not exist in the xml."
        }
    }
}

Now that the helper functions are added, it is time to create the meat of the script where you will update the version number and write it back to the file.

  1. Get the Full Path of the Nuget spec file from parameter $NuSpecFile

     $NuSpecFile = Resolve-Path $NuSpecFile
    
  2. Once we have the full path, we need to get the contents of the files as an Xml document so that we can run xpath queries on it.

     [ xml ]$fileContents = Get-Content -Path $NuSpecFile
    
  3. Then we need to get the current version number

     $originalVersion = Get-XmlElementsTextValue -XmlDocument $fileContents -ElementPath "package.metadata.version"
    
  4. Set a default version number

     $newVersion = "1.0.0"
    
  5. If there is an existing version number, then we can update it. If not, then use the default version number. This bit of code will break apart the version number into individual segments that we can then update.

     if ($originalVersion -ne $null)
     {
         $segments=$originalVersion.Split(".")
         $v1="1"
         $v2="0"
         $v3="0"
    
         if ($segments.Length -gt 0) { $v1=$segments[0] }
         if ($segments.Length -gt 1) { $v2=$segments[1] }
         if ($segments.Length -eq 3) { $v3=$segments[2] }
    
         $v1 = AssignVersionValue $v1 $MajorVersion
         $v2 = AssignVersionValue $v2 $MinorVersion
         $v3 = AssignVersionValue $v3 $RevisionVersion
    
         if ($v1 -eq $null) { $v1 = 1 }
         if ($v2 -eq $null) { $v2 = 0 }
         if ($v3 -eq $null) { $v3 = 0 }
    
    
         $newVersion = "$v1.$v2"
             #Write-Host "REV: $RevisionVersion"
         if ($v3 -ne $null -and ($RevisionVersion -eq $null -or $RevisionVersion -eq "")) {
             $v3 = ($v3 -as [int]) + 1
         }
    
         $newVersion = "$newVersion.$v3"
     }
    
  6. Lastly, we need to set the new version number and save the file.

     write-host "Setting Version to $newVersion"
    
     Set-XmlElementsTextValue -XmlDocument $fileContents -ElementPath "package.metadata.version" -TextValue $newVersion
     $fileContents.Save($NuSpecFile)
    

Now that we have the powershell script built to increment the version number, the next piece is to create the batch file that calls the script and nuget pack.

For the batch file you can pass in /major, /minor, /revision, and /outputdir. If any of these are not passed in, it will use the existing values from the nuget spec and output into the current directory.

  1. Create a file called build.cmd

  2. Setup the parameters

     @echo off
     SET OUTPUTDIR=""
     SET REVISION=""
     SET MAJOR=""
     SET MINOR=""
    
  3. Next we need to check the parameters and set each of the individual values.

     :checkparameters
     :: REM Grab the first variable supplied as a whole. Ex. /action:start
     set SWITCHPARSE=%1
    
     :: REM Check to see if there are no more switches, if so goto end of
     :: parsing, prevents endless loop
     IF [%SWITCHPARSE%] == [] goto end
    
     :: REM Reset variables as clean up.
     set SWITCH=
     set VALUE=
    
     :: In the SWITCHPARSE variable, grab the two tokens separated
     :: by a : and assign the first to SWITCH and the second to VALUE
     for /F "tokens=1,2 delims=:" %%a IN ("%SWITCHPARSE%") DO (
         SET SWITCH=%%a
         set VALUE=%%b
     )
    
     :: Check which action to perform based on the switch
     IF [%SWITCH%] == [/revision] goto setrevision
     IF [%SWITCH%] == [/major] goto setmajorversion
     IF [%SWITCH%] == [/minor] goto setminorversion
     IF [%SWITCH%] == [/outputdir] goto setoutputdir
     :: Perform the action by setting the variable for later use and
     :: shift the command line parameters so the next in line is
     :: ready to be processed
    
     goto end
    
     :setrevision
     set REVISION=%VALUE%
     SHIFT
     goto checkparameters
    
     :setmajorversion
     set MAJOR=%VALUE%
     SHIFT
     goto checkparameters
    
     :setminorversion
     set MINOR=%VALUE%
     SHIFT
     goto checkparameters
    
     :setoutputdir
     set OUTPUTDIR=%VALUE%
     SHIFT
     goto checkparameters
    
  4. Finally we need to call the powershell script we created above and increment the version number in the nuget spec.

     :end
     echo.
    
     echo PowerShell -NoProfile -ExecutionPolicy Bypass -File "..\IncrementNugetVersionNumber.ps1" -NuspecFile "log4net.blank.config.nuspec" -RevisionVersion %REVISION% -MajorVersion %MAJOR% -MinorVersion %MINOR%
    
     PowerShell -NoProfile -ExecutionPolicy Bypass -File "..\IncrementNugetVersionNumber.ps1" -NuspecFile "log4net.appender.console.nuspec" -RevisionVersion %REVISION% -MajorVersion %MAJOR% -MinorVersion %MINOR%
    
  5. Finally, we need to call nuget pack to build the nuget package

     echo.
     echo nuget pack -OUTPUTDIR "%OUTPUTDIR%"
     nuget pack -OUTPUTDIR %OUTPUTDIR%
    

Now anytime that you need to build the nuget package, you just have to call build.cmd and it will spit out an new package with an incremented version number.

I know this post was a lot of code. Both of the files that we create can be downloaded at:

IncrementNugetVersionNumber.ps1

build.cmd