In part 3 of this blog post series we configured our first Azure DevOps CI/CD pipeline.

In this post we will enhance our pipeline to create a NuGet package for our Twitter Sentiment Analysis library then publish this to a private Azure Artifacts feed ready for consumption in our Web Application. You you could push directly to Nuget.org and there is a Task for doing that however I want to show how to create your own private feed which covers more of the Azure Artifacts functionality

If this is the first time you have used Artifacts, you may need to add the ‘Package Management’ extension for your user account. You can do this from ‘Organisation Settings’ found at the bottom left of the main menu, then under ‘Users’ select the user account you wish to enable for managing Artifacts and ensure you have enabled the ‘Package Management’ extension.

Once you have the correct permissions, under the Artifacts section click ‘New Feed’, provide a name for your feed and click create.

Once created, click ‘Connect to feed’ and copy the Package source URL, we will need this to setup authorization for our pipeline to push and pull packages from this feed.

We need to enable authorization for our feed. Under Project Settings create a new Service Connection for type ‘NuGet’

Enter the connection name ‘WholeschoolPackagesAuth’ to match the name provided in our YAML file, paste in the Feed Url we copied from the previous screen and finally enter a randomly generated ApiKey (you could use a Guid Generator if you wish).

Click OK then return to our YAML pipeline in VSCode. We want to add a few more steps to push our package to this new feed

Add the following to the bottom of your file.

    - task: DotNetCoreCLI@2
        displayName: 'dotnet pack'
        inputs:
            command: pack
            nobuild: true
            projects: '$(projectName)/$(projectName).csproj'
            versioningScheme: byPrereleaseNumber
            versionEnvVar: byPrereleaseNumber
            majorVersion: 0
            minorVersion: 1
            patchVersion: 0
        
 - task: DotNetCoreCLI@2
     inputs:
      command: 'push'
      nuGetFeedType: 'internal'
      packagesToPush: '$(build.artifactStagingDirectory)/*.nupkg'
      publishVstsFeed: "WholeschoolPackages"
      publishFeedCredentials: WholeschoolPackagesAuth
           
- task: PublishBuildArtifacts@1

The code above contains two tasks, one for versioning and packaging our library using a PreReleaseNumber versioning scheme and the second task pushes our package to the private NuGet feed we have just setup.

Your file should now look like

# ASP.NET Core
# Build and test ASP.NET Core projects targeting .NET Core.
# Add steps that run tests, create a NuGet package, deploy, and more:
# https://docs.microsoft.com/azure/devops/pipelines/languages/dotnet-core

pool:
  vmImage: 'VS2017-Win2016'

variables:
  buildConfiguration: 'Release'
  projectName: 'TwitterSentiment'

steps:
- script: |
    dotnet build $(projectName)/$(projectName).csproj --configuration $(buildConfiguration)
    dotnet build $(projectName).Tests/$(projectName).Tests.csproj --configuration $(buildConfiguration)
    dotnet test $(projectName).Tests/$(projectName).Tests.csproj --configuration $(buildConfiguration) --logger trx
    dotnet publish $(projectName)/$(projectName).csproj --configuration $(buildConfiguration) --output $BUILD_ARTIFACTSTAGINGDIRECTORY
  displayName: 'Build & Test'
  
- task: PublishTestResults@2
  inputs:
    testRunner: VSTest
    testResultsFiles: '**/*.trx'

- task: DotNetCoreCLI@2
  displayName: 'dotnet pack'
  inputs:
    command: pack
    nobuild: true
    projects: '$(projectName)/$(projectName).csproj'
    versioningScheme: byPrereleaseNumber
    versionEnvVar: byPrereleaseNumber
    majorVersion: 0
    minorVersion: 1
    patchVersion: 0

- task: DotNetCoreCLI@2
  inputs:
    command: 'push'
    nuGetFeedType: 'internal'
    packagesToPush: '$(build.artifactStagingDirectory)/*.nupkg'
    publishVstsFeed: "WholeschoolPackages"
    publishFeedCredentials: WholeschoolPackagesAuth

- task: PublishBuildArtifacts@1

We are using the .NET Core task for configuring some of our steps, it is a bit more declarative than just using scripts and I find this approach easier to read and see explicitly what is happening in each step.

You can read more on configuring .Net Core tasks in the YAML pipeline here :
https://docs.microsoft.com/en-us/azure/devops/pipelines/languages/dotnet-core?view=vsts&tabs=yaml

We can now push our changes, if all has been configured correctly, our pipeline should now generate a NuGet package and push to our private repository.

git add .
git commit -m "Add nuget support"
git push

Return to the Azure DevOps portal and wait for the latest build to complete. If you receive an error at this stage:

Step input publishFeedCredentials references service connection WholeschoolPackagesAuth which could not be found. The service connection does not exist or has not been authorized for use. For authorization details, refer to https://aka.ms/yamlauthz

The easiest way to resolve this appears to be saving the pipeline directly from the portal. From the Build screen, click ‘Edit’ then ‘Edit in the visual designer’, change the name of the pipeline, click Save, then revert the name back to its original name and click ‘Save & Queue’. This appears to activate the Service Connection for the pipeline and allow the build to continue.

Once you get a successful build through, navigate to the Artifacts section and you should now see your package listed.

We have one final step before we finish this particular post, back on our developer machine, we now need to install the Azure Artifacts Credential provider that will allow us to consume our packages from a private repository. Follow the installation steps found here

Once installed we should have everything needed to consume our package into our Web Application which we cover in part 7 of this blog series.

Before we start building out our Web Application however, we want to add one more step to our CI/CD pipeline in our next blog post to run static analysis of our library codebase using SonarCloud