Използвайки номериране на версиите като ГЛАВНА.ВТОРОСТЕПЕННА.КРЪПКА, трябва да увеличите:
ГЛАВНАта версия, при направени несъвместими промени вприложно-програмният интерфейс (API).
ВТОРОСТЕПЕННАта версия, когато промените са обратно съвместими.
КРЪПКА-номера, когато са направени обратно съвместими поправки.
Допускат се допълнителни етикети за предварителни издания и билд-метаданни,като разширения на формата за версия ГЛАВНА.ВТОРОСТЕПЕННА.КРЪПКА.
В света на софтуерния мениджмънт съществува страховито място,наречено „ад на зависимостите“. Колкото по-голяма става вашата системаи колкото повече библиотеки интегрирате в проекта си, толкова по-голямае вероятността един ден да се окажете в тази яма на отчаяние.
В системи с множество зависимости пускането на нови версии на някои от тяхможе бързо да се превърне в кошмар. Ако спецификациите за зависимост сатвърде стриктни, рискувате да блокирате версията (невъзможността да обновите пакет,освен ако не пускате нова версия при всяка промяна в библиотека, от която тойзависи). Ако зависимостите са посочени твърде слабо, неизбежно ще ви застигненесъвместимостта на версиите (поради заявена съвместимост с повече бъдещи версии,отколкото е разумно). Адът на зависимостта е мястото, където се намирате, когатоблокирането на версията и/или несъвместимостта на версията ви пречат лесно ибезопасно да движите проекта си напред.
За справяне с този проблем предлагам прост набор от правила и изисквания,които определят как се присвояват и увеличават номерата на версиите. Тези правиласе основават на (но не непременно са ограничени до) съществуващи широко разпространениобщи практики. Те са приложими както в затворени проекти, така и в такива сотворен код. За да работи тази система, първо трябва да определите публичен API.Той може да бъде описан в документация или да се определя от самия код. Важното етози API да бъде ясен и точен. След като идентифицирате публичния си API, виесъобщавате промените в него с конкретни увеличения в номера на вашата версия.Нека разгледаме формат на версия X.Y.Z (ГЛАВНА.ВТОРОСТЕПЕННА.КРЪПКА).Поправки на грешки, които не засягат версията на API увеличават версията накръпката. Oбратно съвместими добавки/промени, увеличават второстепенната версия,а обратно несъвместими промени в API увеличават главната версия.
Аз наричам това „Семантично версиониране“ (Semantic Versioning).При тази схема, номерата на версиите и начинът, по който се променят,предават значение за състоянието на изходния код и какво е променено между две версии.
Ключовите думи „ЗАДЪЛЖИТЕЛНО“ (MUST), „ЗАДЪЛЖИТЕЛНО НЕ“ (MUST NOT),„ИЗИСКВА“ (REQUIRED), „БИ ТРЯБВАЛО“ (SHELL), „НЕ БИ ТРЯБВАЛО“ (SHELL NOT),„ТРЯБВА“ (SHOULD), „НЕ ТРЯБВА“ (SHOULD NOT), „ПРЕПОРЪЧИТЕЛНО Е“ (RECOMMENDED),„МОЖЕ“ (MAY) и „НЕЗАДЪЛЖИТЕЛНО“ (OPTIONAL) в този документ се тълкуват,както е описано вRFC 2119.
Продуктите, използващи „Семантично версиониране“, ЗАДЪЛЖИТЕЛНО деклариратпубличен API. Този API може да бъде деклариран в самия код или да съществувастрого в документацията. Независимо от метода, ТРЯБВА да бъдепрецизен и изчерпателен.
Нормалният номер на версията ЗАДЪЛЖИТЕЛНО има вид X.Y.Z, където X, Y и Z санеотрицателни цели числа и ЗАДЪЛЖИТЕЛНО НЕ съдържат водещи нули. X е главнатаверсия, Y е второстепенната, а Z е версията на кръпката. Всеки елементЗАДЪЛЖИТЕЛНО се увеличава числено. Например: 1.9.0 -> 1.10.0 -> 1.11.0.
След като бъде пусната дадена версия, съдържанието на тази версияЗАДЪЛЖИТЕЛНО НЕ се променя. Всякакви модификации е ЗАДЪЛЖИТЕЛНО да бъдатпуснати като нова версия.
Главна версия нула (0.y.z) е за първоначална разработка. Всичко можеда се промени по всяко време. Публичният API НЕ ТРЯБВА да се счита за стабилен.
Версия 1.0.0 определя публичния API. Начинът, по който се увеличава номерътна версията след това издание, зависи от това, как се променя той.
Версия на кръпката Z (x.y.Z | x > 0) ЗАДЪЛЖИТЕЛНО се увеличава, ако се правятсамо обратно съвместими корекции на грешки. За такива (bug fix) се считат вътрешнипромени, които коригират неправилно поведение.
Второстепенната версия Y (x.Y.z | x > 0) ЗАДЪЛЖИТЕЛНО се увеличава, когатов публичния API се въвежда нова, обратно съвместима функционалност. ЗАДЪЛЖИТЕЛНОе тя да се увеличи, ако някоя публична API функция е маркирана катооттеглена (depricated). МОЖЕ да се увеличи, ако се въведе съществена новафункционалност или подобрения в рамките на не-публичния програмен код.МОЖЕ да включва промени характерни за кръпка. Версията на кръпката ЗАДЪЛЖИТЕЛНОсе връща на 0, когато второстепенната версия се увеличава.
Главната версия X (X.y.z | X > 0) ЗАДЪЛЖИТЕЛНО се увеличава, когато в публичнияAPI се въведат каквито и да е обратно несъвместими промени. МОЖЕ да включва ивторостепенни промени или кръпки. Версията на кръпката и второстепенната версияЗАДЪЛЖИТЕЛНО се нулират (X.0.0), когато основната версия се увеличава.
Предварително издание (pre-release) МОЖЕ да бъде обозначено веднага следверсията на кръпката чрез добавяне на тире, следвано от разделени с точкиидентификатори. Те ЗАДЪЛЖИТЕЛНО съдържат само ASCII буквено-цифровипоследователности и тире [0-9A-Za-z-]. Идентификаторите ЗАДЪЛЖИТЕЛНО НЕтрябва да са празни. Числовите идентификатори ЗАДЪЛЖИТЕЛНО НЕ включватводещи нули. Предварителните издания имат по-нисък приоритет от свързанатаосновна версия. Предварителното издание показва, че версията е нестабилнаи може да не отговаря на предвидените изисквания за съвместимост, заявениот свързаната с нея нормална версия. Примери:1.0.0-alpha, 1.0.0-alpha.1, 1.0.0-0.3.7, 1.0.0-x.7.z.92.
Билд-метаданни МОЖЕ да бъдат обозначени чрез добавянена знак плюс и серия от разделени с точка идентификатори непосредствено следверсията на кръпка или на предварителното издание. Идентификаторите ЗАДЪЛЖИТЕЛНОсъдържат само ASCII буквено-цифрови последователности и тире [0-9A-Za-z-].Идентификаторите ЗАДЪЛЖИТЕЛНО НЕ трябва да са празни. Билд-метаданнитеЗАДЪЛЖИТЕЛНО се игнорират при определяне на приоритета на версията. Потози начин две версии, които се различават само в билд-метаданните са седнакъв приоритет. Примери:1.0.0-alpha+001, 1.0.0+20130313144700, 1.0.0-beta+exp.sha.5114f85.
Приоритетът определя как версиите се нареждате една спрямо друга.При изчисление на приоритетът, версията ЗАДЪЛЖИТЕЛНО се разделя на ГЛАВНА, ВТОРОСТЕПЕННА,КРЪПКА и идентификатори за предварително издание в този ред (билд-метаданнитесе пренебрегват при определянето му). Подредбата се определя от първата разликапри сравняване на всеки от тези компоненти отляво надясно, както следва:Главните, второстепенните и версиите на кръпките винаги се сравняват като числа.Пример: 1.0.0 < 2.0.0 < 2.1.0 < 2.1.1. Когато всички те са еднакви, версиятас предварително издание има по-нисък приоритет от основната версия.Пример: 1.0.0-alpha < 1.0.0. Приоритет за две версии с предварително издание(при еднакви ГЛАВНА, ВТОРОСТЕПЕННА и КРЪПКА) ЗАДЪЛЖИТЕЛНО се определя чрезсравняване на всеки един идентификатор отляво надясно, докатоне се установи разлика, както следва: идентификаторите, състоящи се само от цифри,се сравняват цифрово, а идентификаторите с букви или тиретата се сравняватлексикално в ред на сортиране по ASCII. Цифровите идентификатори винаги иматпо-нисък приоритет от текстовите. По-голям набор от полета на предварителнотоиздание има по-висок приоритет, ако всички предходни идентификатори са равни.Пример: 1.0.0-alpha < 1.0.0-alpha.1 < 1.0.0-alpha.beta < 1.0.0-beta <1.0.0-beta.2 < 1.0.0-beta.11 < 1.0.0-rc.1 < 1.0.0.
<valid semver> ::= <version core> | <version core> "-" <pre-release> | <version core> "+" <build> | <version core> "-" <pre-release> "+" <build><version core> ::= <major> "." <minor> "." <patch><major> ::= <numeric identifier><minor> ::= <numeric identifier><patch> ::= <numeric identifier><pre-release> ::= <dot-separated pre-release identifiers><dot-separated pre-release identifiers> ::= <pre-release identifier> | <pre-release identifier> "." <dot-separated pre-release identifiers><build> ::= <dot-separated build identifiers><dot-separated build identifiers> ::= <build identifier> | <build identifier> "." <dot-separated build identifiers><pre-release identifier> ::= <alphanumeric identifier> | <numeric identifier><build identifier> ::= <alphanumeric identifier> | <digits><alphanumeric identifier> ::= <non-digit> | <non-digit> <identifier characters> | <identifier characters> <non-digit> | <identifier characters> <non-digit> <identifier characters><numeric identifier> ::= "0" | <positive digit> | <positive digit> <digits><identifier characters> ::= <identifier character> | <identifier character> <identifier characters><identifier character> ::= <digit> | <non-digit><non-digit> ::= <letter> | "-"<digits> ::= <digit> | <digit> <digits><digit> ::= "0" | <positive digit><positive digit> ::= "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9"<letter> ::= "A" | "B" | "C" | "D" | "E" | "F" | "G" | "H" | "I" | "J" | "K" | "L" | "M" | "N" | "O" | "P" | "Q" | "R" | "S" | "T" | "U" | "V" | "W" | "X" | "Y" | "Z" | "a" | "b" | "c" | "d" | "e" | "f" | "g" | "h" | "i" | "j" | "k" | "l" | "m" | "n" | "o" | "p" | "q" | "r" | "s" | "t" | "u" | "v" | "w" | "x" | "y" | "z"
Това не е нова или революционна идея. Всъщност вероятно вече използвате нещоподобно. Проблемът е, че „подобно“ не е достатъчно еднозначно. Безсъответствие с някаква официална спецификация, номерата на версиите сапо същество безполезни за управление на зависимости. Като бъдат наречени и ясноопределени, става лесно да заявите вашите намерения пред потребителитена вашия софтуер. След като тези намерения са ясни и гъвкави (но не твърде),спецификациите за зависимости най-накрая могат да бъдат направени.
Прост пример ще демонстрира как „Семантично версиониране“ може да изпрати „адътна зависимостите“ в миналото. Представете си библиотека, наречена „Пожарна кола“.Тя изисква пакет използващ „Семантично версиониране“, наречен „Стълба“. По времето,когато „Пожарна кола“ е създадена, „Стълба“ е версия 3.1.0. Тъй като „Пожарна кола“използва известна функционалност, представена за първи път в 3.1.0, можетеспокойно да заявите зависимост от „Стълба“, версия по-голяма или равна на 3.1.0,но по-малка от 4.0.0. Сега, кога основни версии 3.1.1 и 3.2.0 стават достъпни,можете да ги интегрирате с вашата система, знаейки, че те ще бъдат съвместимисъс текущата функционалност.
Като отговорен разработчик, разбира се, ще искате да проверите, че обновения пакетсе държи според очакванията. Реалният свят е хаотичен; нищо не може да се направипо въпроса, освен да сме бдителни. Това, което може да се направи, е да оставите„Семантично версиониране“ да ви предоставя разумен начин за издаване и надграждане напакети, без да се налага да го правите при всяка промяна на зависимости,спестявайки ви време и нерви.
За да се възползвате от описаното до тук, трябва просто дазаявите, че използвате „Семантично версиониране“ и да следвате правилата.Поставете връзка към този уеб сайт във вашия README файл, така че значението напромените във версията да бъде ясно за всички.
Просто започнете първоначалната си версия от 0.1.0 и след това увеличавайтевторостепенната версия за всяко следващо издание.
Ако вашият софтуер се използва в производствена среда, вероятно вече трябвада бъде 1.0.0. Ако имате стабилен API, от който потребителите зависят,трябва да е 1.0.0. Ако се притеснявате много за обратната съвместимост, вероятновече трябва да е 1.0.0.
Главната версия 0 е свързана с бързата разработка. Ако променяте APIвсеки ден, трябва или да бъдете във версия 0.y.z, или да използвате отделенклон за работата върху следващото главно издание.
Това е въпрос на отговорно развитие и предвидливост. Несъвместимипромени не трябва да се въвеждат с лека ръка в софтуер, от който зависятмного проекти. Цената на такова надграждане може да бъде значителна.Това, че трябва да се увеличи главната версия, за да се издадат несъвместимипромени, означава, че ще обмислите последствията от променитеи ще отчетете съотношението цена/полза.
Ваша отговорност като професионален разработчик е качественото документиране насофтуера, предназначен за широко използване. Управлението на сложносттана софтуера е изключително важна част от поддържането на ефективен проект.Това е трудно да се направи, ако никой не знае как се използва вашиясофтуер или какви методи могат да бъдат извиквани безопасно. В дългосрочен план,„Семантичното версиониране“ и настойчивостта за качествено документиране на публичнияAPI помага за безпроблемната работа.
Веднага щом разберете, че сте нарушили спецификацията на SemVer, поправетепроблема и пуснете нова вторична версия, която коригира проблема ивъзстановява обратната съвместимост. Дори и в такава ситуация е неприемливода подменяте вече издадена версия. Ако е подходящо, документирайте некоректнатаверсия и информирайте потребителите си за проблема с обратната съвместимост в нея.
Това може да се разглежда като съвместимо изменение, тъй като не засягапубличния API. Софтуер, който има същите зависимости като вашияпакет трябва да има свои спецификации за зависимост и авторът да забележиевентуални конфликти. За определяне дали промяната е ниво на кръпка иливторостепенна модификация зависи от това дали сте актуализирали зависимостите си,за да коригирате грешка или да въведете нова функционалност. Обикновено за второтое очакван допълнителен код, при което следва да се промени второстепенната версия.
Използвайте най-добрата си преценка. Ако имате огромна публика, която ще бъде драстичнозасегната от промяната на поведението към това, което е планирано в публичният APIможе би е най-добре да извършите издание на главна версия, въпреки че поправката можестрого погледнато да се счита за издаване на кръпка. Не забравяйте, че „Семантичноверсиониране“ се стреми строго да следва спецификациите. Ако тези промени са важниза вашите потребители, използвайте номера на версията, за да ги информирате.
Оттеглянето на съществуващата функционалност е нормална част от разработкатана софтуер и често се изисква за постигне на напредък. Когато оттегляте частот вашия обществен API, трябва да направите две неща: (1) обновете документациятаси информирайки потребителите за промяната, (2) направете ново незначително изданиес оттеглянето. Преди да премахнете напълно функционалността в нова главна версиятрябва да има поне едно второстепенно издание, което съдържа оттеглянето, така чече потребителите могат плавно да преминат към новия API.
Не, но използвайте добра преценка. Например, вероятно е излишно версията да е дълга 255символа. Също така, специфичните системи могат да налагат свои собствениограничения за дължината на низа.
Не, „v1.2.3“ не е семантично-зададена версия. Префиксиране на семантично-зададенаверсия с „v“ е често срещан начин (на английски език) да се посочи, че става думаза номер на версия. Съкращаването „версия“ като „v“ често се наблюдава присистеми за контрол на версиите. Пример:git tag v1.2.3 -m "Release version 1.2.3"
, в случая „v1.2.3“ е маркер,а семантично-зададената версия е „1.2.3“.
Съществуват два такива израза. Първият е за системи поддържащи PCRE(Perl Compatible Regular Expressions, тоест Perl, PHP и R), Python и Go.
Вижте:https://regex101.com/r/Ly7O1x/3/
^(?P<major>0|[1-9]\d*)\.(?P<minor>0|[1-9]\d*)\.(?P<patch>0|[1-9]\d*)(?:-(?P<prerelease>(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+(?P<buildmetadata>[0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$
Другият използва номерирани групи (при него cg1 = ГЛАВНА, cg2 = ВТОРОСТЕПЕННА,cg3 = КРЪПКА, cg4 = предварително издание и cg5 = билд-метаданни) и есъвместим с ECMA Script (JavaScript), PCRE (Perl Compatible Regular Expressions,тоест Perl, PHP и R), Python и Go.
Вижте:https://regex101.com/r/vkijKf/1/
^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$
Автор на спецификацията „Семантично версиониране“ (SemVer) еТом Престон-Вернер, основател на Gravatars исъучредител на GitHub.
За обратна връзка, молясъздайте запитване вGitHub.