2
votes

Given a solution containing both a Web Api project, an Api Client Project, and an assembly project containing shared Dto objects as follows:

  • Solution
    • Web Api Project
    • Shared Dto Project
    • Api Client Project
    • Unit Tests

I have an Azure Devops build pipeline to build and test all of the projects. Within that build pipeline, I have a step that creates 2 Nuget packages for the Api Client and the Shared Dto projects.

However, I don't need to create new packages all of the time, say I only added a unit test or fixed a bug in the Web Api project, I don't want to create new Nuget packages (or worry about bumping the Package version) if nothing in the Api Client Project (or its dependencies) has changed, but I do still want to regression test it.

I thought that perhaps I could do this by having multiple build pipelines, one for the Web Api project, and others for the Api Client & Shared projects, then use path filters to trigger the appropriate builds.

Are there any other ways to achieve this? Whats the best practice? I don't want to have to maintain more build definitions than I need.

2
Just found this, going to see if it works for me: stackoverflow.com/questions/53227343/…philreed
Any update for this issue? Have you resolved this issue? If not, would you please let me know the latest information about this issue?Leo Liu-MSFT
I managed to cobble something together using this answer stackoverflow.com/a/53837840/2064829philreed

2 Answers

2
votes

I ended up using a slight modification of this answer

# Checks which files have been updated to determine if new Nuget packages are required
$editedFiles = git diff HEAD HEAD~ --name-only
echo "$($editedFiles.Length) files modified:"
$editedFiles | ForEach-Object {
   echo $_
    Switch -Wildcard ($_ ) {
        'Whds.Configuration.Models/*' { 
              # If the Models project is updated, we need to create both packages
              Write-Output "##vso[task.setvariable variable=CreateModelsPackage]True"
              Write-Output "##vso[task.setvariable variable=CreateClientPackage]True"  
         }        
        'Whds.Configuration.Standard/*' { Write-Output "##vso[task.setvariable 
variable=CreateClientPackage]True" }
        # The rest of your path filters
    }
}

This script sets variables which are then referenced in custom conditions in the dotnet pack step in the build pipeline:

and(succeeded(), eq(variables['CreateModelsPackage'], 'True'))

If the Dto project is changed, both variables are set in order to create both packages. If only the client (aka Standard) project is the only thing that has changed, the package for the Dto project will not be created.

1
votes

Are there any other ways to achieve this? Whats the best practice? I don't want to have to maintain more build definitions than I need.

There are different ways to achieve it, but not sure which one is the best practice, it all depends on your requirements or tastes.

The simple method is similar to your thought. Also need to create a new build pipeline. The difference is that we do not need to maintain this build definition.

Details:

  • Add a new pipeline without any more task in this pipeline, and use path filters to trigger the appropriate builds (Api Client and the Shared Dto projects).
  • Add a build completion to your original Azure Devops build pipeline: enter image description here
  • Add a custom condition for the step that creates 2 Nuget packages based on the Build.Reason, like:

    and(succeeded(), eq(variables['Build.Reason'], 'BuildCompletion'))
    

Now, the steps to create 2 Nuget packages only executed when the file changes come from a specific project. Of course the limitation of this solution is that if you already have a build completion, it will not work.

If the above method is not what you want, we could invoke the REST API commits to get the commit info for each build:

GET https://dev.azure.com/{organization}/{project}/_apis/git/repositories/{repositoryId}/commits/{commitId}?changeCount=100&api-version=5.1

We could find the changes/path in the returned body:

  "changes": [
    {
      "item": {
        "gitObjectType": "blob",
        "path": "/.gitattributes",
        "url": "https://dev.azure.com/fabrikam/_apis/git/repositories/278d5cd2-584d-4b63-824a-2ba458937249/items/.gitattributes?versionType=Commit"
      },
      "changeType": "add"
    },
    {
      "item": {
        "gitObjectType": "blob",
        "path": "/.gitignore",
        "url": "https://dev.azure.com/fabrikam/_apis/git/repositories/278d5cd2-584d-4b63-824a-2ba458937249/items/.gitignore?versionType=Commit"
      },
      "changeType": "add"
    },
    {
      "item": {
        "gitObjectType": "tree",
        "path": "/MyWebSite",
        "isFolder": true,
        "url": "https://dev.azure.com/fabrikam/_apis/git/repositories/278d5cd2-584d-4b63-824a-2ba458937249/items/MyWebSite?versionType=Commit"
      },
      "changeType": "add"
    },

Then we could use the powershell script to traverse these paths to see if they include Api Client and the Shared Dto projects, if yes, we set a variables with a value, add condition based on this value for the steps that creates 2 Nuget packages.

Note: Before use the REST API commits, we need use the Commits - Get Commits to get the latest commit Id:

GET https://dev.azure.com/{organization}/{project}/_apis/git/repositories/{repositoryId}/commits?$top=1&api-version=5.1

Hope this helps.