Overview
You just finished writing your NuGet package and it’s ready for the world to see, congratulations! Before you think of pushing it to the public feed, it might be a good idea to publish it locally first. I wrote a handy article about this. You can find it here, or a video version here.
The following tutorial shows how to publish your own NuGet package to a public feed using Azure DevOps with automatic versioning based on the build number.
Video
Setup
What I expect you to have at this point is:
- A solution with a class library project which is ready for packing
- A repository in Azure DevOps (or other source control tool) hosting the said solution
To quickly confirm your class library packs properly, you can navigate to the class library folder containing the *.csproj file locally and run nuget pack
command there like on the gif below.
You can also check if your package name (it’ll be the same as your project name) is globally unique in regards to the feed you’ll upload to. We’ll use nuget.org, so just navigate there and search for your package name. If it already exists, you’ll have to update your project name.
I’m using a package I’m currently writing called TestFactory, which is not quite ready for production yet, but I’m happy to push it to the test environment.
You’ll also notice I can view the contents of my NuGet Package. This is possible using NuGet Package Explorer.
If you still don’t have your solution in Azure DevOps, you can do it in a number of different ways, refer to this article for more details.
Create an API Key
When pushing your library, you have to decide where would you like to host it. You can either use a public NuGet org feed, which is the most popular option for public packages or host it in your private/public DevOps feed. If you choose the private one, the package will not be available publicly (it will only be available to people with your Feed API token).
Once we have our repository in Azure and the package ready to publish, we have to connect to our feed of choice, for which we’ll need an API Key. Let’s generate one.
Nuget.org Feed Key
- Navigate to NuGet.org
- Sign up/Sign in
- Click on top-right menu
- Navigate to API keys
- Select Create
- Fill in the name of your key and select global pattern as “*” (ensures access to all packages – we can change this once we push our package and it appears on that list). You can also just input the exact name of your to-be package here, but we’ll change that to it later anyway.
- Click Create
- Click Copy and keep that API key in a safe place (you won’t be able to copy it again)
Connect to a NuGet Feed
- Navigate to your project in DevOps
- In the bottom-left, click Project Settings
- Select Service Connections
- In top-right click New Service Connection
- Select NuGet from the list and press Next
- Select ApiKey as authentication method
- Use https://api.nuget.org/v3/index.json for public package or your private feed URL otherwise as Feed URL
- Paste your API Key from the previous step
- Choose your new connection name (can be anything, but make it meaningful)
- Tick Grant access permission to all pipelines box
- Press Save
Design the Pipeline & Push the Package
Using classic editor
- Go back to your project in DevOps
- Go to Pipelines
- Select New Pipeline
- Select Use the classic editor
- Choose your Source (DevOps in our case), project, repository and branch and press Continue
- Press Start with an empty job on top of the template list
- Select a name for your pipeline. I use the convention {RepoName}-{Environment} (in my case it will be TestFactory-Test)
- Add the following steps (press “+” next to Agent Job 1):
- .NET Core > change the command to build
- .NET Core > change the command to pack
- NuGet > change the command to push (you may want to place that task in your release pipeline and not the build one in the future, but for our purpose that’s ok now)
- Go to Variables tab
- Add a new environment variable, set the key as nupkgVersion and the value as the predefined
$(Build.BuildNumber)
variable - Go to Options tab
- Set your build number with the convention you follow. Mine is Major.Minor.Patch-Prerelease flag (i.e. 2.0.12-beta). For the purpose of auto-incrementing, we’ll use another predefined variables, so use: 1.0.$(Rev:r)-beta (I’ve only added “-beta” suffix as it’s a test pipeline).
- Go back to the Tasks tab
- Select dotnet pack task
- Set the Path to csproj or nuspec file(s) to pack to the class library project to be pushed as a package (the standard would be RepoName/ProjectName.csproj).
- Under Pack options > Automatic Version Packaging, choose Use an environment variable and under the value of the env variable set nupkgVersion. One thing to note is that this option also allows out of the box solution (Use the build number) for build number versioning, but it had trouble adding prerelease flags, so this is the workaround. Refer to my answer on SO here.
- Select NuGet push task
- Set your Path to NuGet package(s) to publish to
$(Build.ArtifactStagingDirectory)/*.nupkg
(this is the default location, if you specifed something different in dotnet pack > Package Folder, you have to change it)
- Set your Path to NuGet package(s) to publish to
- Select your desired destination under Target feed location.
- For nuget.org, select External Nuget Server and choose the Service connection you created in the previous step.
- For private feed, select This organization/collection and select your feed
- Press Save & Queue on the top task bar and wait till it finishes
Using YAML
You can view the generated YAML version by pressing on Agent Job 1 stage and clicking on View YAML. Here’s my version:
pool:
name: Azure Pipelines
steps:
- task: DotNetCoreCLI@2
displayName: 'dotnet build'
- task: DotNetCoreCLI@2
displayName: 'dotnet pack'
inputs:
command: pack
packagesToPack: TestFactory.Lib/TestFactory.Lib.csproj
versioningScheme: byEnvVar
versionEnvVar: nupkgVersion
- task: DotNetCoreCLI@2
displayName: 'dotnet push'
inputs:
command: push
nuGetFeedType: external
publishFeedCredentials: 'Nuget.org svc conn'
enabled: false
- task: NuGetCommand@2
displayName: 'NuGet push'
inputs:
command: push
packagesToPush: '$(Build.ArtifactStagingDirectory)/*.nupkg'
nuGetFeedType: external
publishFeedCredentials: 'Nuget.org svc conn'
Once the pipeline succeeds, you can view your package in nuget.org > Manage Packages > Published Packages. It will be in review for a few minutes and should fairly quickly get listed. Then, you’ll be able to download it via the Manage NuGet Packages menu (right-click on the project in VS), or the Package Manager console.
There should also be some auto-generated documentation on nuget.org/packages/{yourPackageName}
Update Your API Key
You can now navigate to nuget.org > API Keys again and change the global filter from “*” (all packages) to the one you just pushed. Just input the exact name of it and it should match it directly.
Common issues
DotNetCore currently does not support using an encrypted Api Key
This happens when using dotnet nuget push task. The workaround is to use NuGet push task instead.
Using an ApiKey is currently not supported in dotnet because the required libraries for encrypting the key are not available, sorry for the inconvenience. You should be able to use a service endpoint configured with a username/password combination. If you can only use an ApiKey, I would suggest using the nuget 2.* task to push.
https://github.com/microsoft/azure-pipelines-tasks/issues/7160#issuecomment-387805025
The specified API key is invalid, has expired, or does not have permission to access the specified package (403).
This means the API key you provided while creating your Service Connection doesn’t have access to the NuGet package you’re publishing. This can occur if you chose a package name that already exists in the feed. For example, I’ve named my package TestFactory, which already exists in nuget.org. I’ve had to re-create the project with another name, I chose TestFactory.Lib, which worked perfectly.
You also have to make sure that while your package doesn’t exist, you either have the global filter set to “*” in nuget.org > API Keys or to your exact package name (to limit the scope).
Conclusion
In this tutorial, you learned how to push your NuGet package using Azure DevOps Pipelines with auto-versioning based on the build number.