Nuget Package Create through Powershell

Nuget packages are a popular way to distribute .NET libraries both internally and externally. Creating a nuget package from a project source is easy.

In the command window, navigate to the project directory

  1. nuget spec
    
  2. nuget pack <Project>.csproj or Nuget pack <Project>.vbproj

That is quite straightforward and easy to script. However this process has two assumptions:

  1. The Project file has all the details specified, like Assembly Company and Assembly Description
  2. All the files needed in the nuget package are the result of building the project

Let’s get around the 2 assumptions with a small Powershell script and create our package dynamically.

Let’s get started

  1. Open your favourite editor for scripting (a program like Atom is helpful as it recognises Powershell scripts syntax)
  2. Type in the two nuget commands, highlighted earlier (here we assume the script is in the project directory)
    1. # To make the script re-useable let’s fetch the project file name automatically
    2. $projectFile = get-childitem . -Path *.csproj | select -limit 1
    3. # Since we are in the project directory we can create the nuget specification file directly
    4. nuget spec
    5. # Package the project. Note that $projectFile is a FileInfo object so we need to get the name property for the command
    6. nuget pack $projectFile.Name

Getting around Assumption 1: Company and Description info are in the assembly information

The following code treats the nuspec file created by ‘nuget spec’ as an XML Document. If you need help with opening the file as XML Document, please refer to the full code at the end of the article.

  1. Get the contents of the AssemblyInfo.cs
    1. $assemblyInfo = (Get-Content Properties\AssemblyInfo.cs)
  2. Next retrieve the AssemblyCompany info from the file
    1. # Using regular expression we get the line containing the information required
    2. $author = ($assemblyInfo -match 'AssemblyCompany\(".*"\)')
    3. # Next we need to obtain the information within the double quotes. This can be done through regex but for this example we use the split command.
    4. $author = $author -split ('"')
    5. # The split command returns an array of 3 items, zero-indexed. The first entry is the AssemblyCompany text and the second entry in the array is the actual information in the double quotes.
    6. $author = $author[1]
  3. Now check the $author variable for any contents. If it is an empty string then change the Author element from the nuget token to a fixed string.
    1. If ($author -eq "") {
    2.   $authorNode = $nuspecXml.SelectSingleNode("//authors")
    3.   $authorNode.InnerText = "Author"
    4.  
    5.   $ownersNode = $nuspecXml.SelectSingleNode("//owners")
    6.   $ownersNode.InnerText = "Owner"
    7. }

    Note: The nuspec XML has two entries, one is owners and the other is authors. These normally point to the same nuspec token $authors. However they can be set to separate values if required.

  4. The same three steps can be performed for the Description, and any other element that needs to be verified for empty strings. The code below shows the instructions for the Description field.
    1. $description = ($assemblyInfo -match 'AssemblyDescription\(".*"\)')
    2. $description = $description -split ('"')
    3. $description = $description[1]
    4.  
    5. If ($description -eq "") {
    6.     $descriptionNode = $nuspecXml.SelectSingleNode("//description")
    7.     $descriptionNode.InnerText = "My Assembly Description"
    8. }

Getting around Assumption 2: All files are the result of project build

Adding files to the nuget specification files in the nuspec file is done by creating new XML elements in the file. The below steps add only 1 folder to the package but the code can be repeated multiple times to add as many folders or files as required.

  1. Create the <files> XML node which will contain the references to the folders and files outside the project build that are to be added to the nuget package
    1. $filesNode = $nuspecXml.CreateNode("element", "files", "")
  2. The <files> XML node is a list of <file> nodes, so the new <file> node needs to be created
    1. $fileNode = $nuspecXml.CreateNode("element", "file", "")
  3. Looking at the nuget documentation the <file> XML element needs two attributes the src and target attributes. These are created outside the <file> node and then attached in step 4.
    For the purpose of this article it is assumed that the original files are stored in the location nonProjectFiles. Note the location is relative to the nuspec file

    1. $fileSrcAttribute = $nuspecXml.CreateAttribute(“src")
    2. $fileSrcAttribute.Value = "..\nonProjectFiles\**\*"
    3. $fileTargetAttribute = $nuspecXml.CreateAttribute("target")
    4. $fileTargetAttribute.Value = "content\additionalFiles\$assemblyName"
  4. Now the attributes are bound to the <file> node
    1. $fileNode.SetAttributeNode($fileSrcAttribute)
    2. $fileNode.SetAttributeNode($fileTargetAttribute)
  5. The final step is to append the file node to the files node, and the files node to the nuspec file.
    1. $filesNode.AppendChild($fileNode)
    2. $nuspecXml.LastChild.AppendChild($filesNode)

    Note: To add more files or folders to the <files> tag, one has to add more child nodes to the $filesNode variable.

So putting this altogether the script look like

  1. get-childitem . -Path *.csproj | select -limit 1 | % {
  2.   nuget spec -f  # The -f is to force file recreation
  3.   $assemblyName = [System.IO.Path]::GetFileNameWithoutExtension($_.Name)
  4.   $nuspec = "$assemblyName.nuspec"
  5.   # Open nuspec file as an XML Document
  6.   [xml]$nuspecXml = (Get-Content $nuspec)
  7.  
  8.   # Assumption 1 workaround
  9.   $author = ($assemblyInfo -match 'AssemblyCompany\(".*"\)')
  10.   $author = $author -split ('"')
  11.   $author = $author[1]
  12.  
  13.   If ($author -eq "") {
  14.     $authorNode = $nuspecXml.SelectSingleNode("//authors")
  15.     $authorNode.InnerText = "Authors"
  16.  
  17.     $ownersNode = $nuspecXml.SelectSingleNode("//owners")
  18.     $ownersNode.InnerText = "Owners"
  19.   }
  20.  
  21.   $description = ($assemblyInfo -match 'AssemblyDescription\(".*"\)')
  22.   $description = $description -split ('"')
  23.   $description = $description[1]
  24.  
  25.   If ($description -eq "") {
  26.     $descriptionNode = $nuspecXml.SelectSingleNode("//description")
  27.     $descriptionNode.InnerText = "My Module Description"
  28.   }
  29.  
  30.   # Assumption 2 workaround
  31.   if (Test-Path ..\nonProjectFiles) {
  32.     $filesNode = $nuspecXml.CreateNode("element", "files", "")
  33.     $fileNode = $nuspecXml.CreateNode("element", "file", "")
  34.     $fileSrcAttribute = $nuspecXml.CreateAttribute("src")
  35.     $fileSrcAttribute.Value = "..\nonProjectFiles\**\*"
  36.     $fileNode.SetAttributeNode($fileSrcAttribute)
  37.     $fileTargetAttribute = $nuspecXml.CreateAttribute("target")
  38.     $fileTargetAttribute.Value = "content\additionalFiles\$assemblyName"
  39.     $fileNode.SetAttributeNode($fileTargetAttribute)
  40.     $filesNode.AppendChild($fileNode)
  41.     $nuspecXml.LastChild.AppendChild($filesNode)
  42.   }
  43.  
  44.   $nuspecXml.Save($_.Directory.FullName + "\" + $nuspec)
  45.  
  46.   # Create the nuget package
  47.   nuget pack $_.Name
  48. }

 

Note: Code can be downloaded from GitHub: https://github.com/kdemanuele/Nuget-Powershell

 

References