Skip to main content

Плейсхолдеры в версиях

Когда в монорепозитории работает больше одного разработчика возникает проблема рассинхронизаций версий пакетов между разными ветками, особенно если у вас задействовано сквозное версионирование.

Для примера, у нас включено сквозное версионирование на весь репозиторий, и мы создаем новый пакет zap@1.0.0, например, в одноименном бранче, и он зависит от пакета core@^1.0.0.

Далее в мастере пакет core обновился до версии 1.1.0:

eyJ2ZXJzaW9uIjoiMSIsImVuY29kaW5nIjoiYnN0cmluZyIsImNvbXByZXNzZWQiOnRydWUsImVuY29kZWQiOiJ4nNVYW1PiSFx1MDAxNH73V1D4Kpm+X+Zha8XRXHUwMDE5t1x1MDAxY3DFLUd3Z62WNFx1MDAxMFx0SSZcdIhO+d+3OzAkXHUwMDA0mEVWXexcdTAwMDeK9OnLOd3fdy79fadSqab3ka6+r1T1uK18z43VXXXP9o90nHhhYEQo+07CYdzORvbSNErev3uXz3Da4WAyS/t6oIM0MeP+NN+Vyvfst7CP71x1MDAwNTpcdTAwMWKb9Vx1MDAxNnZcdTAwMTG43NtcYoNsR4hcdTAwMTijlFwiOFx1MDAxYuAlXHUwMDFmzE6pdo20o/xE51x1MDAxMttVvTpTXHUwMDA3XHUwMDFkzlx1MDAxYfe1yzhcdTAwMWPdnn1rhCco37Xj+X4rvfcn5qh2b1x1MDAxOFx1MDAxN3RK0jjs61x1MDAwYs9Nez+sL/TP5iWhMT6fXHUwMDE1h8NuL9CJNVx1MDAxZMx6w0i1vfTeXHUwMDFhXHUwMDAx8l5cdTAwMTV0szXynrFcdTAwMWSBicNcdTAwMTlliM667cRcdTAwMWGS0GFcdTAwMDJTiTmRtpGSSlx1MDAwN6FcdTAwMWbGVqVdKFx1MDAxOFE3uVI3qt3vXHUwMDFhzVx1MDAwMnc2Jo1VkEQqNreUj7ubXHUwMDFhXHUwMDBiJXIg45JcdJ4r0dNet5dadVx1MDAxZCBLXG4mOrtcdTAwMDNEsORcdTAwMDRJOVx1MDAxM9hdo2M3XHUwMDAzwtfyXHUwMDEx9lRcdTAwMWNNj6qaaVfQ2H5cdTAwMWVOULQwPVxuvVwitmzL/1Xy48w+Zv+/7i1cdTAwMWS9zFbbarmZ+Vx1MDAxMjulpaq+StKDcDDwUoPCU6uXUStcdTAwMTj6fsFWXHUwMDE1p/txXHUwMDFj3vW0cstSXHUwMDFkuGVZJnrce1wibSRcXElcdTAwMWJAXHUwMDA1h1hSvDZvPp6ST4dXv336OFwiR0dj91x1MDAxYVx1MDAwYve8se28XHUwMDExzMGCcVjEbEZcdTAwMWNMkSFcdTAwMTS0klx1MDAxNydcdTAwMGVcdTAwMTVcdTAwMGWjXGJcdTAwMTO8nDiLlDGKIYO+guBcclBmiZW21ayBb4EseCVZXGKmwlx1MDAwMFx1MDAwNPC1uaLi4CE6Pz+oX0imwOWxeyX/cFdwpYT5/5EpgsLFXGKDKXYwIOxcdTAwMDdPXpAoRDhCgqJcdTAwMDYzljBo2Gq8XHUwMDE1XFwki5CUc0oxeENkWbDUtoKNW8SXVI/TZXwxOdfK4Fx1MDAwMiFcdTAwMDNcdTAwMDTgJyRl7c5tMkpcdTAwMDc37Lo+/MCG5+Pa8VF9s+BcdTAwMDJfjTJcZjhSoiWxxXBcdGDGXHUwMDAxXCLchNhC8lpmXGZtXHUwMDFit+JuzphcdTAwMDJcdTAwMTlnVIGyzFx1MDAxMGk04YhcdTAwMTP2dIYk9mNNhnTCIG15XHUwMDBmXHUwMDE5XHUwMDAy2Fxc75FcdTAwMWF4vj2a/CQyWNmLXHUwMDBmY/0rdExeVTyFRGdu2sbOuVx1MDAxOfu+11xysmlGXHUwMDAxXHUwMDFkz+Ey9UzJMVx1MDAxYjDwXFzXXFzZT6Gtfd+LkqXRXHUwMDAwXG6xXHUwMDEy3dhQ12SBSK6N7svBt3rTPbl/uOrD46uzfTS4kF82XHRcdTAwMDevh23OXHUwMDFkhkreeFx1MDAwMm6GXHUwMDFkIZCA6F/jwVx1MDAxYejeXHUwMDE1qC2hXkQ2XHUwMDAyXHUwMDBl5Vx1MDAwMi6LXHUwMDA1Rlx1MDAwNODyWFx1MDAwMFx0N1x1MDAxMsCkeFx1MDAxMahv5ijlakcpqcFcdTAwMTFF6ydcdTAwMTYnLX490rfN5C45Olxi49vjodvV2+4njYt04Io0XFxQk3YwREzuXHUwMDAwJZflfOe1XSVcdTAwMDJcdTAwMDZ1XHUwMDAwwsLorfOV8Ll9ZVx1MDAxYUZcdTAwMWI7SszoKnQjc5RCMLR+ifk5aiaaS0rEbT/ot9TpR9Lc324/ibCpb1xmbuefRWaeknEhIcFCXHUwMDAwXHUwMDA0xH/B9jM7SmRKNckxflx1MDAxOZhvXG4mQtkqMEGOXHUwMDE4sYlcZl1cdTAwMWJN9WG/qfjFaLx/0+jrhqo3cfB5u9GEoX2vIMte+jCAjkRcdTAwMTRcdTAwMDBhXFzpT9G0Rlx1MDAxObaLkLjR7NnCLsMmV4CAblCDvVTYNfnJ6kdjYa7CkJKsn8I12OFDcEuCxvis8/tcdTAwMTdcdTAwMTehZlx1MDAxY7e2PvCaO5s8XHUwMDBll8GEpVx1MDAxMSAwf5nP/e5VeFGZoVxiizJ4MEaE2lx1MDAwN5YtjLhcdTAwMGYqmlx1MDAxNCd/XHUwMDA1ldovWfz9e3WxUvCmc1x1MDAwMdjXnXSt8LszLfSrKopaqUrtolx1MDAxM8BXR56+qy8hcidr09XNyXnu1MhpYf+48/hcdTAwMGa0zmYpIn0=core@1.0.0core@1.1.0zap@1.0.0->core@^1.0.0

