Всем привет! Меня зовут Семен. Я Java-разработчик и руководитель группы Java-разработки в Центре Big Data компании MTS Digital. В этом посте я хочу поговорить о Release Notes. Что это такое, почему не стоит писать их вручную и какие есть способы автоматизации. Покажу и реальный пример того, как организована работа с Release Notes в нашем проекте.

А что это?
Release Notes — это документация, которая характеризует последний инкремент продукта. Новые фичи, баг фиксы и так далее. Она представляет ценность как для разработчиков, так и для менеджеров, которые хотят знать, как развивается проект. Если вы хотя бы раз проводили еженедельные демо, то понимаете, как сложно порой вспомнить все мелкие детали, которые были затронуты в текущем спринте. Как же составлять Release Notes? Самый очевидный способ — руками. Его и рассмотрим.
Делаем Release Notes вручную
Самый главный плюс — для этого не требуется никаких специальных знаний, а также изменения существующих CI/CD процессов. Нанимаем технического писателя и поручаем это все ему. Что может быть проще? Однако тут «есть пара моментов».
-
Технический писатель может быть не погружен глубоко в продукт. Соответственно, он не сможет самостоятельно определить, что конкретно изменилось в новой версии. Понадобится разработчик, который будет доносить эту информацию. Фактически здесь получается двойная работа. Разработчик объясняет техническому писателю, что добавилось и убавилось. А технический писатель в свою очередь рассказывает всем остальным.
-
Процесс по документированию новой версии будет очень трудоемким. Здесь оказывает влияние много факторов. Например, как вы описываете задачи в таск-трекинговых системах (полно и развернуто, или кратко и на скорую руку). Как вы оформляете пул-реквесты. Как в целом архитектурно построена ваша система, насколько сложно в ней ориентироваться.
-
Велика вероятность допустить ошибку. Забыть упомянуть об исправлении важного бага, или случайно написать про фичу, которая будет сделана только в следующем спринте. Чем больше изменений в релизе, тем больше вероятность промаха.
Как видим, формировать Release Notes с помощью человеческих усилий не рационально. Значит, нужно автоматизировать процесс, верно? Да, но и тут возникает ряд вопросов.
-
Как определять, что изменилось, а что — нет?
-
Если установить определенные правила контрибьютинга, по которым изменения можно будет отслеживать, как гарантировать их соблюдение?
-
Мы поддерживаем несколько версий одновременно. Как автоматизировать процесс единообразно?
Что есть «изменение»?
Инкремент продукта можно мерить маленькими и большими шагами. Маленький — это коммит в репозитории. Большой шаг — это задача в таск-трекере (Jira, Trello, Github/Gitlab Tickets и так далее). Первый я бы назвал technical oriented, тогда как второй — business oriented стратегией. Они не являются взаимоисключающими и их вполне можно использовать совместно, ведь у каждого способа есть свои плюсы и минусы.
Technical Oriented Release Notes
Давайте начнем с более простого варианта. Предположим, что мы используем классический git-flow: develop и master ветка. Из develop раскатываются nightly билды, а при каждом мерже в master деплоится новая версия на production. Иначе говоря, осуществляется релиз. Вот на эти релизы нам и нужны Release Notes.
Найти коммиты нового релиза не составляет труда.
git rev-list $PREV_HASH..HEAD
Где $PREV_HASH — это коммит предыдущего релиза. Получив список хэшей, можно узнать сообщения коммитов. Ну а далее вставляем эту информацию в письмо и рассылаем всем подписчикам. Profit! Более того, многие CI системы умеют формировать changelog подобных изменений автоматически.
На словах все звучит красиво. Но на деле есть много подводных камней.
-
Зачастую разработчики пренебрегают написанием информативных сообщений коммитов. Из-за этого вместо понятного списка изменений вы можете получить полотно из fix, update, add, delete и.т.д.
-
В Release Notes попадут также инфраструктурные сообщения от вашего вендора git-сервера (Merge branch x to y, Merge pull request from z).
-
Во многих командах есть практика squash’ить коммиты перед мержем пул-реквеста, из-за чего большая часть информации о релизе будет безвозвратно утеряна.
Однако проблемы на этом не заканчиваются. Дело в том, что коммиты, (к сожалению), не представляют большой ценности для бизнеса. Даже если каждое сообщение будет максимально продумано и грамотно написано, скорее всего, это не даст продакт-оунеру никакой ценной информации, ведь разрозненный список коммитов тяжело дифференцировать в отдельные юниты работы.
Есть вариант хранить «бизнес-описания» в файле CHANGELOG.md. Однако это тоже ручная работа, автоматизации здесь нет. Тем не менее, у этого подхода есть и плюс — файл CHANGELOG.md тоже хранится в репозитории и, соответственно, его можно выносить на код-ревью.
Business Oriented Release Notes
Вот если бы вы вставили названия и описания задач, которые сделали, это было бы другое дело. (с) Типичный продакт-оунер.
Ну что же, давайте попробуем.
Список новых коммитов — это единственное, что мы можем узнать при формировании релизов. Значит, нужно каким-то образом связать коммиты и задачи в доске между собой. Решение довольно очевидно: в каждом сообщении о коммите указываем id соответствующей задачи. После этого можно распарсить айдишники из отфильтрованных коммитов и с помощью API получить необходимую нам информацию.

