Всем привет! Меня зовут Женя Мельцайкин, я старший инженер-программист в компании Контур. Большую часть времени в Контуре я работал и работаю Android-разработчиком, но статья будет не про Android и даже не про мобильную разработку. А про команду Мобильной разработки в Контуре и про наши процессы разработки! Давайте же начнем разбираться, причем тут мобильные разработчики, автоматизация и бэкенд.

Про наши процессы

В Контуре на данный момент более 20 мобильных разработчиков. Но количество приложений «немного» больше – почти в 2 раза, если считать по платформам. И помимо приложений у нас есть общие модули: дизайн система, авторизация, чат и другие.

У каждого приложения своя команда, свои менеджеры проектов, свои внешние команды-интеграторы, но у всех приложений общие процессы у команды мобильной разработки. Хотя так было не всегда. 

Итак, внешних команд много, а команда мобильной разработки одна. Так что в нашем процессе работы самое главное – это прозрачность. Поэтому в прошлом году мы полностью пересмотрели процесс работы с задачами и теперь кроме обычных статусов Backlog, Development, Review, Testing и Done добавились новые. Вся статусная модель выглядит так:

  • Backlog – здесь собираются задачи, без доказанной бизнес ценности

  • Submitted – сюда падают все новые задачи для дальнейшего обсуждения

  • Open – статус, где задачи с подтвержденной пользой для проекта

  • On hold – сюда попадают задачи, которые блокированные внешними факторами

  • System engineering – этап проектирования задачи. Тут проходит подготовка аналитики, дизайнов, API. 

  • Ready to dev – задача готова к разработке

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

  • Development – активная разработка задачи

  • Review – задача на ревью

  • Waiting for build – задача прошла ревью, но ожидает тестовую сборку в сервис тестирования

  • Ready for QA – сборка с задачей загружена в сервис тестирования и ожидает тестировщика

  • Testing – задача в тестировании

  • Done – задача выполнена в полном объеме и ошибок в ней не обнаружено

  • Verified – задача прошла регрессионное тестирование

  • Released – задача вышла в релиз

Решив проблему прозрачности статусов задач, мы создали себе другую проблему – стало труднее поддерживать задачи в актуальном статусе. Данная проблема особо сильно возникает на статусах, где происходит активная разработка задачи: Development, Review, Waiting for build и Ready for QA. У части нашей команды осталась привычка держать статусы по задачам в голове. Не все сходу привыкли к расширенному перечню статусов, а когда задачи не в актуальном статусе, наши тестировщики и менеджеры начинают негодовать. Самое главное, теряется прозрачность процесса.

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

Какие были варианты

Прежде чем решать проблему неактуальных статусов, нужно верхнеуровнево определить, что для этого нужно. В качестве трекера задач мы используем YouTrack и Gitlab для хранения и управления репозиториями Git. Так как мы решаем проблему в статусах активной разработки, то хотим, чтобы для наших статусов был один источник правды — Gitlab. И уже на основе этого источника правды, мы обновляем статусы задач, добавляем дополнительную информация к задачам и так далее. 

Для построения такой интеграции мы рассматривали несколько решений:

  1. Базовая интеграция Youtrack и Gitlab. Данная интеграция нам не подошла, так как в наших статусах задачах есть дополнительные поля, которые нужно заполнить перед тем, как поменять статус задачи. Сразу увидели это ограничение и стали искать дальше.

  2. Решение на основе Youtrack Workflow. Тоже не подошло, так как оно работает наоборот: за счет событий Youtrack мы можем влиять на внешние сервисы. Также у данного решения есть проблемы с хранением секретных данных: токены авторизации, логины, пароли и так далее. 

  3. Решение на основе Gitlab Webhook и своего сервера. А вот это звучит интереснее!

Про Gitlab Webhook

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

Gitlab с помощью Webhook отправляет нашему серверу информацию о произошедшем событии. Сервер принимает событие, обрабатывает его и отправляет на его основе необходимый Http запрос в API Youtrack’a. Такая структура позволяет добавлять неограниченное количество внешних сервисов, например мы дополнительно отправляем информацию о новых релизах общих модулей в наш корпоративный мессенджер.

Либо уведомляем разработчиков если ревью задерживается.

Так как мы мобильные разработчики, а ещё и Android-разработчики, то и стек для нашего сервера мы подбирали такой, чтобы нам с ним было удобно работать. В качестве языка программирования выбрали Kotlin, а в качестве фреймворка для сервера Ktor. И это всё, что нам нужно! 

Как это работает

Давайте реализуем небольшой пример, который будет отправлять сообщение в мессенджер при совершении какого-либо действия с merge request в Gitlab.

Для начала нам нужно создать базовый проект сервера. Это можно сделать с помощью помощника генератора проектов ktor. В данном помощнике есть много плагинов, которые можно заранее выбрать для вашего проекта: логирование, документирование, авторизация, база данных и ещё много всего. 

Как только вы создадите проект, то сразу сможете запустить у себя локально и проверить, что всё работает.

Чтобы Gitlab сообщал нашему серверу информацию о событии, нужно настроить Gitlab Webhook в нашем репозитории. Для этого зайдите в Gitlab Settings -> Webhooks.

Нажмите «Add new webhook». Введите путь адрес для отправки события.

