Опубликовать один npm-пакет — тривиально. Опубликовать 145 взаимозависимых пакетов с корректными бампами версий, верифицированными артефактами, сгенерированными changelog и планом отката — это пайплайн.
Мы построили его внутри самого KB Labs. Release-плагин оркестрирует полный поток: plan → snapshot → checks → build → verify → version → changelog → publish → git → report. Вот что мы узнали.
Этапы пайплайна
1. Plan
Планировщик сканирует git-историю с момента последнего release-тега, чтобы определить, какие пакеты изменились и какой бамп им нужен. Использует скоупд теги (@kb-labs/core-types@1.2.3) для независимого версионирования и теги v* для lockstep-релизов. Три стратегии:
- Independent — каждый пакет версионируется отдельно на основе своих коммитов
- Lockstep — все пакеты получают максимальный бамп (breaking change в одном пакете бампит всё)
- Adaptive — lockstep только на breaking changes, иначе independent
2. Snapshot
Перед изменением любого файла пайплайн сохраняет версии всех package.json в .kb/release/backup.json. Если что-то сломается дальше, restoreSnapshot() восстановит исходное состояние. История сохраняется с временными метками в .kb/release/history/ для аудита.
3. Checks
Настраиваемые проверки перед публикацией: audit, lint, type-check, test. Запускаются до 8 проверок параллельно. Каждая может быть optional (только предупреждение) или strict (блокирует публикацию). Провальные строгие проверки прерывают пайплайн до любого бампа версий.
4. Build (безопасная сборка)
Здесь есть тонкость: tsup с clean: true очищает директорию dist/ перед сборкой. Если вы локально запускаете REST API из того же dist/, сервис падает в середине сборки.
Решение: сборка во временную директорию, затем атомарный rename-своп. Запущенный сервис никогда не видит неполный dist/.
5. Верификация пакета
Этот этап ловит то, что CI пропускает. Для каждого пакета верификатор запускает npm pack, распаковывает тарбол и проверяет:
- Нет тестовых файлов в пакете (
.spec.,.test.,__tests__) - Все экспорты, объявленные в
package.json, реально существуют - Нет импортов директорий, которые работают локально, но ломаются при установке
- Нет отсутствующих зависимостей
Это ловит класс багов, проявляющихся только после публикации: пакет работает в монорепо из-за поднятых зависимостей, но падает при установке как standalone.
6–9. Version → Changelog → Publish → Git
Бампы версий записываются в package.json. Changelog генерируется по каждому пакету из диапазонов git-коммитов — с шаблонами (compact, corporate, technical) и опциональным LLM-улучшением для человекочитаемых резюме. Публикация использует OTP для CLI или токен для CI. Git получает коммит и теги.
Паттерн адаптеров внутри пайплайна
Ядро пайплайна ничего не знает о CLI-промптах или REST API. Два ключевых интерфейса делают его переносимым:
- PackagePublisher — CLI-версия запрашивает OTP через промпт; REST-версия использует токен. Один пайплайн, разные паблишеры.
- ChangelogGenerator — с LLM или без. Один пайплайн, разные генераторы.
CLI и REST API — тонкие адаптеры над одним и тем же ядром runReleasePipeline(options).
Что мы узнали, публикуя 145 пакетов
- Верификация пакета обязательна. В первом реальном прогоне поймали 7 пакетов со сломанными экспортами.
- Снепшот с возможностью отката спас нас дважды — когда npm publish падал на середине батча из-за сетевых проблем.
- Безопасная сборка (temp dir + атомарный своп) убрала целый класс ошибок «в CI работало, локально сломалось».
- Independent versioning лучше lockstep для монорепы такого размера — lockstep создаёт лишний version churn в пакетах, которые не менялись.