Привет! Мы GrandCore Foundation. Создаём идеальную организацию для развития свободных проектов: ПО, этичных онлайн-сервисов и стандартов изделий. Подробнее читайте здесь. Присоединяйтесь к нашему чату в Telegram. Всегда рады единомышленникам!
Для нашего нового проекта — универсального генератора документации у нас появилась потребность в создании монорепозитория, поскольку функционал генератора будет расширяться плагинами. Ниже читайте как мы полностью автоматизировали данный процесс при помощи GitHub Actions и Lerna.
Что такое монорепозиторий
Монорепозиторий — это совокупность множества проектов в одном репозитории.
Преимущества:
Нет необходимости поддерживать огромное количество репозиториев отдельно;
Возможность отслеживания и редактирования кода во всем проекте каждой отдельной командной;
Атомарные коммиты.
Проблемы:
Увеличение объема данных;
Возможная проблема с версионированием каждого подпроекта;
Уменьшение ответственности каждой команды, которая работает над подпроектами репозитория.
Lerna
В качестве основного инструмента работы с монорепозиторием в NPM необходимо рассматривать Lerna. Она является достаточно удобным инструментом для работы над монорепозиториями. Lerna позволяет решить множество возникающих при работе проблем.
Возможности:
Сквозное версионирование;
Индивидуальное версионирование каждого подпроекта;
Удобная публикация всех подпроектов;
Автоматическое управление зависимостями между подпроектами;
Работа с Yarn или NPM.
Создание монорепозитория
Для демонстрации исходных настроек Lerna мы создадим проект по умолчанию, а далее продемонстрируем вариант, который мы реализовали в нашем проекте.
Установите Lerna глобально:
npm i lerna -g
Инициализируйте проект:
lerna init
После этого в корне проекта создаестся директория packages/
, файл package.json
и файл lerna.json
.
Содержимое lerna.json
:
{
"packages": [
"packages/*"
],
"version": "0.0.0"
}
Сначала указывается директория со всеми вложенными проектами (директорий может быть несколько). Далее указывается версия монорепозитория (для сквозного версионирования данное значение совпадает во всех подпроектах).
Cоздание нового пакета (подпроекта):
lerna create <name-pkg>
После ввода данной команды в директории packges/
(по умолчанию) создается новый каталог с именем, которое указывается в качестве параметра. Далее необходимо ответить на несколько стандартных вопросов от Lerna. Важно отметить, что для всех пакетов желательно использовать названия следующего типа @project_name/pkg_name
. Таким образом можно привязать все пакеты, которые разрабатываются внутри репозитория, к одному монорепозиторию или одной организации.
Допустим, что мы создали новый пакет. Тогда в директории с этим пакетом создается файл package.json
. Чтобы данный пакет в дальнейшем был доступен публично и корректно публиковался, необходимо добавить в данный файл:
"publishConfig": {
"access": "public"
}
Естественно, подпроекты можно создавать и вручную.
Публикация изменённых проектов:
lerna publish
Возможные проблемы:
Не авторизованы в NPM;
Забыли добавить публичный доступ в
package.json
пакета;Занятое или некорректное название одного из пакетов.
Важные замечания:
Публикация возможна только после коммита при использовании данной команды в таком виде;
По умолчанию задается сквозная версия для всех пакетов.
Рекомендуем почитать в документации Lerna о дополнительных параметрах данной команды.
В нашем проекте
Если вы хотите использовать собственную структуру проекта, то вы можете её изменить. В качестве примера рассмотрим наш проект универсального генератора документации.
Мы решили оставить в основном модуле только функционал менеджера потока, а также markdown финализатор, совместимый с основными генераторами статических сайтов. Для того, что бы обрабатывать сами файлы с документацией, необходимо подключить соответствующие плагины.
Наш файл lerna.json
:
{
"packages": ["plugins/*", "main/"],
"version": "0.0.0",
"command": {
"run": {
"npmClient": "npm"
}
}
}
В нашем проекте все плагины находятся в каталоге plugins/
, а основной модуль в папке main/
. Таким образом структура нашего проекта представляет из себя следующий вид:
├── lerna.json
├── LICENSE
├── main
│ ├── index.js
│ ├── lib
│ │ ├── finMd.js
│ │ └── manager.js
│ ├── package.json
│ └── README.md
├── package.json
├── plugins
│ ├── fin-html
│ │ ├── index.js
│ │ ├── package.json
│ │ └── README.md
│ └── gen-js-jsdoc
│ ├── index.js
│ ├── package.json
│ └── Readme.md
│ ...
└── README.md
Взаимодействие модулей и зависимости
Поговорим теперь о взаимодействиях между разными частями проекта. Очевидно, что в монорепозитории каждый подпроект может как-то зависеть от других. Возникает проблема зависимостей между подпроектами. Кроме того, не очень удобно устанавливать зависимости отдельно для каждого подпроекта. Lerna умеет решать данные проблемы.
Установка зависимостей:
lerna bootstrap
При выполнении данной команды Lerna анализирует все подпроекты и выполняет npm install
в каждом из них. Если в зависимостях одного локального модуля (подпроекта) B находится другой A, то Lerna создает символическую ссылку в папке node_modules/
подпроекта B на подпроект A. Таким образом не происходит лишних копирований файлов в node_modules/
. Работая с монорепозитоием, мы должны выполнять данную команду вместо установки зависимостей в каждом подпроекте.
Рекомендуем почитать в документации Lerna о дополнительных параметрах данной команды.
Автодеплой с помощью GitHub Actions
Мы создали монорепозиторий, даже смогли опубликовать его в NPM, но теперь возникает вопрос: "Как можно автоматически отправлять изменения в NPM?". Для решения такой задачи можно использовать GitHub Actions.
Создадим в корне проекта папку .github/workflows/
, а в ней файл main.yml
(название может быть любое). Рассмотрим на нашем примере.
Содержание нашего yml файла:
name: autodeploy
on:
push:
branches: [main]
pull_request:
branches: [main]
jobs:
default:
runs-on: ubuntu-latest
steps:
- name: checkout
uses: actions/checkout@v2
- name: Install Packages
run: npm install
- name: Authenticate
run: |
echo "@grandcore:registry=https://registry.npmjs.org/" > .npmrc
echo "registry=https://registry.npmjs.org/" >> .npmrc
echo "//registry.npmjs.org/:_authToken=$NPM_TOKEN" >> .npmrc
env:
NPM_TOKEN: ${{ secrets.NPMTOKEN }}
- name: Publish
lerna publish from-package --yes --no-verify-access
env:
NPM_TOKEN: ${{ secrets.NPMTOKEN }}
Что тут происходит:
В качестве тригера запуска установлен коммит или пулл реквест в мастер;
Операционной системой выбирается Ubuntu;
Далее в склонированном репозитории выполняется установка npm-пакетов;
После этого добавляем пользователя NPM. Для этого необхдоимо создать секрет в проекте с npm-токеном. В нашем проекте токен содержится в секрете NPMTOKEN.
Выполняем публикацию с помощью соответствующей команды
lerna publish from-package --yes
. В нашем случае, мы обновляем только те пакеты, которым мы поменяли вручную версию в их файлахpackage.json
.Отслеживать ход выполнения можно во вкладке Actions в вашем репозитории.
Будем рады выслушать ваши замечания в комментариях.
Если читателя заинтересуют наши Open Source проекты, будем рады видеть в нашем чате.
Комментарии (16)
MarkFish
01.10.2021 01:37-1Последний раз когда читал про монорепозитории, писали, что проблем с ними дофика. Особенно с opensource проектами (историю коммитов рвет, чего-то там с отправкой pull reques и т.д.).
rock
01.10.2021 11:09Вы это себе как представляете? С точки зрения системы контроля версий, это обычный репозиторий, только в нем несколько пакетов, а инструменты вроде lerna просто упрощают управление этими пакетами (связывание, публикация и т.п.) - так что это не тот уровень, на каком могут возникнуть упомянутые вами проблемы.
Shatun
01.10.2021 13:39Последний раз когда читал про монорепозитории, писали, что проблем с ними дофика. Особенно с opensource проектами (историю коммитов рвет, чего-то там с отправкой pull reques и т.д.).
А это несовсем монорепозиторий в том смысле в котором обычно имеется ввиду. Монорепо с лерной - это чаще просто репа, в которой хранится несколько проектов. С точки зрения гита, туллинга это одна гит репа(либ гит модули)
MarkFish
01.10.2021 13:46Монорепо с лерной - это чаще просто репа, в которой хранится несколько проектов.
Я с ними не работал, читал только, так что ничего не утверждаю.
Люди писали, что при публикации как раз таки проекта хранащегося в монорепе, та же история его коммитов будет оборвана.
Pugavkomm
01.10.2021 17:21+1lerna позволяет даже добавлять другие репы в монорепу с сохранением истории коммитов, поэтому тут все хорошо.
DuD
01.10.2021 04:28Есть ли в lerna способ задать ту версию которую я хочу выставить? По дефолту она вроде сама инкрементит версию, но как быть если версия приходит "извне" и не поддается простой логике lerna?
grandcore Автор
01.10.2021 09:09Смотри вариант в нашем проекте который мы сделали. Там по изменению версии в пэкеджджэйсоне как раз автоматически пушится на нпм всё. Если версию не менять - экшен просто в холостую пробегается.
Lodin
02.10.2021 15:491) А как у вас
npm install
безactions/setup-node
работает?2) Если использовать
actions/setup-node
, то необходимости вызывать командуecho "//registry.npmjs.org/:_authToken=$NPM_TOKEN" >> .npmrc
нет, т.к. экшн это делает по-умолчанию. Разве что переменная называетсяNODE_AUTH_TOKEN
.
grandcore Автор
08.10.2021 14:50Там была ошибка -
http вместо https. Поправили конфиг гитхаб экшена.
Oxyd
Я правильно понимаю, что это только для проектов на js или?...
Shatun
Да, только для js(ts). Честно говоря немогу представить зачем вам бы понадобилась лерна для других языков-там свои аналоги и части проблем которые есть в js нету(зато есть свои)
Pugavkomm
Lerna - это инструмент, который оптимизирует рабочий процесс по управлению репозиториями с несколькими пакетами с помощью git и npm.
Oxyd
Ясно, спасибо.