Такой подход позволяет объединить изменения кода с конкретными задачами бизнеса. Более того, коммиты (хэши и сообщения) можно вставлять в письма как дополнительную техническую информацию.
Все в этом подходе хорошо, кроме одной детали. Откуда мы знаем, что разработчики будут писать сообщения в коммитах так, как мы этого хотим? Хороший вопрос. Конечно, можно установить правила, описать их в CONTRIBUTING.md и надеяться, что люди станут их выполнять.

Но хотелось бы автоматизировать эту проверку. Причем желательно, чтобы она носила превентивный характер. То есть не позволяла бы написать некорректное сообщение коммита. К счастью, такой инструмент есть.
Есть еще один момент. Откуда мы знаем, что задачи в таск-трекинговой системе тоже будут описаны грамотно? На самом деле, это проблема. Часто разработчики решают вопросы голосом, а потом описывают задачу двумя-тремя словами. К сожалению, нет универсального способа проверить название и описание задач на полноту и ясность. Единственное решение — это внедрение культуры. Объяснять и рассказывать, почему это важно. Например, если вы завели двусмысленную задачу в пятницу вечером, есть вероятность, что в понедельник утром вы уже не вспомните, что конкретно надо было сделать.
Git Hooks
Когда люди упоминают Git хуки, чаще всего на ум приходят webhooks: триггеры на пул-реквесты, комментарии, аппрувы и так далее. Однако Git нативно поддерживает локальные хуки, которые слушают события, происходящие в репозитории на вашей машине. Одно из таких событий — попытка создания коммита. И вот это уже интересно.

Git hook представляет собой обычный скрипт, который выполняется перед или после какой-то операции. В данном случае мы будем говорить о хуке commit-msg. Если код возврата хука отличен от нуля, дальнейшая операция отменяется.
Все хуки по умолчанию должны быть расположены в директории .git/hooks. Чтобы хук начал работать, нужно создать файл с соответствующим названием (commit-msg) в директории. Например, вот простой хук на Python, проверяющий наличии ID задачи в сообщении коммита.
#!/usr/bin/env python3 import sys import re with open(sys.argv[1]) as file: content = file.read() if (not re.match(r'\[MTS-\d+\] - .+', content)): raise NameError("The commit is not validated: {}".format(content))
В качестве аргумента командной строки передается путь до файла .git/COMMIT_EDITMSG. Он содержит в себе сообщение коммита. Прочитав его, мы легко можем проверить строку на соответствие регулярному выражению.
Есть пара нюансов с хуками, которые нужно понимать.
Первый нюанс — хуки не сохраняются в системе контроля версий, так как директория .git не индексируется. То есть мы не можем просто запушить их в репозиторий и тем самым автоматически применить для всех будущих контрибьюторов.
Однако у этой проблемы есть решение. Например, можно создать отдельную директорию в проекте, которая будет индексироваться и где мы будем хранить хуки. После этого человеку требуется лишь сделать symlink между нашей кастомной директорией и .git/hooks. А лучше даже написать скрипт, который выполнит это автоматически.
Второй нюанс — хуки сложно написать совместимыми сразу с несколькими операционными системами. Например, если вы написали хук на bash и у вас на Linux все работает отлично, то у вашего коллеги на Windows или MacOS могут возникнуть проблемы. Поэтому для хуков используют высокоуровневые языки, но с ними мы должны быть уверены, что у всех разработчиков будет установлен язык, на котором написан хук (да еще и нужной версии). Лишние зависимости — лишние проблемы.
К счастью, многие инструменты сборки проектов предоставляют плагины/библиотеки для кросс-платформенной реализации git hooks. Моим личным фаворитом является npm пакет Husky. С помощью пары конфигурационных файлов можно добавить необходимый нам хук.
Далее просто указываем команду обновления хуков в package.json “preinstall”. Теперь перед каждой установкой зависимостей проекта вместе с тем будут обновляться и хуки. Также хорошим автономным решением является пакет pre-commit.
Semantic Release
Если мы хотим автоматизировать процесс доставки Release Notes, встает проблема версионирования ПО. Можно создать свой набор правил. Например, поднимать нужную цифру в номере версии в зависимости от названия ветки, анализировать коммиты, задачи, теги и так далее. Или можно взглянуть на существующие готовые решения, которые подойдут для вашего проекта.
Semantic Release — это npm пакет, который полностью автоматизирует процесс деплоя, релизов, а также инкрементов версий.
Библиотека следует нескольким концепциям.
-
Есть одна главная ветка в репозитории (master). Все остальные — короткоживущие
-
Новый Pull Request — новый релиз
-
Начальная версия продукта — 1.0.0
По умолчанию Semantic Release следует правилам коммитирования по гайдлайну Angular Commit Messages. Однако их можно переопределять, что поможет в решении проблемы с культурой написания commit-message, которую я описывал выше.
Когда очередной пул-реквест попадает в master ветку, semantic release производит скан коммитов от текущего состояния до последнего релизного тега. fix поднимает patch версию, feat — минорную, а ! — мажорную (парсер при необходимости можно переопределить).
Была версия 1.0.1 feat: … feat: … fix: … feat: … Стала: 1.1.0 Была версия 2.0.3 build: ... refactor: … fix: … Стала: 2.0.4 Была версия 12.12.4 docs: … feat!: … fix: … Стала: 13.0.0
После успешного релиза (команда определяется конфигом) в Git создается новый тег, в описании которого добавляется отформатированная информация из полученных коммитов.
Вот пример конечного Release Notes из репозитория eslint-plugin-unicorn.

