Некоторые знания Javascript и Git и учетная запись Github. Также на вашем компьютере должен быть установлен NodeJS. Если он у вас еще не установлен, я рекомендую сделать это с помощью диспетчера версий, такого как nvm.

Контекст

монорепо

Монорепозиторий (моно = один, репо = репозиторий) — это подход к управлению несколькими программными проектами внутри одного репозитория, часто называемого пакетами.

Лерна

Lerna — это инструмент для управления проектами JavaScript с несколькими пакетами.

Обычные коммиты

Обычные коммиты — это соглашение, основанное на коммитах и ​​состоящее из набора правил, которым нужно следовать при написании сообщений коммитов. Чтобы указать характер измененного кода, необходимо выполнить набор инструкций, соответствующих спецификации SemVer (Semantic Versioning).

Пакеты GitHub

Пакеты Github — это реестр пакетов Github. Это позволяет разработчикам хранить пакеты программного обеспечения для некоторых из наиболее часто используемых реестров пакетов (Npm, Docker, Maven…). В нашем случае мы будем использовать npm.

Что будем строить?

Мы создадим монорепозиторий, который будет содержать два проекта (пакета). После внесения изменений в любой из проектов мы зафиксируем их в соответствии с обычной спецификацией коммитов.

После завершения внесения изменений мы будем использовать Lerna в сочетании с обычными коммитами для анализа истории коммитов и определения того, какие пакеты были изменены, уровня влияния этих изменений и определения версий, которые должны быть изменены и опубликованы в реестре.

Руки вверх

Настройка монорепозитория

Самое первое, что нужно сделать, это создать новый репозиторий Github. Я назову его monorepo.

Клонируйте репозиторий, перейдите в корневую папку и выполните следующую команду, чтобы инициализировать проект npm.

$ npm init

После этого установите Lerna как зависимость и выполните команду для инициализации проекта Lerna:

$ npm install --save lerna

$ lerna init --independent

Будет сгенерирован следующий файл lerna.json. Этот файл используется для настройки различных параметров, поддерживаемых Lerna. Флаг --independent важен, потому что мы хотим, чтобы каждый пакет в репозитории имел независимую версию, а не одну версию для всех пакетов.

{
  "packages": [
    "packages/*" <-- folder where the packages will be located
  ],
  "version": "independent" <-- versioning strategy
}

Чтобы не публиковать папку node_modules в репозиторий, создайте файл .gitignore со следующим содержимым:

node_modules

Структура нашего проекта должна выглядеть так:

/
  .gitignore <-- avoid publish certain files to the repository
  package.json <-- Lerna installed in the root dependencies
  lerna.json <-- Lerna configuration file
  packages/ <-- folder where the packages will be located

Теперь давайте опубликуем эти первоначальные изменения в репозитории, следуя стандартной спецификации коммитов (обратите внимание, что мы используем feat в качестве типа фиксации и root в качестве области действия). Позже, в разделе scope коммита, мы установим имя затронутого пакета, но, поскольку текущие изменения являются глобальными, мы просто выберем имя, такое как root, или любое другое, которое вы предпочитаете:

$ git add .
$ git commit -m "feat(root): adds npm, lerna and packages"
$ git push

Создание пакетов

Мы создадим следующие два пакета:

  • date-logic: будет экспортирована функция, которая возвращает текущую дату.
  • date-renderer: он будет использовать date-logic для вывода текущей даты на консоль.

Пакет 1 (дата-логика)

Создайте новую папку с именем date-logic внутри папки packages, перейдите к ней и выполните npm i, чтобы сгенерировать собственный файл package.json. После этого примените следующие изменения:

  1. Добавьте область действия npm к атрибуту name, чтобы указать, кто является владельцем пакета. В моем случае @xcanchal.
  2. Добавьте атрибут repository с URL-адресом репозитория Github.
  3. Добавьте атрибут publishConfig.registry, указывающий на реестр Github Packages. Это указывает реестр npm, в котором будут опубликованы пакеты.

package.json должен выглядеть следующим образом:

{
  "name": "@xcanchal/date-logic", <-- @{scope}/{package-name}
  "version": "1.0.0",
  "description": "A package that returns the current date",
  "main": "index.js",
  "repository": "https://github.com/xcanchal/monorepo", <-- repo
  "publishConfig": { <-- publish config
     "@xcanchal:registry": "https://npm.pkg.github.com/xcanchal"
  }
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "Xavier Canchal",
  "license": "ISC"
}

Теперь мы реализуем очень простой скрипт для пакета date-logic. Создайте новый файл index.js со следующим содержимым:

module.exports = function getDate() {
  return new Date();
};

Давайте внесем изменения в репозиторий (помните, что мы должны следовать стандартной спецификации коммитов). Поскольку изменения касаются добавления новой функции в пакет date-logic, мы будем использовать тип фиксации feat и область действия date-logic:

