Skip to main content

Процесс обновления (WIP)

Давайте рассмотрим команду pvm update которая обновляет состояние нашего репозитория.

Прежде чем начнем, небольшое замечание: cама команда предназначения для работы в CI окружении и поэтому создает коммит с помощью Platform API, либо пытается отправить коммит через git push. Для локального обновления используйте команду yarn -s pvm local update.

Этапы обновления

Команда update работает в несколько этапов:

  1. Определение списка пакетов, которые нуждаются в обновлении.
  2. Вычисление следующий версии для пакетов
  3. Запись новых версий
  4. Обновление ченжлога
  5. Создание релизного тега

Или если в виде схемы, что принимаем на вход, что получаем на выходе:

<что поменялось?>, <как изменить?> -> update() -> {<новые версии>, <ченжлог>, <релизный тег>}

Ниже рассмотрим этапы более подробно

что поменялось ?

Конечная цель этого этапа – список пакетов. Но в начале мы будем его формировать на основе измененных файлов:

  1. Все что поменялось с последнего релиза. Если релиза не было, то вместо него берем git reference в порядке убывания приоритета
    • из настройки update.no_release_ref
    • текущий бранч не master ? тогда merge-base между ним и текущей веткой
    • иначе берем родителя первого не-мерж коммита
  2. Если включена опция update.include_uncommited, будут также учитываться незакомиченные изменения.

Ок, теперь оперируем списком пакетов, и добавляем к результату следующие позиции:

  1. Если задан массив dangerously_opts.always_changed_workspaces, то все пакеты которые попадут под эти маску тоже будут считаться измененными всегда.
  2. Массив заматченных пакетов в force-release.packages в файле update-hints.toml.

Обновление зависимостей

По умолчанию зависимости между пакетами обновляются. То есть, если у вас есть пакет B, и он зависит от пакета A, то при обновлении пакета A пакет B тоже обновит версию пакета A в своем списке зависимостей и свою собственную версию.

как изменить ?

Теперь как pvm решает как поднять версию того или иного пакета. В порядке понижения приоритета будет выбран один из пунктов ниже:

  1. Если мы вручную обновили версию у пакета – оставляем как есть.
  2. Если не выключена опция update.workspace_release_files то учитываются наличие файлов none, prerelease, prepatch, patch, preminor, minor, premajor, major, если такой файл присутствует, то тип релиза будет соответствовать имени файла.
  3. Выполняется опциональный хук release-type. Если метод вернет false или undefined вместо типа релиза, то продолжаем дальше.
  4. Релизный тип в update-hints.toml для заданного пакета, при наличии.
  5. Если в прошлом релизе не было версии для этого пакета, считаем пакет новым и версию оставляем как есть.
  6. Далее обрабатываем опцию update.release_type_overrides. В данном случае логика такая: release_type_overrides задает список кортежей, где каждый кортеж это тип релиза и список масок для файлов. Далее, берем список измененных файлов для пакета, и если этот список полностью "закрывается" масками из одного или нескольких кортежей, тогда переопределение считаем успешным и берем максимальный тип релиза из сматченных кортежей.
  7. Результат вызова хука release-type-by-commits. Если хук пуст или он вернул falsy значение идем дальше.
  8. Конфиг update.default_release_type.

При этом зависимые пакеты обновляется по другому. За их обновление отвечает настройка update.dependants_release_type которая либо может иметь тип релиза (по умолчанию patch), либо равняться as-dep и тогда типа релиза зависимого пакета, будет равнятся типу релизу пакета, от которого зависит зависимый пакет.

Запись новых версий

И тут надо сразу определиться с тем, какие есть версии и еденицы смысла завязанные на них в разрезе работы pvm.

  1. Внутреняя версия для установщика пакетов, будь то yarn или npm или еще какой-то, хранится всегда в поле version файла package.json пакета. В рамках монорепозитория, мы например можем выставлять все версии всегда одинаковыми, чтобы получить гарантию того, что пакеты в репозитории и внутренние зависимости на них, это всегда одно и тоже.
  2. Внешняя версия используемая для публикации и релиза пакетов. Храниться она может где угодно.

pvm update оперирует внешними версиями, и в зависимости от настроек, они могут храниться в разных местах:

versioning.sourceместо хранения версии
tag, версия одна на все пакетырелизный тег вида vX.Y.Z
tag, независимые версиипачка тегов вида pkg-name-vX.Y.Z
fileфайл который задается настройкой versioning.source_file
по умолчанию versions.json
packageполе version в package.json пакетов

Обновление ченжлога

Прежде всего формируется release notes на основе коммитов, сделанных начиная с последнего релиза (или соответствующего git reference если его не было, об этом рассказно выше). При этом плагины могут реализовывать хук commits-to-notes, c помощью которого как-то иначе преобразовать коммиты в release notes, чем это делает сам pvm.

Собственно это и делает плагин @pvm/plugin-conventional-changelog, добавляющий поддержку conventional-changelog для преобразования коммитов в markdown описание.

Допустим, с предыдущего релиза мы сделали две задачи в двух коммитах:

[skip-ci] исправлен парсинг аргумента -S для pvm update
добавлена документация на русском языке

Pvm обработает эти коммиты, и на выходе получится описание релиза в markdown синтаксисе:

- исправлен парсинг аргумента -S для pvm update
- добавлена документация на русском языке

Далее на основе полученных release notes формируется (или нет, если включить опцию release.disable_changelog) новая запись в ченжлог через механизм отрисовщика ченжлогов. Отрисовщик ченжлога задается опцией changelog.renderer, это просто строка. Непосредственно связь между именем и конкретной реализации отрисовщика реализуется через хук: changelog.<имя>. Т.е. если вы зададите имя my-renderer, будет взят хук changelog.my-renderer. По умолчанию используется отрисовщик builin.list отрисовывющий коммиты в виде обычного списка. Помимо прочих, стоит упомянуть еще опцию changelog.path – куда сохранить ченжлог.

Теперь насчет ченжлогов индивидуально для каждого пакета. По умолчанию они отключены, включить их можно через опцию changelog.for_packages.enabled. При этом pvm будет генерировать ченжлоги для пакетов на основе только тех коммитов, которые имели отношение к заданному пакету.

Опции здесь:

  • changelog.for_packages.path – куда сохранять ченжлог, путь относителен для каждого пакета. Или
  • changelog.for_packages.output_dir – отменяет предыдущюю опцию, если задан данная директория будет использована для сохранения всех ченжлогов пакетов в одной директории.

В обоих случаях можно задать опцию front_matter. При этом задавать дефисы сверху и снизу не нужно.

Инкрементальная генерация

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

Неинкрементальные ченжлоги и локальные релизы

Есть два вида отрисовщиков ченжлогов: инкрементальные и нет. В зависимости от реализуемого интерфейса:

export interface Renderer {
render(releases: Iterable<ReleaseInfo>, forPkg?: string): string,
}

export interface IncrementalRenderer extends Renderer {
append(changelog: string, release: ReleaseInfo, forPkg?: string): string,
}

Инкрементальный рендерер обязан уметь добавлять в существующий ченжлог информацию о новом релизе, но не всегда это удобно. Вы можете иметь неинкрементальный рендерер, однако, в данном случае pvm должен где-то хранить все релизы под рукой, чтобы все их отдать отрисовщику, и не бегать за release notes в платформу, что может быть долго и дорого.

Для этого есть опция local_releases.enabled, включив которую, pvm начнет складывать информацию о релизах локально в файл .pvm/releases.toml для того чтобы быстро отдавать весь список релизов отрисовщику.

Включив опцию первый раз, имеет смысл выполнить команду yarn pvm fetch-releases для того чтобы наполнить локальный релизный файл в первый раз. Далее он будет обновляться автоматически командой pvm update при релизах.

Создание релизного тега

В зависимости от выбранных настроек pvm будет по разному создавать релизный тег:

НастройкиВид тегов или тега
Все пакеты имеют одну версиюvX.Y.Z
Простой репозиторий с одним пакетомvX.Y.Z
Монорепозиторий с разными версиямиrelease-YYYY.MM.DD-{random-word}

Если опция versioning.source равна tag и пакеты имеют независимые версии, то также будет создаваться пачка тегов вида pkg-name-vX.Y.Z. Однако такой подход не рекомендуется, в виду излишнего замусоривания тегами репозитория.

К релизному тегу через platform API также записываются release notes, о которых мы говорили выше.

После создания всех необходимых тегов, команда pvm update (или pvm local update) завершает свою работу.