В нашем проекте при каждом деплое разработчики, тестеры и ещё пара людей получают замечательные письма:
Такое решение помогло нам избавиться от вопросов тестеров «Ну что, выложили уже исправление бага XXX?», «Что нового на тестовом сервере?». Так же — все члены команды, отдел внедрения и руководство в курсе, что происходит с кодом на серверах.
Для работы используется git, capistrano (+ multistage), php, bash (+ некоторые консольные утилитки). Если интересно — заходим под кат.
Будем считать, что у нас в корне репозитория есть файл configs.ini, который содержит ключ runtime.version. За основу был взят gist#381852.
Что здесь происходит:
Читаем входящие параметры и убеждаемся, что пришёл тег:
Выделяем название тега, разбиваем его на части, ищем предыдущий тег для этого сервера:
Если в $prevtag пусто — значит это первая установка на сервер. Если версия у нового и старого тега совпадает — это обновление, если же нет — установка новой версии. Таким образом мы генерируем корректный заголовок письма.
Начнём формировать тело письма. Сперва — определим кто осмелился задеплоить:
Теперь разберём коммиты по задачам. Последние в Jira именуются по маске <алиас проекта>-<id задачи>, все разработчики в обязательном порядке указывают алиас задачи (uppercase) в коммите. Если задача крупнее чем на 30 минут и требует более 1 коммита — создаётся ветка, по алиасу задачи, и тогда в коммитах эту самую задачу мы уже не упоминаем. Итого, чтобы достать список задач нам нужно выполнить не сложную обработку регуляркой:
Если в итоге файл tmpfile не пуст — добавляем его к телу письма.
Дальше идёт информация, которая интересует только разработчиков проекта: списки коммитов и изменённых файлов:
Ну и, наконец, тупая отправка письма:
Все файлы можно взять на гитхабе: github.com/zvirusz/git-deploy-notify
P.S. Если кто-нибудь поможет переписать кусок кода, выдёргивающий имена задач, на perl/bash — я буду очень рад.
Subject: Наш проект версии v1.1.1 обновлён на сервере 'testing'
user1 выложил следующие обновления на сервер 'testing':
Коммиты по задачам:
jira.local/browse/PROJECT-1234
Полный список коммитов с предыдущего обновления:
4392a53 Thu Aug 18 17:50:32 2011 +0700 user1 / [PROJECT-1234] сделал полезное
f2fcfe2 Thu Aug 18 17:37:53 2011 +0700 user1 / сделал страшное
cb1fcbe Wed Aug 17 15:18:10 2011 +0700 user2 / зарефакторил
Изменения по файлам:
file1 | 4 ++--
file2 | 8 ++++----
file3 | 8 ++++----
3 files changed, 10 insertions(+), 10 deletions(-)
Такое решение помогло нам избавиться от вопросов тестеров «Ну что, выложили уже исправление бага XXX?», «Что нового на тестовом сервере?». Так же — все члены команды, отдел внедрения и руководство в курсе, что происходит с кодом на серверах.
Для работы используется git, capistrano (+ multistage), php, bash (+ некоторые консольные утилитки). Если интересно — заходим под кат.
Алгоритм работы
- Обновляем код на сервере testing (cap testing deploy)
- После deploy:restart срабатывает хук, создающий тег в репозитории. Тег формируется на основе версии проекта (хранится в конфиге, в репозитории), названии staging-сервера и названии релиза
- В репозитории срабатывает хук. Если пришёл не тег — игнорируем, если же тег:
- Распиливаем его на компоненты: версия, сервер
- Определяем предыдущий тег на этом же сервере
- Если тега нет — значит эта первая установка и генерировать различия не стоит, там может быть несколько тысяч коммитов
- Если же есть тег — генерируем список различий, выдёргиваем из них список задач; составляем список изменённых файлов
- Отправка сгенерированного письма
Создание тега
Про настройку capistrano и capistrano-multistage уже где только не написано, поэтому я только расскажу, как у нас добавляется тег.Будем считать, что у нас в корне репозитория есть файл configs.ini, который содержит ключ runtime.version. За основу был взят gist#381852.
namespace :deploy do
...
after "deploy:restart", "deploy:git:push_deploy_tag"
namespace :git do
desc "Place release tag into Git and push it to server."
task :push_deploy_tag do
user = `git config --get user.name`.strip
email = `git config --get user.email`.strip
version = `git cat-file -p #{real_revision}:configs.ini | fgrep runtime.version | awk -F '[ =]+' '{print $2}'`.strip
puts `git tag v#{version}-#{stage}-#{release_name} #{real_revision} -m "Deployed by #{user} <#{email}>"`
puts `git push --tags`
end
end
end
Что здесь происходит:
- Извлекаем данные текущего пользователя (имя и почту) из конфига гита
- Берём файл configs.ini из устанавливаемой ревизии и выдёргиваем версию
- Создаём аннотированный тэг. В аннотации указываем, кто и когда задеплоил
- Публикуем теги
Обрабатываем обновление репозитория
Хуку pre-receive на вход (stdin) подётся 3 значения: предыдущая и текущая ревизии, refname.Читаем входящие параметры и убеждаемся, что пришёл тег:
while read oldrev newrev refname
do
rev_type=$(git cat-file -t $newrev 2>/dev/null)
case "$refname","$rev_type" in
refs/tags/*,tag)
...
;;
esac
done
Выделяем название тега, разбиваем его на части, ищем предыдущий тег для этого сервера:
tag=${refname##refs/tags/}
version=`echo $tag | cut -d- -f1`
server=`echo $tag | cut -d- -f2`
prevtag=$(git describe --tags --abbrev=0 --match="*-$server-*" $newrev^ 2>/dev/null)
Если в $prevtag пусто — значит это первая установка на сервер. Если версия у нового и старого тега совпадает — это обновление, если же нет — установка новой версии. Таким образом мы генерируем корректный заголовок письма.
Начнём формировать тело письма. Сперва — определим кто осмелился задеплоить:
eval $(git for-each-ref --shell --format='
tagger=%(taggername)
tagged=%(taggerdate)' $refname
)
echo "$tagger выложил следующие обновления на сервер '$server':" > msg
Теперь разберём коммиты по задачам. Последние в Jira именуются по маске <алиас проекта>-<id задачи>, все разработчики в обязательном порядке указывают алиас задачи (uppercase) в коммите. Если задача крупнее чем на 30 минут и требует более 1 коммита — создаётся ветка, по алиасу задачи, и тогда в коммитах эту самую задачу мы уже не упоминаем. Итого, чтобы достать список задач нам нужно выполнить не сложную обработку регуляркой:
git log $rev_range --abbrev-commit --pretty="format:%s" > tmpfile
php >tickets <<END
<?php
\$f = file_get_contents("$tmp");
if (preg_match_all("#([A-Z.]+-\d+)#", \$f, \$matches)) {
\$matches[1] = array_unique(\$matches[1]);
foreach (\$matches[1] as \$match) {
echo '$JIRA_HOST/browse/', \$match, PHP_EOL;
}
}
END
Если в итоге файл tmpfile не пуст — добавляем его к телу письма.
Дальше идёт информация, которая интересует только разработчиков проекта: списки коммитов и изменённых файлов:
echo "Полный список коммитов с предыдущего обновления:" >> msg
git log $rev_range --no-merges --abbrev-commit --pretty="format:%h %ad %an / %s" >> msg
echo -e "\n\nИзменения по файлам:" >>msg
git diff --stat=140,110 $rev_range >>msg
Ну и, наконец, тупая отправка письма:
cat msg | mail -s "$subject" $MAIL_TO
Все файлы можно взять на гитхабе: github.com/zvirusz/git-deploy-notify
P.S. Если кто-нибудь поможет переписать кусок кода, выдёргивающий имена задач, на perl/bash — я буду очень рад.