All posts by lloydholman

Executing psake build scripts from TeamCity

I came across an issue with a few of our TeamCity build configurations the other day; We had a situation where TeamCity was reporting ‘Success’ instead of ‘Failure’, lucky we spotted this early.

Executing psake build scripts from TeamCity

Post2-TeamCityIncorrectBuildStatus

Post2-TeamCityIncorrectExitCode

As can be seen above the build is failing with the following zero exit code.

[code language=”bash”]
`default.ps1:Error executing command: msbuild $sln_file /t:Build /p:Configuration=Release /v:q [17:15:08]: Process exited with code 0`
[/code]

I had a sniff around and came to the conclusion the problem was in the way we were calling our psake build script from within a batch file. After a bit of googling I found this is a common stumbling block, there were some rather confusing and over the top approaches but I finally found this very useful article, this post builds on this approach and adds a bit more detail.

So we identified we were doing the following in our `build.bat`:

[code language=”powershell”]powershell.exe -command "& {.build.ps1 Package}"[/code]

This is potentially wrong due to the following reasons:

  1. PowerShell by default continues running scripts on errors
  2. The executing batch file `build.bat` was not trapping the error status returned from the psake PowerShell script `default.ps1`.
  3. We weren’t importing the psake PowerShell module and therefore the $psake hashtable variables weren’t getting set.

I didn’t need to do anything for point 1 as we had however already implemented the exec {} helper function which forces psake to throw an error.

To resolve point 2 I referred to Dusty’s article , split it out a bit more and implemented the following in the original batch file `build.bat` and new build.ps1 script respectively

build.bat
[code language=”bash”]@echo off
powershell.exe -NoProfile -ExecutionPolicy unrestricted -command ".build.ps1 %1;exit $LASTEXITCODE"
[/code]

[code language=”bash”]
if %ERRORLEVEL% == 0 goto OK
echo ##teamcity[buildStatus status=’FAILURE’ text='{build.status.text} in compilation’]
exit /B %ERRORLEVEL%

:OK
[/code]

build.ps1

[code language=”powershell”]
param([string]$task = "compile")
Import-Module ‘.libpsakepsake.psm1′;
Invoke-psake -t $task;
if ($Error -ne ”)
{
Write-Host "ERROR: $error" -fore RED;
exit $error.Count
}
Remove-Module psake -ea ‘SilentlyContinue';
[/code]

The above do the following:

  • `build.bat` wraps `build.ps1` and executes it, passing the first parameter passed to the batch file in, i.e. the psake task.
  • `build.ps1` then starts by importing the psake PowerShell module (we have this in a relative path in source control).
  • `build.ps1` then invokes the default psake build script in the current working folder i.e. `default.ps1`, passing the first parameter passed to the batch file in as the psake task. `Default.ps1` obviously does all the build work for us.
  • the if block then simply checks the Powershell $error array and returns a count of the errors to `build.bat`
  • `build.bat` echo’s out a nice TeamCity buildStatus which then shows up in the TeamCity gui.
  • `build.bat` fails with the ERRORLEVEL returned by `build.ps1`.
  • finally the psake PowerShell module is removed.

Point 3 was resolved above by explicitly importing the psake PowerShell module. I also tapped the following into a PowerShell command line.

[code language=”powershell”]Get-Help Invoke-psake –full[/code]

Which gives, amongst other detail, the following Output definition.

[code language=”bash”]…..
OUTPUTS
If there is an exception and ‘$psake.use_exit_on_error’ -eq $true
then runs exit(1) to set the DOS lastexitcode variable
otherwise set the ‘$psake.build_success variable’ to $true or $false depend
ing
on whether an exception was thrown

NOTES

When the psake module is loaded a variabled called $psake is created it
is a hashtable
containing some variables that can be used to configure psake:

$psake.use_exit_on_error = $false # determines if psake uses the "exi
t()" function when an exception occurs
$psake.log_error = $false # determines if the exception detai
ls are written to a file
$psake.build_success = $false # indicates that the current build
was successful
$psake.version = "4.00" # contains the current version of p
sake
$psake.build_script_file = $null # contains a System.IO.FileInfo for
the current build file
$psake.framework_version = "" # contains the framework version #
for the current build

$psake.use_exit_on_error and $psake.log_error are boolean variables tha
t can be set before you call Invoke-Psake.

You should see the following when you display the contents of the $psak
e variable right after importing psake

PS projects:psake> Import-Module .psake.psm1
PS projects:psake> $psake

Name Value
—- —–
version 4.00
build_script_file
use_exit_on_error False
build_success False
log_error False
framework_version
…..
[/code]

I therefore modified the `build.ps1` psake script above set the $psake.use_exit_on_error variable to true and forced it to always use the 64 bit version of the .NET 3.5 framework:

[code language=”powershell”]param([string]$task = "compile")
Import-Module ‘.libpsakepsake.psm1′;
#$psake
$psake.use_exit_on_error = $true
Invoke-psake -t $task -framework ‘3.5×64′;
if ($Error -ne ”)
{
Write-Host "ERROR: $error" -fore RED;
exit $error.Count
}
#$psake
Remove-Module psake -ea ‘SilentlyContinue';
[/code]

Executing the batch file on the command line within my development environment was now returning the correct results. Therefore I purposefully checked in a dodgy change into our Source control system (TFS) and watched a TeamCity build kick off……

The correct error code and the buildStatus from `build.bat` displaying nicely.

 Post2-TeamCityCorrectBuildStatus

Post2-TeamCityCorrectExitCode

….much to my delight the build failed with the correct error code this time

[code language=”bash”]
[13:47:47]: ControllersAccountController.cs(73,62): error CS1002: ; expected
[13:47:48]: default.ps1:Error executing command: & msbuild $sln_file /t:Build /p:Configuration=Release /v:q
[13:47:48]: ##teamcity[buildStatus status=’FAILURE’ text='{build.status.text} in compilation’]
[13:47:48]: Process exited with code 1
[/code]

No more fibbing build status’ for us.

AppHarbor supports lib and src style folder structures

I’m currently using AppHarbor on a small freelance piece of work and I must say I’m seriously impressed, all the usual things are making me happy, the simplicity of pushing code from a Git repothe execution of tests and pushing of code live (i.e. continuous integration in the cloud) and not to mention the support for SQL Server and MySQL.

This is hopefully the first of a few posts about my recent experiences  with www.appharbor.com, as their tagline says “Azure done right”.

I’m currently using AppHarbor on a small freelance piece of work and I must say I’m seriously impressed, all the usual things are making me happy, the simplicity of pushing code from a Git repothe execution of tests and pushing of code live (i.e. continuous integration in the cloud) and not to mention the support for SQL Server and MySQL.

However the thing that impressed me recently was the way it all “just works”, after starting with a basic “Hello World” app I decided to restructure my source control tree and start pushing some more serious code up.  I changed from a simple single solution folder to some thing more akin to how I like to structure my projects:

Post1-AppHarborFolderStructure

As you can see I have nice ‘lib’ and ‘src’ folders, now the AppHarbor getting started guide states that you need to create your Git repository in the ‘pathtomyapplication’.  As you can see above this isn’t the case in my example above, my repo is created right up in the ‘~DevelopmentAYH.CHP’ folder, the .sln file for the solution is down in ‘DevelopmentAYH.CHPtrunksrc’.  I was unsure whether this was going to work, but pressed on and ran:

[code language=”bash”]$ git push appharbor master[/code]

Much to my delight AppHarbor didn’t sniff, it simply enumerated my repo and found the one and only .sln file and built it, see the latest successful build here:

Post1-AppHarborBuild

So don’t be scared of trying something different, the guys at www.appharbor.com seem to have most things covered.