Также отмечу, что библиотека не привязана строго к среде Javascript/Typescript. Благодаря наличию гибкой конфигурации ее можно интегрировать с любой системой сборки проектов, которую вы предпочитаете. Помимо этого, есть уже готовые решения. Например, плагин для Maven и Gradle.
Как вы заметили, Semantic Release исповедует подход Technical Oriented Release Notes. На мой взгляд, он отлично подходит для разработки библиотек и фреймворков (особенно open-source), но является спорным решением для сервисов.
Во-первых, Semantic Release не подходит для поддержки нескольких версий одновременно. Строго говоря, эта фича поддерживается, но на мой взгляд сложность конфигурации и дополнительные специфические правила коммитирования не оправдают итоговый результат.
Во-вторых, релизы как правило привязываются к milestone или story, которые определяются потребностью бизнеса, а не признаком того, в каком формате вы написали сообщение коммита. Ну и в-третьих, релиз/деплой при каждом мерже в определенных ситуациях мера избыточная.
Как мы автоматизировали Release Notes
Вот мы и плавно подобрались к теме того, как автоматизация реализована у нас. Стек следующий: Gitlab, Jenkins, Jira и SonarQube. Схематично процесс формирования отображен на рисунке 3.

Разработчик пушит свои изменения в feature-branch (docs, refactoring, bugfix, и.т.д.) и создает Merge Request. Если все проверки (в том числе code-review) прошли успешно, изменения отправляются в develop ветку. Раз в день develop ветка разворачивается на dev-стенд (nightly builds).
После деплоя происходит процесс парсинга коммитов с целью определить ID задач, которые были затронуты в текущем релизе.
[MTS-2312] - PUT /rest/safe/rule/{ruleId} может вызывать только админ … [MTS-2326] - добавил сортировку по полю stage
В данном примере будет получен список из задач MTS-2312 и MTS-2326.
Теперь по JIRA REST API запрашиваем основную информацию по каждой задаче (название и описание).
GET /rest/api/2/issue/{issueIdOrKey}?fields=summary,description&expand=renderedFields
Обратите внимание на параметр expand=renderedFields. Дело в том, что summary и description возвращаются в формате JIRA разметки. При добавлении таких строк в письма их становится тяжело читать и воспринимать. Ранее описанный параметр добавляет в ответ эти же поля в виде HTML, который гораздо проще сверстать в письме.
После проставляем на задачи лейблы конечных стендов, где были развернуты изменения. Например, dev, staging или prod.
PUT /rest/api/2/issue/{issueIdOrKey}
{ "update": { "labels": [ { "add": "staging" } ] } }
В конце информация о выполненных задачах рассылается всем заинтересованным людям. Процесс для master ветки выполняется аналогичным образом.
К плюсам такого подхода можно отнести то, что он легко расширяем. Например, если мы поддерживаем несколько версий в production, то можно без проблем выполнять вышеописанный процесс для каждой релизной ветки.
В итоге мы получили автоматизированный процесс по рассылке писем со всеми задачи, которые были выполнены в рамках текущего релиза, а также их описаниями. Более того, он не требует значительных изменений в процессе разработки. Разработчикам достаточно лишь указывать ID задачи в Jira в каждом коммите. Но для того чтобы реализовать это на практике, требуется внедрение соответствующей культуры. Нужно объяснять сотрудникам, почему это важно и насколько позволяет повысить качество конечного продукта, ведь Release Notes — это тоже часть итогового результата.
Пожалуй, это все, что я вам хотел рассказать про автоматизацию Release Notes. Делитесь своими историями в комментариях. Расскажите, как этот процесс реализован у вас в проекте и спасибо за чтение!
ссылка на оригинал статьи https://habr.com/ru/articles/572774/
Добавить комментарий