# Git hook post-receive для деплоя на сервере

Индекс LLMS: [llms.txt](/llms.txt)

---

Деплой через CI — это хорошо, но иногда нужно просто закинуть код на сервер одним `git push`. `post-receive` hook в bare-репозитории решает эту задачу без лишних зависимостей: пушитшь на сервер — хук автоматически вытягивает файлы в рабочую директорию.

## Схема: bare repo как деплой-триггер

Логика простая:

1. На сервере создаётся bare-репозиторий (например, `/srv/deploy/app.git`).
2. Разработчик добавляет его как remote и делает `git push origin main`.
3. Git принимает данные и запускает `hooks/post-receive`.
4. Скрипт хука делает `git --work-tree=/var/www/app --git-dir=/srv/deploy/app.git checkout -f main`.

> [!NOTE]
> Bare-репозиторий не содержит рабочей директории. Именно поэтому в `post-receive` нужно явно указывать `--work-tree`, чтобы `checkout` знал, куда записать файлы.

Это не CI — это прямой триггер на уровне Git. Никаких пайплайнов, артефактов, очередей. Подходит для небольших сервисов, VPS и внутренних инструментов.

## Создание bare репозитория на сервере

Подключаешься к серверу и создаёшь repo:

```bash
mkdir -p /srv/deploy
cd /srv/deploy
git init --bare app.git
```

После этого структура будет стандартной: `hooks/`, `objects/`, `refs/`, `HEAD`, `config`. Хуки по умолчанию лежат в `/srv/deploy/app.git/hooks/` с суффиксом `.sample` — их нужно переименовать или создать свои.

> [!WARNING]
> Убедись, что директория `/srv/deploy` принадлежит пользователю, от которого ты пушишь. Иначе права на запись в `objects/` будут закрыты.

## Написание hook post-receive

Создаёшь файл `/srv/deploy/app.git/hooks/post-receive`:

```bash
#!/usr/bin/env bash
set -euo pipefail

REPO_DIR="/srv/deploy/app.git"
WORK_TREE="/var/www/app"
BRANCH="main"

while read oldrev newrev refname; do
  if [ "$refname" = "refs/heads/$BRANCH" ]; then
    echo "Deploying $BRANCH to $WORK_TREE..."
    git --work-tree="$WORK_TREE" --git-dir="$REPO_DIR" checkout -f "$BRANCH"
    echo "Deployment complete."
  fi
done
```

Ключевые моменты:

| Элемент | Назначение |
|---|---|
| `set -euo pipefail` | Скрипт падает при любой ошибке, не продолжает с неверными данными |
| `while read oldrev newrev refname` | Хук передаёт три аргумента построчно за каждый обновлённый ref |
| `refs/heads/$BRANCH` | Проверяем, что пушится именно нужная ветка |
| `checkout -f` | Принудительно синхронизирует рабочее дерево с референсом |

> [!TIP]
> Если после деплоя нужно перезапустить сервис, добавь в конец скрипта `systemctl restart app` или `supervisorctl reload app`. Хук выполняется в контексте Git-пользователя, поэтому убедись, что у него есть права на перезапуск.

## Настройка прав и доступа

Самая частая проблема — права. Git-пользователь, в который ты пушишь, должен иметь право писать в `WORK_TREE` и выполнять команды из хука.

Варианты настройки:

- **Один пользователь**: Git-юзер и веб-пользователь — одно лицо. Просто `chown -R deploy:deploy /srv/deploy /var/www/app`.
- **Группа**: Добавляешь Git-пользователя в группу веб-сервера. `usermod -aG www-data deploy`, затем `chmod -R g+w /var/www/app`.
- **SSH-ключ**: Пушишь через SSH, ключ авторизуется под нужным пользователем.

```bash
# Пример: деплой-пользователь deploy, веб-корень /var/www/app
sudo useradd -m deploy
sudo mkdir -p /var/www/app
sudo chown -R deploy:deploy /var/www/app
sudo chmod -R 755 /var/www/app
```

Не забудь сделать хук исполняемым:

```bash
chmod +x /srv/deploy/app.git/hooks/post-receive
```

## Пуш с клиента и проверка

На машине разработчика добавляешь remote и пушишь:

```bash
git remote add production deploy@server:/srv/deploy/app.git
git push production main
```

На сервере в логе хука увидишь:

```
Deploying main to /var/www/app...
Deployment complete.
```

Проверяешь файлы на сервере:

```bash
ls -la /var/www/app
git --git-dir=/srv/deploy/app.git --work-tree=/var/www/app status
```

> [!WARNING]
> Если пуш падает с `Permission denied` — проверьй владельца `hooks/post-receive` и целевой директории. Если `checkout` не записывает файлы — убедись, что `WORK_TREE` указывает на существующую директорию и у пользователя есть на неё права.

Это всё. Никаких дополнительных инструментов — только Git и shell. Для продакшена с нагрузкой это, разумеется, не замена нормальному CI, но для быстрого деплоя на один-два сервера работает надёжно и без лишней магии.
