npm, Cargo, Composer ו-pip מפרשים את אותו קיצור semver בצורה שונה, ורק שניים מתוך שלושה-עשר ביטויי טווח שבדקתי מתנהגים באופן זהה בכל ארבעתם.
כתבתי מפרשים (parsers) נפרדים לכל מנהל, פעלתי לפי המפרטים הרשמיים שלהם, ואז הרצתי כל טווח מול מטריצה של תשעה עשר מספרי גרסה. רשת ה-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", כתבו זאת במלואו. זה עולה בכמה תווים נוספים; זה מבטיח פתרון (resolution) צפוי בכל מקום שבו הקוד רץ.
מתי קיצורים עדיין הגיוניים
קיצורים נותרים אטרקטיביים עבור פרויקטים בשפה אחת. בתוך אקוסיסטם טהור של npm, הקיצור ^1.2.3 לוכד בתמציתיות את המשמעות "תואם לגרסאות minor ו-patch עתידיות". הדבר נכון גם עבור ה-caret של Cargo או ה-tilde של Composer כאשר אתם יודעים שהכלי הצורך לא ישתנה. הסכנה מופיעה כאשר אותו manifest משמש מחדש מעבר לגבולות שפות, או כאשר תהליך CI מושך תלויות ממספר אקוסיסטמים.
שורה תחתונה
הסתמכות על קיצורי semver לצורך תאימות בין שפות היא הימור. הדרך האמינה היחידה להבטיח שאילוץ גרסה אומר את אותו הדבר בכל מקום היא לכתוב אי-שוויון מפורש. כשאתם זקוקים לתמציתיות, הישארו בתוך אקוסיסטם יחיד; כשאתם זקוקים לעקביות, החליפו את הקיצור בבהירות.
