npm, Cargo, Composer та pip по-різному інтерпретують одне й те саме скорочення semver, і лише дві з тринадцяти перевірених мною діапазонних виразів працюють ідентично у всіх чотирьох.
Я написав окремі парсери для кожного менеджера, дотримуючись їхніх офіційних специфікацій, а потім перевірив кожен діапазон на матриці з дев'ятнадцяти номерів версій. Сітка 13 × 19 (247 комірок) продемонструвала фрагментарну картину: лише 17 комірок дали однакову відповідь «так» або «ні» у всіх інструментах, і більшість із цих 17 були одностайними відмовами. Коротше кажучи, інструменти частіше погоджуються на «ні», ніж на «так».
Чому інструменти розходяться
Усі чотири менеджери стверджують, що дотримуються Semantic Versioning, проте кожен додає власні правила скорочень.
- Caret (^) та dot-x — npm підтримує caret; pip ігнорує і caret, і dot-x. Тільки це створює 49 розбіжностей у комірках між npm та pip.
- Bare versions — Cargo сприймає звичайну
1.2.3як діапазон caret, що означає «сумісно з 1.x». npm та Composer розцінюють той самий рядок як точний збіг, приймаючи лише1.2.3. - Tilde (~) — tilde в Cargo фіксує minor версію (
~1.2відповідає1.2.*, але не1.3.0). Tilde в Composer фіксує major версію (~1.2відповідає1.*). Ці дві інтерпретації розходяться на будь-якій версії, де змінюється minor-компонент.
Обробка pre-release версій додає ще один сюрприз. Діапазон на кшталт ^1.2.3 не включає 2.0.0-rc.1, навіть якщо числова частина менша за верхню межу. Правило таке: pre-release версії виключаються, якщо діапазон явно не містить ідентифікатора pre-release.
Єдина безпечна синтаксична конструкція для всіх інструментів
Експеримент показує, що явні діапазони нерівностей — наприклад, >=1.2.0 <2.0.0 — працюють однаково в npm, Cargo, Composer та pip. Кожен менеджер сприймає ці дві межі як буквальні числові ліміти, без прихованої семантики caret або tilde.
Якщо вам потрібно виразити «будь-яку версію 1.x, яка є щонайменше 1.2», запишіть це повністю. Це коштуватиме кількох зайвих символів, але гарантує передбачуване вирішення залежностей незалежно від того, де запускається код.
Коли скорочення все ще має сенс
Скорочення залишаються привабливими для проєктів на одній мові. У межах суто npm-екосистеми ^1.2.3 лаконічно означає «сумісно з майбутніми minor та patch релізами». Те саме стосується caret у Cargo або tilde у Composer, якщо ви впевнені, що інструмент, який використовує ці залежності, не зміниться. Небезпека виникає тоді, коли той самий маніфест використовується в межах різних мов або коли CI-завдання підтягує залежності з кількох екосистем.
Висновок
Покладатися на скорочення semver для забезпечення крос-мовної сумісності — це ризик. Єдиний надійний спосіб гарантувати, що обмеження версії означає одне й те саме всюди, — це записувати явні нерівності. Коли вам потрібна стислість, залишайтеся в межах однієї екосистеми; коли вам потрібна узгодженість, відмовтеся від скорочень на користь чіткості.
