How to Publish NuGet Packages to a Private Feed Using Azure Artifacts

Written by willvelida | Published 2021/07/13
Tech Story Tags: azure | devops | azure-devops | dotnet | csharp | programming | tutorial | nuget-package

TLDR Software Engineer and Microsoft Data Platform MVP based in Auckland, New Zealand based in New Zealand. He is an MVP of Microsoft's Data Platform and is a Microsoft data platform MVP. He also works as a software engineer at Microsoft and New Zealand-based New Zealand company. He has also worked with Microsoft in Australia, Australia and Australia in the past two years as a Microsoft employee of the Data Platform team at the company's Data Lab in New York City, New York, Australia, Singapore and Australia.via the TL;DR App

Introduction

Using Azure Artifacts, we can publish NuGet packages to a private (or public) NuGet feed. These feeds can be scoped in Azure DevOps at either an organization level or at a project level.

Creating a private NuGet feed in Azure DevOps is really simple. This article below shows have you can set one up. If you’re following along and you haven’t set up an internal feed yet, stop reading this article and check out the article here. Once you’re done with that, you can return here.

This post will show you how to use a YAML build file to publish the NuGet packages we create to our own Private feeds in Azure Artifacts.

Now you might have an opinion on YAML in general, and yes, we can achieve the same result via the Classic user interface. Still, I like being able to include our build files within our code repository and providing me with the ability to check the history of our build file using the git history of that YAML file.

To publish our NuGet package to our internal feed, we need to do the following:

  • Create our NuGet package.
  • Decide our versioning strategy for our package.
  • Publish our package to our Internal Feed.

For this post, I will be using a helper library that I’ve been using for my own health application. If you want to look at it while reading this (it includes the YAML file), check it out here.

Creating our NuGet Package

Before jumping into the meat of our build file, We need to set some things up:

trigger:
  - main

pool:
  vmImage: 'ubuntu-latest'

variables:
  ProjectName: 'MyHealth.Common'
  ProjectPath: '**/MyHealth.Common.csproj'
  buildConfiguration: 'Release'

steps:
  - task: UseDotNet@2
    displayName: 'Install .NET Core SDK'
    inputs:
      packageType: 'sdk'
      version: '3.x'
  
  - task: DotNetCoreCLI@2
    displayName: 'dotnet restore'
    inputs:
      command: 'restore'
      projects: '$(ProjectPath)'
  
  - task: DotNetCoreCLI@2
    displayName: 'Build $(ProjectName)'
    inputs:
      command: 'build'
      arguments: '--configuration $(buildConfiguration)'
      projects: '$(ProjectPath)'

Let’s break this down:

  • We trigger the build to kick off every time we make a commit to our main branch.
  • We set some common variables that we will need throughout our build pipeline.
  • We’re using a Linux image as our build agent.
  • We then install the .NET Core SDK on our build agent, restore our project using a DotNetCoreCLI task that runs the restore command, and build the project using another task that runs the build command.

To create a NuGet package in our build file, we need to add a DotNetCoreCLI task like so:

 - task: DotNetCoreCLI@2
    displayName: 'Pack $(ProjectName)'
    inputs:
      command: 'pack'
      arguments: '--configuration $(buildConfiguration)'
      packagesToPack: '$(ProjectPath)'
      nobuild: true
      versioningScheme: 'off'

In this task, we run the pack command and tell the task to pack our project path that I’ve defined earlier in the YAML file. I’ve set the nobuild argument to true since I’ve already built my project.

My package is a .NET Standard package. For .NET Core and .NET Standard packages, Microsoft recommends that you use the DotNetCoreCLI tasks. If you’re building packages for .NET Framework, you can use a NuGet task.

Versioning Strategy

There are a couple of ways we can do this. In my package .csproj file, I’ve specified my Version number like so:

<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <TargetFramework>netstandard2.1</TargetFramework>
    <Description>Common Helper Library for MyHealth Applications</Description>
    <Authors>Will Velida</Authors>
    <Product>MyHealth</Product>
    <Version>1.4.0</Version>
    <GeneratePackageOnBuild>false</GeneratePackageOnBuild>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="Azure.Messaging.ServiceBus" Version="7.1.2" />
    <PackageReference Include="Azure.Storage.Blobs" Version="12.8.1" />
    <PackageReference Include="Microsoft.Azure.Cosmos.Table" Version="1.0.8" />
    <PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
  </ItemGroup>

</Project>

If we wanted to achieve this via a build task defined in YAML, we could do so like this:

