Как силами мобильных разработчиков автоматизировать процесс разработки
Всем привет! Меня зовут Женя Мельцайкин, я старший инженер-программист в компании Контур. Большую часть времени в Контуре я работал и работаю 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. И уже на основе этого источника правды, мы обновляем статусы задач, добавляем дополнительную информация к задачам и так далее.
Для построения такой интеграции мы рассматривали несколько решений:
Базовая интеграция Youtrack и Gitlab. Данная интеграция нам не подошла, так как в наших статусах задачах есть дополнительные поля, которые нужно заполнить перед тем, как поменять статус задачи. Сразу увидели это ограничение и стали искать дальше.
Решение на основе Youtrack Workflow. Тоже не подошло, так как оно работает наоборот: за счет событий Youtrack мы можем влиять на внешние сервисы. Также у данного решения есть проблемы с хранением секретных данных: токены авторизации, логины, пароли и так далее.
Решение на основе 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? = 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, то отправится запрос по нашему адресу и выведется сообщение в консоль
С полным кодом можно ознакомиться по ссылке.
Заключение
Немного о результатах нашей автоматизации. Полноценно сервер автоматизации мы запустили 30 августа 2024 и постепенно добавляли новую функциональность и за это время сервер автоматизации:
Обновил 602 статуса
Дозаполнил 1424 полей в задачах
Проинформировал 32 раза о новых релизах
Напомнил 132 раза о забытых ревью
Запросил 15 ежедневных отчетов
Уменьшил количество негодования тестировщиков и менеджеров проектов :)
Делитесь в комментариях своим опытом автоматизации ваших процессов. Какие технологии используете и с какими трудностями сталкивались. Буду рад почитать и подискутировать с вами!