Git Hooks — automatyzacja deployment przez git push
Opublikowano: 9 kwietnia 2026 · Kategoria: DevOps / VPS
Git hooks to skrypty uruchamiane automatycznie przez git w kluczowych momentach cyklu życia
repozytorium. Dzięki nim możesz wdrożyć aplikację na serwer jednym poleceniem git push production main — bez GitHub Actions, bez Jenkins, bez żadnej zewnętrznej
usługi. Oto jak to skonfigurować krok po kroku.
Rodzaje git hooków
Git rozróżnia hooki po stronie klienta (lokalne) i po stronie serwera (zdalne). Do automatyzacji deploymentu używamy hooków serwerowych:
| Hook | Strona | Kiedy się uruchamia | Typowe zastosowanie |
|---|---|---|---|
pre-commit | Klient | Przed zatwierdzeniem commita | Lint, formatowanie, testy jednostkowe |
pre-push | Klient | Przed wysłaniem do remote | Testy integracyjne, sprawdzenie branch |
pre-receive | Serwer | Przed zaakceptowaniem push | Walidacja, wymuszanie konwencji |
post-receive | Serwer | Po zaakceptowaniu push | Deploy — checkout, build, restart |
update | Serwer | Przed aktualizacją ref-a | Kontrola dostępu per branch |
Krok 1: Bare repo na serwerze
Na serwerze (przez SSH) utwórz repozytorium bare — przechowuje ono historię git, ale nie ma katalogu roboczego z plikami:
# Na serwerze (przez SSH) ssh [email protected] # Utwórz katalogi mkdir -p /home/deploy/app.git mkdir -p /var/www/app # Zainicjuj bare repo cd /home/deploy/app.git git init --bare # Sprawdź zawartość — brak katalogu roboczego! ls # HEAD branches/ config description hooks/ info/ objects/ refs/
Krok 2: Hook post-receive
Utwórz plik /home/deploy/app.git/hooks/post-receive:
#!/bin/bash
# /home/deploy/app.git/hooks/post-receive
set -e # Przerwij przy błędzie
REPO_DIR="/home/deploy/app.git"
WEB_DIR="/var/www/app"
BRANCH="main"
# Odczytaj które gałęzie są pushowane
while read oldrev newrev refname; do
PUSHED_BRANCH=${refname##refs/heads/}
if [ "$PUSHED_BRANCH" != "$BRANCH" ]; then
echo "Pominięto gałąź $PUSHED_BRANCH (deploy tylko z $BRANCH)"
continue
fi
echo "=== Deploy z gałęzi $BRANCH ==="
# Checkout plików do webroot
git --work-tree="$WEB_DIR" --git-dir="$REPO_DIR" checkout -f "$BRANCH"
cd "$WEB_DIR"
# PHP: instalacja zależności
if [ -f "composer.json" ]; then
echo "--- composer install ---"
composer install --no-dev --optimize-autoloader --quiet
fi
# Node.js: instalacja i build
if [ -f "package.json" ]; then
echo "--- npm ci && npm run build ---"
npm ci --production=false
npm run build
fi
# Laravel: migracje i cache
if [ -f "artisan" ]; then
echo "--- artisan migrate + cache ---"
php artisan migrate --force
php artisan config:cache
php artisan route:cache
php artisan view:cache
fi
# Restart PHP-FPM (opcjonalnie)
# sudo systemctl reload php8.2-fpm
echo "=== Deploy zakończony pomyślnie ==="
done # Nadaj uprawnienia wykonywania chmod +x /home/deploy/app.git/hooks/post-receive
Krok 3: Remote na lokalnej maszynie
Na lokalnym komputerze dodaj serwer jako remote git:
# Dodaj remote o nazwie "production" git remote add production ssh://[email protected]/home/deploy/app.git # Wyślij kod i uruchom deploy git push production main # Wynik w terminalu: # === Deploy z gałęzi main === # --- composer install --- # --- artisan migrate + cache --- # === Deploy zakończony pomyślnie ===
Zero-downtime deploy przez symlink
Standardowy checkout nadpisuje pliki live — przez kilkanaście sekund aplikacja może być w niespójnym stanie. Bezpieczniejsze podejście to symlink swap (wzorzec Capistrano):
#!/bin/bash
# post-receive — wersja z symlinkami (zero-downtime)
REPO_DIR="/home/deploy/app.git"
RELEASES_DIR="/var/www/releases"
CURRENT_LINK="/var/www/current"
TIMESTAMP=${$(date +%Y%m%d%H%M%S)}
RELEASE_DIR="$RELEASES_DIR/$TIMESTAMP"
while read oldrev newrev refname; do
[ "${refname##refs/heads/}" = "main" ] || continue
# Utwórz katalog dla nowego release
mkdir -p "$RELEASE_DIR"
# Checkout do nowego katalogu
git --work-tree="$RELEASE_DIR" --git-dir="$REPO_DIR" checkout -f main
cd "$RELEASE_DIR"
# Build
composer install --no-dev -q
php artisan migrate --force
php artisan config:cache
# Atomowe przełączenie symlinku (ln -sfn jest atomowe)
ln -sfn "$RELEASE_DIR" "$CURRENT_LINK"
echo "Deploy $TIMESTAMP aktywny"
# Zachowaj tylko 5 ostatnich releases
ls -dt "$RELEASES_DIR"/* | tail -n +6 | xargs rm -rf
done
Nginx serwuje z /var/www/current/public. Przełączenie symlinku jest atomowe —
użytkownicy nie zauważą przerwy. Rollback to jedno polecenie: ln -sfn /var/www/releases/POPRZEDNI /var/www/current.
Zmienne środowiskowe w hooku
Hook post-receive działa w ograniczonym środowisku — PATH może nie zawierać katalogu z
composer czy node. Kilka zasad:
#!/bin/bash # Na początku hooka — ustaw PATH i zmienne środowiskowe export PATH="/usr/local/bin:/usr/bin:/bin:/home/deploy/.composer/vendor/bin:$PATH" export HOME="/home/deploy" export NVM_DIR="/home/deploy/.nvm" # Załaduj nvm jeśli używasz Node.js przez nvm [ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" # Załaduj .env.production zamiast wbudowywania sekretów w hook if [ -f "/etc/app/env.production" ]; then source /etc/app/env.production fi
Obsługa błędów i powiadomienia
#!/bin/bash
set -e
# Przechwytuj błędy i wysyłaj powiadomienie
trap 'on_error' ERR
on_error() {
local EXIT_CODE=$?
echo "BLAD: Deploy nieudany (kod $EXIT_CODE)" >&2
# Powiadomienie na Slack/Discord
curl -s -X POST "$SLACK_WEBHOOK_URL" \
-H "Content-Type: application/json" \
-d '{"text": "Deploy FAILED na produkcji! Sprawdz logi."}' || true
exit $EXIT_CODE
}
# Logowanie do pliku
exec >>/var/log/deploy.log 2>&1
echo "[$(date)] Deploy z commita $newrev" Git Hooks vs GitHub Actions — kiedy co wybrać?
| Kryterium | Git Hooks | GitHub Actions |
|---|---|---|
| Zależności zewnętrzne | Zero — tylko git i SSH | GitHub account, internet |
| Szybkość deployu | Bardzo szybki (sekundy) | Wolniejszy (runner startup ~30s) |
| Koszty | Zero (już masz serwer) | Darmowy dla public repos, płatny dla private |
| Testy przed deployem | Tylko to co wpiszesz w skrypcie | Pełna macierz (node 18/20/22, Ubuntu/Windows) |
| Deploy na wiele serwerów | Trudniejszy (SSH w pętli) | Łatwy (matrix lub parallel jobs) |
| Sekrety i klucze API | Pliki na serwerze (/etc/app/) | GitHub Secrets (szyfrowane) |
| Rollback | Ręczny symlink lub git checkout | Depends on workflow design |
| Idealny dla | Małe projekty, hobby, VPS solo | Zespoły, open source, multi-env |
Git hooks to świetny wybór dla małych projektów na jednym VPS — zero konfiguracji poza serwerem, zero kosztów, maksymalna kontrola. GitHub Actions warto wybrać gdy potrzebujesz testów na wielu środowiskach, deploymentu na wiele serwerów lub integracji z zewnętrznymi serwisami (Slack, Sentry, Datadog).