Pull to refresh

Команда игнорировала линтеры и я написал свой нотификатор

Reading time6 min
Views1.3K

Привет! Меня зовут Иван, и я автор проекта «Код на салфетке» — небольшой команды, в которой мы совмещаем написание обучающих статей, коммерческую разработку и open source.

Сегодня расскажу об одном из наших инструментов, который родился из боли всей команды: как мгновенно узнавать о проблемах в CI/CD, не заглядывая в почту и не обновляя вкладку репозитория.


Предпосылки

В конце прошлого года в нашей команде был разработчик, который упорно игнорировал запуск линтеров и тестов перед пушем. Он просто отправлял изменения и уходил в закат, пока через несколько часов мы (или он сам, часов через пять) не замечали, что пуш завалился, и нужно править.

Это доставляло массу неудобств:

  • Приходилось чаще проверять статусы CI/CD;

  • Возникала лишняя ругань.

Так родилась идея найти «оповещатор» о статусе воркфлоу прямо в Telegram-чат. Через некоторое время после появления идеи — когда без такого инструмента уже нельзя было обходиться — появился Actions Telegram Notifier. Наш собственный Action для GitHub/Gitea Actions.


Поиск решения

Когда возникла потребность, я сразу начал искать готовые решения. В GitHub Actions Marketplace их действительно много. Но, внимательно изучив все варианты - выяснил, что ни одно из них полностью не соответствует нашим требованиям.

Я выделил такие требования к готовому решению:

  • Поддержка тем в супергруппах;

  • Информативность;

  • Кастомизируемость.

Сравнение с "конкурентами":

  • Поддержка тем в супергруппах. В наших проектах мы используем Telegram-супергруппы, в которых создаём темы под разные проекты. Почти все готовые решения поддерживают отправку уведомлений только в обычные чаты (или личку).

    Пример организации супергруппы в Telegram:

  • Информативность. Оповещения должны быть информативными, но не перегруженными. Большинство готовых решений предлагают одну строку текста с описанием события — и всё.

    Пример встроенного в GitLab оповещения:

    Пример оповещения нашего Action:

  • Кастомизируемость. Большая часть решений не позволяет управлять содержанием уведомления: ни убрать/добавить информацию, ни вставить свой текст. На скриншоте выше, например, строка "job: pre-commit linters" — это как раз пример пользовательского текста. При желании, можно вообще оставить только поле со статусом и кнопкой на коммит.

Кандидатов нет - пишем своё!

В итоге я оказался в классической ситуации: «ничего из готового не покрывает хотелки». Оставался один путь — писать свой Action.

Как писать свой экшен? Нужно создать специальный файл action.yml, в котором описываются параметры, способ запуска и прочие детали экшена.

На чём писать экшен? Тут тоже всё просто — на чём угодно! Можно на Python, JS или любом другом языке — главное, чтобы код запускался в среде раннера. Я выбрал Rust. На тот момент это было моё первое знакомство с языком, и без сложностей не обошлось.

Как запускать экшен? Если взглянуть на блок runs на скрине выше, есть два варианта:

  1. Непосредственно в среде раннера. Обычно так запускаются экшены на JS, которым достаточно Node.js, уже установленного в среде.

  2. С помощью Docker-образа. Это заранее собранный образ, указанный в репозитории экшена. Я выбрал этот вариант, поскольку Rust — компилируемый язык.


Разработка экшена

Разработка началась с изучения матчасти, а именно:

  • Как работают экшены?

  • Как их писать?

  • Какие данные о воркфлоу доступны внутри раннера?

  • С чем едят Rust?

  • И десятки других вопросов.

Часть из них я уже затронул в предыдущем блоке, но самый важный — про данные внутри раннера. Нужно было чётко понимать, что именно мне доступно для работы.

Поскольку используемая нами Gitea почти полностью совместима с GitHub, начал изучение с документации GitHub Actions. К счастью, у GitHub в этом плане всё отлично — документация подробная и понятная.

Первая версия

Первая версия была написана "на коленке" — быстро и без лишних заморочек.

Кому интересно, вот последний коммит той самой первой версии: ссылка на коммит

Принцип работы я подсмотрел у "конкурентов": посмотрел, как они это делают, и собрал свою реализацию. Как и большинство, я получал данные из переменных окружения, доступных внутри раннера.

Логика была простая:

  1. Получаем данные из окружения и складываем в словарь;

  2. Формируем текст сообщения;

  3. Отправляем его в Telegram.

Весь экшен умещался примерно в 215 строк кода в одном main.rs.

В первой версии, в тексте оповещения я выделил следующие поля:

  • Workflow status - статус выполнения воркфлоу, получаемый из {{ job.status }};

  • Actor: инициатор запуска воркфлоу;

  • Repository: название репозитория в котором запустился воркфлоу;

  • Branch: название ветки в которой запустился воркфлоу;

  • Commit Message: сокрашённое до первой строки сообщение коммита;

  • Message и Footer: дополнительные текстовые поля.

Скриншот оповещения первой версии:

Минусы первой версии

Любое отклонение в структуре данных могло «сломать» отправку. Всё потому, что я завязался не на реальные данные раннера, а на переменные окружения — а они зависят от типа события. В итоге экшен нормально работал только при событии push.

Второй минус — неподдерживаемость. Двести строк кода в main.rs было тяжело расширить или адаптировать под другие события.

Ну и если говорить честно — это был далеко не лучший код. Сделаем скидку: это было моё первое знакомство с Rust =)

Вторая версия экшена

Спустя некоторое время, уже после того как я немного разобрался в Rust и написал ReBack (и статью про него), появилась новая потребность — добавить уведомления о Pull Request.

