Релиз и публикация
Полный цикл релиза: поднять версию через PR/MR, поставить тег, создать релиз и опубликовать в npm — четыре отдельных, компонуемых шага, плюс sync-deps — чтобы поправить другие локальные пакеты, у которых после этого остался диапазон зависимости на устаревшую версию. Бампнуть партию пакетов и затем опубликовать или зарелизить только часть из них — разные по смыслу решения, которые не всегда происходят одновременно, поэтому они никогда не объединены принудительно. Работает с GitHub и GitLab, автоматически определяемыми для каждого репозитория — см. «Конфиг и поиск репозиториев».
bump
Выбор пакетов, поднятие версии (по умолчанию patch), открытие PR/MR, merge в основную ветку и тег релиза.
polyrepo bump [options]- Показывает чекбокс пакетов с текущей версией и версией, до которой она поднимется (
1.2.9 → 1.2.10для patch-бампа —--minor/--majorподнимают другую часть версии, обнуляя всё, что ниже, как у любого semver-инструмента). Пакеты с грязным рабочим деревом помечены — они будут пропущены. В описании подсвеченного пакета показано, что реально изменилось с последнего git-тега — вычисляется для всех пакетов параллельно. - После подтверждения — для каждого выбранного пакета по одному:
git fetch origin→git checkout <default branch>→git merge --ff-only origin/<default branch>(bump-ветка всегда создаётся от актуальной основной ветки, а не от той, на которой репозиторий случайно оказался);- проверяет состояние предыдущей попытки — есть ли уже смёрдженный PR/MR, открытый, либо просто запушенная ветка с именем
<new-version>-version-bump(либо<sanitized-name>-<new-version>-version-bumpдля участника pnpm-воркспейса — так два пакета, забамленных до одной версии в одном репозитории, никогда не спорят за одно имя ветки). В зависимости от того, что найдено, процесс продолжается с нужного места вместо падения на «ветка уже существует» или создания дубликата; - если
package.jsonна ветке ещё не бампнут — поле"version"обновляется (текстовая замена — форматирование и порядок полей не трогаются). Если у пакета уже естьCHANGELOG.md, туда же добавляется черновик записи## [x.y.z] - YYYY-MM-DD(в стиле Keep a Changelog) со списком коммитов с последнего тега — черновик для правки, не готовый changelog; gh pr create(GitHub) либоglab mr create(GitLab) в основную ветку (если PR/MR ещё не существует);- с
--wait-checks: ожидание CI перед merge — механизм отличается по хостингу, см. описание опции ниже; отсутствие настроенных проверок не считается ошибкой; gh pr merge/glab mr merge— через PR/MR, не прямым push;- локальная основная ветка синхронизируется с только что смёрдженным PR/MR;
- git-тег
v<new-version>создаётся и пушится, если его ещё нет —<name>@<new-version>для участника pnpm-воркспейса, по той же причине избежания коллизий, что и с именем ветки выше.
- Любая ошибка на этом пути помечает пакет ✗ с понятным сообщением, и процесс переходит к следующему пакету, не прерывая весь запуск целиком.
- После обработки всех выбранных пакетов — отдельная проверка по всем пакетам (не только только что бампнутым): не остался ли у какого-то локального пакета диапазон зависимости, не соответствующий новой версии бампнутого пакета — здесь ничего не меняется автоматически (см.
sync-depsдля исправления). - Если хотя бы один пакет реально был затегован (и это не был
--dry-run) — один раз для всей партии спрашивает, опубликовать ли эти пакеты в npm прямо сейчас, — если только не задан--publish, который пропускает вопрос и публикует сразу (под--yesбез--publishвопрос пропускается целиком, точно так же, как вопрос про релиз уtag). В любом случае это ровноpublish, запущенный для ровно тех пакетов, что только что были бампнуты — подробнее в «Публикации» ниже.
Публикация
Без --publish вопрос выше — единственное место, где bump вообще трогает npm. С флагом (или ответом «да») это ровно polyrepo publish для ровно тех пакетов, что только что были бампнуты: логин в npm проверяется один раз в начале, зависимости участника pnpm-воркспейса при необходимости сначала (пере)линкуются, а пререлизная версия (например, 2.0.0-beta.0) автоматически публикуется под dist-тегом next вместо latest — те же правила, что и у самой publish. Предлагаются только пакеты, реально затегованные в этом запуске — пропущенный из-за грязного рабочего дерева или упавший на середине пакет никогда не публикуется.
Реальный вывод — --dry-run, пока ещё ничего не существует:
[1/1] os-detect 2.1.5 → 2.1.6
$ git fetch origin
$ git checkout master
Your branch is up to date with 'origin/master'.
Already on 'master'
$ git merge --ff-only origin/master
Already up to date.
✓ master is up to date.
[dry-run] would create branch 2.1.6-version-bump, ensure version 2.1.6 (+ a CHANGELOG.md entry if one exists), commit/push if needed, then open + merge a PR, then tag v2.1.6.Повторный запуск bump на пакете, где предыдущая попытка уже прошла весь путь до конца, гораздо короче — всё уже сделанное просто подтверждается, а не выполняется заново:
[1/1] vue-toast-kit 1.0.7 → 1.0.8
✓ master is up to date.
✓ Already merged as PR #12 — master already has it.
✓ Tag v1.0.8 already exists on origin.(На GitLab-репозитории это выглядит так же, но с «MR» вместо «PR» — провайдерный слой сам подставляет нужную метку, независимо от хостинга.)
--prerelease, либо --preid в паре с --major/--minor, вычисляют целевую версию иначе, но проходят через те же самые шаги ветка → PR/MR → merge → тег:
[1/1] os-detect 2.1.5 → 2.1.6-beta.0
$ git fetch origin
$ git checkout master
Your branch is up to date with 'origin/master'.
Already on 'master'
$ git merge --ff-only origin/master
Already up to date.
✓ master is up to date.
[dry-run] would create branch 2.1.6-beta.0-version-bump, ensure version 2.1.6-beta.0 (+ a CHANGELOG.md entry if one exists), commit/push if needed, then open + merge a PR, then tag v2.1.6-beta.0.Опции
--dry-run
Печатает план по каждому пакету, ничего не пушит и не меняет — включая запись в CHANGELOG.md, ожидание CI и git-тег.
--minor
Поднять minor-версию вместо patch (например, 1.2.9 → 1.3.0).
--major
Поднять major-версию вместо patch (например, 1.2.9 → 2.0.0).
--prerelease
Поднять (или начать) пререлиз вместо patch/minor/major (например, 1.2.9 → 1.2.10-alpha.0, либо 1.2.10-alpha.0 → 1.2.10-alpha.1). В паре с --preid задаётся имя — node-semver сам решает, добавить ли -alpha.0 к текущей версии или продвинуть номер уже существующего пререлиза.
--preid <name>
Идентификатор пререлиза для --prerelease, либо в паре с --minor/--major — для preminor/premajor-пререлиза (например, --major --preid beta на 1.2.9 → 2.0.0-beta.0). По умолчанию alpha.
--custom-version <version>
Задать точную версию вместо вычисления. Работает только вместе с --packages, называющим ровно один пакет — применять одну и ту же версию сразу к нескольким пакетам никогда не имеет смысла.
--packages <a,b,c>
Имена директорий пакетов через запятую вместо интерактивного чекбокса — для скриптов. Неизвестные имена выводятся как предупреждение и пропускаются.
--yes
Пропустить подтверждение «proceed?».
--wait-checks
Дождаться CI перед merge; не мёрджить, если проверки не прошли. На GitHub это gh pr checks --watch, привязанный к PR. У GitLab нет аналога, привязанного к MR — вместо этого используется glab ci status --branch --wait для пайплайна bump-ветки, суть та же. Отсутствие настроенных проверок не считается ошибкой в обоих случаях — просто нечего ждать.
--publish
После тегирования сразу опубликовать в npm — без вопроса (см. «Публикация» выше).
Пример:
# сначала dry run — ничего не пушится, не коммитится, не мёрджится и не тегается
polyrepo bump --dry-run
# поднять или продвинуть beta-пререлиз
polyrepo bump --prerelease --preid beta
# начать premajor release candidate (например, 2.0.0-rc.0)
polyrepo bump --major --preid rc
# задать точную версию для одного пакета, например для хотфикса
polyrepo bump --packages os-detect --custom-version 2.1.6-hotfix.1
# бамп, тег и публикация, неинтерактивно
polyrepo bump --publish --yes
# полностью неинтерактивно, для скрипта/CI
polyrepo bump --packages vue-toast-kit,os-detect --yestag
Для пакета, версия которого уже была поднята каким-то другим способом (не через bump, либо до того, как он начал тегировать) — release не с чем работать, тега для текущей версии ещё нет. tag ставит недостающий тег на текущую версию без повторного бампа и без открытия PR/MR.
polyrepo tag [options]- Подтягивает основную ветку origin для каждого репозитория (один раз на физический репозиторий), затем проверяет по каждому пакету (параллельно), существует ли уже тег
v<local version>(<name>@<local version>для участника pnpm-воркспейса). Пакет, у которого локальная версия отличается от того, что реально на origin, помечается уже здесь — это важно знать, потому что следующий шаг подтягивает локальную копию до основной ветки origin перед тегированием, так что создаваемый тег может в итоге назвать не ту версию, что реально будет затегована. - Показывает чекбокс: пакет, версия, тег — с пометкой
⚠ origin has <version>, если версия отличается от origin. Незатегованные пакеты отмечены по умолчанию; уже затегованные тоже можно выбрать вручную (безвредно — просто подтверждает, что тег на месте). - После подтверждения — для каждого выбранного пакета: синхронизирует основную ветку (так же, как
bump), затем заново читает версию изpackage.json— теперь, когда локальная копия точно совпадает с origin. Версия на диске могла измениться, если локальная копия не была уже на основной ветке или отставала. Тег всегда называет версию, которая реально здесь сейчас, а не ту, что была до синхронизации — если из-за этого имя тега поменялось, печатается предупреждение. Создаёт и пушит (возможно, переименованный) тег, если его не хватает. - Если хотя бы один пакет реально был затегован (и это не был
--dry-run) — спрашивает: «Create a release for the N package(s) just tagged?» — ответ «да» запускает тот же процесс, что иrelease, но именно для этих пакетов.
Реальный вывод — у текущей версии тег уже есть, так что остаётся только его подтвердить:
[1/1] os-detect v2.1.5
$ git fetch origin
$ git checkout master
Your branch is up to date with 'origin/master'.
Already on 'master'
$ git merge --ff-only origin/master
Already up to date.
✓ master is up to date.
✓ Tag v2.1.5 already exists on origin.Реальный вывод — локальная копия отставала от origin, так что синхронизация основной ветки поменяла то, что реально тегируется:
[1/1] os-detect v2.1.5 (origin has 2.1.6)
$ git fetch origin
$ git checkout master
$ git merge --ff-only origin/master
Fast-forwarded to origin/master.
✓ master is up to date.
! Version changed from 2.1.5 to 2.1.6 after syncing with origin — tagging v2.1.6 instead of v2.1.5.
✓ Tagged and pushed v2.1.6.Опции
--dry-run
Печатает план; ничего не тегает, не пушит и не релизит.
--packages <a,b,c>
Список пакетов вместо интерактивного чекбокса.
--yes
Пропустить подтверждение «proceed?» (вопрос про релиз тоже пропускается — релиз не создаётся, пока не указан --release).
--release
Создать релиз сразу после тегирования, без вопроса — для скриптов.
Пример:
polyrepo tag --packages vue-toast-kit,os-detect --yes --releaserelease
Выбор пакетов и создание релиза для тега их текущей версии.
polyrepo release [options]- Проверяет по каждому пакету (параллельно) наличие тега
v<local version>(<name>@<local version>для участника pnpm-воркспейса) на origin (того самого, что создаётbumpилиtag), и есть ли у этого тега уже релиз. - Показывает чекбокс: пакет и его тег. Пакеты без тега для текущей версии показаны недоступными для выбора («run
bumpfirst»); уже зарелиженные — доступны, но не отмечены по умолчанию, на случай если нужно повторить. - После подтверждения — для каждого выбранного пакета:
gh release create <tag> --verify-tag(GitHub) либоglab release create <tag>(GitLab) — чекбокс и так предлагает только пакеты с уже существующим тегом, так что новый тег никогда не придумывается;--verify-tagу GitHub — это вторая, собственная гарантия того же самого, у GitLab такого флага нет. Текст релиза берётся из соответствующего разделаCHANGELOG.md, если он есть. Иначе — на GitHub--generate-notes(сводка по смёрдженным PR/коммитам); у GitLab аналога нет, поэтому вместо этого используется простой список коммитов с последнего тега.
Реальный вывод — на GitHub-репозитории:
[1/1] os-detect v2.1.5
✓ No changelog entry — using --generate-notes.
$ gh release create v2.1.5 --verify-tag --title os-detect@2.1.5 --generate-notes
✓ Created release os-detect@2.1.5.Опции
--dry-run
Печатает, что было бы создано, ничего реально не создавая.
--packages <a,b,c>
Список пакетов вместо интерактивного чекбокса.
--yes
Пропустить подтверждение «proceed?».
Пример:
polyrepo release --packages vue-toast-kit,os-detect --yespublish
Выбор пакетов и запуск npm publish — по умолчанию отмечены те, что обогнали реестр.
polyrepo publish [options]- Подтягивает основную ветку origin для каждого репозитория (один раз на физический репозиторий), затем проверяет версию каждого пакета в реестре (
npm view <pkg> version) в сравнении с локальной версией — параллельно. Пакет с"private": true(в том числе собственный корень pnpm-воркспейса) исключается полностью, ещё до построения чекбокса — он в принципе не предназначен для попадания в реестр. - Показывает чекбокс: версия в реестре → локальная версия, плюс dist-тег, под которым пойдёт публикация (см.
--dist-tagниже), и пометку⚠ origin has <version>, если у основной ветки origin версия отличается от локальной. Пакеты, у которых версия в реестре отличается от локальной, отмечены по умолчанию; уже опубликованные остаются доступны для выбора (например, чтобы republish после unpublish). - После подтверждения — один раз для всей партии (пропускается под
--dry-run) проверяетnpm whoami— если логин не активен, запускаетnpm login(в реальном терминале, так что и браузерный OTP-флоу, и обычный запрос учётных данных работают как обычно) до начала публикации, вместо того чтобы узнать об этом только на середине публикации первого пакета. - Для каждого выбранного пакета по одному:
npm publish(либоnpm publish --dry-runс флагом--dry-run) — либо, для участника pnpm-воркспейса, вместо этогоpnpm publish --no-git-checks, так что зависимостьworkspace:*на соседний пакет резолвится в реальный номер версии, а не валит публикацию. Работает с реальным терминалом, без перехвата вывода — запрос 2FA/OTP от npm работает как обычно.
Реальный вывод — средняя часть это собственный вывод сборки/тестов/упаковки самого npm для этого пакета, показанный вживую в реальном терминале, а не то, что polyrepo как-то перехватывает или переформатирует:
[1/1] os-detect@2.1.5
$ npm publish --dry-run
… (сборка и тесты самого пакета, список содержимого тарбола от npm)
✓ Published os-detect@2.1.5 (dry run).publish никогда не проверяет состояние git — как и обычный npm publish никогда не проверял — она всегда публикует ровно то, что на диске. Зато предупреждает (не блокируя) прямо перед подтверждением, если выбранный пакет не на основной ветке, содержит незакоммиченные изменения, или его локальная версия отличается от origin:
! os-detect is on branch fix-typo, not master — publishing what's on disk anyway.
! vue-toast-kit has uncommitted changes — publishing what's on disk anyway.Dist-теги
Пререлизная версия (что угодно с -, например 2.0.0-beta.0) по умолчанию публикуется под dist-тегом next вместо принятого в npm по умолчанию latest — иначе она стала бы той версией, что получает любой, кто ставит пакет без явного указания версии, а это почти никогда не то, чего хочет пререлиз. --dist-tag переопределяет это для всех выбранных пакетов (включая автоопределение) — передайте флаг явно, чтобы опубликовать и стабильную версию под чем-то отличным от latest (например, под LTS-каналом).
Опции
--dry-run
npm publish --dry-run вместо реальной публикации.
--dist-tag <tag>
Dist-тег npm, под которым публикуется каждый выбранный пакет (по умолчанию: next для пререлизной версии, иначе принятый в npm по умолчанию latest). См. «Dist-теги» выше.
--packages <a,b,c>
Список пакетов вместо интерактивного чекбокса.
--yes
Пропустить подтверждение «proceed?».
Пример:
polyrepo publish --packages vue-toast-kit,os-detect --yes
polyrepo publish --packages os-detect --dist-tag next # принудительно задать dist-тегsync-deps
Обратная сторона колонки «Deps» в list и раздела «Cross-package dependencies» в doctor: у локального пакета диапазон версии на другой локальный пакет больше не совпадает с текущей версией этого пакета (например, всё ещё стоит ^1.1.0, хотя зависимость уже переехала на 1.2.0). Все остальные места только сообщают об этом — эта команда единственная, кто реально чинит.
polyrepo sync-deps [options]- Читает все найденные пакеты (не только те, что названы в
--packages) — чтобы найти устаревший диапазон, нужно знать текущую версию каждого локального пакета, а не только тех, что чинятся. - Показывает чекбокс «до/после» для каждого найденного устаревшего диапазона:
"depName" "oldRange" → "newRange", сохраняя стиль каждого диапазона (^остаётся^,~остаётся~, точная версия остаётся точной — всё необычное сводится к диапазону^, что видно в этом же превью до того, как что-либо будет записано).--packagesсужает, для каких именно зависимых пакетов предлагаются проблемы — то же значение, что и везде в этом CLI. - После подтверждения обновляет
package.jsonна месте для каждой выбранной проблемы — текстовая замена ровно пары"depName": "oldRange", так что форматирование, порядок полей и стиль кавычек в остальном файле не трогаются.
Правит только package.json на диске — без коммита, без push, без ветки/PR. Просмотрите изменение (git diff), затем закоммитьте его так, как разрешают правила каждого репозитория, командой polyrepo commit.
Реальный вывод:
Sync local dependency ranges
app-shell @acme/ui-kit "^1.1.0" → "^1.2.0"
app-shell @acme/api-client "~2.0.0" → "~2.1.0"
✓ app-shell: "@acme/ui-kit" "^1.1.0" → "^1.2.0"
✓ app-shell: "@acme/api-client" "~2.0.0" → "~2.1.0"Опции
--dry-run
Печатает, что было бы обновлено, без реальной записи в package.json.
--packages <a,b,c>
Предлагать проблемы только для этих зависимых пакетов — то же значение, что и везде.
--yes
Пропустить подтверждение «proceed?».
Пример:
polyrepo sync-deps # посмотреть, что устарело, затем обновить
polyrepo sync-deps --dry-run # напечатать план, ничего не менять
polyrepo sync-deps --packages a,b --yes # неинтерактивно, для скриптов/CI