$ git add .
$ git commit -m "feat(date-logic): creates package"
$ git push

Теперь мы опубликуем самую первую версию пакета в реестре Github Packages npm, чтобы мы могли установить его из второго пакета, который мы реализуем позже (date-renderer).

Аутентификация в пакетах Github и npm

Прежде чем мы сможем публиковать пакеты, мы должны настроить токен личного доступа Github и изменить файл конфигурации .npmrc, чтобы иметь возможность аутентифицироваться при выполнении команд publish или install.

  1. Перейдите в «Github › Настройки › Настройки разработчика › Токены личного доступа» и нажмите «Создать новый токен». В форме задайте описательное имя и проверьте разрешения write:packages, (read:packages неявно) и delete:packages:

Вы можете узнать больше об аутентификации пакетов Github в документах.

  1. Добавьте следующие строки в файл .npmrc, который является файлом конфигурации для npm:
@xcanchal:registry=https://npm.pkg.github.com/xcanchal
always-auth= true //npm.pkg.github.com/:_authToken={YOUR_GITHUB_TOKEN}

Наконец, мы можем опубликовать наш пакет date-logic. Для этого выполните следующую команду из папки пакета:

$ npm publish

Мы увидим следующий вывод (обратите внимание, что версия 1.0.0 была опубликована):

npm notice npm notice 📦 @xcanchal/[email protected]
npm notice === Tarball Contents ===
npm notice 61B index.js
npm notice 400B package.json
npm notice === Tarball Details === 
npm notice name: @xcanchal/date-logic
npm notice version: 1.0.0
npm notice filename: @xcanchal/date-logic-1.0.0.tgz
npm notice package size: 397 B npm notice unpacked size: 461 B
npm notice shasum: 4e48d9d684539e0125bf41a44ae90d6c6fc4b7df
npm notice integrity: sha512-DowuECiLPHd55[...]/LV5T/2pFqucQ==
npm notice total files: 2 npm notice
+ @xcanchal/[email protected]

Давайте посмотрим, как это выглядит в Github. Откройте браузер и перейдите в свой репозиторий Github. Там вы можете увидеть опубликованные пакеты в правом нижнем углу страницы:

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

Пакет 2 (рендерер даты)

Теперь давайте реализуем наш второй пакет: файл date-renderer. Создайте новую папку date-renderer под packages и повторите те же действия, что и для пакета date-logic.

Затем установите пакет date-logic в качестве зависимости (помните, что date-renderer будет использовать логику даты для вывода значения на консоль).

$ npm install --save @xcanchal/date-logic

Отлично, мы установили пакет из нашего реестра пакетов Github! После этого мы создадим новый файл index.js и добавим следующий код, представляющий собой простой скрипт, который импортирует пакет date-logic и выполняет экспортированную туда функцию для вывода даты на консоль.

const getDate = require('@xcanchal/date-logic');

(() => {
  console.log(`Date: ${getDate()}`);
})();

Мы можем протестировать его, чтобы убедиться, что он работает правильно:

$ node index.js
// -> Date: Wed Sep 22 2021 22:50:51 GMT+0200 (Central European Summer Time)

Структура нашего проекта теперь должна выглядеть так (так выглядит типичный проект Lerna):

/
  package.json
  lerna.json
  packages/
    date-logic/
      index.js
      package.json
    date-renderer/
      index.js
      package.json <-- date-logic installed as a dependency

Давайте также опубликуем пакет date-renderer в реестре пакетов Github, запустив npm publish из папки пакета.

Изменение пакетов

Давайте внесем некоторые изменения в наши пакеты. Измените код в файле index.js пакета date-logic, чтобы дата отображалась в формате в соответствии с заданным языковым стандартом и некоторыми параметрами:

module.exports = function getDate(
  locale = 'en-US',
  options = { weekday: 'long', year: 'numeric', month: 'long', day: 'numeric' }
) {
  return new Date().toLocaleDateString(locale, options);
};

Прежде чем вносить эти изменения, мы должны определить тип коммита, так как он повлияет на потребителей, которые используют наш пакет. Поскольку мы изменили возвращаемый тип функции с объекта Date на String, мы можем считать это критическим изменением. Чтобы указать его с помощью обычных коммитов, тело нижнего колонтитула должно быть многострочным, а строка нижнего колонтитула должна начинаться с «BREAKING CHANGE:».

$ git add .

$ git commit -m "feat(date-logic): returns localized date string
BREAKING CHANGE: changes the return type of the getDate function"

$ git push

Использование силы Лерны

Выполните git log, чтобы увидеть три разных коммита, которые мы сделали до сих пор (от самого нового к самому старому):