Обратите внимание: чтобы Gitlab смог доставить событие на ваш сервер, нужно открыть доступ к вашему внешнему IP-адресу в настройках вашего роутера.

Далее выбираем событие, на которое нужно реагировать. В нашем случае это merge request.

Нажимаем на кнопку «Add webhook».

Теперь при любом взаимодействии с merge request Gitlab будет отправлять событие нашему серверу. Более подробно про Gitlab Webhook можно почитать в официальной документации.

Теперь напишем обработчик событий на стороне сервера. Для этого определим основные классы для работы с merge request.

Класс обертка под webhook эвент

@Serializable
data class ApiMergeWebhookDto(
   @SerialName("object_attributes")
   val objectAttributes: ApiObjectAttributesDto,
   @SerialName("reviewers")
   val reviewers: List<ApiWebhookUserDto>? = null,
)

Класс с общей информацией о событии

@Serializable
data class ApiObjectAttributesDto(
   @SerialName("action") val action: ApiMergeAction? = null,
   @SerialName("url") val url: String,
)

Класс с информацией о пользователе

@Serializable
data class ApiWebhookUserDto(
   @SerialName("email") val email: String?,
   @SerialName("username") val username: String,
)

Перечисление событий с merge request

@Serializable
enum class ApiMergeAction {
   @SerialName("open")
   Open,
   @SerialName("merge")
   Merge,
   @SerialName("update")
   Update;
}

Далее нужно написать клиент для работы с мессенджером. В качестве примера я буду выводить сообщение в консоль. Вы же можете написать интеграцию с любым мессенджером, которое предоставляет свое API: telegram, mattermost, slack, email и другие.

class LoggerMessengerClient : MessengerClient {
   private val logger = System.getLogger(Logger.ROOT_LOGGER_NAME)
   override fun sendMessage(
       channelId: String, message: String
   ) {
       logger.log(System.Logger.Level.INFO, "Message receive to channel $channelId.\n$message")
   }
}

Последним шагом остается только принять эвент от gitlab на стороне нашего сервера.

fun Application.configureGitlabWebhooks() {
   val messengerClient: MessengerClient = LoggerMessengerClient()
   routing {
       post("gitlab/merge/webhook") { // Задаем путь для нашего запроса
           val request = call.receive<ApiMergeWebhookDto>() // Принимаем body запроса
           val message = when (request.objectAttributes.action) { // Обрабатываем действие с merge request
               ApiMergeAction.Open -> {
                   val reviewers = request.reviewers?.joinToString { "@${it.username}" } ?: "Empty"
                   "[Merge request](${request.objectAttributes.url}) was opened. Reviewers: $reviewers"
               }


               ApiMergeAction.Merge -> {
                   "[Merge request](${request.objectAttributes.url}) was merged."
               }


               ApiMergeAction.Update -> {
                   "[Merge request](${request.objectAttributes.url}) was updated."
               }


               null -> {
                   return@post call.respond(HttpStatusCode.NoContent)
               }
           }
           messengerClient.sendMessage(
               channelId = "68ff6cad-c31b-461c-855e-47323341fd9c",
               message = message
           )
           call.respond(HttpStatusCode.OK)
       }
   }
}

Теперь если мы произведем какое-либо действие с merge request в нашем Gitlab, то отправится запрос по нашему адресу и выведется сообщение в консоль

С полным кодом можно ознакомиться по ссылке.

Заключение

Немного о результатах нашей автоматизации. Полноценно сервер автоматизации мы запустили 30 августа 2024 и постепенно добавляли новую функциональность и за это время сервер автоматизации:

  1. Обновил 602 статуса

  2. Дозаполнил 1424 полей в задачах

  3. Проинформировал 32 раза о новых релизах

  4. Напомнил 132 раза о забытых ревью

  5. Запросил 15 ежедневных отчетов

  6. Уменьшил количество негодования тестировщиков и менеджеров проектов :)

Делитесь в комментариях своим опытом автоматизации ваших процессов. Какие технологии используете и с какими трудностями сталкивались. Буду рад почитать и подискутировать с вами!

Комментарии (3)


  1. AleksSl
    11.11.2024 09:44

    Ну ради статусов писать свою приблуду - то такое. )
    Да и весь этот ворох статусов работает ровно до того момента, пока есть кто-то кто за этим следит. Как только "инициатива будет утеряна" все это... Это как с канбаном )
    И тогда, из-за большого количества статусов все начнут путаться, особенно если настроен автоперевод на других людей при изменении статуса (например пример "Ready for QA" может автоматом переводить таски на лида QA). Поэтому статусов лучше делать меньше, а не больше. "Прозрачность" лучше принести в жертву функциональности )

    Но, если рассматривать с точки зрения технического решения, вполне интересно получилось.


    1. Newbilius
      11.11.2024 09:44

      Этот ворох статусов работает и не теряется, когда они делают удобно всем людям в цепочке разработки, т.е. пользуется ими не только менеджер, а все в команде :) Например разработчик пушнул в нужную ветку - автоматом ушла задача на ревью, сервер пинганул ревьюера, тестеру пришла в чат ссылка на сборка и т.п. Не нужно ничего триггерить и делать руками, одно действие - куча результата. Кайф!


  1. iborisikhin
    11.11.2024 09:44

    Такое нам надо!