Creating NuGet packages in Azure DevOps with Azure Pipelines and YAML

Dan Cokely
7 min readMay 29, 2019

--

Intro to Azure DevOps

Azure DevOps is Microsoft’s cloud CI/CD platform covering repos, build and release pipelines, work tracking, artifact feeds, testing, and many more features (full docs here). It's a very actively developed platform, and as such, sometimes functionality evolves as you’re using it. A great example is the CI portion of Azure DevOps, aka build pipelines. What initially started as a wysiwyg set of choosable build tasks has quickly moved into a YAML based end-to-end pipeline definition where the pipeline code can live directly in your project repo. Even better, the pre-canned tasks are still available as templates and produce YAML directly into your pipeline file. Currently in preview is the ability to support multi-stage build pipelines; meaning you can build, test, and deploy to lifecycle environments all from one pipeline definition. This article will outline the basics of setting up an Azure Pipeline in YAML for creating a custom NuGet package from a .NET Standard project, and publishing it to a private feed where it can be consumed by other projects. We’ll first go over some basics of NuGet, then start on building our NuGet package.

NuGet

The easiest description is nuget.org’s own: “NuGet is the package manager for .NET”. You would be hard pressed to find a .NET (full or Core) project that doesn't utilize NuGet packages, they are the standard packaging format for the .NET platform. They are zip files with a manifest of metadata, and some set of data, typically dlls, but can have symbols, config files, or anything really (its just not a normal convention), saved as .nupkg files. A great deeper dive on the spec can be found on the MSFT docs site here, or you can get the current xsd from the NuGet repo here. The manifest (the .nuspec file) has basic metadata such as the id, version, authors, and other basic descriptive metadata, as well as any dependencies the package requires. This is an important point: say you have written a great library for your company to use, it has a collection of Azure utilities, and custom JSON handling. Chances are, this project is going to pull in multiple external packages (Newtonsoft, Azure/MSFT libraries etc.). In order for someone to consume your library, they will need all the dependencies your library references. By adding these references as dependencies in your .nuspec file, they can be automatically pulled down anytime someone installs your package. Building and publishing your own NuGet packages for a project can be very advantageous, even if they are only privately available to your organization. Common cross cutting libraries can be standardized and reused across projects, apps, teams, etc. and versioning can be handled in a more manageable way than 20 copies of a dll, or worse yet, a copy and pasted around shared codebase. The good news is that creating and publishing a custom NuGet package using a .Net Core or .Net Standard library and Azure DevOps is fairly straightforward.

Demo

This demo will walk through creating a .NET Standard library, then creating a CI/CD pipeline in Azure DevOps for the project, which will build and publish a NuGet package for consumption by other projects. We’ll be using the YAML style pipeline definition.

Prerequisites

This demo assumes you have an active Azure DevOps organization, if you dont, its free to create one (with some usage caveats) and you can get started here. I’m using VS Community 2019 (16.1.1) for the code development. Working knowledge of git/version control is assumed.

Setup

We are going to start by creating a repo in which the library will reside. In order to make things simple I’ll create a new Azure DevOps project which will automatically get a repo. Log into your Azure DevOps org and from the home page select “Create Project”:

Next fill out some basic project info:

Next either clone the default repo created with the project, or create a new repo under the project (I suggest you don't put spaces in the project name otherwise the default repo will have spaces and the whole %20 thing can get ugly, or create a new repo without spaces), and clone it locally. If you’re working from within Visual Studio you can simply connect to the Azure DevOps instance under the “Team Explorer” tab and select the repo, then create a new project within it:

The new project is going to be a .NET Standard Class Library:

The code is going to be very simple, there will be one class, CommonUtils.cs:

Next we need to edit the csproj file. When we create the NuGet package, we are going to be using the ‘dotnet pack’ command. This is slightly different from the traditional NuGet CLI where you create a nuspec file and run ‘nuget pack’ against the nuspec. The ‘dotnet pack’ command will create a nuspec file from the csproj, and then package the code into a nupkg file. There are a few key pieces of information that will need to be added to the csproj file to ensure it can create the NuGet package correctly. First we need a PackageId, this will be the name of the NuGet package itself. Depending on where you’re going to publish it, this will need to be globally unique as it can't conflict with existing public (nuget.org) packages. Next is the Version, which will be the published package version number. There are entire articles on just how to version packages, but in general a safe bet is to follow semantic versioning (read more here), so for now we’ll statically set the package version to 1.0.1, and if we need to push a new version, we’ll increment the version manually in the csproj file.

The last important part is the YAML pipeline definition, the code of the pipeline below has been commented to give an idea of what's happening at each point. Name the file azure-pipelines.yml and put it in the root of the repo.

This yaml schema can get fairly complicated, for a more complete definition check out here.

At this point the project will look something like the following on disk:

Inside the CommonLib folder is our CommonUtils.cs and CommonLib.csproj files.

There is one last step before pushing our code to the remote and trying to see if we can publish our NuGet package to a feed, and that's the feed itself. We are going to create a feed within Azure DevOps, this way we can keep our packages private to just our Azure DevOps organization and easily integrate with our repo and pipeline.

First log into Azure DevOps and go to the ‘Artifacts’ section. Since we don't have any exiting feeds, we need to create a new one, select ‘New Feed’:

Next you have the option to set the visibility of the packages, as well as the option to have this feed pull through packages from public repos. This is useful because if you have your development and build environments point to this feed, you are able to find any publicly available package as well as your privately published ones.

Finally we can commit and push all our code, for simplicity I added a .gitignore so all the lib and obj and .vs stuff can be filtered out. Notice as soon as you push and go to the Pipeline section of Azure DevOps, you should see a build definition, which should be in the process of being run.

Look for a build in progress

Sometimes the first run after the build YAML has been added doesnt kick off, if you don’t see a build in progress, hit “Queue”

If you click into a build, you can see the steps of the pipeline being run, as well as a tail of the job itself. All steps should complete successfully:

Finally with the build succeeded, our new package should be available in the “Artifacts” tab, under our newly created Feed:

Notice the Dan.CommonLib version 1.0.1 is now available!

Wrap Up

With the package now available, you can point Visual Studio to the feed, and download the newly published package:

--

--