18 July 2013

Review Board + Mercurial — опыт внедрения и автоматизации процесса code review

ProgrammingVersion control systemsMercurial
mercurial-review-boardНекоторое время назад в компании, где я работаю в связи с расширением комманды было принято решение о введении процесса 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).

  1. Клоним наш репо в папку ~/home/viktor/autoreview/gdam_frontend/
  2. Клоним плагин для RB в папку ~/home/viktor/autoreview/hgreviewboard/
  3. В Review Board создаем пользователя c интуитивным логином review_bot и даем ему право постить ревью от лица другого пользователя («Can submit as user» permission)
  4. Идем в конфигурационный файл репозитория /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

  5. Теперь создаем повешенный на 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
    

    Пройдемся по ключевым моментам скрипта:

    1. Определяем директорию, в которой находится скрипт
      script_path=`readlink -f "$0"`
      		script_dir=`dirname "$script_path"`
    2. Определяем хеш-айди обрабатываемого коммита, приходит как параметр $HG_NODE с incoming хука
      revision=$1
    3. Определяем файл с комит-меседжами, используется для того, чтобы пропускать коммиты с одинаковыми меседжами (зачастую, такие появляются при graft-e комитов)
      messages_file='/home/viktor/autoreview/messages.txt'
    4. Определяем пользователя, сделавшего коммит (вывод коммит инфо (hg log -r $revision) + grep + sed для «выдирания» юзера)
      user=`hg log -r $revision | grep  'user:' | sed -r 's/^user:\s{0,}//g'`
    5. Определяем хеш-айди обрабатываемого коммита, приходит как параметр $HG_NODE с incoming хука
      revision=$1
    6. Определяем строку соответствия пользователя 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`
    7. Выдираем коммит месседж
      commit_message=`hg log -r $revision | grep  'summary:' | sed -r 's/summary:\s{1,}//g'`
    8. Проверяем, а нет ли такого меседжа в файле messages_file
      message_found=`grep "$commit_message" "$messages_file"`)
    9. Проверяем, не мерж коммит ли это
      merge_commit=`echo $commit_message | grep '^\s\{0,\}[M|m]erge'`)
    10. Записываем коммит в message_file
      echo $commit_message >> "$messages_file"
    11. Выдираем логин RB пользователя с строки соответствия
      rb_user=`echo $user_found | sed -r "s/^'$user'\s//g"`
    12. Проверяем, а не апдейт ли это существующего ревью — договоренность апдейты обозначать RB-{{id}} в коммит меседже, где {{id}} — айди ревью в RB
      review_id=`echo $commit_message | grep -o '[Rr][Bb]-[[:digit:]]\{1,\}' | sed -r 's/^[R|r][B|b]-//g'`
    13. Постим ревью
      hg postreview --submit_as $rb_user -p $revision
      или же апдейтим существующее
      hg postreview -e $review_id --submit_as $rb_user -p $revision

  6. Создаем скрипт, который будет делать 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. После выполнения пула, мы этот файл удаляем.
  7. Добавляем в расписание cron созданный скрипт. Пример для выполнения каждые 5 минут:
    0,5,10,15,20,25,30,35,40,45,50,55 * * * * /home/viktor/autoreview/pull_a5.sh
    в коментах подсказали более оптимальный вариант записи:
    */5 * * * * /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}} — айдишник ревью тикета.

Что можно улучшить?

  1. Отфильтровывать коммиты закрытия веток (по аналогии с мержами)
  2. Отфильтровывать backout-коммиты (по аналогии с мержами)
  3. Усовершенствовать проверку уникальности коммит меседжей, чтобы она происходила без использования файла messages.txt (пока нет идей, как это можно сделать)

С радостью рассмотрю все замечания и комментарии.
Спасибо, что дочитали до конца!
Tags:code reviewreview boardmercurialtortoisehg
Hubs: Programming Version control systems Mercurial
0
11.9k 35
Comments 24
Top of the last 24 hours