Invoking Pester Tests on commit with client-side git hooks

So I’m sitting here at PowerShell.asia and thought I’d best blog a cool nugget from the day, lifted from Ravikanth Chaganti’s session on “Infrastructure as Code with Desired State Configuration (DSC)”

Using git local hooks, you can have poor man’s CI on your PowerShell scripts. What do I mean? Well, let’s imagine your powershell script has Pester tests rolled up with it in your repo. And let’s imagine you commit some bad code without having fired Invoke-Pester.

Manual steps like that are a muda – a way of introducing waste via defects and a way of introducing wasted work by extra meaningless typing. We hate manual steps here in DevOps land.

Now, those of us with the luxury of CI or CD pipelines can integrate our Pester tests there. Indeed at Domain, we have a box that runs tests on behalf of Octopus Deploy, and we have the option of using TeamCity or Bamboo to run Pester tests. But lots of people don’t have the luxury of spare environments and perhaps don’t need the complexity.

Ravi’s recommendation was to use git client-side hooks to automatically trigger pester tests on your local machine. Which is great. So I had a quick look.

Turns out there is a gotcha in there. It’s not sufficient to just drop in a post-commit.ps1 and hope for that to run. git won’t run .ps1 files by default. Being a bit linux-centric, it expects a bash script, or perhaps perl or python in an executable script, with no file extension.

The trick is to use bash to fire posh.

I found the solution over here. Take that bash script, put it into <repository>\.git\hooks with filename “post-commit”. Change it slightly so it points to your <repository>\.git\hooks\post-commit.ps1 script, and you’re pretty much done.

I conigured it up, changed a readme line and committed.

Pester fired up. Yay!

Pester failed. Booo!

Turns out, I had a step which checks that all exported functions in my module have a valid “SYNOPSIS” in their Get-Help text. And I’d spelled “Synopsis” wrong. Twice.

Fixed that, and I was up and flying.

Incidentally, the Pester script that checks for Documentation looks a little like this, as a bonus:

        It "Has Documentation on every exported Function" {
            $valid = $true
            $exportedCommands = (gmo Kraken).ExportedCommands
            $exportedCommands.GetEnumerator() | % {
                $functionName = $_.Key
                $help = Get-Help $functionName 
                if($help.synopsis -match $functionName)
                {
                    Write-Host $_.Key "has no valid help" 
                    # help has been generated, not written
                    $valid = $false
                }
            }
            $valid | Should Be $true
        }

HOWEVER if you want to use a pre-commit hook, and abort a commit if your tests fail, this method will not work because of a bug and because of the way Pester works by default.

First of all, to get Pester to return a non-zero status on failure, you need to add the -EnableExit parameter. This basically causes Pester to exit with an integer equal to the number of failed tests – zero for a good run, 1 or greater for a bad one.

Adding that is not enough. You need to invoke powershell.exe with a -command, not a -file, because of the bug I mentioned above.

Then, you need a shell script file that looks like this

#!/bin/sh
echo 
powershell.exe -NoProfile -ExecutionPolicy Bypass -Command "Write-Host "Invoking Pester" -fore DarkYellow; Invoke-Pester -EnableExit;"
exit $?

This makes a complex pre-commit command a little trickier to write, but no massive biggie. But it certainly aborts a commit if your tests fail – and THAT will make your repo cleaner and meaner immediately.

Leave a Reply

Your email address will not be published. Required fields are marked *