Организация хранения кода в GitLab и интеграция код ревью в GitFlow

    Не так давно на одном из проектов нашей компании было принято решение наконец отказаться от использования Subversion для хранения и версионирования кода в пользу Git.



    Основными целями перехода были следующие:


    • Повышение прозрачности процесса разработки.
    • Внедрение обязательной процедуры код ревью до выноса обновлений на тестовые среды.
    • Внедрение непрерывной интеграции для сборки обновлений после код ревью и установки их на тестовые среды.

    Обязательным условием для достижения поставленных целей было использование GitLab (этот сервер Git уже использовался у заказчика и там даже уже жил код, относящийся к фронтовой части решения) и Jira (также уже использовалась у заказчика).


    В качестве целевой модели разработки было предложено использовать Git Flow, добавив в неё процедуру код ревью. Данная модель разработки де факто стала стандартом в разработке программного обеспечения с открытым исходным кодом и используется большинством гигантов индустрии. Именно поэтому её поддержка встроена во многие популярные средства работы с Git. На тему его использования написано большое количество материалов, приведу наиболее удачные из них для первоначального ознакомления: раз и два.


    Сама по себе эта модель предлагает лишь общие принципы ведения кода, оставляя за рамками процессы, сопутствующие его написанию. Поэтому реализация всего остального, в том числе код ревью, зависит от конкретного сервера Git. В этом плане наиболее удобен GitHub: он изначально строился как платформа для совместной работы большого количества независимых разработчиков и позволяет ограничивать права на отправку коммитов (Push) в репозитории с возможностью создания запросов на отправку кода. Помимо этого, GitLab предлагает свой рабочий процесс для ведения кода под названием GitLab Flow, заточенный под использование GitLab CI. Поэтому в GitLab функционал по созданию запросов на отправку кода не реализован и для проведения ревью кода изменений предлагается использовать запросы на слияние веток. Для сборки и установки артефактов на проекте уже использовался Jenkins, позволяющий гибко создавать и настраивать задачи сборки и развёртывания, и на GitLab CI было решено не переходить, попутно отбросив идею использования GitLab Flow.


    Также отмечу, что для проекта были настроены интеграции в Jira и Git. В Jira в плагине Git был добавлен для отслеживания репозиторий, созданный для хранения исходного кода, а в GitLab у данного репозитория была настроена интеграция с Jira в разделе «Интеграции» репозитория.


    Для решения данной задачи был разработан рабочий процесс для работы с кодом, по своей структуре схожий с Git Flow, но позволяющий производить ревью кода при каждом выносе изменений в основные ветки процесса (develop, release-n и master) средствами GitLab. Далее будет описан получившийся процесс, а также смежные с ним этапы непрерывной интеграции и доставки ПО на стреды. В скобках приведены соответствующие команды для выполнения.


    Репозиторий, созданный для хранения исходного кода, выкачивается в локальный репозиторий (git clone) и в нём инициализируется Git Flow (git flow init) — помимо ветки master (для создания тегов с целью хранения стабильных релизов) создаётся ветка develop (основная ветка разработки, в которую интегрируются ветки функций, релизов и исправлений), задаются маски для веток функций, релизов и исправлений, а также совершается переход в ветку develop.



    Далее в рабочую копию переносится актуальная ветка исходного кода из Subversion, производится коммит кода (git add -A + git commit -m “Commit message”) в ветку develop локального репозитория и его загрузка в удалённый репозиторий (git push origin develop). После этого можно начинать разрабатывать новый функционал, используя Git для версионирования кода.


    При разработке загружается актуальная версия ветки develop и из неё создаются ветки для разработки новых функций (git flow feature start MYFEATURE) в соответствии с кодами задач Jira, в рамках которых ведётся разработка.



    Автоматически производится переход в созданную ветку (git checkout MYFEATURE), запланированный функционал разрабатывается и изменения коммитятся в локальную ветку MYFEATURE (git commit -m “Commit message”). Заметим, что для корректной интеграции Git и Jira в сообщениях коммитов следует указывать код задачи в Jira, к которой это исправление относится. Тогда данные коммиты будут отображаться в соответствующих им задачах, а также в разделе «Коммиты Git» проекта, с помощью которого однозначно можно установить, что вошло в тот или иной релиз.


    Когда функционал выбранной задачи разработан и готов к выносу на среду тестирования, производится загрузка созданных коммитов в удалённую ветку с аналогичным названием (git push -u origin MYFEATURE) и на тимлида разработки или исполняющего его обязанности заводится запрос на слияние загруженной ветки с веткой develop.



    Для запроса на слияние разработчик разрешает конфликты слияния (в случае их наличия) и тимлид разработки (или и.о.) производит code review, в ходе которого возможно создание дополнительных коммитов (git commit -m “Commit message”) с исправлениями замечаний, полученных в ходе ревью кода, в ветке с новым функционалом и их отправка в центральный репозиторий (git push -u origin MYFEATURE). После успешного завершения ревью тимлид разработки (или и.о.) подтверждает слияние веток. Здесь не лишним является установка флага удаления ветки после слияния – в противном случае количество веток может быстро разрастись до неприличных масштабов.


    Чтобы обеспечить непрерывную интеграцию в репозитории GitLab, в разделе «Интеграции» настраивается Web Hook, который осуществляет вызов в Jenkins задачи для сборки и установки нового функционала на тестовую среду. Jenkins с помощью плагина для работы с Git выкачивает исходный код, получает из него название задачи и с помощью API Jira запрашивает список компонентов, которые были изменены и должны быть собраны, запускает процесс сборки, осуществляет прогон Unit тестов и при их удачном прохождении загружает созданные артефакты в Sonatype Nexus и устанавливает их на тестовую среду. Если же на одном из этапов произошёл сбой или Unit тесты завершаются неудачей, то с помощью плагина для Telegram команда разработки оповещается об исходе сборки. Если же установка прошла успешно, то команда QA оповещается о готовности задачи к тестированию.


    Если появляются дефекты, то производится загрузка актуальной версии ветки develop и от коммита слияния ветки MYFEATURE с веткой develop создаётся ветка hotfix-MYFEATURE (git checkout [BASECOMMIT] -b hotfix-MYFEATURE).



    При создании автоматически производится checkout в созданную ветку, вносятся исправления и изменения коммитятся в локальную ветку hotfix-MYFEATURE (git commit hotfix-MYFEATURE -m “Commit message”). Когда исправление закончено и готово к выносу на среду тестирования, производится их пуш в удалённую ветку с аналогичным названием (git push -u origin hotfix-MYFEATURE) и создаётся запрос на слияние с веткой develop.



    Для запроса на слияние разработчик разрешает конфликты слияния (в случае наличия) и производится code review, в ходе которого возможно создание дополнительных коммитов с исправлениями полученных замечаний. После успешного завершения ревью производится слияние веток. Сразу после переноса исправления в ветку develop также срабатывает Web Hook для вызова задачи в Jenkins для сборки, прогона Unit тестов, загрузки созданных артефактов в Sonatype Nexus и установки исправления на тестовую среду. Для исправлений работает аналогичный механизм оповещений.


    Если все дефекты исправлены, то производится загрузка актуальной версии ветки develop и от коммита слияния ветки hotfix-MYFEATURE с веткой develop создаётся ветка release-m.n (git flow release start RELEASENAME [BASECOMMIT]).



    Создание релизной ветки также инициализирует запуск Web Hook для вызова задачи в Jenkins, которая выкачивает исходный код из Git, получает из него название релизной ветки и с помощью API Jira запрашивает список компонентов, которые были изменены в рамках задач релиза, выкачивает актуальные версии из Sonatype Nexus и устанавливает их на среду регрессионного тестирования. Вслед за установкой релиза на среду регрессионного тестирования запускаются скрипты подготовки среды к тестированию (перезапуск приложений, очистка БД и пр.) и производится прогон регрессионных автотестов для проверки работы основного функционала системы, по результатам которого формируется отчёт с помощью плагина Allure Reports для Jenkins. После установки команда QA оповещается в Telegram о результатах прогона автотестов и готовности релиза к ручному регрессионному тестированию.


    Если в ходе регрессионного тестирования появляются дефекты, то производится загрузка актуальной версии ветки release-m.n и от последнего коммита создаётся ветка hotfix/BUGNAME по имени дефекта в Jira (git checkout -b hotfix/BUGNAME [BASECOMMIT]).



    Автоматически производится checkout в созданную ветку, вносятся необходимые исправления и изменения коммитятся в локальную ветку hotfix/BUGNAME (git commit hotfix/BUGNAME -m “Commit message”). Когда исправление закончено и готово к выносу на среду регрессионного тестирования, производится их пуш в удалённую ветку с аналогичным названием (git push -u origin hotfix/BUGNAME) и создаётся запрос на слияние с веткой release-m.n.



    Для запроса на слияние разработчик разрешает конфликты слияния (в случае наличия) и производится code review, в ходе которого возможно создание дополнительных коммитов с исправлениями замечаний, полученных в ходе ревью кода. Эти коммиты также производятся в локальную ветку hotfix/BUGNAME (git commit hotfix/BUGNAME -m “Commit message”) и производится их пуш в удалённую ветку с аналогичным названием (git push -u origin hotfix/BUGNAME). После успешного завершения ревью производится слияние веток. Слияние инициализирует запуск Web Hook для вызова задачи в Jenkins, аналогичной предыдущей, но отличающейся тем, что она выкачивает код из Git, получает из него название дефекта, с помощью API Jira запрашивает список компонентов, которые были изменены в рамках исправления, собирает эти компоненты, загружает в Sonatype Nexus и устанавливает их на среду регрессионного тестирования. Далее по аналогии производится подготовка среды к автотестированию, прогон регрессионных автотестов и нотификация о его результатах.


    Когда все дефекты исправлены, производится установка релиза на продуктивную среду. Для этого производится слияние ветки release-m.n с ветками develop и master, а также создаётся релизный тег.



    При его создании инициализирует запуск Web Hook для вызова задачи в Jenkins, которая выкачивает исходный код из Git, получает из него номер релиза и с помощью API Jira запрашивает список задач, которые вошли в релиз и компонентов, которые были изменены в рамках этих задач, после чего выкачивает актуальные версии артефактов из Sonatype Nexus и устанавливает их на продуктивную среду.


    С хотфиксами для прода было решено использовать процесс, аналогичный релизному – в противном случае теряются стадии тестирования выносимых изменений.


    При внедерении процесса также было проведено обучение для сотрудников, не имеющих практики работы с Git и GitLab, для которого была разработана соответствующая программа обучения. С её помощью вы сами сможете проводить обучение по использованию Source Tree и Intellij IDEA для работы с Git, а также GitLab для проведения ревью кода. В следующем посте приведу её, дополнив иллюстрациями.

    Неофлекс
    56,00
    Компания
    Поделиться публикацией

    Комментарии 10

      0
      Есть несколько важных нюансов:
      Для запроса на слияние разработчик разрешает конфликты слияния (в случае наличия)

      Решать конфликты можно только локально из-за бага Gitlab, который делает еще и бэкмерж.

      Вся политика слетает в первую неделю, если нет update гит хуков, которые проверяют всё — именование, исходную и целевую ветку слияния. Проверить именование можно только простой регулярочкой, типа такой:
      ^(refs\/heads\/)?(dev|master|private\/.+|feature\/.+|bugfix\/PX-[0-9]+|hotfix\/PX-[0-9]+|release\/[0-9]+\.[0-9]+\.[0-9]+(\.[0-9]+)?)

      Здесь не лишним является установка флага удаления ветки после слияния – в противном случае количество веток может быстро разрастись до неприличных масштабов.

      Чистить придется всё равно. Это можно сделать вручную через веб интерфейс, отсортировав бранчи по дате или простой коммандой в две строки:
      for branch in $(git branch -r | grep -Ev \'origin/release\' | grep -Ev \'origin/master\'); do if [[ "$(git log $branch --after "60 day" | wc -l)" -eq 0 ]]; then echo $branch | sed -e \'s:origin/::g\' >> ../for_delete_now.txt; fi; done

      Проверить всё ли ок по удаляемым веткам в for_delete_now.txt и не удалим ли мы лишнего и далее:
      cat ../for_delete_now.txt | while read branch; do git push origin --delete $branch; done; git remote prune origin


      Такая схема очень быстро обрастает энтропией и ломается, если её не поддерживать.
      К тому же гитлаб имеет очень странные баги, которые не исправляются годами и ломают некоторые моменты, например резолв конфликтов слияния, которые можно нормально сделать только локально, но сам МР только через веб, если нет руби. Пришлось переписывать МР на bash+curl через API что бы оно работало у разработчиков из CLI не затаскивая руби.
        0
        Спасибо большое за ценные дополнения!
        На счёт конфликтов — двояко: большая часть доступна для решения из GUI, но попадаются такие, которые можно решить только локально.
          0
          бекмерж не является багом как таковым. Это нормальный способ решения конфликтов, если ветка отводилась от той же ветки в которую сливается.
          Лучше конечно они бы делали промежуточную ветку, где решался бы конфликт, но уж что имеем.

          При чистке полезно бывает проверить, что ветка уже слита. Например так:
          git branch --list --remotes origin/* --merged=origin/master


          Удалять можно не git командами, а через GitlabAPI, в моем случае это оказалось быстрее раза в 3.
            0
            То ли у меня кривой инстанс (self-hosted не под моим управлением) то ли это баг, но API в этих эндпоинтах не стабилен.
            Проверку на мерж не делаю, потому что если прошло 2 месяца с последнего коммита, то ветку так и так надо удалять.

            0
            На всякий случай: [0-9] = \d
              0

              Ишью в трекере гитлабу соответствующие, надеюсь, созданы? Движение по ним есть ?

              0

              JenoOvchi я силюсь и пытаюсь понять: какое тут ноу-хау? Или зря? Или это просто описание Вашего подхода (и не более)?

                0
                Приветствую!
                По сути да — описание получившегося подхода.
                Когда я брался за эту задачу, то стал искать готовые варианты модели работы с Git, включавшие бы в себя ревью кода, и мне они не попались. Поэтому, когда мы пришли к более-менее рабочему варианту, я систематизировал получившийся материал и решил поделиться.
                З.Ы. Для контроля скинул текст паре человек на ревью — им показалось интересно :)
                0
                Процесс GitFlow реализует, в целом, всё необходимое. Один момент, который мне пока не удалось решить это непрерывность разработки в некоторых случаях. Например, случай когда создаётся ветка с новым функционалом, потом он закончен и отправляется запрос на code review. Какое-то время занимает тестирование и проверка, потом только разработчик получает feedback. В это время разработчик конечно может продолжить работу и начать новую ветку с новым функционалом. Но возникают вопросы когда в новом функционале нужен код из предыдущей ветки, а он ещё согласовывается и проверяется. Из-за этого разработчику надо либо ждать, либо как-то пробовать начать работу с уже изменёнными файлами и потом заниматься решением конфликтов при слиянии. В итоге пока так и не придумали как такие случаи правильно решать. Может быть у кого-то уже был подобный опыт?
                  0
                  Был. Выходят по разному. Мне нравится вариант отведения новой фичи от старой.
                  тогда в реквесте новой фичи видно попала ли старая уже в dev или нет.
                  Но в других командах вижу что ведут «спринт-ветки» или что-то подобное, что в dev попадает уже бОльшим куском чем «одна фича».

                  Впрочем все равно это плохие кейсы, т.к. при провале Code-Review возможно придется исправлять и старую фичу и новую, на нее завязанную.

                Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

                Самое читаемое