Монорепозитарии на примерах с использованием Lerna. Часть 1
Определения монорепозитария могут быть разными, но мы будем считать репозитарий как моно если выполняются следующие условия :
- Репозиторий содержит более одного логического проекта (например, клиент iOS и веб-приложение)
- Эти проекты, скорее всего, не связаны между собой или слабо связаны другими способами (например, с помощью инструментов управления зависимостями)
- Эти проекты, скорее всего, не связаны между собой или слабо связаны другими способами (например, с помощью инструментов управления зависимостями)
Так же еще нужно учесть что рост каждого репозитория по отдельности обусловлен следующими параметрами:
- Количеством коммитов
- Количество веток и / или тегов
- Количество отслеженных файлов
- Размер отслеживаемого содержимого (измеряется путем замером каталога .git).
Вместо то что бы высказываться за или против использования монорепозитарий я планирую в этой статье исследовать несколько практических вопросов использования их. В частности для управления проектов, я планирую использовать инструмент Lerna на примере тестового проекта monorepo (проект будет на JavaScript).
Финальная версия проекта monorepo доступна по ссылке.
Введение
Прежде чем мы перейдем к созданию проекта, на нужно установить Lerna. Хорошая новость в том, что это очень просто.
Установите Lerna глобально командой:
sudo npm install --global lerna
примечание: это статься написана с использованием Node.js v8.9.4 и Lerna v2.9.0.
Создайте новую папку и в ней запустите следующие команды для инициализации Lerna.
git init
lerna init
В результате должна появиться следующая структура файлов:
Создание Packages
Теперь, когда у нас есть проект monorepo c Lerna, мы можем начать создавать пакеты. В контексте развертывания в npm Registry пакетами будут отдельные пакеты npm. То есть каждый пакет npm по сути будет отдельный проект. Так же пакеты будут иметь слабую связь между собой и независимое управление зависимостями (подробнее об этом позже).
Мы создаем отдельные проекты, создавая папки в директории packages, например, apple, banana и grocery, и запустим следующую команду в каждой из них:
npm init -y
Теперь структура папок у нас будет такая:
Сторонние зависимости
Допустим что нам нужно что бы все три проекта зависили бы от npm пакета sillyname@0.0.3 (с определенной версией); для этого нам нужно запустить команду (команды Lerna могут быть запущены в любой папке проекта):
lerna add sillyname@0.0.3
Теперь у нас будет такая структура папок:
В результате:
- Так как нам нужно что бы наши пакеты (проекты) были независимы друг от друга, у каждого пакета будет свой package.json (с зависимостью sillyname).
- В каждой папке будет сгенерированный новый package-lock.json
- С Lerna, мы можем добавить зависимости в несколько пакетов с помощью одной команды.
В итоге в каждом пакете своя копия пакета sillyname; что не очень хорошо с точки зрения потребления дискового пространства и более медленной процедуры инсталляции. Для решения этих проблем можно вручную обновить каждый package.json и установить пакет sillyname только один раз в родительскую папку node_modules ( Node.js будет искать пакеты в родительских папках). Но это достаточно утомительно.
У Lerna есть другое решение; опция hoist:
lerna add sillyname@0.0.3 --hoist
В результате структура папок будет такой (на скриншете не показа родительская папка node_modules):
В итоге мы получим:
- В каждом package.json есть зависимость от sillyname.
- Пакет sillyname инсталлирован в корневой папке node_modules .
Выборочное обновление
Допустим нам нужно обновить версию sillyname только для проекта grocery; для этого нам нужно запустить команду:
lerna add sillyname@0.1.0 —scope=grocery
В результате получим такую структуру папок (на скриншете не отображена корневая папка node_modules):
В итоге:
- Файл package.json из проекта grocery обновлен и туда включена зависимость sillyname v0.1.0
- В папке grocery появилась папка node_modules с новой версией sillyname.
Внутренние зависимости
Сейчас допустим что нам надо что бы пакет grocery зависил от пакетов apple и banana. Для запустим команду:
lerna add apple banana --scope=grocery
Теперь у нас будет такая структура папок:
Как результат:
- Файл package.json проекта grocery обновлен и в нем указаны зависимости от пакетов apple и banana.
- Lerna сама создала линки в папке node_modules проекта grocery.
Код
Теперь когда у нас установлены все зависимости мы можем написать какой нибудь код.
packages/apple/index.js
const sillyname = require('sillyname'); module.exports = `apple and ${sillyname()}`;
packages/banana/index.js
const sillyname = require('sillyname'); module.exports = `banana and ${sillyname()}`;
packages/apple/grocery.js
const sillyname = require('sillyname'); const apple = require('apple'); const banana = require('banana'); console.log(`grocery and ${sillyname()}`); console.log(apple); console.log(banana);
Далее выполним следующую команды из папки пакета grocery:
node index.js
Мы должны получить что-то вроде этого:
grocery and Linenhiss Butterfly
apple and Trailspeaker Scribe
banana and Translucentpuma Kangaroo
Закончив с кодирование, внесем изменения в .gitignore, так как нам не нужно хранить папку node_modules в системе контроля версий.
**/node_modules
Затем добавьте файлы в коммит и отправим проект в удаленный репозиторий.
Запуск (Bootstrap)
Теперь предположим, что другой член команды захочет работать над проектом. Сначала ему нужно глобально устанавливить Lerna.
sudo npm install --global lerna
Затем ему нужно клонировать репозиторий и из корневой папки проекта запустить следующую команду, чтобы установить все зависимости (включая символические ссылки).
lerna bootstrap --hoist
Все этого достаточно что бы приступить к работе над кодом.
Следующая статья: Монорепозитарии на примерах с использованием Lerna. Часть 2
Оригинал статьи: Monorepos By Example: Part 1