Reputation: 185
I am using Azure DevOps pipelines to build and deploy my applications. So far, I have only worked with single-projects that open in Visual Studio Code. Now I am trying to create builds for older .NET Framework Visual Studio solutions. The way we have always organized our Visual Studio Solutions is having a .Core project (referenced by each of the other application projects in the solution), and then one project per application (i.e., a Web app, one or more Console apps, and possibly some Windows Services). When I use VSBuild@1 to build my solution, it seems to build all projects in the solution, but it only seems to publish artifacts for the Web app project. Does anyone know the best practice for dealing with multi-application solutions such as mine in YAML pipelines? Basically, I want the build to generate an artifact for each application.
Upvotes: 1
Views: 1806
Reputation: 185
I ended up using separate MsBuild steps to build each project. Below is the complete pipeline I have so far, which will generate an artifact for my Web project, as well as an artifact for my ConsoleApp project. Any feedback as to any other issues is greatly appreciated. Note that I use a custom build number and I did not clean up the variables and/or their usage.
#-------------------------------------------------#
# PIPELINE IS TRIGGERED FOR ANY BRANCH #
# THIS IS THE DEFAULT IF NO TRIGGER IS SPECIFIED, #
# BUT I WANT TO MAKE IT VERY CLEAR. #
#-------------------------------------------------#
trigger:
batch: true # we only want to run this pipeline one at a time in order
branches:
include:
- '*'
name: 'Set dynamically below in a task'
#--------------------------------------------------------------------#
# THIS ALLOWS US TO DETERMINE IF IT IS MASTER BRANCH OR A DEV BRANCH #
#--------------------------------------------------------------------#
variables:
isMaster: $[eq(variables['Build.SourceBranchName'], 'master')] # runtime
expression
version.MajorMinor: '1.0' # Manually adjust the version number as needed for semantic versioning. Patch is auto-incremented.
version.Patch: $[counter(variables['version.MajorMinor'], 0)] # Reset the patch number every time the MajorMinor changes
versionNumber: '$(version.MajorMinor).$(version.Patch)'
solution: '**/*.sln'
buildPlatform: 'Any CPU'
buildConfiguration: 'Release'
buildPathFilter: '**\!(*.pdb|*.xml|*.manifest|*.host|*.vshost.exe)'
pool:
vmImage: 'windows-2019' # Can't use newer version because solution still targets .NET Framework 4.6.1 which is deprecated
stages:
- stage: BUILD
jobs:
#----------------#
# SET BUILD NAME #
#----------------#
- job: Set_Build_Name
steps:
- task: PowerShell@2
displayName: Set Build Name
inputs:
targetType: 'inline'
script: |
[string] $buildName = "$(versionNumber)_$(Build.SourceBranchName)"
Write-Host "Setting the name of the build to '$buildName'."
Write-Host "##vso[build.updatebuildnumber]$buildName"
#-------------------------------#
# BUILD: Build & Run Unit Tests #
#-------------------------------#
- job: Build_and_Run_Unit_Tests
dependsOn: Set_Build_Name
steps:
- task: NuGetToolInstaller@1
displayName: Install NuGet
- task: NuGetCommand@2
displayName: Restore NuGet Packages
inputs:
restoreSolution: '**/*.sln'
- task: MSBuild@1
displayName: Build Unit Tests
inputs:
solution: '**/MySolution.UnitTests/MySolution.UnitTests.csproj'
msbuildVersion: '16.0'
platform: 'AnyCPU'
configuration: 'Release'
clean: true
- task: VSTest@2
displayName: Run Unit Tests
inputs:
platform: '$(buildPlatform)'
configuration: '$(buildConfiguration)'
#------------#
# BUILD: Web #
#------------#
- job: Build_Web
dependsOn: Build_and_Run_Unit_Tests
steps:
- task: NuGetToolInstaller@1
displayName: Install NuGet
- task: NuGetCommand@2
displayName: Restore NuGet Packages
inputs:
restoreSolution: '**/*.sln'
- task: MSBuild@1
displayName: Build Project
inputs:
solution: '**/MySolution.Web/MySolution.Web.csproj'
msbuildVersion: '16.0'
platform: 'AnyCPU'
configuration: 'Release'
msbuildArguments: '/p:DeployOnBuild=true /p:WebPublishMethod=Package /p:PackageAsSingleFile=true /p:SkipInvalidConfigurations=true /p:PackageLocation="$(build.artifactStagingDirectory)"'
clean: true
- task: PublishBuildArtifacts@1
displayName: Publish Artifact
continueOnError: false
inputs:
ArtifactName: 'MySolution.Web-$(Build.BuildNumber)'
TargetPath: '$(Build.ArtifactStagingDirectory)'
#------------------------------#
# BUILD: MySolution.ConsoleApp #
#------------------------------#
- job: Build_Console_App
dependsOn: Build_and_Run_Unit_Tests
steps:
- task: NuGetToolInstaller@1
displayName: Install NuGet
- task: NuGetCommand@2
displayName: Restore NuGet Packages
inputs:
restoreSolution: '**/*.sln'
- task: MSBuild@1
displayName: Build Project
inputs:
solution: '**/MySolution.ConsoleApp/MySolution.ConsoleApp.csproj'
msbuildVersion: '16.0'
platform: 'AnyCPU'
configuration: 'Release'
clean: true
- task: CopyFiles@2
displayName: Copy to Artifact Staging Dir
inputs:
SourceFolder: '$(Build.SourcesDirectory)\$(Build.Repository.Name)\MySolution.ConsoleApp\bin\$(BuildConfiguration)'
Contents: '$(buildPathFilter)'
TargetFolder: '$(Build.ArtifactStagingDirectory)\MySolution.ConsoleApp'
CleanTargetFolder: true
OverWrite: true
flattenFolders: false
- task: PublishBuildArtifacts@1
displayName: Publish Artifact
continueOnError: false
inputs:
ArtifactName: 'MySolution.ConsoleApp-$(Build.BuildNumber)'
TargetPath: '$(Build.ArtifactStagingDirectory)'
Upvotes: 0
Reputation: 1646
Julie explains a some what similar situation on her blog:
https://julie.io/writing/monorepo-pipelines-in-azure-devops/
The repos I have come across with multi project solutions and artifacts always have multiple yamls in them.
Furthermore publishing and using these artifacts are more a architectural choice with several options found here.
After reading your comment a manual solution could be copying the builds from every referenced project (in the example only one), but you are looking for a more automated way I guess:
variables:
BuildPlatform: 'Any CPU'
BuildConfiguration: Release
SolutionFile: app-name.sln
BuildPathFilter: '**\!(*.pdb|*.xml|*.manifest|*.host|*.vshost.exe)'
BuildPath: app.name\bin\$(BuildPlatform)\$(BuildConfiguration)
otherbuildpath: referenced.project\bin\$(BuildPlatform)\$(BuildConfiguration)
System.Debug: false
trigger:
branches:
include:
- master
steps:
- task: VSBuild@1
displayName: 'Build solution $(SolutionFile)'
inputs:
solution: '$(Build.Repository.Name)\$(SolutionFile)'
vsVersion: latest
platform: '$(BuildPlatform)'
configuration: '$(BuildConfiguration)'
clean: true
condition: succeededOrFailed()
- task: CopyFiles@2
displayName: 'Copy Files from buildpath to: $(Build.ArtifactStagingDirectory)'
inputs:
SourceFolder: '$(Build.SourcesDirectory)\$(Build.Repository.Name)\$(BuildPath)'
Contents: '$(BuildPathFilter)'
TargetFolder: '$(Build.ArtifactStagingDirectory)'
CleanTargetFolder: true
OverWrite: true
flattenFolders: false
- task: CopyFiles@2
displayName: 'Copy Files from otherProjectBuildpath to: $(Build.ArtifactStagingDirectory)'
inputs:
SourceFolder: '$(Build.SourcesDirectory)\$(Build.Repository.Name)\$(otherBuildPath)'
Contents: '$(BuildPathFilter)'
TargetFolder: '$(Build.ArtifactStagingDirectory)'
CleanTargetFolder: true
OverWrite: true
flattenFolders: false
- task: PublishBuildArtifacts@1
displayName: 'Publish Artifact: $(Build.BuildNumber)'
inputs:
ArtifactName: '$(Build.BuildNumber)'
Upvotes: 1