Webhooki wysyłane przez hostingi Gita mogą być używane między innymi do aktualizacji wersji aplikacji na serwerze (deployment). Po określonej akcji – np. wysłanie poprawek do repozytorium – skrypt po stronie hostingu może wykonać zapytanie HTTP do skryptu w celu uruchomienia procedury automatycznej aktualizacji. W tym artykule pokażę jak utworzyć taki skrypt, podpiąć go do repozytorium i zabezpieczyć przed niepowołanymi żądaniami.
Skrypt
Webhook wyzwalany po wysłaniu poprawek musi wykonywać jakieś żądanie, w tym przypadku będzie to nasz skrypt na serwerze. Może on wykonywać dowolną akcję, na przykład uruchomienie skryptu Bash aktualizującego aplikację Laravel (poniższy skrypt to tylko przykład):
#!/bin/bash
php artisan down
git pull
composer update
php artisan upW tym przykładzie zajmę się implementacją aktualizacji na wydarzenie repository push. Jeżeli gałąź to master (czyli wersja produkcyjna), to skrypt będzie kontynuował działanie (doprowadzając do aktualizacji kodu na produkcji).
W przypadku tego eventu, zarówno Bitbucket, jak i Github, wysyłają żądanie HTTP do skryptu za każdym razem gdy wyślemy jakieś dane do repozytorium, na przykład commita lub tag. W treści żądania są zawarte wszystkie informacje potrzebne żeby skrypt mógł podjąć decyzję o kontynuowaniu działania – czyli wykonaniu aktualizacji – lub zakończeniu. Zacznijmy od odebrania danych i ich zdekodowania:
<?php
// Odbieranie zawartości żądania
$input = file_get_contents('php://input');
// Dekodowanie JSONa do obiektu PHP
$json = json_decode($input);Następnie skrypt sprawdzi branch, i jeżeli nie jest to master, to zakończy działanie:
// Nazwa brancha do którego została wysłana zmiana
$branch = $json->push->changes[0]->new->name;
// Zakończ jeżeli branch to nie jest master
if ($branch != 'master')
die();
/* Dalsze elementy skryptu, np. aktualizacja aplikacji */W tym przypadku ustawiłem „na sztywno” pobieranie wartości ze struktury JSON. Warto dodać blok try .. catch w celu zwracania kodu HTTP innego niż 200, wtedy łatwo odnaleźć w panelu Webhooków błędne wykonania skryptu.
Struktura danych wysyłanych przez providera jest zależna od niego, np. Bitbucket wysyła inną strukturę niż Github. Po szczegóły odsyłam do dokumentacji. W celu zobaczenia całego żądania jakie wysyła Bitbucket, można ustawić webhooka na dowolny skrypt (będący pod kontrolą – nie polecam wysyłać na losową stronę), spowodować jego wykonanie i rzucić okiem na pole Request body w panelu webhooków.
Naturalnie, po instrukcji die() należy dopisać czynności które powinny się wykonać po spełnieniu powyższego warunku; może to być nawet instrukcja exec('git pull'), ale polecam skonstruowanie skryptów odpornych na różnego rodzaju błędy.
Skrypt należy umieścić w osobnym folderze, osiągalnym z poziomu przeglądarki. Ważne są też uprawnienia chmod, skrypt może nie działać bez ich ustawienia.
Konfiguracja repozytorium
Poniżej instrukcja konfiguracji dla repozytorium Bitbucket, ale dla Githuba i innych providerów kroki są dość podobne. Zakładam tutaj że jest włączona domyślnie nowa skórka interfejsu.
Najpierw należy wejść w ustawienia całego repozytorium (nie konta użytkownika). Następnie, wybrać opcję Webhooks w sekcji Workflow (pod General). Jest tam obecny przycisk dodawania nowego webhooka, który po kliknięciu ukazuje taki oto formularz:

W tym punkcie należy wpisać adres URL do naszego skryptu na serwerze, można też wybrać inne akcje, które spowodują wykonanie żądania; polecam zajrzenie do dokumentacji, treść JSON może się różnić w zależności od triggera.
I… to tyle z konfiguracji! Skrypt będzie się wykonywał za każdym razem, gdy zostanie wysłana poprawka do gałęzi master. W przypadku gdyby coś nie działało, polecam wejście w szczegóły dodanego webhooka:

Po przejściu do podstrony z detalami danego żądania, można sprawdzić jakie żądanie zostało wysłane do skryptu oraz jak skrypt odpowiedział. Jest tam również przycisk Resend request, który pozwala na debugowanie skryptu.
Zabezpieczenie
Adres naturalnie jest osiągalny również przez przeglądarkę z dowolnego miejsca na ziemi. Rodzi to pewne problemy, np. możliwość wywoływania skryptu przez dowolną osobę. Na szczęście, providerzy udostępniają adresy IP swoich serwerów wyjściowych w dokumentacji, zatem nie pozostaje nic innego niż napisać plik .htaccess:
# Składnia dla Apache <2.4
order deny,allow
deny from all
# Adresy IPv4 BitBucket
allow from 104.192.143.192/28
allow from 104.192.143.208/28
allow from 104.192.143.0/24
allow from 34.198.203.127
allow from 34.198.178.64
# Adresy IPv6 BitBucket
allow from 2401:1d80:1010::/64
allow from 2401:1d80:1003::/64…i umieścić go w folderze ze skryptem PHP. Od teraz jest on zabezpieczony przed wywołaniami z obcych miejsc w sieci – tylko żądanie pochodzące od Bitbucket może uruchomić skrypt.