Привет! Я — Алексей Бондаренко, работаю в команде Платформа Банки.ру. Сегодня хочу рассказать о 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 пакет результата.

Ограничения подхода 

  1. Не получится откатить изменения. Если выпустили версию и в ней обнаружились проблемы, решить это можно только выпуском новой версии. 

  2. Коммит-сообщения должны быть сделаны по правилам  соглашений о коммитах. Эту задачу можно автоматизировать. Например, с помощью commitlint.

  3. К процессу нужно подходить ответственно. Делить коммиты на законченные порции изменений. Любителей коммитов типа «fix fix fix» точно ждут разочарования. 

  4. На ревью следите, чтобы коммит-сообщения отражали изменения в коде.

  5. Для любителей сквош коммитов тоже будут разочарования: соглашение будет нарушено и семантик релиз не увидит изменения.

Альтернативы

Вывод

Обкатали новый подход. Да, поймали много проблем, но нам понравилось! Сейчас 10+ библиотек деплоятся с помощью semantic-release в полностью автоматическом режиме. Скорость деплоя увеличилась в разы. Не надо никого обучать публиковать пакеты руками и тратить на это время.

Комментарии (3)


  1. dyadyaSerezha
    05.04.2024 16:52

    Можно ли сделать коммит, сообщение которого не надо добавлять в chanhelog? Иначе там окажутся десятки/сотни мелких и/или дублирующих пунктов.


    1. BondbonD Автор
      05.04.2024 16:52

      Да, можно, если указать тип chore . Подробнее.
      Но лучше, перед мержем в мастер все коммиты привести в порядок. Надо смотреть на коммит-мессаджи, как на будущую документацию.


      1. dyadyaSerezha
        05.04.2024 16:52
        +1

        Вы представляете количество коммитов в средних и больших проектах между релизами? Их могут быть сотни. Зачем пользователю (например, программисту в соседнем отделе) читать эти простыни с мельчайшими деталями? Для этого можно просто посмотреть сам список коммитов и всё. Я уже не говорю, что часто одна проблема фиксится несколько раз (ровно, как написано в тексте) - это жизнь.