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):
1 2 3 4 5 |
#!/bin/bash php artisan down git pull composer update php artisan up |
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:
1 2 3 4 5 6 |
<?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:
1 2 3 4 5 6 7 8 |
// 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
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
# 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.