How to push NuGet packages to Azure Artifacts from Bitbucket Pipelines
After the recent MyGet outage, some of their users are going to be migrating to other private feed services. Major git repo hosting providers, such as GitHub, GitLab, or Azure DevOps have built-in support for NuGet private feeds, unfortunately it is not the case for Bitbucket Pipelines. So developers who use Bitbucket Pipelines for their CI/CD will need to utilize 3rd party services to store NuGet packages privately.
This article demonstrates how to use Azure Artifacts private feeds from Bitbucket Pipelines. We explore how we can authenticate, push and pull packages from the Pipelines, as well as locally.
Packaging .NET code to a NuGet package
We can generate a NuGet package from .NET code either by enabling Produce a package file during build operations under Project properties > Package > General or by running the dotnet pack tool.
The second option gives us more control so we are going to use it. An example command that packs SecretCodeLibrary.csproj
from the SecretCodeLibrary
folder in the Release
configuration and generates a NuGet package in the current folder with 1.0.42
version:
dotnet pack SecretCodeLibrary/SecretCodeLibrary.csproj -o . --configuration Release -p:Version=1.0.42
The result of the command - file with .nupkg
extension:
Creating a private feed in Azure Artifacts
Now it’s time to create a private feed in Azure Aritfacts so that we can push and pull our newly created NuGet package.
Go to your Azure DevOps, select a project and navigate to Artifacts in the main menu:
You should be able to click Create Feed button, and after providing a descriptive name, your new Azure Artifacts private feed is created.
In this article I created a project scoped feed, but feel free to use organization scoped feed if you want to broader access for your teams.
Connecting to the private feed from your local environment
Before accessing Azure Artifacts from your local environment we need to make sure the necessary tools are installed.
We need .NET Core SDK 2.1.400+, which I assume you already have installed since you’ve built your package. But just in case, here is the link. Get the latest 6.0.x since according to the documentation, the credential provider needs 6.0 SDK.
Install the credential provider. For Windows, the only thing needed is this command executed from powershell:
iex "& { $(irm https://aka.ms/install-artifacts-credprovider.ps1) }"
Once we have the tools installed, let’s follow the instruction we have in the “Connect to feed” screen:
Let’s add the nuget.config
we have in the first block to the root folder with our .sln
file. My file looks like this, but yours will differ a little bit:
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<packageSources>
<clear />
<add key="PrivateArtifactsFeed" value="https://mobile-kingdom.pkgs.visualstudio.com/ArtifactsDemo/_packaging/PrivateArtifactsFeed/nuget/v3/index.json" />
</packageSources>
</configuration>
Let’s try to push our package with the following command (--api-key
is required, but the actual parameter is not used, can be any random string):
dotnet nuget push --source "PrivateArtifactsFeed" --api-key az SecretCodeLibrary.1.0.42.nupkg
And we get this result:
As you can see, the tool was able to silently authenticate using the Microsoft Authentication Library (MSAL):
[CredentialProvider]VstsCredentialProvider - Acquired bearer token using 'MSAL Silent'
Since my local Windows user is the same one I used to create the feed, the authentication process happened automatically.
Let’s go back to the Azure DevOps portal and see if we can locate the new package. And indeed, we can:
Consuming private packages from Visual Studio
So far we were able to create the private feed and access it locally from the command line. To finish our local environment setup we need to make sure we can work with Azure Artifacts from Visual Studio:
Visual Studio automatically picks up nuget.config
file we added earlier, so there’s no additional configuration, since, again, we authenticate with the current Windows user.
You may have noticed that there’s only one feed in Visual Studio NuGet Package Manager. It happens because in our nuget.config
we have <clear />
line, which clears all global feeds. Our private feed will use so-called upstream sources to fetch and cache the packages for public NuGet feed:
A single feed for a solution is considered a best practice, read more here.
Pushing and pulling private NuGet packages in Bitbucket Pipelines
Bitbucket Pipelines is an external source, so we are going to authenticate using a Personal Access Tokent or PAT.
Go to User Settings > Personal access tokens in Azure DevOps and generate a new one with Read & Write packaging permissions:
Create a new Repository Variable in Bitbucket’s Repository Settings called ARTIFACTS_FEED_ACCESS_TOKEN
with the value you obtained in the previous step:
Do not store your PAT in your source code. PAT is a sensitive secret and storing secrets in your code is a bad practice. That’s why we have just used Repository Variables to separate it from our codebase.
Now we can finally use this variable for our feed authentication inside our pipeline. Let’s add the following step which programmatically replaces the nuget source from our local nuget.config
with the same one, but the new source includes our PAT as the password (again, we are doing it only during the pipeline build, so the PAT never gets to the repo’s source code):
- step:
name: "Push Nuget to Azure Artifacts"
script:
- dotnet nuget remove source PrivateArtifactsFeed
- dotnet nuget add source https://mobile-kingdom.pkgs.visualstudio.com/ArtifactsDemo/_packaging/PrivateArtifactsFeed/nuget/v3/index.json --name PrivateArtifactsFeed --store-password-in-clear-text --username <YOUR_EMAIL> --password $ARTIFACTS_FEED_ACCESS_TOKEN
- dotnet pack SecretCodeLibrary/SecretCodeLibrary.csproj -o . --configuration Release -p:Version=1.0.$BITBUCKET_BUILD_NUMBER
- dotnet nuget push --source "PrivateArtifactsFeed" --api-key az SecretCodeLibrary.1.0.$BITBUCKET_BUILD_NUMBER.nupkg
You will need to change the feed URL with your own and provide a correct user name. Also, notice how we used $ARTIFACTS_FEED_ACCESS_TOKEN
and $BITBUCKET_BUILD_NUMBER
variables.
With this step added, you will be able to create a new NuGet package version and push it to your private Azure Artifacts feed each time the pipeline is run.