Git hook post-receive для деплоя на сервере
Деплой через CI — это хорошо, но иногда нужно просто закинуть код на сервер одним git push. post-receive hook в bare-репозитории решает эту задачу без лишних зависимостей: пушитшь на сервер — хук автоматически вытягивает файлы в рабочую директорию.
Схема: bare repo как деплой-триггер
Логика простая:
- На сервере создаётся bare-репозиторий (например,
/srv/deploy/app.git). - Разработчик добавляет его как remote и делает
git push origin main. - Git принимает данные и запускает
hooks/post-receive. - Скрипт хука делает
git --work-tree=/var/www/app --git-dir=/srv/deploy/app.git checkout -f main.
Bare-репозиторий не содержит рабочей директории. Именно поэтому в post-receive нужно явно указывать --work-tree, чтобы checkout знал, куда записать файлы.
Это не CI — это прямой триггер на уровне Git. Никаких пайплайнов, артефактов, очередей. Подходит для небольших сервисов, VPS и внутренних инструментов.
Создание bare репозитория на сервере
Подключаешься к серверу и создаёшь repo:
После этого структура будет стандартной: hooks/, objects/, refs/, HEAD, config. Хуки по умолчанию лежат в /srv/deploy/app.git/hooks/ с суффиксом .sample — их нужно переименовать или создать свои.
Убедись, что директория /srv/deploy принадлежит пользователю, от которого ты пушишь. Иначе права на запись в objects/ будут закрыты.
Написание hook post-receive
Создаёшь файл /srv/deploy/app.git/hooks/post-receive:
Ключевые моменты:
| Элемент | Назначение |
|---|---|
set -euo pipefail | Скрипт падает при любой ошибке, не продолжает с неверными данными |
while read oldrev newrev refname | Хук передаёт три аргумента построчно за каждый обновлённый ref |
refs/heads/$BRANCH | Проверяем, что пушится именно нужная ветка |
checkout -f | Принудительно синхронизирует рабочее дерево с референсом |
Если после деплоя нужно перезапустить сервис, добавь в конец скрипта 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, ключ авторизуется под нужным пользователем.
Не забудь сделать хук исполняемым:
Пуш с клиента и проверка
На машине разработчика добавляешь remote и пушишь:
На сервере в логе хука увидишь:
Проверяешь файлы на сервере:
Если пуш падает с Permission denied — проверьй владельца hooks/post-receive и целевой директории. Если checkout не записывает файлы — убедись, что WORK_TREE указывает на существующую директорию и у пользователя есть на неё права.
Это всё. Никаких дополнительных инструментов — только Git и shell. Для продакшена с нагрузкой это, разумеется, не замена нормальному CI, но для быстрого деплоя на один-два сервера работает надёжно и без лишней магии.