Если вы когда-нибудь сталкивались с продажами, то наверняка знаете про такое понятие, как "боль клиента". Означает оно, что у каждого клиента есть потребности, определив которые, ему можно продать товар либо услугу для решения его задач.
Данная статья и родилась вследствие того, что на текущем проекте в нашей команде разработки есть боль: когда на беке вливают МР в ветку develop, то стенд становится временно недоступен из-за деплоя. Основные участники, испытывающие эту боль — фронтенд разработчики и аналитики, которым чаще всего приходится взаимодействовать с бекендом. И во время недоступности стенда непонятно, деплой идёт или стенд стал временно недоступен из-за ошибки.
Нет, конечно, можно зайти в Gitlab, проверить, идёт ли сейчас деплой или посмотреть логи контейнера. Но для этого нужно быть в курсе, из-за деплоя каких конкретно микросервисов (естественно мы говорим о микросервисной архитектуре) стенд может быть временно недоступен. Ну и плох тот программист, который не хотел бы упростить жизнь себе и коллегам, автоматизировав при этом чаво-нибудь! :)
Как уже понятно из названия статьи, мы будем использовать цепочку из 3 звеньев: Gitlab Webhook + Yandex Cloud Functions + Telegram Bot. Аналогичные статьи-инструкции вы вполне вероятно видели в этих ваших интернетах, но подобной статьи с такой связкой лично я не нашёл (хотя прям в глубинах глубин гугла и яндекса я не ковырялся, отмечу справедливости ради).
Кратенькое ТЗ: нам нужно, чтобы Telegram-бот присылал уведомления каждый раз, когда на беке вливают МР и стартует пайплайн с деплоем в последней джобе, из-за чего временно становится недоступен дев стенд. Итак, давайте перейдём к делу.
Для начала нам нужно создать Telegram-бота. Это делается очень легко: в самой телеге переходим в праотца всех ботов @BotFather и создаём бота командой “/newbot”. После успешного создания бота, в ответ мы получим сообщение с его токеном. Он нам скоро понадобится, не теряем его.
На втором этапе нашей цепи переходим в Yandex Cloud под своей учётной записью, либо создаём новую, заполняем данные платёжного аккаунта, привязываем карту и переходим в раздел функций Яндекс. В месяц 1.000.000 вызовов бесплатно, если что:
Создаём новую Яндекс-функцию и переходим в редактор кода. Мой пример кода написан на Node.js, есть куча вариантов альтернативных решений, тут уже что вашей душе угодно, как грится :)
Непосредственно сам фрагмент кода:
module.exports.handler = async function (event, context) {
var action = JSON.parse(event.body).object_attributes.action;
if (action === 'merge') {
require('https').get('https://api.telegram.org/bot<TELEGRAM_BOT_TOKEN>/sendMessage?chat_id=<TELEGRAM_CHAT_ID>&text=%D0%9E%D0%B1%D0%BD%D0%BE%D0%B2%D0%BB%D1%8F%D0%B5%D1%82%D1%81%D1%8F%20%D0%B1%D0%B5%D0%BA%D0%B5%D0%BD%D0%B4.%20%D0%92%20%D0%B1%D0%BB%D0%B8%D0%B6%D0%B0%D0%B9%D1%88%D0%B8%D0%B5%2010%20%D0%BC%D0%B8%D0%BD%D1%83%D1%82%20%D0%B2%D0%BE%D0%B7%D0%BC%D0%BE%D0%B6%D0%BD%D1%8B%20%D0%BF%D0%B5%D1%80%D0%B5%D0%B1%D0%BE%D0%B8%20%D0%B2%20%D0%B5%D0%B3%D0%BE%20%D1%80%D0%B0%D0%B1%D0%BE%D1%82%D0%B5%20%D0%BD%D0%B0%20dev%20%D1%81%D1%82%D0%B5%D0%BD%D0%B4%D0%B5.');
}
return {
statusCode: 200,
body: typeof event.body === 'string' ? JSON.parse(event.body).x : event.body,
};
};
Вместо “<TELEGRAM_BOT_TOKEN>” в коде нужно вставить токен-бота, который мы получили после его создания в респонсе от @BotFather, а вместе “<TELEGRAM_CHAT_ID>” — айдишник чата, в который наш бот будет отправлять указанный текст. Значение параметра text в urlencoded.
С параметрами chat_id есть нюансы, с которыми мне пришлось столкнуться. Чтобы получить этот id, я зашёл в Telegram через браузерную веб-версию, выбрал нужный мне чат и из урла скопировал id. И начал тестировать, после чего несколько дней не понимал, почему бот не присылает уведомления, хотя я перепробовал самые разные вариации. В итоге пришёл к тому, что это не тот chat_id, а чтобы получить нужный айдишник, нужно:
1. Добавить бота в чат, в который нужно отправлять уведомления.
2. Сделать GET запрос по адресу: https://api.telegram.org/bot<TELEGRAM_BOT_TOKEN>/getUpdates
3. Найти в респонсе рядом с именем нужного чата его идентификатор.
Возвращаемся к Yandex Cloud Function. В поле "Точка входа” вписываем значение “index.handler” для данного фрагмента кода выше. Остальные значения оставляем дефолтными. Затем переходим в раздел “Обзор” слева, и для поля “Публичная функция” меняем значение с false на true. Из поля “Ссылка для вызова” чуть выше копируем значение URL, оно нам понадобится на следующем шаге.
И теперь в Gitlab нам необходимо настроить webhook в модуле, уведомления которого мы хотим получать. Для этого нужно обладать правами мейнтейнера. Слева переходим в раздел "Settings” и выбираем пункт “Webhooks”. Снимаем дефолтную галочку с триггера “Push events” и вешаем галочку на триггер “Merge request events”. Дефолтную галочку “Enable SSL verification” внизу страницы оставляем в значении true. В URL прописываем ссылку на Yandex-функцию из поля “Ссылка для вызова”.
И вуаля! Теперь всё должно зафурычить :)
А теперь, когда у вас тоже всё успешно заработало, открывается огромнейшее пространство для манёвров. Кратко опишу, как у меня получилось доработать бота на текущий момент и расскажу о трудностях, с которыми столкнулся.
Первое, что я доработал — это второй webhook в модуле фронтенда. И в зависимости от параметра url из payload, бот теперь шлёт уведомления о деплое на бекенде либо фронтенде с разными текстами.
var url = JSON.parse(event.body).object_attributes.url;
Вторая доработка — это отправка одинаковых уведомлений в 2 разных Telegram-чата. На каждый чат я сделал реквест один за другим, но столкнулся с проблемой, что иногда во второй чат уведомление не приходило (в первый приходило всегда). Решил проблему путём добавления задержки в 2 секунды между реквестами.
setTimeout(() => {
// second request
}, 2000);
И последняя, третья фича — это добавление заголовка МР’а в уведомления для удобства аналитиков в отслеживании выполнения тасок. Получить заголовок из вебхука можно следующим образом:
var title = JSON.parse(event.body).object_attributes.title;
Итого, что сейчас реализовано: бот присылает разные уведомления о деплое на бекенде или фронтенде в 2 разных чата и сообщает заголовок МР’а.
Вот ссылка, которая 100% вам пригодится, если вы также будете ковыряться в этой теме и постоянно улучшать функциональность в своей Яндекс функции:
https://docs.gitlab.com/ee/user/project/integrations/webhook_events.html#merge-request-events
Заключение
Надеюсь, данная статья была для вас полезной и пригодится в решении аналогичной задачи.
Спасибо за внимание и хорошего дня ;)
P.S. Хотелось бы сказать огромное спасибо моему коллеге Максу, у которого я подглядел эту идею, и который меня направил в нужное русло после того, как я ему сказал, что планирую это реализовать сам и написать после получения результата статью-инструкцию для Хабра.