Собравшись с мыслями, я решил полностью переписать экшен.

Ссылка на актуальный репозиторий

При переработке проекта я учёл все прошлые недочёты и начал с самого важного — архитектуры. Теперь всё разбито на логические модули. Вместо словаря с данными используются структуры и энумы.

Часть параметров по-прежнему берётся из переменных окружения — например, токен бота или дополнительный текст. Но основные данные я теперь получаю из файла /github/workflow/event.json. В нём удобно и структурированно лежит вся необходимая информация. Более того, там есть данные, которых нет в стандартных переменных окружения.

Это открывает большие возможности — теперь при необходимости можно легко:

  • добавить новые поля в уведомления;

  • обрабатывать новые события.

Основной алгоритм работы остался прежним — собираем данные, формируем сообщение, отправляем в Telegram.

Во второй версии текст оповещения тоже претерпел некоторые изменения:

  • В поле Actor теперь ссылка на инициатора;

  • В поле Repository также теперь ссылка на репозиторий;

  • Для события Pull Request создаётся кнопка со ссылкой на PR;

  • Для события Pull Request отображается поле PR Title с заголовком PR'а и ссылкой на него.

Добавить ссылки позволило чтение данных из файла event.json.

Пример оповещения о пуше:

Пример оповещения о Pull Request:

Сборка образа экшена

Напомню: Rust — это компилируемый язык и с этим связанна одна особенность...

При запуске экшена в воркфлоу он начинал компилироваться прямо на раннере. Это тратит время и ресурсы впустую.

Чтобы решить эту проблему, я настроил сборку Docker-образа прямо в репозитории проекта.

Теперь экшен использует уже собранный образ, а не компилируется каждый раз при запуске. Всё стало быстрее и стабильнее.

Вклад от сообщества

Неожиданно, ещё в первой версии кто-то открыл issue. Это был пользователь Kastov, который предложил улучшить текст уведомления. После того как я внёс это изменение, он прислал ещё и полноценный Pull Request с рядом улучшений.

А уже после выхода второй версии обратился ZAlexanderV. Он столкнулся с тем, что экшен поддерживал только события Push и Pull Request, а ему нужно было ещё и Workflow Dispatch.

Сложность добавления Workflow Dispatch заключалась в его кастомной природе. Я не могу заранее предусмотреть все возможные поля, которые туда передаются, — каждый проект настраивает это по-своему. Пришлось искать компромисс между универсальностью и поддержкой пользовательских параметров.

Пример оповещения на событие Workflow Dispatch:

Меня удивляло то, что этим проектом пользуются другие люди и когда я видел открытый issue, честно, даже немного нервничал. Мне было приятно, когда Kastov внёс некоторые изменения и интересно добавлять возможности для ZAlexanderV. Наверное, ради таких эмоций и стоит заниматься опенсорсом.


Пример использования

Использовать экшен очень просто — достаточно добавить в существующий workflow следующий шаг:

- name: Run Telegram Notify Action  
  uses: proDreams/actions-telegram-notifier@main  
  with:  
    token: ${{ secrets.TELEGRAM_BOT_TOKEN }}  
    chat_id: ${{ secrets.TELEGRAM_CHAT_ID }}  
    status: ${{ job.status }}  
    notify_fields: "actor,repository,workflow,repo,commit"  
    message: "Docker image successfully built and pushed to registry."  
    footer: "Workflow completed"

Обязательно добавьте в secrets два поля:

  • TELEGRAM_BOT_TOKEN — токен Telegram-бота

  • TELEGRAM_CHAT_ID — ID чата, куда будут приходить уведомления

Поле status оставляем как есть — оно нужно, чтобы в уведомлении отображался актуальный статус выполнения джоба.

Остальные параметры — опциональные. Вы можете кастомизировать их под свои задачи.

Подробное описание всех доступных полей — в README.md репозитория.


Что дало это команде?

У меня нет каких-то конкретных цифр, которые обычно пишут в таких постах — типа “Продуктивность выросла на 80%!!!”.

Но могу точно сказать по ощущениям: мы стали быстрее реагировать на проблемы со сборкой или тестами. Раньше нужно было открывать репозиторий и следить за процессом вручную — это было неудобно и отнимало время.

Теперь всё иначе: сделал задачу, запушил изменения — и можно идти дальше. Когда воркфлоу завершится (или упадёт), в Telegram сразу приходит уведомление.

То же самое с Pull Request’ами: не нужно больше писать “я там PR открыл” — достаточно просто создать его, и все увидят уведомление в чате.


Заключение

Этот проект оказался очень полезным для нашей команды, и, судя по количеству запусков (а их уже больше 2000!), он пригодился не только нам.

С его помощью я:

  • глубже разобрался в механике GitHub/Gitea Actions;

  • прокачал навыки в Rust, особенно в работе с архитектурой и обработкой внешних данных;

  • на практике увидел, как важны хорошие уведомления для быстрой реакции и прозрачности рабочих процессов;

  • и просто получил удовольствие от того, что можно сделать что-то маленькое — но реально полезное.

Буду рад, если экшн окажется полезен и в вашем проекте. А если захотите предложить улучшения, добавить поддержку других событий или просто что-то обсудить — пишите issue!

Обсудим, подумаем, возможно — добавим!

Подписывайтесь на наш Telegram‑канал «Код на салфетке», у нас много интересного как для новичков, так и профессионалов!

Tags:
Hubs:
If this publication inspired you and you want to support the author, do not hesitate to click on the button
+9
Comments11

Articles