Как силами мобильных разработчиков автоматизировать процесс разработки

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

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

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

132733248d08b13048ddfedbd3fb35ea.png

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

Итак, внешних команд много, а команда мобильной разработки одна. Так что в нашем процессе работы самое главное — это прозрачность. Поэтому в прошлом году мы полностью пересмотрели процесс работы с задачами и теперь кроме обычных статусов 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

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

dc2672322c2c91baea815f3b3cad79d3.png

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

e7d9081afe8368c3dcd8a111254608cf.png

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

167ec769e23a89af8c262a7bb9defd1c.png

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

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

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

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

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

edc9fd2d58640441bb74e893e1012348.png

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

d04448035a415be0b55f6f8379085935.png

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

23e28ab8c5e8d29f312230a14f658603.png

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

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

9e5ce500484fef2fc6c3fe5e0c4ac143.png

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

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

b01709cfe800ca73ff1bda19c47c3f61.png

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

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

@Serializable
data class ApiMergeWebhookDto(
   @SerialName("object_attributes")
   val objectAttributes: ApiObjectAttributesDto,
   @SerialName("reviewers")
   val reviewers: List? = 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() // Принимаем 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, то отправится запрос по нашему адресу и выведется сообщение в консоль

4224df4af8103c0c46593fb7d0493e39.png

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

Заключение

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

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

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

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

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

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

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

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

© Habrahabr.ru