Menu
Szybki wybór
Hosting Domeny VPS SSL Kalkulator Porównania FAQ
Aktywne kody
Wszystkie kody rabatowe

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

Najczęstsze pytania

Czym jest git hook post-receive? +
Post-receive to skrypt wykonywalny umieszczony w katalogu hooks/ repozytorium bare na serwerze. Git uruchamia go automatycznie po każdym git push — możesz w nim umieścić dowolne polecenia: checkout kodu do webroot, instalację zależności, restart serwera aplikacji. Skrypt działa z uprawnieniami użytkownika który push-uje lub właściciela procesu git na serwerze.
Czym jest repozytorium bare i dlaczego jest potrzebne do deploymentu? +
Repozytorium bare (git init --bare) przechowuje tylko historię git bez kopii roboczej plików. Na serwer push-ujesz do repo bare (np. /home/deploy/app.git), a hook post-receive checkout-uje pliki do osobnego katalogu webroot (np. /var/www/app). Nie można push-ować do zwykłego repozytorium z zaznaczoną gałęzią — git odmówi przyjęcia zmian.
Kiedy wybrać git hooks zamiast GitHub Actions? +
Git hooks sprawdzają się przy: małych projektach z jednym serwerem, braku konta GitHub/GitLab, gdy chcesz mieć 100% kontrolę bez zewnętrznych usług, lub gdy GitHub Actions jest zbyt wolny dla częstych deploymentów. GitHub Actions jest lepszy gdy potrzebujesz: testów na wielu wersjach środowiska, powiadomień Slack/email, równoległych pipeline'ów lub deploy na wiele serwerów jednocześnie.
Jak zrobić rollback po nieudanym deployu przez git hook? +
Najbezpieczniejszy rollback to symlink swap: hook wypakuje kod do katalogu releases/$(date +%s)/, a następnie przełącza symlink current → nowy katalog (ln -sfn). Jeśli cokolwiek zawiedzie — wystarczy ln -sfn releases/poprzedni current i reload Nginx. Alternatywnie: trzymaj poprzednią wersję w /var/www/app.prev i po awarii mv app app.broken && mv app.prev app.

Sprawdź oferty pasujące do tego scenariusza

Poniżej masz szybkie przejścia do ofert i stron z kodami rabatowymi tam, gdzie są dostępne.