В этой статье я расскажу про то как я пытался создать бета-стенд и встроить его в обычный gitflow. Совместно с читателями мы пройдем путь от проблем связанных с этим до новой схемы работы с гитом.
Наш Gitflow
В нашей компании мы использовали всем известный gitflow. Те, кто знает, что это такое может сразу перейти к следующему разделу. Для тех, кто не знает, расскажу.
Основная работа ведется в development ветке. Под каждую новую фичу создается отдельная feature-ветка. При слиянии feature-ветки в development осуществляется сборка и выкладка приложения на тестовый стенд, где QA специалисты проверяют её работу.
Под каждый найденный баг от development создаётся hotfix ветка, в которой он устраняется. Далее hotfix ветка сливается обратно в development — и все по новой: тестовый стенд обновляется и QA снова проверяет.
Когда development ветка отдебажена и в ней накопилось достаточное количество фич для релиза создается release-ветка. В ней всегда находится код, который в любой момент можно смержить в мастер и тем самым обновить продакшн стенд.
Предпосылки создания бета-стенда
За счет наличия петли в описанной схеме: выкатили на тестовый стенд, проверили, исправили, снова выкатили, исчезает огромное количество ошибок. Но, увы, не все.
Конечно наш QA хорошо делает свою работу, количество багов по мере приближения к мастеру уменьшается, но устранить их все не получается по следующим причинам:
-
Реальное поведение пользователей гораздо сложнее и непредсказуемей синтезированных тест-кейсов.
-
Не учитываются особенности реальных пользователей (устройство, ОС, браузер, персональные настройки и т.п.).
- Тестирование осуществляется на тестовом стенде с тестовой базой данных, а она отличается от реальной базы данных (имеет артефакты).
Цели и задачи бета-стенда
Устранить указанные проблемы с тестированием мы решили с помощью beta стенда — т.е. дать нашим внутренним пользователям системы, заказчикам, доверенным клиентам и прочим лицам ранний доступ.
Теперь после development стенда, где их проверяет QA специалист, новые фичи попадают на beta стенд, где с ними работают реальные пользователи. Они оповещаются о начале бета-тестирования сразу после обновления бета-стенда. Ошибки в бета-версии приложения отображаются в системе логирования. Периодически они фиксятся и бета-стенд обновляется. Когда ошибок больше не возникает создается релиз. Таким образом широкая аудитория пользователей получает стабильную версию приложения.
На этапе беты тестирования есть возможность получить обратную связь от пользователей, узнать будет ли введеный функционал удобен широкой аудитории и что нужно изменить. Beta является своего рода пилотной версией приложения.
Такая схема соответствует основным стадиям разработки: альфа, бета и релиз.
Мы обсудили аспект Continuous Delivery, т.е. в какой момент создавать предрелизы и релизы. Самое время перейти к Continuous Integration, т.е. разработать саму схему работы с git с учетом beta.
Попытка внедрить бета в Gitflow
Первое, что приходит на ум — использовать release ветку для деплоя на beta. Release ветка в таком случае рассматривается как намерение релиза, т.е. говоря иначе — это предрелиз (почти что бета). А что гармонично получается, и ничего менять в gitflow не надо. Нужно только накрутить новое правило в CD для выкладки билда на создание/изменение release ветки и все.
Такая схема приблизительно будет выглядеть так:
Замечание: На графе пунктирными линями обозначены BASE коммиты.
Что происходит на графе?
- Под задачу создается feature ветка от development
- Ветка feature сливается обратно в development ветку. При изменении development ветки осуществляется выкладка на development стенд. QA Специалист начинает тестирование.
- Под найденные ошибки от development создается hotfix ветка, в которой ошибки устраняются и она вмерживается обратно. Если ошибок не найдено, от development ветки создается release ветка, чтобы зафиксировать стабильное состояние development ветки.
- При создании release ветки создаётся сборка приложения (release condidate), которая выкатывается на beta стенд. Пользователям рассылается оповещение (например в таск менеджере создается коммент у соответствующих задач или в мессенджере создается сообщение).
- Когда release ветка отдебажена она сливается в master ветку, происходит продакшн сборка приложения и выкатывание на продакшн.
- Если на бою находится баг, от мастера создается hotfix ветка, в которой он исправляется. Далее она протягивается по всем веткам путем последовательного слияния.
На первый взгляд рабочая схема. Теперь давайте рассмотрим её плюсы и минусы.
Плюсы:
-
Небольшое количество основных веток, которые нужно поддерживать в актуальном состоянии. Большую часть времени их будет всего 2-е: dev и master.
- Есть возможность обновлять основные ветки прямо из github при помощи создания PR — отпадает необходимость стягивать ветку к себе, чтобы произвести ребейз. Тем не менее потребуется стягивать ветку к себе чтобы проставить новую версию, а также в случае конфликта.
Минусы:
-
Первая проблема возникает когда требуется внести hotfix в master, не дожидаясь подхода нового релиза. Ладно, можно вмержить hotfix в master и затем в release и dev в общем-то это не проблема (разве что с мержами запутаться можно). Проблема появляется когда релизной ветки нет. А как обновить бета-окружение без релизной ветки, а никак, разве что вручную. Не правильно как-то, у нас же настроена CD схема, скажете вы, и я соглашусь.
-
Есть временной лаг при внесении hotfix в основные ветки. На представленной схеме флоу внесения hotfix в мастер выглядит так: hotfix → master → dev → release. А должно быть так: hotfix → master → release → dev, поскольку release важнее dev и в ней изменения должны появиться раньше. Смержить dev, при внесении в него hotfix, в ветку release сразу может не получиться — dev может содержать изменения, которые не должны попасть в текущую (открытую) release ветку. Таким образом нужно ждать следующего релиза прежде чем hotfix появится на бета-стенде. Или например если в beta потребуется внести hotfix будет такой флоу: hotfix → beta → master → dev, а должен быть такой: hotfix → beta → dev → master. В этой схеме нарушается принцип причинности.
-
Регрессионное обновление основных веток (release и dev) происходит через merge commit, что усложняет накладывание CD схемы на CI. Также в этой схеме легко запутаться — большое количество степеней свободы. Например можно hotfix слить в master, потом слить в release ветку, но забыть о dev.
-
Автоматическое назначение версии невозможно из-за обновляющих ветки "слева" мерж-коммитов. Придется вручную проставлять номера версии, в которых можно запутаться. Потребуется делать версионный коммит вручную, в нужной ветке. Также существует вероятность того, что мейнтейнер забудет установить тег и тогда приложение будет выкачено со старой версией, что приведет к некорректному логгированию ошибок.
- Неочевидный способ обновления бета-сцены. Релизная ветка после слияния удаляется — в этом особенность схемы.
Новая CI схема с бетой
Давайте попробуем избавиться от этих проблем.
Поскольку beta стенд существует всегда — нужно сделать так, чтобы release ветка существовала всегда. Давайте назовем её beta. Тогда станет возможным вносить hotfix на бета-стенд в отсутствие release ветки. Но в таком случае проблема с избыточным количеством merge commit усилится, поскольку количество веток увеличилось. Для решения этой проблемы достаточно отказаться от обновления основных веток стратегией merge.
Графически отобразить получившийся флоу можно следующим образом.
Замечание: На графе пунктирными линями обозначены BASE коммиты.
Что происходит на графе?
- Под задачу создается feature ветка, также как и в случае gitlow.
- Ветка feature сливается обратно в development ветку.
- От development ветки создается pre-release ветка, чтобы зафиксировать стабильное состояние development ветки. Осуществляется сборка и выкладка на development стенд. QA специалист начинает тестирование.
- Если были найдены баги — от pre-release ветки создается hotfix ветка, в ней баги устраняются и ветка сливается обратно. После этого все ветки левее pre-release ребезятся от неё. Если фичи прошли проверку QA и готовы к публичному тестированию мы вмерживаем pre-release ветку в beta ветку
- При изменении beta ветки создается бета-сборка приложения, которая выкатывается на бета-стенд. Пользователи получают ранний доступ к фичам. В процессе пользования бетой в системе логирования аккумулируются возникающие ошибки. Которые периодически исправляются с помощью hotfix ветки. Далее hotfix ветка вмерживается обратно в beta ветку — стенд обновляется. На каждое изменение beta ветки основные ветки слева нужно ребейзить от неё. Если уже существуют pre-release ветки — их нужно отребейзить от dev.
- Когда в бета-версии приложения устранены все ошибки, создается release ветка.
- Ветка release вмерживается в мастер.
- Если в продакшне находится баг, от master создается hotfix, в котором он исправляется. Далее hotfix протягивается по всем веткам путем переноса BASE основных веток на HEAD веток, от которых те были созданы.
Замечание: обновлением основных веток на всех стадиях (pre-alpha, alpha, beta...) занимается ответственный за релиз (мейнтейнер проекта). Члены команды работают только c dev веткой.
Схема выглядит довольно круто, не правда ли? Давайте рассмотрим её плюсы и минусы и сравним её с предыдущей.
Плюсы:
-
Более четкая роль беты в CI/CD. Не будет проблем с хотфиксами и протягиванием изменений через нее. Т.е. принцип причинности не нарушается, флоу будет таким: hotfix → master → beta → dev.
-
Возможно автоматизированное проставление версий и сбор changelog, что особенно важно для библиотек. Нет вероятности ошибиться.
-
Тесты лишний раз не запускаются, что ускоряет процесс принятия изменений.
-
Нет лишних мерж коммитов.
- CI / CD полностью соответствует стадиям разработки (wikipedia) (pre-alpha, alpha, beta, release-condidate, release, post-release).
Минусы:
-
Большое количество веток. Но это не страшно поскольку ими занимается ответственный за релиз. Также у каждой ветки своя роль в автоматизированном тестировании, об этом пару слов напишу ниже.
- Нет возможности управлять актуальностью веток через github. Тем не менее в случае merge стратегии (которая используется в предыдущей схеме) могут возникать конфликты, из-за которых все равно придется стягивать ветку на локальную машину.
Связь со стадиями разработки
Предложенная схема полностью отвечает всем стадиям разработки программного обеспечения.
-
пре-альфа — фичи сделаны, но еще не протестированы QA специалистом. В них может содержаться большое количество багов.
-
альфа — на этом этапе приложение собирается и выкатывается на development стенд, где QA мануальщик проверяет его работу.
-
бета — условно стабильная сборка приложения, протестированная QA специалистом. Сборка на этом этапы выкладывается на бета стенд для тестирования реальными пользователями.
-
релиз кондидат — сборка прошедшая все этапы тестирования и теперь ожидающая своего релиза.
- релиз — стабильная версия приложения.
У наших проектов большое количество автоматизированных тестов. Выполнение всех тестов занимает примерно 1 час. Чтобы ускорить принятие PR в ветках на каждом этапе внедрения фичи мы выполняем только важные для этого этапа тесты. Например для принятия кода в pre-alpha мы запускаем самые простые тесты: lint и unit. На этапе принятия beta выполняются также интеграционные тесты. На этапе release-condidate помимо озвученных тестов запускаются также acceptance тесты. Причем на этом этапе тесты запускаются на раннерах с разными ОС и под разными браузерами. После создания релиза (этап post-release не обозначенный в схеме) запускаются smoke тесты.
Задавайте вопросы в комментариях, если что-то интересное осталось за кадром.
Ссылки:
- Стадии разработки программного обеспечения
- Удачная модель ветвления для Git
- Continuous Integration
- Continuous Delivery
Автор: ajile