Wednesday, May 27, 2015

Handling Errors from Non-cmdlets in PowerShell

Lately I have found myself implementing multiple builds with TFS that basically just call PowerShell scripts. One of the issues that I have with these scripts is getting good error reporting back to TFS.

Specifically, the issue comes down to the fact that non-cmdlet commandline tools generally return error codes and are incapable of throwing PowerShell errors when they fail. To get the commandline tools working like cmdlets, I took a page out of James Kovacs book (blog?) and implemented a Exec function:

function Exec([scriptblock]$cmd) { 
    $result = & $cmd 
    if ($LastExitCode -ne 0) {
        throw $result
    } 
    else {
        foreach ($line in $result) {
            Write-Host $line
        }
    }
}

Which you can simply call with:

Exec { sometool.exe "with parameters" }    

In the case of an error, it will throw an exception. Otherwise, the output will be written to standard output.

In the context of TFS, I put together a nice sequence that will fail the build if an exception is thrown.

First, I include the Exec function. You can find it on github gist. This one is slightly modified to output the last 50 lines of output when an error occurs.

Next, wrap all of your Exec calls in a try/catch like so:

try {    
    Exec { sometool.exe "with parameters" }    
} catch {
    Write-Error $_
    Exit 1
}

Now presumably, you will be calling your PowerShell script with an InvokeProcess activity from your Build Process Template. In the scope that the activity will be run, create a variable called ExitCode of type Int32:

Adding an exit code

Now set the Result field of the InvokeProcess activity to the new variable:

Setting the exit code

This will set the return code of your PowerShell build script into the variable you created.

Now add a SetBuildProperties activity after the InvokeProcess activity. In the properties of the SetBuildProperties activity, in the PropertiesToSet field check off Status. Then set the Status field to

If(ExitCode = 0, BuildStatus.Succeeded, BuildStatus.Failed)

Setting the build property

Now when your script throws an error, it will return an exit code of 1 and that will be caught by TFS which will fail the build. I acknowledge that it is ironic to convert the tool’s return code to an exception and then back to a return code, but that is what is necessary to have it flow through to TFS correctly.

No comments:

Post a Comment