Некоторое время назад в компании, где я работаю в связи с расширением комманды было принято решение о введении процесса code review. Выбор инструмента пал на Review Board — продукт обладает достаточным функционалом, активно разрабатывается с 2006 года и является open source. В качестве системы контроля версий у нас используется Mercurial
О том, с чем какими задачами столкнулись при организации процесса код ревью для связки Review Board + Mercurial — под катом.
И так, начали мы с разворачивания сервера. К сожалению, информации по требованиям к серверу на сайте RB найти не удалось. После ознакомления с результатами поиска по теме, стало понятно, что главным параметром, в который «упирается» RB является оперативка. Чаще всего в качестве «комфортного объема» упоминалась цифра 4GB. Мы начали с 2 но RB на них не «взлетела» — при просмотре diff-ов то и дело вылетала ошибка «Can not allocate memory». Подняли до 4GB и все пошло, как по маслу.
Далее начался процесс настройки и введения самого процесса код ревью по докам с офф. сайта.
Все шло более-менее гладко, пока мы не дошли до выбора инструмента для публикации ревью. Выбор стоял между официальной тулзой от команды Review Board — post-review и плагином для Mercurial
Официальная тулза имеет явный плюс — она поддерживается разработчиками Review Board, при этом ее главный минус — она не имеет GUI. Плагин заточен конкретно под Mercurial, поэтому имеет некоторые преимущества перед post-review, но главным его достоинством является интеграция в GUI TortoiseHg. Так как большинство девелоперов у нас сидят на TortoiseHg, было принято решение пользовать плагин.
Ознакомились с докой плагина — вроде все гладко. Скачали главную версию (на странице предлагается еще альтернативная), которая в тот момент еще хостилась на Google Code. Попытались ее использовать — а не тут-то было — она оказалась в очень плачевном состоянии — баги лезли пачками, да и совместимость с последними версиями Mercurial была не полная. Поставили альтернативу — дела пошли получше, но полной совместимости с Mercurial >= 2.3 у нее тоже не было. Но мы не теряли надежду и путем копания в форках, был обнаружен рабочий! Но и с ним были проблемы — у некоторых юзеров почему-то плагин постоянно вываливал 404 ошибку при попытке запостить ревъю, причем как из под виртуалки с Unix, так и с под «реальной» железяки с Windows. Были еще какие-то мелкие глюки, сейчас уже не припомню. На тот момент разбираться с этим уже не было времени, поэтому использовали что есть.
Так продолжалось добрых пол-года. За это время мы поняли, что сформированный процесс постинга ревьюзанимает достаточно много времени, да и некоторые юзера забывали (а некоторые и забивали) постить ревью. Поэтому было принято решение автоматизировать процесс насколько это возможно.
Порыв информацию по этому поводу, был принят следующий план:
- Клоним на сервере, работающем 24/7 наш репозиторий
- Запускаем по расписанию pull репозитория
- Вешаемся на incoming хук Mercurial-a скриптом, который автоматом постит ревью для каждого «затянутого» коммита
В процессе продумывания реализации мы поняли, что нам потребуется возможность постить ревью от имени другого пользователя. Плагин к Меркуриалу этого не умел. В надежде на чудо, заходим на страницу плагина на сайте Меркуриала и… не верим своим глазам — плагин ожил и начал развиваться! Официальный репозиторий переехал на bitbucket, плагин теперь работает с последней версией Mercurial, а также поддерживает постинг ревью от имени другого пользователя. Воодушевленные, приступили к реализации.
Итак, как это реализовано (все действия происходят на сервере c CentOS 6.3).
- Клоним наш репо в папку ~/home/viktor/autoreview/gdam_frontend/
- Клоним плагин для RB в папку ~/home/viktor/autoreview/hgreviewboard/
- В Review Board создаем пользователя c интуитивным логином review_bot и даем ему право постить ревью от лица другого пользователя («Can submit as user» permission)
- Идем в конфигурационный файл репозитория /home/viktor/gdam_frontend/.hg/hgrc и дописываем туда следующие строчки:
[extensions]
reviewboard = /home/viktor/autoreview/hgreviewboard[reviewboard]
# Review Board Repository ID, details — www.reviewboard.org/docs/manual/1.7/admin/configuration/repositories/
repoid = 1# Review Board Review Group, details — www.reviewboard.org/docs/manual/1.7/admin/configuration/review-groups/
target_groups = A5_Reviewers# Review Board Site, details — www.reviewboard.org/docs/manual/1.7/admin/installation/creating-sites/
server = reviews/revievboard
user = review_bot
password = bot_password[hooks]
# Mercurial incoming hook, details — hgbook.red-bean.com/read/handling-repository-events-with-hooks.html#sec:hook:incoming
incoming.autoreview = /home/viktor/autoreview/a5.sh $HG_NODE - Теперь создаем повешенный на incoming хук скрипт /home/viktor/autoreview/a5.sh:
function log { echo '>> '$1 echo `date`' >> ' $1 >> $script_dir/autoreview.log } script_path=`readlink -f "$0"` script_dir=`dirname "$script_path"` revision=$1 log "Processing revision $revision" messages_file='/home/viktor/autoreview/messages.txt' cd /home/viktor/autoreview/gdam_frontend user=`hg log -r $revision | grep 'user:' | sed -r 's/^user:s{0,}//g'` log "Revision $revision is made by $user " user_found=`grep "^'$user'" $script_dir/users.txt` if [ "$user_found" != "" ] then commit_message=`hg log -r $revision | grep 'summary:' | sed -r 's/summary:s{1,}//g'` message_found=`grep "$commit_message" "$messages_file"` if [ "$message_found" == "" ] then merge_commit=`echo $commit_message | grep '^s{0,}[M|m]erge'` if [ "$merge_commit" == "" ] then echo $commit_message >> "$messages_file" rb_user=`echo $user_found | sed -r "s/^'$user's//g"` log "Found revision board user - $rb_user" review_id=`echo $commit_message | grep -o '[Rr][Bb]-[[:digit:]]{1,}' | sed -r 's/^[R|r][B|b]-//g'` if [ "$review_id" != "" ] then log "Updating review $review_id" hg postreview -e $review_id --submit_as $rb_user -p $revision else log "Posting new review for $revision" hg postreview --submit_as $rb_user -p $revision fi else log "Skipping merge revision $revision ($commit_message)" fi else log "Skipping already processed revision $revision ($commit_message)" fi else log "User $user is not listed in users.txt mapping file" fi
Пройдемся по ключевым моментам скрипта:
- Определяем директорию, в которой находится скрипт
script_path=`readlink -f "$0"` script_dir=`dirname "$script_path"`
- Определяем хеш-айди обрабатываемого коммита, приходит как параметр $HG_NODE с incoming хука
revision=$1
- Определяем файл с комит-меседжами, используется для того, чтобы пропускать коммиты с одинаковыми меседжами (зачастую, такие появляются при graft-e комитов)
messages_file='/home/viktor/autoreview/messages.txt'
- Определяем пользователя, сделавшего коммит (вывод коммит инфо (hg log -r $revision) + grep + sed для «выдирания» юзера)
user=`hg log -r $revision | grep 'user:' | sed -r 's/^user:s{0,}//g'`
- Определяем хеш-айди обрабатываемого коммита, приходит как параметр $HG_NODE с incoming хука
revision=$1
- Определяем строку соответствия пользователя Review Board пользователю, сделавшему коммит. Определяется путем поиска в специальном файле — users.txt, который выглядит следующим образом:
'Vasya Pupkin <Vasya.Pupkin@example.com>' vasya-p 'Ivan Petrov <Ivan.Petrov@example.com>' ivan-p
Для первой строчки: Vasya Pupkin <Vasya.Pupkin@example.com> — юзер, числящийся в коммитах, vasya-p — логин соответствующего юзера в Review Board
user_found=`grep "^'$user'" $script_dir/users.txt`
- Выдираем коммит месседж
commit_message=`hg log -r $revision | grep 'summary:' | sed -r 's/summary:s{1,}//g'`
- Проверяем, а нет ли такого меседжа в файле messages_file
message_found=`grep "$commit_message" "$messages_file"`)
- Проверяем, не мерж коммит ли это
merge_commit=`echo $commit_message | grep '^s{0,}[M|m]erge'`)
- Записываем коммит в message_file
echo $commit_message >> "$messages_file"
- Выдираем логин RB пользователя с строки соответствия
rb_user=`echo $user_found | sed -r "s/^'$user's//g"`
- Проверяем, а не апдейт ли это существующего ревью — договоренность апдейты обозначать RB-{{id}} в коммит меседже, где {{id}} — айди ревью в RB
review_id=`echo $commit_message | grep -o '[Rr][Bb]-[[:digit:]]{1,}' | sed -r 's/^[R|r][B|b]-//g'`
- Постим ревью
hg postreview --submit_as $rb_user -p $revision
или же апдейтим существующее
hg postreview -e $review_id --submit_as $rb_user -p $revision
- Определяем директорию, в которой находится скрипт
- Создаем скрипт, который будет делать pull нашего репозитория по расписанию /home/viktor/autoreview/pull_a5.sh
message_file='/home/viktor/autoreview/messages.txt' echo '' > "$message_file" cd '/home/viktor/autoreview/gdam_frontend' hg pull rm $message_file
Обратите внимание, в этом файле мы создаем пустой messages.txt, который используется в а5.sh для определения повторных вхождений коммита в текущий pull. После выполнения пула, мы этот файл удаляем.
- Добавляем в расписание cron созданный скрипт. Пример для выполнения каждые 5 минут:
0,5,10,15,20,25,30,35,40,45,50,55 * * * * /home/viktor/autoreview/pull_a5.sh
На выходе имеем следующую структуру директории autoreview:
/home/viktor/autoreview
--/gdam_frontend — клон репозитория, для которого организовываем code review
--/hgreviewboard — плагин Review Board для Mercurial
--*a5.sh — скрипт, выполняющий постинг ревью риквестов
--autoreview.log — лог действий скрипта a5.sh
--*pull_a5.sh — скрипт, выполняющий pull репозитория, запускается по расписанию с помощью сron
--users.txt — файл соответствия пользователей Mercurial пользователям Review Board
* означает, что файл имеет флажок «исполняемый».
И того, процесс постинга ревью риквестов у нас автоматизирован и происходит «незаметно» для коммитеров. Единственное, что нужно делать коммитерам — при коммите изменений для ревью тикета, коммит меседж должен начинаться с RB-{{ID}}, где {{ID}} — айдишник ревью тикета.
Что можно улучшить?
- Отфильтровывать коммиты закрытия веток (по аналогии с мержами)
- Отфильтровывать backout-коммиты (по аналогии с мержами)
- Усовершенствовать проверку уникальности коммит меседжей, чтобы она происходила без использования файла messages.txt (пока нет идей, как это можно сделать)
С радостью рассмотрю все замечания и комментарии.
Спасибо, что дочитали до конца!
Автор: zuzusik