Sunday, September 13, 2009

Build automation with psake

I finally got a chance to take a proper look at psake, a build tool using PowerShell for the build scripts. Below is a psake script I did for a propject. It can compile the code by using MSBuild, clean up binaries, run NUnit tests, measure test coverage, and show the coverage results in the NCover gui. To do so a few paths are setup in the propoerties section, and it is assumed that tests reside in projects with names that end with Test:
properties {
$base_dir = Resolve-Path .
$testAssemblies = (Get-ChildItem "$base_dir" -Recurse -Include *Test.dll -Name | Select-String "bin")
$buildartifacts_dir = (Get-ChildItem "$base_dir\src\" -Recurse -Include bin)
$src_dir = "$base_dir\src"
$tools_dir = "$base_dir\tools"
$nunit_dir = "$tools_dir\NUnit-2.5.2.9222\bin\net-2.0"
$ncover_dir = "$tools_dir\ncover"
$db_script_dir = "$src_dir\Database"

$env:Path += ";$ncover_dir;$nunit_dir"

$test_result_file_postfix=".test-result.xml"
}

task default -depends Compile

task Compile {
exec msbuild "$src_dir\DummyNameToProtectTheInnocent.sln"
}

task Test -depends Compile {
foreach($test_asm_name in $testAssemblies) {
$file_name = [System.IO.Path]::GetFileName($test_asm_name.ToString())
exec nunit-console.exe $test_asm_name /nodots
}
}

task Coverage -depends Compile {
$old_dir = pwd

foreach($test_asm_name in $testAssemblies) {
$file_name = [System.IO.Path]::GetFileName($test_asm_name.ToString())
$working_dir = [System.IO.Path]::GetDirectoryName($test_asm_name.ToString())
$out_file_name = "$base_dir\$file_name".ToString().Replace(".dll", "") + ".coverage.xml"

cd $working_dir
NCover.Console.exe nunit-console.exe $file_name /nodots //x $out_file_name
cd $old_dir
}
}

task Coverage_Gui -depends Coverage {
foreach($xml in (Get-ChildItem . -Name -Include *.coverage.xml)) {
tools\ncoverexplorer\NCoverExplorer.exe $xml
}
}


task SetupDB {
."$base_dir\execute-sqlfile.ps1"

Execute-SqlFile "$db_script_dir\RestoreBaseLineDB.sql" ".\SQLExpress" "master"
Execute-SqlFile "$db_script_dir\SQL_CreateLoginAndPermissions.sql" ".\SQLExpress" "ConnectPortal"
Execute-SqlFile "$db_script_dir\AddCompanyIDsToModul.sql" ".\SQLExpress" "ConnectPortal"
Execute-SqlFile "$db_script_dir\UpdateOrder.sql" ".\SQLExpress" "ConnectPortal"
}

task Clean {
remove-item -force -recurse $buildartifacts_dir -ErrorAction SilentlyContinue
}

I like the idea of using PowerShell for automation in projects targeting Windows for two reasons: One, its a real programming language (as opposed to xml), two, more and more things on Windows boxes can be set up and controlled through PowerShell. I want a real language for my build automation becuase it tends to become complicated over time, so sooner or later it I need the power of a programming language. And if the project only targets Windows, then PowerShell is good choice because its simply the Windows command line, and things like IIS, Exchange, SQL Server and so on provide PowerShell interfacfes.