Привет! Я — Алексей Бондаренко, работаю в команде Платформа Банки.ру. Сегодня хочу рассказать о semantic-release и его практическом применении на примере упрощения разработки и внедрения библиотеки в проект.
План такой:
начну с определений и принципов версионирования, которые нужны для понимания темы;
расскажу, что дает использование semantic-release;
разберу, как работает инструмент, в чем его особенности и ограничения.
В конце статьи будет ссылка на репозиторий. Его можно использовать в качестве стартовой точки для работы с semantic-release.
Что такое версионирование
Хотя с этим понятием большинство знакомо, напомнить считаю нелишним.
С версионированием мы встречаемся часто. Будь-то модель автомобиля в разных поколениях и модификациях или литературное произведение в разных изданиях.
С кодом похожая история. Пишем код, начинаем его использовать — одна версия. Делаем доработки, включаем новый функционал — появляется другая.
Встает вопрос — а как называть эти версии? Словами, которые передают смысл изменений, нумеровать по порядку или как-то еще?
В разработке используют разные варианты присваивания версий коду. Например, Calendar Versioning (CalVer), zer0ver. Подробнее о них можно почитать в этой статье.
Но самый популярный, пожалуй, это семантическое версионирование или semver.
Принципы семантического версионирования
Семантическое версионирование — это набор правил и требований, которые определяют, как назначаются и увеличиваются номера версий.
Версия в семантическом версионировании — это три числа, разделенные точкой.
где:
Z — патч-версия, когда меняется что-то внутри и публичное API остается прежним. Например, поменялся метод сортировки в одном из элементов, библиотека стала быстрее работать.
Y — минорная версия, когда добавлена новая функциональность и не нарушена обратная совместимость API. К публичному методу добавился дополнительный необязательный параметр.
X — мажорная версия, когда нарушена обратная совместимость API . Например, переименовали публичный метод.
Приведу аналогию из жизни автомобилиста.
Z — поменяли лампочки в фарах автомобиля: свет как был, так и остался, просто стали ярче гореть фары.
Y — появился новый режим у кондиционера: добавили обдув заднего стекла.
Z — поменяли органы управления: кнопку перенесли из одного места в другое. Тут без прочтения инструкции не обойтись.
Что дает semver
Корректная схема управления версиями помогает разработчикам легко идентифицировать текущий выпуск, внесенные изменения, а также совместимость версии с предыдущими. Конкретно семантическое версионирование имеет смысл только при разработке библиотек: кодовая база одна, а потребителей много.
Как писал мои коллега в одной из статей, мы прошли долгий путь от монолита с сабмодулями, до микрофронтендов с большим количеством общих библиотек. Наша команда разрабатывает и поддерживает внутренний UI-KIT компонентов, которые используют в проектах всех продуктовых вертикалей компании. С этой библиотекой внутри компании мы работаем как с open-source проектом: каждая команда может внести изменения и прислать их на ревью.
Раньше библиотека компонентов подключалась к основному монолитному проекту сабмодулем (git submodule). В такой схеме были риски: можно было установить указатель не на тот коммит и сломать код. Чтобы этого избежать, приходилось использовать скрипт, который проверял, что в релизе хеш сабмодуля указывает на коммит из мастера.
В нашем случае использование семантического версионирования помогает понять и определить масштаб «бедствия» при установке обновления пакета. Дает возможность планировать работу и делает ее более предсказуемой. Экономит время, силы и нервы.
Как начать использовать семантическое версионирование
Допустим, мы поняли, что семантическое версионирование это здорово, и решили его использовать. Что для этого нужно?
Начнем с минимума — ручного управления версиями и изменениями без деплоя.
Представим, что у нас есть репозиторий, где мы храним код.
Один из вариантов — добавить к коду тег, он же — версия:
git tag 1.0.0
и отправить в репозиторий (со всеми тегами):
git push --tags
Потом установить оттуда:
npm i <путь к репозиторию>#<тег>
// например npm i https://github.com/shakacode/bootstrap-loader.git#v1.0.10
Присвоить тег можно и через веб-интерфейс git репозитория.
С этим минимумом работать можно, но неудобно. Не хватает одного важного момента. Как было написано выше, семантическое версионирование помогает понять, какие изменения были сделаны. А значит, где-то нужно хранить информацию об этих изменениях. Обычно их фиксируют в файле changelog.md.
Как это всё синхронизировать? И тег проставить, и написать, какие изменения были сделаны конкретно в этой версии.
Устанавливать пакет напрямую из git репозитория — хорошее решение для начала, но для production разработки не очень подходит. Так как исходный код пакета, как правило, отличается от поставляемого кода
Всем хочется простоты: устанавливать уже готовые к работе библиотеки, используя их имена. Обычно для этого используют реестр пакетов npm. Код готовят и публикуют в этом реестре. Здесь важно следить за правильностью версии в package.json. Если нужно, присваивать тэг.
Задача опять усложняется: нужно синхронизировать git репозиторий, информацию о внесенных изменениях (changelog.md) и реестр пакетов.
Делать это руками можно, но сложно. Человеческий фактор часто приводит к ошибкам. Semantic-release позволяет всю эту рутину автоматизировать и ошибок избежать.
Как работает semantic-release
Semantic-release — это npm пакет, который полностью автоматизирует большой объем работы.
Соглашение о коммитах
Самое важное для корректной работы semantic-release — писать коммит-сообщения в определенном формате. Для этого есть правила, описанные в cоглашениях о коммитах. По умолчанию используются Angular коммит-сообщение.
Когда мы пишем коммиты в соответствии с соглашениями, мы тем самым решаем проблему «добавления информации о внесенных изменениях» (changelog). В коммитах указываем, какие изменения были внесены в код. Semantic-release будет анализировать эти сообщения и формировать список изменений для определенной версии кода: какие изменения попадают в мажор, минор и патч-версию.
Шаблон коммит-сообщения выглядит следующим образом:
<тип>[необязательный контекст]: <описание>
[необязательное тело]
[необязательная(ые) сноска(и)]
Пример такого сообщения:
feat: Добавлена возможность отображения виджета вертикально
Добавлена пропса direction отвечающая за направление отображения виджета.
Task: #WIDGETS-32
Настройка semantic-release
Semantic-release работает по принципу плагинов и включает в себя девять шагов. На каждом шаге управление передается плагинам для выполнения определенных действий.
В semantic-release по умолчанию включены следующие плагины:
@semantic-release/commit-analyzer
@semantic-release/release-notes-generator
@semantic-release/npm
@semantic-release/github
@semantic-release/commit-analyzer — анализирует коммит-сообщения.
@semantic-release/release-notes-generator — генерирует список изменений (changelog) от последнего релиза до текущего коммита.
@semantic-release/npm — публикует пакет в npm.
@semantic-release/github — работает с github релизами и комментирует в issues.
Стартовать можно с конфигом и плагинами по умолчанию.
Semantic-release можно запустить локально с помощью команды (написана ниже). Предварительно нужно сделать коммит в соответствии с соглашениями о коммитах.
NPM_TOKEN=<npm_token> GH_TOKEN=<github_token> npx semantic-release --no-ci --dry-run
Эта команда с ключиком --dry-run
совершит тестовый запуск semantic-release (без шагов: подготовка, публикация, добавление канала, успех и сбой). Флаг --no-ci
позволяет запустить semantic-release с локальной машины (пропускает проверку среды непрерывной интеграции).
Если хотите, чтобы эта команда отработала по-настоящему, уберите флаг --dry-run
.
Как создать github персональный токен (<github_token>
) можно узнать — тут. А npm токен (<npm_token>
) — тут. Строчки, которые будут сгенерированы, надо добавить в команду выше.
В результате мы должны получить опубликованный пакет в npm и запись в релизах.
Для того, чтобы было совсем хорошо не хватает еще двух плагинов:
@semantic-release/changelog
@semantic-release/git
@semantic-release/changelog — вносит изменения в changelog файл, из которого потом можно получить информацию о внесенных в код изменениях (не все же используют github для пакетов).
@semantic-release/git — коммитит изменения в git репозиторий.
Здесь нам уже не обойтись без конфигурационного файла. В простом варианте он будет выглядеть следующим образом:
// .releaserc
{
"branches": ["master"],
"plugins": [
"@semantic-release/commit-analyzer",
"@semantic-release/release-notes-generator",
"@semantic-release/changelog",
"@semantic-release/npm",
"@semantic-release/github",
"@semantic-release/git"
]
}
Подробнее о конфигурировании можно почитать тут.
После запуска semantic-release мы должны увидеть запись в changelog файле и коммит с изменениями в git репозитории. Нужно помнить, что бот делает коммит в ветке, которая у вас указана для деплоя. В нашем случае это master. Следовательно, этому пользователю нужно прописать права на пуш в мастер.
Semantic-release имеет много плагинов на разные случаи. С их перечнем можно ознакомиться на официальном сайте. Либо написать свой.
Песочница для тестирования semantic-release
Чтобы поиграться с semantic-release, используйте подготовленный для этих целей github repo. Там есть более подробная инструкция. А здесь можно увидеть npm пакет результата.
Ограничения подхода
Не получится откатить изменения. Если выпустили версию и в ней обнаружились проблемы, решить это можно только выпуском новой версии.
Коммит-сообщения должны быть сделаны по правилам соглашений о коммитах. Эту задачу можно автоматизировать. Например, с помощью commitlint.
К процессу нужно подходить ответственно. Делить коммиты на законченные порции изменений. Любителей коммитов типа «fix fix fix» точно ждут разочарования.
На ревью следите, чтобы коммит-сообщения отражали изменения в коде.
Для любителей сквош коммитов тоже будут разочарования: соглашение будет нарушено и семантик релиз не увидит изменения.
Альтернативы
go-semantic-release
release-please
standard-version
release-it
GitVersion
release-me
changesets
Вывод
Обкатали новый подход. Да, поймали много проблем, но нам понравилось! Сейчас 10+ библиотек деплоятся с помощью semantic-release в полностью автоматическом режиме. Скорость деплоя увеличилась в разы. Не надо никого обучать публиковать пакеты руками и тратить на это время.