Привет! Меня зовут Алексей, я работаю в команде Авторизации Банки.ру. Сегодня хочу познакомить вас с тем, как написать свой плагин для semantic-release. Что это такое и как с ним работать я описал в своей предыдущей статье.

Эта статья будет полезна тем, кто хотел бы встроить дополнительный функционал в поток выпуска новых версий пакетов.

Semantic-release – это npm пакет, который автоматизирует весь рабочий процесс релиза (выпуска) пакета, включая: определение номера следующей версии, создание примечаний к релизу (release notes) и публикацию пакета.

Исторически сложилось, что все npm пакеты в компании мы публикуем с помощью semantic-release. У нас достаточно много внутренних библиотек и он нам очень помогает, беря на себя много рутинной работы. У нас есть библиотека, содержащая сам semantic-release и все необходимые плагины. Это позволяет централизованно обновлять все semantic-release зависимости, а так же однотипность публикаций для всех проектов.

Принцип работы Semantic-release

Semantic-release работает по принципу подключаемых плагинов. Это позволяет гибко расширять основной функционал. 

Приведу пример конфигурации:

// .releaserc
{
  "plugins": [
    "@semantic-release/commit-analyzer",
    "@semantic-release/release-notes-generator",
    "@semantic-release/npm",
    "@semantic-release/git"
  ]
}

Жизненный цикл

У semantic-release, есть такое понятие, как жизненный цикл. Каждый плагин “подписывается” на определенную фазу жизненного цикла. Поэтому, при разработке своего плагина нужно решить на какой (или каких) фазе жизненного цикла будет работать ваш плагин. Чтобы определиться с этим попробую описать фазы жизненного цикла и за что они отвечают. 

Semantic-release имеет следующие фазы жизненного цикла:

verifyConditions

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

analyzeCommits

Отвечает за определение типа следующего релиза (major, minor or patch).

verifyRelease

Отвечает за проверку параметров (версия, тип, тег дистрибутива (dist-tag) и т. д.) релиза, который будет опубликован.

generateNotes

Отвечает за создание описания релиза.

addChannel

Отвечает за добавление канала выпуска (dist-tag)

prepare

Отвечает за подготовку выпуска, например создание или обновление файлов, таких как package.json, CHANGELOG.md

publish

Ответственный за публикацию релиза

success

Отвечает за уведомление о новом релизе

fail

Отвечает за уведомление о неудачном релизе.

Теория написания плагина

Структура проекта

Плагин для semantic-release представляет из себя npm пакет. Типическая структура проекта содержит папку lib (реже src) с основным кодом плагина и папку test с тестами. Главным входным файлом является index.js лежащий в корне проекта. На него указывает поле main в package.json.

Типическая структура semantic-release плагина
Типическая структура semantic-release плагина

Весь код начинается с главного файла index.js лежащего в корне проекта.

Файл index.js должен экспортировать функции, имена которых должны соответствовать названиям жизненных циклов. Таким образом semantic-release понимает в какой фазе жизненного цикла какой код вызвать.

Пример:

// index.js
async function verifyConditions(pluginConfig, context) {
	...
}
async function analyzeCommits(pluginConfig, context) {
  ...
}

Таким кодом мы сказали semantic-release, что нашему плагину важны фазы verifyConditions и analyzeCommits, и чтобы он вызвал соответствующие функции, когда наступит соответствующая фаза.   

Параметры передаваемые в плагин

Semantic-release на этапе вызова функций жизненного цикла плагина передает в функцию два параметра pluginConfig и context.

pluginConfig - объект, содержащий параметры, которые пользователь может передать через конфигурационный файл (например .releaserc или аналогичный)

context - контекст вызова, который предоставляется semantic-release и имеет разные значения в зависимости от жизненного цикла.

Как передать параметры плагину?

Параметры для плагина передаются через конфигурационный файл (например .releaserc или аналогичный).

Пример:

// .releaserc
{
  [ 
    "semantic-release-my-special-plugin", 
    { 
       path: "path-my-special-plugin",
       message: "My cool release message"
    }
  ]
}

Эти параметры можно достать из первого параметра (pluginConfig) метода жизненного цикла. Semantic-release анализирует конфигурационный файл, определяет параметры и передает из плагину.

async function verifyConditions(pluginConfig, context) {
  const { message, path } = pluginConfig;
  // path будет иметь значение "path-my-special-plugin"
  // message будет иметь значение "My cool release message"
}

Хорошей практикой считается добавление функции для фазы жизненного цикла verifyConditions. На этой фазе необходимо проверить параметры, которые передали в плагин. В случае невалидности значений можно прервать выполнение программы и вывести ошибку или просто вывести сообщение и пропустить работу плагина.

Логирование выполнения программы

Вторым параметром в метод жизненного цикла передается объект контекста (context). Он разный для разных методов жизненного цикла. Но в каждый метод жизненного цикла в контекст передается logger

Пример

async function verifyConditions(pluginConfig, context) {
  const { logger } = context;
}

logger - это объект с четырьмя уровнями логирования (log, warn, success, error).

Пример информационного лога:

async function verifyConditions(pluginConfig, context) {
  const { logger } = context;
  logger.log('Сообщение из нашего плагина')
}

В консоли получим сообщение вида:

[3:24:04 PM] [semantic-release] [<имя нашего плагина>] › ℹ  Сообщение из нашего плагина.

Прерывание выполнения кода

Для того, чтобы прервать релиз (сообщить semantic-release об ошибке) и закончит выполнение кода нужно бросить ошибку. Эта ошибка должна иметь тип SemanticReleaseError или просто Error. Это позволит сообщить  semantic-release о сбои в выпуске и прервать публикацию.

async function verifyConditions(pluginConfig, context) {
  const { logger } = context;
  const result = validation(pluginConfig);
  if (!result) {
    logger.error('Недопустимый параметр');
    throw new Error();
    // или
    // throw new SemanticReleaseError();
  }
}

Заключение

В статье рассказал об основных теоретических моментах, которые понадобятся для старта написания своего плагина.  Дальше - это полет вашей фантазии.

У меня есть плагин, который проверяем коммит сообщения на соответствие соглашениям о коммитах. Там можно посмотреть код и перевести теорию в практическую плоскость.

Также можно посмотреть множество других плагинов для semantic-release и почерпнуть много полезной информации от туда.

Еще создал стартовый npm пакет, который можно взять за основу. Если будут вопросы или недопонимание - готов доработать стартовый плагин.