How To Create NuGet Packages

Jamie Taylor
Creating nuspec Files for Your Projects Header ImageSource: https://unsplash.com/photos/HtBlQdxfG9k Copyright 2017: Daniel McCullough (@d_mccullough)

Today’s header image was created by Daniel McCullough at Unsplash

What Is a nuspec File?

Firstly a little about NuGet packages. You’ll have heard of these, I’m sure

you’ve been using them throughout your .NET Core journey so far – all of the System namespaces are NuGet packages

They are the .NET equivalent of NPM packages, in that .NET developers can encapsulate entire libraries into a single package file which can be added to a project to provide access to the methods and classes that the libraries expose.

No doubt you’ll have all heard of NewtonSoft.Json, which is arguably the most famous of NuGet packages.

But how do we encapsulate everything into a NuGet package? And why bother, if we can get the source from GitHub (or similar)?

I’ll answer with another question: Say you’re creating the greatest app in the world, do you really want to have to pull down each library from GitHub, build them, and pull the DLLs into your project? Surely

I’m deadly serious, and don’t call me… you get the joke

that would cause all sorts of headaches – not least of which would be keeping all of the DLLs in sync with their source repos. So why not have the original developers of those libraries do the hard work for you?

Releasing code into a single package, which can be added to a solution and built alongside your code, you take all of the headaches away

not all of them, but the majority anyway

Plus the libraries that you pull down will be built for the architecture that your solution targets – because Visual Studio is ace like that.

Anyway, less about NuGet packages for now. More about nuspec files.

nuspec Files

A nuspec file is an XML file which describes what the contents of a NuGet package should be. It is described in the Microsoft documentation as:

an XML manifest that contains package metadata. This manifest is used both to build the package and to provide information to consumers. The manifest is always included in a package

source: https://docs.microsoft.com/en-us/nuget/reference/nuspec

It is used by the nuget and dotnet pack command line applications to package up a code base into a NuGet package file. NuGet packages can also be generated within Visual Studio, too

there are a bunch of different downloads for NuGet tooling here

My choices are pretty limited, however. This is because I run Ubuntu and Mac OS X on my development machines at home. Specifically why I do this is beside the point, but it’s important to know what tooling is available for which operating systems.

  • If you’re running Windows, then you can use anything.
  • If you’re running a Linux distribution, then you can use either dotnet pack or nuget

assuming that you install Mono first

  • If you’re running Mac OS X, then you can use dotnet pack

whilst it’s possible to run nuget on Mac OS X, I’ve not attempted it

Creating And Consuming A nuspec File

Since a nuspec file is an XML formatted plain text file you can, technically, use any text editor to create one. I prefer to use Visual Studio Code to create and edit them as it’s light weight, has autocomplete, and is very fast. But feel free to use what ever you’d like.

In order to create a nuspec file (assuming you’ve opened your code directory in Visual Studio Code), click on the new file button:

Visual Studio Code New File Button
The new file button is easy to miss

Then give it a useful name. I tend to go with “<the name of the project>.nuspec”, but feel free to follow whatever format you wish:

Newly Created nuspec file

Then it’s a case of looking through the documentation and creating a file with the relevant tags. Simple, right?

… What are the relevant tags? Let me talk you through the nuspec file for my OnionAcrh project.

The nuspec File

The first thing you’ll need to do is make yourself familiar with the layout of my OnionArch project layout in the image above

or you can take a look at the code in the GitHub repo for it

The hierarchy of the project looks like this (from top to bottom):

  • Onion.Web
  • Onion.Service
  • Onion.Repo
  • Onion.Data

The entry point for the application is Onion.Web which has references to each of the other layers. Onion.Service has references to Repo and Data, and Repo has a reference to Data. A graphical representation of the project layout would look like this:

OnionArch Layout

As such, the nuspec file will have to retain that directory layout.

Now that we have an idea of the layout, lets take a look at the nusepc file

because I can tell how excited you are to see it