commit 7decbab3aab121c2235e3fa8fd79fe30ad4350c4 (HEAD -> main, origin/main, origin/HEAD)
Author: Xavier Canchal <[email protected]>
Date:   Thu Sep 23 13:45:02 2021 +0200

  feat(date-logic): returns localized date string

  BREAKING CHANGE: changes the return type of the getDate function

commit d2497bbb357d41b0f4ed81e9a5f1af45b38e5fce
Author: Xavier Canchal <[email protected]>
Date:   Thu Sep 23 12:48:59 2021 +0200

  feat(date-renderer): creates package

commit 857efc7057941c254f97d7cf2d49b4f8eae3b196
Author: Xavier Canchal <[email protected]>
Date:   Thu Sep 23 09:48:02 2021 +0200

  feat(date-logic): creates package

Теперь мы будем использовать Lerna для анализа истории обычных коммитов, чтобы определить, какие пакеты были изменены, а также уровень влияния этих изменений, чтобы определить, какую версию следует обновить.

Выполните следующую команду из корневой папки монорепозитория (обратите внимание на флаг --conventional-commits).

$ lerna version --conventional-commits

Появятся некоторые журналы, и Lerna перечислит пакеты, для которых будут установлены версии, и запросит подтверждение:

[...]
Changes:
- @xcanchal/date-logic: 1.0.0 => 2.0.0
? Are you sure you want to create these versions? (ynH)

Если мы подтвердим, нажав клавишу y, Lerna обновит атрибут version в package.json date-logic и отправит тег на Github. Смотрите вывод:

lerna info execute Skipping releases
lerna info git Pushing tags...
lerna success version finished

Если мы посетим страницу тегов нашего репозитория Github, мы увидим созданный тег:

Но это еще не все! Lerna также сгенерировала отдельный CHANGELOG.md для пакета date-logic со всей историей изменений. Довольно аккуратно, правда?

Мы до сих пор не опубликовали эту новую версию 2.0.0. Для этого воспользуемся другой командой Lerna: lerna publish с аргументом from-git. Этот аргумент говорит Lerna решить, какие версии должны быть опубликованы, просматривая теги Git, которые используются в качестве источника правды.

Но сначала мы должны расширить конфигурацию Lerna, добавив URL-адрес реестра под атрибутом commands.publish.registry в наш файл lerna.json, который теперь выглядит так:

{
  "packages": [
    "packages/*"
  ],
  "version": "independent",
  "command": {
    "publish": {
      "registry": "https://npm.pkg.github.com/xcanchal"
    }
  }
}

Зафиксируйте и опубликуйте изменение конфигурации Lerna:

$ git add .
$ git commit -m "feat(root): adds publish registry to lerna config"
$ git push

И выполните команду публикации Lerna:

$ lerna publish from-git

Который также запросит подтверждение, как на этапе version (добавьте флаг --yes, если вы хотите автоподтверждение):

[...]
Found 1 package to publish:
- @xcanchal/date-logic => 2.0.0
? Are you sure you want to publish these packages? (ynH)

Подтверждаем и получаем следующий вывод:

[...]

Successfully published:
 - @xcanchal/[email protected]
lerna success published 1 package

Давайте посетим нашу страницу пакетов репозитория и посмотрим, как теперь у нашего пакета опубликованы две разные версии:

Теперь мы можем использовать новую версию пакета date-logic в пакете date-renderer. Обновите package.json date-renderer до целевой версии 2.0.0 и выше и выполните npm install.

{
...
  "dependencies": {
    "@xcanchal/date-logic": "^2.0.0"
  }
...
}

Перейдите в папку пакета date-renderer и выполните узел index.js, чтобы увидеть обновленный результат:

$ node index.js
// -> Date: Thursday, September 23, 2021

И это все!

Вывод

Что мы рассмотрели в этой статье?

  • Спецификация обычных коммитов.
  • Использование пакетов Github в качестве реестра npm.
  • Настройка аутентификации в пакетах Github и npm.
  • Использование Lerna в сочетании с обычными коммитами для версии и публикации пакетов, а также получение приятного файла CHANGELOG.md в качестве бонуса.

Следующие шаги

  • Настройка проверки синтаксиса коммита (например, commitlint), чтобы избежать человеческих ошибок, которые могут повлиять на управление версиями из-за неправильной истории коммитов.
  • Автоматизируйте рабочий процесс управления версиями и публикации пакетов при отправке нового кода в репозиторий с помощью действий Github.
  • Публикуйте разные типы версий: бета-версии при отправке в разработку и окончательные версии при отправке в мастер в рамках предыдущего действия Github. См. флаги Лерны — обычный-пререлиз и — обычный-выпускник.

Последние два шага описаны в этой следующей статье.

Вы когда-нибудь использовали монорепозиторий для управления пакетами? Вы использовали Lerna или любой другой инструмент? Не стесняйтесь оставлять отзывы!

Первоначально опубликовано на https://dev.to 23 сентября 2021 г.