Итак, у нас для начала уже есть сервер на котором настроен gitolite по адресу git.example.com, на нем расположен репозиторий myrepo в котором есть две основные ветки master и develop.
Общий принцип разработки такой:
Задача заключается в том, чтоб при изменениях в ветке develop они автоматически переносились в рабочую копию на хост develop.example.com, а изменения сделанные в master - на хост production.example.com.
Для начала на develop.example.com создаем пользователя gitbot и генерим для него ssh ключи.
adduser gitbot
su gitbot
ssh-keygen -t rsa
Добавляем gitbot и его ключ в конфигурацию gitolite и разрешаем доступ к репозиторию myrepo, подробно описывать не буду тк это уже описано много раз, в том числе тут.
Далее на develop.example.com создаем место под репозиторий, например /usr/local/www/myrepo и делаем его владельцем пользователя gitbot:
mkdir /usr/local/www/myrepo
chown gitbot /usr/local/www/myrepo
Создаем папку для логов, не обязательно, но желательно, если этого не сделать, то надо убрать код логов из скрипта ниже
mkdir /var/log/gitbot
chown gitbot /var/log/gitbot
Клонируем ветку develop из myrepo, операции выполняем от имени gitbot
su gitbot
git clone --single-branch -b develop git@git.example.com:myrepo /usr/local/www/myrepo
Эта операция также занесет fingerprint хоста, чтоб потом автомату ничего не мешало работать.
Создаем служебный скрипт, который будет пуллить указанный репозиторий, опять же все делаем от имени gitbot, скрипт размещаем в домашней папке
su gitbot
cd ~
echo > pull-repo.sh
chmod +x ./pull-repo.sh
Содержимое скрипта pull-repo.sh примерно такое
#!/bin/sh
# Usage:
# ./pull-repo.sh /path/to/reponame [branchname =master]
#
# Logs:
# /var/log/gitbot/reponame.log
if [ ! -d "$1" ]; then
echo "'$1' is not a directory!"
echo "Usage:"
echo "./pull-repo.sh /path/to/reponame [branchname =master]"
exit 1
fi
if [ -z $2 ]; then
BRANCH=master
else
BRANCH=$2
fi
REPO_DIR=${1%/}
REPO_NAME=${REPO_DIR##*/}
LOG=/var/log/gitbot/$REPO_NAME.log
DATE=`date +%Y-%m-%d.%H:%M:%S`
echo "$DATE $REPO_NAME [$BRANCH] $REPO_DIR" >> $LOG
cd $REPO_DIR
git pull origin $BRANCH 2>&1 >> $LOG
И теперь скрипт который будет пуллить изменения в ветке develop нашего репозитория, опять же все делаем от имени gitbot, скрипт размещаем в домашней папке
su gitbot
cd ~
echo > myrepo.sh
chmod +x ./myrepo.sh
Содержимое скрипта myrepo.sh такое
#!/bin/sh
DIR=/usr/local/www/myrepo
./pull-repo.sh $DIR develop
Обязательно проверяем работоспособность myrepo.sh, что оно не только работает без ошибок, но и ничего не спаршивает.
Теперь на production.example.com создаем такого же пользователя gitbot и переносим его ключи с develop.example.com, важно чтоб ключи были одинаковые на обоих серверах.
Точно также клонируем репозиторий как описано выше, но с поправкой на ветку master; и создаем скрипты для автоматизации, так же с поправкой на ветку master. Не забываем все проверить.
Промежуточный результат достигнут - у нас есть автоматика, которая может пуллить изменения в master и develop на нужных хостах.
Идем на наш gitolite сервер git.example.com и проводим настройки в репозитории myrepo, естественно от имени пользователя git
su git
vim ~/repositories/myrepo.git/hooks/post-receive
Нам надо создать хук, который сработает после получения данных в репозиторий и отправит их "куда надо". Хорошим тоном в git считается, что нельзя пушить в рабочую копию, а наоборот - рабочая копия должна пуллить с bare. В post-receive пишем
#!/bin/sh
while read oldrev newrev refname
do
if [ "$oldrev" != "$newrev" ]; then
branch=$(git rev-parse --symbolic --abbrev-ref $refname)
if [ "master" == "$branch" ]; then
echo Deploying $branch branch to production
ssh -i ~/.ssh/gitbot.id_rsa gitbot@production.example.com "~/myrepo.sh"
elif [ "develop" == "$branch" ]; then
echo Deploying $branch branch to develop
ssh -i ~/.ssh/gitbot.id_rsa gitbot@develop.example.com "~/myrepo.sh"
else
echo "Branch $branch is not deployable"
fi
fi;
done
Взаимодействие с develop.example.com и production.example.com производится с помощью ключей, поэтому приватный ключ от пользователя gitbot с этих серверов нужно поместить куда-то на git.example.com, я разместил его в /home/git/.ssh/gitbot.id_rsa, в случае иного размещения, правьте скрипт выше.
Не забудьте добавить публичный ключ пользователя gitbot в /home/gitbot/.ssh/authorized_keys на develop.example.com и production.example.com, иначе подключиться к ним с использованием приватного ключа не получится.
Тестируем взаимодействие с серверами из консоли (это также обеспечит fingerprint хостов)
su git
ssh -i ~/.ssh/gitbot.id_rsa gitbot@production.example.com "~/myrepo.sh"
ssh -i ~/.ssh/gitbot.id_rsa gitbot@develop.example.com "~/myrepo.sh"
Убедившись, что все работает, проверяем полную схему взаимодействия: делаем коммит + пуш в ветку develop и master, и убеждаемся, что изменения попали на нужные серверы. Результат достигнут.
Если требуется откатить ветку назад через git reset
+ git push --force
, то такого рода изменения автоматически не применятся, и придется аналогичные действия производить на конечных хостах, чтоб этого не делать попробуем автомтизировать.
А если в вашем проекте используются подмодули, то можно их также автоматически обновлять командой git submodule update --init --remote
, ориентируясь на наличие файла .gitmodules в репозитории.
Для этого модернизируем скрипт pull-repo.sh, будем искать следы форсированного пуша в выводе git fetch и проверять наличие файла подмодулей:
#!/bin/sh
# Usage:
# ./pull-repo.sh /path/to/reponame [branchname =master]
#
# Logs:
# /var/log/gitbot/reponame.log
if [ ! -d "$1" ]; then
echo "'$1' is not a directory!"
echo "Usage:"
echo "./pull-repo.sh /path/to/reponame [branchname =master]"
exit 1
fi
if [ -z $2 ]; then
BRANCH=master
else
BRANCH=$2
fi
REPO_DIR=${1%/}
REPO_NAME=${REPO_DIR##*/}
LOG=/var/log/gitbot/$REPO_NAME.log
DATE=`date +%Y-%m-%d.%H:%M:%S`
echo "$DATE $REPO_NAME [$BRANCH] $REPO_DIR" >> $LOG
cd $REPO_DIR
#git pull origin $BRANCH 2>&1 >> $LOG
git fetch origin 2>&1 | grep $BRANCH | grep "(forced update)" >> $LOG 2>&1
if [[ $? -eq 0 ]]; then
git reset --hard origin/$BRANCH >> $LOG 2>&1
echo Resetting brahch $BRANCH
else
git pull origin $BRANCH >> $LOG 2>&1
echo Normal pull branch $BRANCH
fi
[ -f ./.gitmodules ] && git submodule update --init --remote >> $LOG 2>&1
Здесь заменен git pull на git fetch и поиск в его выводе force update в нужной ветке. Если был форсированный пуш, то мы просто ресетим наш репозиторий в состояние удаленного, а если нет - то делаем обычный пулл.