variables:
  Major: '1'
  Minor: '0'
  Patch: '0'

steps:
- task: NuGetCommand@2
  inputs:
    command: pack
    versioningScheme: byPrereleaseNumber
    majorVersion: '$(Major)'
    minorVersion: '$(Minor)'
    patchVersion: '$(Patch)'

With the NuGet task, we can use the Major.Minor.Patch semantic versioning scheme for our builds. However, once a version has been produced, we can’t update or replace that version. They are immutable. To produce new versions of our package each time we update them, we can do the following:

Use the $(rev:.r) variable for the version number that we wish to increment. This will automatically increment the build number for that variable each time we push to our branch while keeping the other variables constant.

Use the $(date:yyyyMMdd) variable. This is ideal for creating prelease labels for the build while keeping our major, minor, and patch versions constant.

Publishing our package to the feed

Now that we have a package, we can publish it in our feed. To publish to our Azure Artifacts feed, we’ll need to set the Project Collection Build Service identity to be a Contributor on the feed.

Once we have enabled that, we can add the following YAML to our build pipeline:

- task: NuGetAuthenticate@0

 - task: NuGetCommand@2
    displayName: 'Publish $(ProjectName)'
    inputs:
      command: push
      feedsToUse: 'select'
      packagesToPush: '$(Build.ArtifactStagingDirectory)/**/*.nupkg;!$(Build.ArtifactStagingDirectory)/**/*.symbols.nupkg'
      nuGetFeedType: 'internal'
      publishVstsFeed: '<projectname>/<feedname>'
      versioningScheme: 'off'
      allowPackageConflicts: true

Here, we are using the NuGetAuthenticate command to authenticate our Build server to push packages to our internal NuGet feed. We then run a NuGetCommand task with the push command to push our package (stored in our Artifact Staging Directory) to our internal NuGet feed.

Here, we have used <projectname>/<feedname> as our VSTS feed to publish to. Remember, we can scope our feeds at either the project level or an organization level in Azure DevOps. Since my feed is scoped at the project level, I need to put the project name here.

I’ve also included the nuGetFeedType argument and stated that our target field is an internal feed. Since we are using the push command in the NuGet command task, this argument is required.

Full YAML file

Here’s our complete YAML file:

trigger:
  - main

pool:
  vmImage: 'ubuntu-latest'

variables:
  ProjectName: 'MyHealth.Common'
  ProjectPath: '**/MyHealth.Common.csproj'
  buildConfiguration: 'Release'

steps:
  - task: UseDotNet@2
    displayName: 'Install .NET Core SDK'
    inputs:
      packageType: 'sdk'
      version: '3.x'
  
  - task: DotNetCoreCLI@2
    displayName: 'dotnet restore'
    inputs:
      command: 'restore'
      projects: '$(ProjectPath)'
  
  - task: DotNetCoreCLI@2
    displayName: 'Build $(ProjectName)'
    inputs:
      command: 'build'
      arguments: '--configuration $(buildConfiguration)'
      projects: '$(ProjectPath)'

  - task: DotNetCoreCLI@2
    displayName: 'Pack $(ProjectName)'
    inputs:
      command: 'pack'
      arguments: '--configuration $(buildConfiguration)'
      packagesToPack: '$(ProjectPath)'
      nobuild: true
      versioningScheme: 'off'

  - task: NuGetAuthenticate@0

  - task: NuGetCommand@2
    displayName: 'Publish $(ProjectName)'
    inputs:
      command: push
      feedsToUse: 'select'
      packagesToPush: '$(Build.ArtifactStagingDirectory)/**/*.nupkg;!$(Build.ArtifactStagingDirectory)/**/*.symbols.nupkg'
      nuGetFeedType: 'internal'
      publishVstsFeed: 'projectname/feedname'
      versioningScheme: 'off'
      allowPackageConflicts: true

Conclusion

Hopefully, you can see how simple it is to publish our NuGet packages to a private NuGet feed in Azure Artifacts. This isn’t just limited to private NuGet feeds. We can also publish packages from DevOps to NuGet.org.

If you want to learn more about Azure Artifacts, I recommend that you check out the documentation that covers topics such as configuring feeds, publish NuGet (and other) types of packages, and more.

If you have any questions about this article, please let me know in the comments, or you can reach out to me on Twitter.


Previously published on dev.to.


Written by willvelida | Customer Engineer at Microsoft based in Auckland, New Zealand
Published by HackerNoon on 2021/07/13