We recently switched from using SVN to Git and GitHub. There are many advantages but there’s one in particular I want to talk about.
Using TeamCity you can automatically build all pull requests. This is incredibly useful as it ensures only code that compiles successfully and passes all unit tests gets back into master. If the build fails you are notified in GitHub.
But, there’s a problem. View errors are not reported because MvcBuildViews is off. One solution is to enable MvcBuildViews for all web projects but in our case this is best avoided as it roughly quadruples compile time. The ideal solution is to enable MvcBuildViews only if a view has been changed in the current build. Unfortunately I struggled to work out how to do this. So I asked a question on Stack Overflow and the first answer I received pointed me the right direction.
I’ve managed to improve on my Stack Overflow answer. It simply enables MvcBuildViews for all .csproj files if a view has changed, but this is still quite slow. It’s much faster to only enable the setting for projects in which views have changed. This may be overkill for many solutions but for ours it’s not.
# This is run as a TeamCity build step
# In TeamCity create a PowerShell build step then
# - Set "Script execution mode" to "Execute .ps1 script with -File argument"
# - Set "Script arguments" to "%system.teamcity.build.changedFiles.file% %teamcity.build.workingDir%"
$changedFileInfoPath = $args
$workingDirectory = $args
# Got this function from http://suhinini.blogspot.co.uk/2010/02/using-xmlpeek-and-xmlpoke-in-powershell.html
function xmlPoke($file, $xpath, $value)
$filePath = $file.FullName
[xml] $fileXml = Get-Content $filePath
$node = $fileXml.SelectSingleNode($xpath)
$node.InnerText = $value
####### Step 1 - Reset all csproj files first because TeamCity doesn't do a clean checkout unless you have checked that option https://confluence.jetbrains.com/display/TCD8/Build+Checkout+Directory
$allWebCsProjFiles = Get-ChildItem -Path $workingDirectory -Recurse -Include "*.csproj"
write-host "##teamcity[message text='Reseting all csproject files...']"
foreach ($csProjFile in $allWebCsProjFiles)
xmlPoke $csProjFile "//*[local-name()='MvcBuildViews']" "false"
write-host "##teamcity[message text='Reset MvcBuildViews to false in $csProjFile']"
####### Step 2 - Get information about changed views
$changedFileData = Get-Content $changedFileInfoPath
$csProjFilesToEnableBuildViews = @()
write-host "##teamcity[message text='Looking for view changes...']"
foreach($line in $changedFileData)
if($line -like "*.cshtml*")
# Line format [fileName]:[status]:[commitHash]
# E.g. Home/Index.cshtml:CHANGED:1400ea14c3196abbfd8de3ab6fe0a902be2ccad8
$lineBits = $line -split ":"
$fileNameChanged = $lineBits
write-host "##teamcity[message text='View change found $fileNameChanged']"
$csProjRoot = $fileNameChanged -match "(.*/)Views/.*"
$csProjDirectory = join-path -path $workingDirectory -childpath $matches
$csProjToChange = Get-ChildItem -Path $csProjDirectory -Recurse -Include "*.csproj"
$csProjFilesToEnableBuildViews += $csProjToChange
####### Step 3 - Set MvcBuildViews to true in all changed projects
if ($csProjFilesToEnableBuildViews.Length -gt 0)
$uniqueCsProjFilesToEnableBuildViews = $csProjFilesToEnableBuildViews | select -Unique
write-host "##teamcity[message text='Poking csproj files...']"
foreach ($csProjFile in $uniqueCsProjFilesToEnableBuildViews)
xmlPoke $csProjFile "//*[local-name()='MvcBuildViews']" "true"
write-host "##teamcity[message text='Set MvcBuildViews to true in $csProjFile']"
write-host "##teamcity[message text='No view changes found']"
A few things to bear in mind:
- I’m no PowerShell expert
- You’ll probably what to change the Get-ChildItem command in step one to point directly at your web project .csproj files rather than all .csproj files
- The Write-Host commands must be formatted in this way so they appear in the TeamCity build log
- By default TeamCity does not do a clean checkout of the source code (although this can be enabled). Instead changes are applied incrementally meaning that alterations to .csproj files are not undone on each build. So MvcBuildViews should be reset on every build
- %system.teamcity.build.changedFiles.file% – This file lists all the changes in the current build in the format [fileName]:[status]:[commitHash]. E.g.Home/Index.cshtml:CHANGED:1400ea14c3196abbfd8de3ab6fe0a902be2ccad8
- %teamcity.build.workingDir% – This is the directory that contains all the files to be built