Scenario
We have TeamCity 8.1.3 building every pull request. Build failures are reported in GitHub. This is great. However view errors are not picked up. This is bad. I could turn on MvcBuildViews across the board but I'd rather not as our solution is quite large and it roughly triples compile time.
What I'd like to do is enable MvcBuildViews only if a view has been changed in a commit in the PR. For example if someone changes a .cs file then compile as normal. If a .cshtml file is changed enable MvcBuildViews and compile.
What I've tried
My first attempt used VCS triggers. I set up two almost identical projects in TeamCity. The only difference was the VCS triggers. One build was designed to build code changes and the other view changes.
Code change trigger rules: -:\**.cshtml
and +:**.cs
View change trigger rules: +:**.cshtml
This didn't work as I'd hoped. Committing a .cs file and a .cshtml file on the same branch would trigger both builds.
My second attempt was using a PowerShell build step. I wondered if PowerShell could be used to read the teamcity.build.changedFiles.file agent build property, determine whether a cshtml file had been changed and if so set MvcBuildViews to true.
This failed because I couldn't figure out out to read the agent properties. I found this relevant SO question, but it didn't work.
My PS build step looks like this. I'm mostly clutching at straws here.
write-host "##teamcity[message text='Starting PhilTest build step']"
write-host "##teamcity[message text='Build number $env:build_number']" #Outputs build number
write-host "##teamcity[message text='Changed files $env:teamcity_build_changedFiles_file']" #Outputs nothing
foreach ($row in $env:teamcity_build_changedFiles_file)
{
write-host "##teamcity[message text='Changed files row $row']" #Outputs nothing
}
write-host "##teamcity[message text='Ending PhilTest build step']"
What next?
Has anyone done this before? Does anyone know how I can get one of my previous attempts to work or know of another way to do it?
The teamcity.build.changedFiles.file has no environment variable as you can see on this doc: Predefined Build Parameters.
In this case you can use %system.teamcity.build.changedFiles.file%. The TeamCity will pass to your PowerShell script the full path to a file with information about changed files included in the build.
The file contains new-line separated files: each line corresponds to one file and has the following format:
<relative file path>:<change type>:<revision>
Using giacomelli's answer as a starting point I've created this TeamCity PowerShell build step which does exactly what I want. It reads in the list of changed files, determines whether a view has been changed and if so sets MvcBuildViews to true in all csproj files. Please be aware:
Less importantly when using this I've noticed that TeamCity thinks the build is over time. Not sure if anything can be done about it.
$changedFileInfoPath = '%system.teamcity.build.changedFiles.file%'
$fileData = Get-Content $changedFileInfoPath
$containsViews = $false
foreach($line in $fileData)
{
write-host "##teamcity[message text='File contents = $line']"
if($line -like "*.cshtml*")
{
$containsViews = $true
break
}
}
if ($containsViews)
{
write-host "##teamcity[message text='View changes found']"
function xmlPoke($file, $xpath, $value)
{
$filePath = $file.FullName
[xml] $fileXml = Get-Content $filePath
$node = $fileXml.SelectSingleNode($xpath)
if ($node)
{
$node.InnerText = $value
$fileXml.Save($filePath)
}
}
$workingDirectory = '%teamcity.build.workingDir%'
$webCsProjFiles = Get-ChildItem -Path $workingDirectory -Recurse -Include "*.csproj"
foreach ($csProjFile in $webCsProjFiles)
{
xmlPoke $csProjFile "//*[local-name()='MvcBuildViews']" "true"
write-host "##teamcity[message text='Set MvcBuildViews true in $csProjFile']"
}
}
else
{
write-host "##teamcity[message text='No view changes were found']"
}
Update 22/10/2014
I've written a slightly more advanced version of this script here.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With