<?xml version="1.0" encoding="utf-8"?>
<package xmlns="http://schemas.microsoft.com/packaging/2012/06/nuspec.xsd">
<metadata>
<id>OnionArch.Mvc</id>
<title>OnionArch.Mvc</title>
<version>0.0.3</version>
<iconUrl>https://raw.githubusercontent.com/GaProgMan/OnionArch/master/onionArch-nuget-logo.png</iconUrl>
<description>
ASP.NET Core MVC using the onion architecture and feature folders
</description>
<summary>
Creates a new ASP NET Core MVC application using the onion architecture and feature folders
</summary>
<requireLicenseAcceptance>false</requireLicenseAcceptance>
<authors>Jamie Taylor</authors>
<copyright>Copyright © Jamie Taylor 2018</copyright>
<licenseUrl>https://raw.githubusercontent.com/GaProgMan/OnionArch/master/LICENSE</licenseUrl>
<projectUrl>https://github.com/GaProgMan/OnionArch</projectUrl>
<tags>Template, AspNet, Mvc, onion architecture, feature folders</tags>
</metadata>
<files>
<file src="template.config/*" target="src/.template.config" />
<file src="Onion.*/**" exclude="Onion.*/obj/**" target="src" />
<file src="OnionArch.sln" target="src" />
<file src="README.md" target="src" />
<file src=".idea/**" exclude="*" />
<file src=".git/**" exclude="*"/>
<file src="./**" exclude="*.png" />
<file src="./**" exclude="*.svg" />
</files>
</package>

There’s an awful lot going on here, so let’s break it down into logical chunks.

Metadata

The metadata section contains… well, metadata about the NuGet package that we’ll create.

<id>OnionArch.Mvc</id>
<title>OnionArch.Mvc</title>
<version>0.0.3</version>
<iconUrl>https://raw.githubusercontent.com/GaProgMan/OnionArch/master/onionArch-nuget-logo.png</iconUrl>
<description>
ASP.NET Core MVC using the onion architecture and feature folders
</description>
<summary>
Creates a new ASP NET Core MVC application using the onion architecture and feature folders
</summary>
<requireLicenseAcceptance>false</requireLicenseAcceptance>
<authors>Jamie Taylor</authors>
<copyright>Copyright © Jamie Taylor 2018</copyright>
<licenseUrl>https://raw.githubusercontent.com/GaProgMan/OnionArch/master/LICENSE</licenseUrl>
<projectUrl>https://github.com/GaProgMan/OnionArch</projectUrl>
<tags>Template, AspNet, Mvc, onion architecture, feature folders</tags>
</metadata>

There’s nothing particularly exciting here, but it’s useful to fill this information in (especially the id, version, description, and authors fields as they are required). Otherwise when you upload the package to NuGet, you’ll have to fill the corresponding fields in anyway

as Scott Hanselman said, “I only have so many key strokes in my fingers”

(i.e. “programmers are lazy”)

Potentially the most interesting parts are the tags and url… er, tags:

<iconUrl>https://raw.githubusercontent.com/GaProgMan/OnionArch/master/onionArch-nuget-logo.png</iconUrl>
<description>
ASP.NET Core MVC using the onion architecture and feature folders
</description>
<summary>
Creates a new ASP NET Core MVC application using the onion architecture and feature folders
</summary>
<requireLicenseAcceptance>false</requireLicenseAcceptance>
<authors>Jamie Taylor</authors>
<copyright>Copyright © Jamie Taylor 2018</copyright>
<licenseUrl>https://raw.githubusercontent.com/GaProgMan/OnionArch/master/LICENSE</licenseUrl>
<projectUrl>https://github.com/GaProgMan/OnionArch</projectUrl>
<tags>Template, AspNet, Mvc, onion architecture, feature folders</tags>

The url tags (iconUrl, licenseUrl, projectUrl) are used to link to external files on the NuGet listing page:

NuGet Listing Urls
I’ve tagged them as 1, 2 and 3 (receptively here)

if you want to see it for yourself, you can click here

I couldn’t figure out how to auto populate the Documentation area, via a nuspec file. But we’ll come to filling that in, in a few moments.

Files

This is where the important stuff goes. Without filling in some of the metadata

id, version, description, and authors are required fields

you could still upload the NuGet file – you’d have to fill the relevant fields in manually, though. But without the files section, the rendered NuGet package would be useless.

The first thing to remember here is that we only want to include source code in the nuget package. The reason for this is that the contents of the NuGet files are meant to be built alongside the source of projects which consume them.

So let’s take a look at the files section of the nuspec file:

<files>
<file src="template.config/*" target="src/.template.config" />
<file src="Onion.*/**" exclude="Onion.*/obj/**" target="src" />
<file src="OnionArch.sln" target="src" />
<file src="README.md" target="src" />
<file src=".idea/**" exclude="*" />
<file src=".git/**" exclude="*"/>
<file src="./**" exclude="*.png" />
<file src="./**" exclude="*.svg" />
</files>

Remember that I asked you to make sure that you had the layout of the OnionArch project in your mind? Here’s why.

Including Files

The first four lines are used to add files to the NuGet package.:

<files>
<file src="template.config/*" target="src/.template.config" />
<file src="Onion.*/**" exclude="Onion.*/obj/**" target="src" />
<file src="OnionArch.sln" target="src" />
<file src="README.md" target="src" />
<file src=".idea/**" exclude="*" />
<file src=".git/**" exclude="*"/>
<file src="./**" exclude="*.png" />
<file src="./**" exclude="*.svg" />
</files>

I’ve highlighted them, here

Each line is a directive telling nuget or dotnet pack to grab any file which matches the pattern we supply in the src attribute and store it in a path (within the rendered NuGet package) which we supply in the target path.

For example, the following line:

<file src="Onion.*/**" exclude="Onion.*/obj/**" target="src" />

tells nuget or dotnet pack to look for any files within a path which matches on the string “Onion.*” and add them to a directory within the NuGet file called “src”

we’ll cover the exclude attribute in a moment

OnionWeb Directories

In the above image, I’ve opened the rendered NuGet package

which is just a zip file, by the way

and navigated to the /src/Onion.Web directory (on the right) to show you that it contains the same files that the Onion.Web directory on disk does (on the left). That is, with the exception of the bin and obj directories.

Excluding Files

The next four lines of the nuspec tell nuget or dotnet pack which files to exclude:

<files>
<file src="template.config/*" target="src/.template.config" />
<file src="Onion.*/**" exclude="Onion.*/obj/**" target="src" />
<file src="OnionArch.sln" target="src" />
<file src="README.md" target="src" />
<file src=".idea/**" exclude="*" />
<file src=".git/**" exclude="*"/>
<file src="./**" exclude="*.png" />
<file src="./**" exclude="*.svg" />
</files>

It’s a similar idea to the include directives, except that it works slightly differently.

Essentially the “src” attribute works the same way, but the “exclude” tag includes a pattern for files which we want to exclude from that directory. Take this line, for instance:

<file src=".git/**" exclude="*"/>

What this does is ensure that your .git directory

which contains, among other things, the entire history of your repo

is excluded from the NuGet package.

Lack of Git Directory

In the above image, I’ve opened the NuGet package (in the foreground) and shown the source directory (in the background) to show that my rule for excluding all files from the “.git” directory is working.

That’s really all there is to nuspec files.

Excluded, The Refrain

You’ll have noticed that I didn’t cover everything that following line of the nuspec file does, earlier:

<file src="Onion.*/**" exclude="Onion.*/obj/**" target="src" />

That was because I thought it best to talk about excluding files first. Hopefully now that I’ve gone over what the “exclude” attribute does, you should be able to tell what it does.

Don’t fret if you don’t quite get what it’s doing. I’ll explain it now by breaking it down into chunks, one attribute at a time:

  • src="Onion.*/**"

The pattern here says that we should look at all directories in the root which start with “Onion” – which is what the “Onion.*” part does

for those who don’t know, the * character can be thought of as the wildcard character

It then says that we should recursively look at all of the sub-directories of the Onion.* directories.

This pattern ensures that we get all of the files and sub-directories in the following directories:

  • Onion.Web
  • Onion.Service
  • Onion.Repo
  • Onion.Data

It does this for each of the above directories in turn – kind of like a foreach.

  • exclude="Onion.*/obj/**"

The pattern here says that we should ignore any files and sub-directories that we find in any obj sub-directories found in the current Onion.* directory.

This means that we’ll never end up with any of the obj files in our rendered NuGet package.

remember, our NuGet package needs to only contain the resources needed to build our project

  • target="src"

This pattern says that we should take all of the files and sub-directories we found by the “src” pattern, but not any that we found in the “exclude” pattern and store them in a directory called “src” within the rendered NuGet package.

How Do I Render a NuGet Package From a nuspec file?

