Azure Pipelines NuGet Monorepo

This post shows how to structure a monorepo for NuGet packages and then automate their build using Azure YAML pipelines.

Project Structure

Use a package’s solution file to define the location of the packages source code and test code. This single solution file can then be passed to the DotNetCoreCLI@2 task to only build and pack that particular package.

Solution files are Common.A.sln and Common.B.sln.

Project file structure for two packages common.A and common.B

/common-nuget-library
   /common
     /common.A
       azure-pipelines.yml
       common.A.sln
       common.A.csproj
       /src
     /common.B  
       azure-pipelines.yml
       common.B.sln
       common.B.csproj
       /src
   /tests  
     common.A.tests
       common.A.tests.csproj
     common.B.tests
       common.B.tests.csproj

Configuring the pipeline for each NuGet package

Next we want to configure a pipeline for each package. The contents of each azure-pipelines.yml are show below.

common.A/azure-pipelines.yml

trigger:
  paths:
    include:
      - common/common.A/*
...
- task: DotNetCoreCLI@2
  displayName: 'DotNet Build'
  inputs:
    command: 'build'
    projects: 'common/common.A/common.A.sln'
- task: DotNetCoreCLI@2
  displayName: 'Dotnet Pack'
  inputs:
    command: 'pack'
    packagesToPack: 'common/common.A/common.A.sln'
    includesymbols: true
    packDirectory: '$(Pipeline.Workspace)/dist'
    configuration: 'Release'
    nobuild: true
    versionEnvVar: 'BUILD_BUILDNUMBER'
    versioningScheme: 'byEnvVar'
    buildProperties: 'SymbolPackageFormat=snupkg'
  • The pipeline is only triggered when changes occur in common/common.A/*
  • The DotNet Build task only builds the packages listed in the solution file ‘common/common.A/common.A.sln’
  • The DotNet pack task only packages ‘common/common.A/common.A.sln’

common.B/azure-pipelines.yml

trigger:
  paths:
    include:
      - common/common.B/*
...
- task: DotNetCoreCLI@2
  displayName: 'DotNet Build'
  inputs:
    command: 'build'
    projects: 'common/common.B/common.B.sln'      
- task: DotNetCoreCLI@2
  displayName: 'Dotnet Pack'
  inputs:
    command: 'pack'
    packagesToPack: 'common/common.B/common.B.sln'
    includesymbols: true
    packDirectory: '$(Pipeline.Workspace)/dist'
    configuration: 'Release'
    nobuild: true
    versionEnvVar: 'BUILD_BUILDNUMBER'
    versioningScheme: 'byEnvVar'
    buildProperties: 'SymbolPackageFormat=snupkg'    
  • The pipeline is only triggered when changes occur in common/common.B/*
  • The DotNet Build task only builds the packages listed in the solution file ‘common/common.B/common.B.sln’
  • The DotNet pack task only packages ‘common/common.B/common.B.sln’

Create a pipeline for each package

Finally you can create a pipeline to build each package by simply selecting ‘New pipeline’ from the Pipelines tab and providing the the azure-pipelines.yml file for that package.