Skip to content
mnaoumov.dev
Go back

git server-side hooks - maintenance

Hi folks.

Server-side hooks that I wrote previously have one huge shortcoming - Maintenance

How do I modify server-side hooks at the moment?

I edit the hook on a client-side, then commit and push. After that I connect to a git server via Remote Desktop, and manually update hooks in the corresponding folder.

This is lame. This is a manual process, which causes its own complexity.

Ideally server-side hook should just reuse the hooks being pushed.

But usually git server repo is bare so it will require some special kind of checkout. Which I’ve just blogged :)

The following code assumes that hooks are located under Tools/GitHooks folder in git repo

git-server-repo\hooks\pre-receive

#!/bin/sh
#
while read oldRef newRef refName
do
    psCommand=".\\hooks\\HookProxy.ps1 -HookName pre-receive -Arguments \"-OldRef\", \"$oldRef\", \"-NewRef\", \"$newRef\", \"-RefName\", \"$refName\""
    c:/windows/system32/WindowsPowerShell/v1.0/PowerShell.exe -Sta -ExecutionPolicy RemoteSigned -NoProfile -Command "$psCommand"

    exitCode=$?

    if [ $exitCode != 0 ]
    then
        exit $exitCode
    fi
done

git-server-repo\hooks\HookProxy.ps1

#requires -version 2.0

[CmdletBinding()]
param
(
    [string] $HookName,
    [object[]] $Arguments
)

$script:ErrorActionPreference = "Stop"
Set-StrictMode -Version Latest
function PSScriptRoot { $MyInvocation.ScriptName | Split-Path }

Trap { throw $_ }

function Main
{
    $tempDir = [Guid]::NewGuid()
    New-Item -Path $tempDir -ItemType Directory | Out-Null

    try
    {
        Git-CheckoutFile -Branch master -RelativeFilePath Tools\GitHooks -DestinationFolder $tempDir

        Invoke-Expression "$tempDir\GitHooks\$HookName.ps1 $Arguments"
    }
    finally
    {
        Remove-Item -Path $tempDir -Recurse -Force
    }

}

function Git-CheckoutFile
{
    param
    (
        [string] $Branch,
        [string] $RelativeFilePath,
        [string] $DestinationFolder
    )

    if ($RelativeFilePath -eq ".")
    {
        $RelativeFilePath = "*"
    }

    $RelativeFilePath = $RelativeFilePath -replace "\\", "/"

    $tempDir = [Guid]::NewGuid()
    New-Item -Path $tempDir -ItemType Directory | Out-Null

    git archive $Branch $RelativeFilePath --output "$tempDir\__temp.tar"
    tar -xf "$tempDir/__temp.tar" -C $tempDir
    Remove-Item "$tempDir/__temp.tar"

    if (-not (Test-Path $DestinationFolder))
    {
        New-Item -Path $DestinationFolder -ItemType Directory | Out-Null
    }

    Copy-Item -Path "$tempDir/$RelativeFilePath" -Destination $DestinationFolder -Recurse

    Remove-Item -Path $tempDir -Recurse -Force
}

Main

Absolutely the same approach could be used for other server-side hooks such as post-receive

And this works as expected

The idea is very simple. We write a proxy (HookProxy.ps1) which will extract corresponding hook from a repo and execute it. So HookProxy.ps1 is the only piece of code should be deployed once to the server, and later on all hooks modifications are made via source control (master branch).

P.S. I’ve just realized that this can be useful for client-side hooks as well. It will get rid of the need to keep hooks updated in all possible branches.


Share this post on:

Previous Post
git checkout on bare repo
Next Post
Get TeamCity build status from PowerShell