GitLab – push webhook (czyli o statycznym serwowaniu plików z repozytorium)

W pracy była potrzeba na postawienie serwera gita wraz z webui. Wybór gitlaba jest chyba w miarę oczywisty. O samej instalacji nie ma co się rozpisywać, ponieważ dokumentacja robi to wystarczająco dobrze i wystarczy ją przeczytać uważnie i wykonać podane polecenia, używając od czasu do czasu odrobiny mózgu.

Ciekawszą sprawą do opisania są webhooki. Jest to odwołanie się gitlaba do podanego adresu www po akcji na repozytorium. Interesującą opcją dla mnie jest „push event”. Do czego? Wystawiania aktualnej wersji repozytorium na statyczny serwer. Tylko… co dalej? Gitlab po otrzymaniu pusha odwołuje się do adresu url podanego w konfiguracji repozytorium Settings -> Web Hooks wysyłając POSTem jsona. Jak on wygląda? Przykład dostępny

tutaj. Które fragmenty są dla mnie interesujące? Repository->url oraz ref.

<?php
gnore_user_abort(true);            //don't want users screwing up the processing of the script by stopping it mid-process 
$root = "/var/www/html/static/";        // set root folder for static content 
$git  = "/home/git/repositories/";      // set git folder 
// I'm sure there are other ways of handling the system call, this is just the method I've chosen // setup the function to make the system call where $cwd is the "Current Working Directory" 
function syscall ($cmd, $cwd) {
    $descriptorspec = array( 1 => array('pipe', 'w') ); // stdout is a pipe that the child will write to
    $resource = proc_open($cmd, $descriptorspec, $pipes, $cwd);
    if (is_resource($resource)) {
           $output = stream_get_contents($pipes[1]);
           fclose($pipes[1]);
           proc_close($resource);
           return $output;
    }
}

if( $HTTP_RAW_POST_DATA ){
    // decode json from gitlab
    if( $data = json_decode( $HTTP_RAW_POST_DATA ) ){
        $repo = str_replace("git@git.example.com:", "", $data->repository->url);
        $project_root = $root . $repo;
        $project_git  = $git  . $repo;

        if( ( $branch = array_pop( preg_split("/[\/]+/", $data->ref ) ) ) != "heads" ){
            $branch_root = $project_root . "/" . $branch;

            // make directories if do not exists
            if( ! is_dir( $project_root ) ){
                $cmd = "mkdir -p $project_root";
                $result = syscall( "$cmd", "$root" );
            }

            // check if branch dir exists
            if( is_dir( $branch_root ) ){
                // update branch
                $cmd = "git pull origin $branch";
                $result = syscall("$cmd", "$branch_root");
            } else {
                // clone new branch
                $cmd = "git clone $project_git $branch";
                $result = syscall("$cmd", "$project_root");

                // checkout branch
                $cmd = "git checkout $branch";
                $result = syscall("$cmd", "$branch_root");
            }
        }

    }
}
?>

W katalogu $root tworzony jest katalog dla projektu (user/repo.git). A następnie jest sprawdzane czy istnieje już katalog z danym branchem – jeśli nie – klonujemy repo i checkoutujemy się do potrzebnego brancha, jeśli już jest – dociągane są różnice. I tyle. Czemu o tym napisałem? Ponieważ rozwiązania, które znalazłem w internecie (i na których się wzorowałem link) wymagały kopii skryptu dla każdego repozytorium (i edycji ścieżek w skrypcie) lub edycji konfiguracji do skryptu w celu dodania kolejnego projektu. Tylko po co? U mnie jest odwrotnie co zezwala po ustawieniu webhooka na obsłużenie każdego projektu w repozytorium.

Spostrzegawczy zauważą, że git jest zaciągany bezpośrednio z katalogu, a nie przez ssh. Czemu? Brak zabawy w dodawanie kluczy ssh (Deploy Keys) w GitLabie dla każdego projektu. Po drugie – to i tak ten sam serwer, więc w ten sposób było wygodniej.