Проблема

Что произойдет если мы смержим ветку zap ?

  1. Версия пакета zap станет неконсистентной, согласно включенному сквозному версионированию. Мы конечно можем эту версию подправить в дальнейшем, но именно мерж-коммит так и останется неконсистентным.
  2. Зависимость от core хоть и будет легитимной для версии 1.1.0, но, тем самым возникает риск что зависимость core для пакета zap будет версии 1.0.0, а не актуальная. Это возможно, например, если у пакета zap есть некоторая зависимость от пакета, который в свою очередь зависит от пакета core@1.0.0. В итоге npm или yarn может в целях оптимизации размера node_modules, поставить пакет core@1.0.0, когда нам нужен core@1.1.0.

Кроме того, версионирование через пакеты может нести проблемы и с независимыми версиями. При создании бранча, когда вы добавляете пакеты и новые зависимости на эти пакеты, могут появляться конфликты буквально с каждым обновлением мастера, которое также привносит изменения в эти зависимости.

Решение

По сути нам нужно чтобы соблюдалось следующее отношение: в каждом коммите все зависимости между пакетами соответствовали текущим версиям для этого коммита, соблюдая при этом правила или ограничения, которые для нашего репозитория применимы, например, сквозное версионирование.

Самый простой способ достичь этого это вообще прописать в коде всем константные версии-плейсхолдеры, а актуальное версионирование вынести в другое место, про которое npm или yarn знать не будут.

Для примера, пусть у нас будет пакет A:

{
"name": "A",
"version": "0.0.0-stub"
}

Зависимый от него будет выглядеть так:

{
"name": "B",
"version": "0.0.0-stub",
"depenencies": {
"A": "0.0.0-stub"
}
}

При этом обновление пакетов не будет приводить к обновлению версий в мапе depenencies, что уберет проблему конфликтов в этом месте.

Публикация

Публикация пакетов при этом не пострадает, перед ней мы просто будем возвращать версии пакетов на место в package.json и только после этого публиковать пакеты.

соглашение о непубликуемых версиях

Внутри pvm есть соглашение что версия-плейсхолдер имеет вид 0.0.0-TAG, где TAG любая строка, которая вам подходит. Строка не должна начинаться на число.

выделенное версионирование

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

Где хранить настоящие версии

Ок, а где тогда хранить актуальные версии пакетов, которые будут использоваться при публикации ? Есть несколько вариантов:

Релизный тег

Все версии пакетов можем хранить в релизном теге. И в зависимости от настроек и количества пакетов брать версию из непосредственно имени релизного тега и/или его аннотации.

Соответствующие настройки в pvm

[versioning]
source = 'tag'

Файл

Также мы можем хранить версии в файле и сопоставлять имя пакета до нужной версии.

Соответствующие настройки в pvm

[versioning]
source = 'file'
# source_file = 'versions.json' # опционально, по умолчанию складывает в файл versions.json
tip

Если у вас еще нет этого файла, и вы только включили эту настройку, его можно проинициализировать командой yarn pvm lint --fix. Хотя это и не обязательно, все же наглядно посмотреть что изменится при релизе не помешает.

Тег на каждый пакет

Можно для каждого пакета включить релизный тег, в котором и хранить версию.

caution

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

Соответствующие настройки в pvm

[versioning]
source = 'tag'

[tagging.for_packages]
enabled = true

В следующей части книги мы подведем итоги этой главы и рассмотрим все варианты версионирования, которые можно настроить с помощью pvm.