I’ve alluded to this several times so far: What you need to do is use either the nuget or dotnet pack commands – head back up to the top of this post to see where you can get these commands from

psst… dotnet pack is built into the .NET Core SDK

nuget

Using the nuget command is easy. Assuming that you’re terminal is in the same directory of the nuspec file, simply run the following command:

nuget pack OnionArch.nuspec

This package everything in your source code up, based on the contents of the nuspec file, and produce a file called “OnionArch.Mvc.0.0.3.nupkg”

I’m assuming that you’re running these commands against the OnionArch repo. The file name will be different, based on your repo

dotnet pack

You can also use the .NET Core SDK to package everything up. The following command (when run from the root of the above repo) will do that:

dotnet pack ./Onion.Web/Onion.Web.csproj --configuration Release --no-build --no-restore --output nupkgs /p:NuspecFile=OnionArch.nuspec

The same thing will happen, except that the .NET Core SDK will do all of the hard work for you.

Where I Have Used Them

All this bleating on about nuspec files is all well and good, but where are the real life examples? Well, we just built one.

ClackMiddlware and OwaspHeaders.Core

These two projects are ASP.NET Core Middleware and are packaged and deployed to NuGet automatically, whenever I commit to their master branches. At the time of writing, OnionArch.Mvc is packaged and uploaded to NuGet manually – I plan to make this automatic, but it’s not at the top of my priorities list at the moment.

What are they, you ask?

as you’re about to see, OnionArch.Mvc includes both of these middleware packages via command line switches

OnionArch.Mvc

If you go and take a look at this GitHub repo and this NuGet package, you’ll find the nuspec file we just created. How cool is that?

Oh, I forgot to say. That project is for a ASP.NET Core MVC template that I’ve been working on for a while

you might remember that I mentioned it back in October of 2017

You should totally check it out if you’re at all interested in the templating engine, the onion architecture or domain driven design. It’s incredibly easy to install, too. Since it’s on Nuget, you can do

dotnet new --install OnionArch.Mvc

at the command line

as per these instructions

and the dotnet SDK will take care of the rest: it’ll get the latest version from NuGet and install it for you.

You should see something like the following output:

Installing OnionArch.Mvc 0.0.4.
...
Templates Short Name Language Tags
--------------------------------------------------------------------------------------------------------
Console Application console [C#], F#, VB Common/Console
Class library classlib [C#], F#, VB Common/Library
Unit Test Project mstest [C#], F#, VB Test/MSTest
xUnit Test Project xunit [C#], F#, VB Test/xUnit
Razor Page page [C#] Web/ASP.NET
MVC ViewImports viewimports [C#] Web/ASP.NET
MVC ViewStart viewstart [C#] Web/ASP.NET
ASP.NET Core Empty web [C#], F# Web/Empty
ASP.NET Core Web App (Model-View-Controller) mvc [C#], F# Web/MVC
Onion Architecture MVC onionArch [C#] Web/MVC
ASP.NET Core Web App razor [C#] Web/MVC/Razor Pages
ASP.NET Core with Angular angular [C#] Web/MVC/SPA
ASP.NET Core with React.js react [C#] Web/MVC/SPA
ASP.NET Core with React.js and Redux reactredux [C#] Web/MVC/SPA
ASP.NET Core Web API webapi [C#], F# Web/WebAPI
global.json file globaljson Config
NuGet Config nugetconfig Config
Web Config webconfig Config
Solution File sln Solution

And to see how to use it, issue the following command:

dotnet new onionArch --help

Which should return something like the following:

Onion Architecture MVC (C#)
Author: Jamie Taylor
Options:
-egp|--enable-gnu-pratchett Whether to include and activate middleware which will include the X-GNU-Pratchett header in all requests
bool - Optional
Default: false / (*) true
-esh|--enable-secure-headers Whether to include and activate middleware which will include a range of OWASP suggested security headers
bool - Optional
Default: false / (*) true
Jamie Taylor
A .NET developer specialising in ASP.NET MVC websites and services, with a background in WinForms and Games Development. When not programming using .NET, he is either learning about .NET Core (and usually building something cross platform with it), speaking Japanese to anyone who'll listen, learning about languages, writing for his non-dev blog, or writing for a blog about video games (which he runs with his brother)