Obsługa webhooków BitBucket w skrypcie PHP

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):

W 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:

Następnie skrypt sprawdzi branch, i jeżeli nie jest to master, to zakończy działanie:

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:

Bitbucket - dodawanie webhooka

Bitbucket – dodawanie webhooka

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:

Bitbucket - szczegóły webhooka

Bitbucket – szczegóły 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:

…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.