2
votes

I have a .NET MVC solution in Visual Studio 2019 that contains 3 projects:

  1. AdminWebApp
  2. SharedCode (which is set as a dependency in both of the other projects in VS)
  3. FrontWebApp

In Azure DevOps Pipelines I want to create separate builds for

AdminWebApp

and

FrontWebApp

which will both contain the

SharedCode

because it contains helpers etc. I want to do it with the YAML way. Should I create 1 or 2 pipelines (each artifact will be later published to its own Azure App Service)? What is the YAML code to achieve it?

2
Not getting your latest information, feel free to let me know if you're still blocked by this issue. And please check answers below when you have the chance. Just a reminder of this~LoLance

2 Answers

2
votes

It would really matter on how the management and release cycle looks like. The puristic way would be to redeploy everything every-time. The realistic way would be to group deployment pipelines together when it make sense.

In terms of what to do in the "YAML way". Would look at using YAML templates

The template parameter would at least by the directory of the project to build. This is an example of a .net Core template but will give you an idea of the thought process: For example purposes this YAML file would be called something like build-corewebapp.yml

parameters:
  SolutionPath: ''
  BuildConfiguration: 'Release'
  projectName: ''
  DependsOn: []
  publish: 'false'

jobs:
- job: Build_${{ parameters.projectName }}
  dependsOn: ${{ parameters.DependsOn }}
  steps:
  - task: DotNetCoreCLI@2
    displayName: 'dotnet restore'
    inputs:
      command: 'restore'
      projects: '$(Build.SourcesDirectory)/${{ parameters.SolutionPath }}/${{ parameters.projectName }}**/*.csproj'

  - task: DotNetCoreCLI@2
    displayName: 'dotnet build'
    inputs:
      projects: '$(Build.SourcesDirectory)/${{ parameters.SolutionPath }}/${{ parameters.projectName }}**/*.csproj'
      arguments: '--configuration ${{ parameters.BuildConfiguration }}'

  - task: DotNetCoreCLI@2
    displayName: 'dotnet test'
    inputs:
      command: test
      projects: '$(Build.SourcesDirectory)/${{ parameters.SolutionPath }}/${{ parameters.projectName }}.Tests/*.csproj'
      arguments: '--configuration ${{ parameters.BuildConfiguration }} --collect "Code coverage" '
      
- job: Publish_${{ parameters.projectName }}
  dependsOn: Build_${{ parameters.projectName }}
  condition: and(succeeded(),eq( ${{ parameters.publish }}, 'true')) 
  steps:
  - task: DotNetCoreCLI@2
    displayName: 'dotnet publish'
    inputs:
      command: publish
      publishWebProjects: false
      projects: '$(Build.SourcesDirectory)/${{ parameters.SolutionPath }}/${{ parameters.projectName }}**/*.csproj'
      arguments: '--configuration ${{ parameters.BuildConfiguration }} --output $(build.artifactstagingdirectory)'
      zipAfterPublish: True
  - task: PublishBuildArtifacts@1
    displayName: 'Publish Artifact: drop'

A template would be called by something similar to:

  jobs:
  - template: build-corewebapp.yml
    parameters:
      projectName: ${{ variables.appProjectName }}
      solutionPath: $(solutionPath)
      publish: 'true'

For max re usability I would recommend any kind of build template to exist in a separate repository so it can be used by other repos. This would be setup in your pipeline by referencing the repo similar to:

resources:
  repositories:
  - repository: repositoryTemplate
    type: git
    name: ProjectName/YAMLTEMPLATERepoName

The pro to using templates is then updating a task version or changing a build/deployment strategy can be updated and referenced in one place.

1
votes

You can use conditions to make separate builds, so that you can put all build steps in one pipeline. Here's similar topic.

Simple sample for your build steps:

steps:
- task: PowerShell@2
  inputs:
    targetType: 'inline'
    script: |
      $files=$(git diff HEAD HEAD~ --name-only)
      $temp=$files -split ' '
      $count=$temp.Length
      For ($i=0; $i -lt $temp.Length; $i++)
      {
        $name=$temp[$i]
        echo "this is $name file"
        if ($name -like "AdminWebApp/*")
          {
            Write-Host "##vso[task.setvariable variable=RunAdminWebApp]True"
          }
        if ($name -like "SharedCode/*")
          {
            Write-Host "##vso[task.setvariable variable=RunSharedCode]True"
          }
        if ($name -like "FrontWebApp/*")
          {
            Write-Host "##vso[task.setvariable variable=RunFrontWebApp]True"
          }
      }
- task: MSBuild@1
  inputs:
    solution: '**/AdminWebApp.csproj'
    msbuildArguments: 'xxx'
  condition: or(variables['RunAdminWebApp'], variables['RunSharedCode'])
- task: MSBuild@1
  inputs:
    solution: '**/FrontWebApp.csproj'
    msbuildArguments: 'xxx'
  condition: or(variables['RunFrontWebApp'], variables['RunSharedCode'])

- task: PublishBuildArtifacts@1
  inputs:
    PathtoPublish: '$(Build.ArtifactStagingDirectory)'
    ArtifactName: 'drop'
    publishLocation: 'Container'
  • If any file in your AdminWebApp project changes, then only build the AdminWebApp and SharedCode project.(First build task)

  • If any file in your FrontWebApp project changes, then only build the FrontWebApp and SharedCode project.(Second build task)

  • And if the file in SharedCode changes, since the two projects depend on it, both two build tasks will run.

You should specify the msbuild arguments(/t:publish...) so that the build task can generate zip package to deploy. (Otherwise you need to add additional task to zip the output files)

Since you'll get two published zip files once the SharedCode project has changes. Then your release pipeline should at least have two deploy tasks. For you release: One PS task(determine whether the A.zip/B.zip exists and then set custom variables DeployA/DeployB) and two conditional Deploy tasks based of the value of DeployA/DeployB.(Just a suggestion,it's not about your original issue, so I won't talk much about it here...)