To build .NET Framework applications with Concourse, the .NET framework and build tools must first be installed and configured on a Concourse Windows worker. Refer to the Concourse documentation for running a worker node for stand alone workers, and Concourse for PCF for bosh managed workers.
The Concourse pipeline and build script can be reused to build and publish .NET applications to PCF. Feel free to use it as reference for your own custom build script, or use it as-is.
The sequence of events is:
CI headless builds for .NET applications are based on msbuild.exe. We are providing a reusable build script sample that:
To use this script as-is in consistent manner the Visual Studio solution must adhere to the following requirements:
Test
configuration - builds projects required for unit testingRelease
configuration - build projects that will be deployed to PCF.nuget
directory with nuget.exe and nuget.config pointing to repository for package installsThe full sample pipeline and build script are downloadable, but let’s go into more detail below.
Unit testing tasks running on windows invokes build.ps1 in test
mode. This Concourse job does nothing more
than invoke the build script’s test target.
jobs:
- name: unit-test
public: true
plan:
- get: code
trigger: true
- task: build-and-test
config:
platform: windows
run:
path: powershell
args:
- ./build.ps1
- -mode test -targetFramework 4.6.1
dir: code
inputs:
- name: code
The build script’s test mode handles the majority of the work, first building the solution via msbuild.exe using the specified framework version passed in from the Concourse task.
function Build-Solution($configuration) {
$code = -1
. {
Configure-Publish
$frameworkVer, $frameworkPath = Get-TargetFramework
$frameworkParam = ""
if($frameworkVer -ne $DefaultFramework) {
$frameworkParam = "/p`:FrameworkPathOverride=`"$frameworkPath`""
}
$app = "$MsBuildApp /m /v:normal /p`:Platform=$Architecture /p`:Configuration=$configuration /nr:false $publish $tools $frameworkParam"
Write-Host "Running the build script: $app" -ForegroundColor Green
Invoke-Expression "$app" | Write-Host
$code = $LastExitCode
} | Out-Null
if($code -ne 0) {
Write-Host "Build FAILED." -ForegroundColor Red
}
else{
Write-Host "Build SUCCESS." -ForegroundColor Green
}
$code
}
Then the script handles installing the xunit console runner and then executing xunit against each found test project. A test project is any found DLL file name that ends with the word Tests
.
function main {
Write-Banner
$buildConfig = if ($(Get-Mode) -eq 'test') {'Test'} Else {'Release'}
$buildResult = Build-Solution $buildConfig
if($buildResult -ne 0) {
Write-Host "Build failed, aborting..." -ForegroundColor Red
Exit $buildResult
}
if($(Get-Mode) -eq 'test') {
Write-Host "Starting unit test execution" -ForegroundColor Green
NuGet-Install 'xunit.runner.console' $XUnitVersion
$failedUnitTests = 0
#Get the matching test assemblies, ensure only bin and the target architecture are selected
$testfiles = Get-ChildItem . -recurse | where {$_.BaseName.EndsWith("Tests") -and $_.Extension -eq ".dll" `
-and $_.FullName -match "\\bin\\" -and $_.FullName -match "$Architecture" }
#Execute unit tests in all assemblies, continue even in case of error, as it provides more context
foreach($UnitTestDll in $testfiles) {
Write-Host "Found Test: $($UnitTestDll.FullName)" -ForegroundColor Yellow
Invoke-Expression "$XUnitApp $($UnitTestDll.FullName)"
if( $LastExitCode -ne 0){
$failedUnitTests++
Write-Host "One or more tests in assembly FAILED" -ForegroundColor Red
}
else
{
Write-Host "All tests in assembly passed" -ForegroundColor Green
}
}
if($failedUnitTests -ne 0) {
Write-Host "Unit testing for $(Get-Mode) configuration FAILED." -ForegroundColor Red
} else {
Write-Host "Unit testing for $(Get-Mode) configuration completed successfully." -ForegroundColor Green
}
Exit $failedUnitTests
}
}
The Concourse publish job and tasks delegate the majority of the work to the build script, however this time running it in publish mode.
- name: publish
public: true
plan:
- get: pipeline
trigger: true
- get: code
passed:
- unit-test
trigger: true
- task: build-and-publish
config:
platform: windows
run:
path: powershell
args:
- ./build.ps1
- -mode publish
dir: code
inputs:
- name: code
outputs:
- name: publish
- put: pcf
params:
manifest: pipeline/dev/manifest.yml
path: publish/{{publish_profile_directory}}
NOTE - Using cf-resource might be a better way to solve this, however it’s unclear if Windows workers are supported.
Powershell script to set publish profile which is invoked by msbuild.exe
function Configure-Publish {
. {
<#
Always download this package and set up the override for the
VSToolsPath as there may be dependencies in the MSBuild file on a
specific version on VisualStudio
#>
Write-Host "Configuring publish support." -ForegroundColor Green
NuGet-Install 'MSBuild.Microsoft.VisualStudio.Web.targets' $PublisherVersion
$script:tools = "/p`:VSToolsPath=$PublisherPath"
if($(Get-Mode) -eq 'publish') {
$pre = '/p:DeployOnBuild=true`;PublishProfile='
$script:publish = "$pre$PublishProfile"
Write-Host $publish -ForegroundColor Yellow
}
Write-Host $tools -ForegroundColor Yellow
Write-Host "Publish support configured." -ForegroundColor Green
} | Out-Null
}