ניתוח גרסאות סמנטי (או SemVer בקיצור) הוא שיטת ניהול גרסאות תוכנה שקובעת מספרי גרסאות בני שלושה חלקים בצורה <עיקרי>.<משני>.<תיקון>, כגון 1.0.2, עם תוספת קדם-שחרור אופציונלית בצורה -<קדם-שחרור>, כגון 1.0.2-beta.

SemVer הינו אולי השיטה הנפוצה ביותר לניהול גרסאות כיום. לדוגמה, גם Nuget וגם npm ממליצים ותומכים בו, ו- VS Code משתמש בו גם כן.

ברוב מאגרי GitHub שמשתמשים בתכונת GitHub Releases לפרסום שחרורים, תראו מספר גרסאות SemVer בשלט שחרור האחרון על דף הבית, כפי שניתן לראות בתמונה למטה:

אני צריך להגדיר מספר גרסאות SemVer לעתים קרובות כאשר בונה פרויקטים של ASP.NET Core API, ואז לקרוא או לדווח על זה בזמן הריצה.

לדוגמה, אם אני בונה API מינימלי עם גירסא שמוגדרת ל1.0.2-beta, זה יודען על ידי שקף /version שמוצג על ידי הAPI, כפי שנראה בתמונה למטה מHoppscotch (זה כלי דומה לPostman עם הנוחות שהוא רץ בדפדפן):

לבדוק שהגירסא שמודענת משירותים מונחים, כמו אפליקציות רשת וAPIים, נכונה היא חלק קריטי של תהליך CD שלי ואחת מבדיקות העשן שאני משתמש בהן כדי להחליט אם ההפעלה הצליחה.

יש קצת הסתבכות כשמגדירים מספר גירסא SemVer על אסמבליות .NET, שהיא שבתחילה .NET השתמשה במספרי גירסא בחלקים ארבעה כמו 1.0.3.212 והאסמבליות עדיין יש להן את אלה (אסמבליה היא המונח של .NET ליחידות קוד שמודרכות לבייטקוד .NET, הכי טיפוסי שבהם הם dll’s וexe’s).

הדבר השני הוא של.NET יש לא אחת אלא מספר רב של מספרי גירסא שונים מעט, שנמצאים באותה אסמבליה.

במאמר הזה, אני אראה לך איך להגביר את הפיקוח על הכלים האלה ולהטביע מספר גירסא SemVer על אסמבליה .NET בזמן בניה. זה אומר, על אסמבליה .exe או .dll, ואיך לקרוא אותו בזמן ריצה.

טבלת תוכן

מבנה מספר גרסה SemVer

שיקול מספר גרסה SemVer כמו 1.0.2 או 1.0.2-beta. יש לו את הצורה <עיקרי>.<משני>.<תיקון><קדם-שחרור>

זה מה שהמרכיבים השונים משמעים:

החלק <major> של מספר הגרסה יוגדל רק אם השחרור החדש ישבור את השחרור הקיים (העדכני ביותר).

במקרה של אפליקציית UI, לקוחות עשויים להתכוון ללקוחות אנושיים. אז אם השחרור החדש ישבור את הנכסים הקיימים של המשתמשים כמו הגדרות פיתוח עבודה, זה יקרא להגדלת מספר הגרסה העיקרי. במקרה זה, אם השחרור הקודם היה 1.0.2, השחרור החדש יהיה 2.0.0 (כל החלקים הנמוכים של מספר הגרסה יאופסו).

במקרה של ספריה, כמו חבילת ספריה ב-Nuget או NPM, הלקוחות יהיו קוד אחר. אז אם השחרור החדש ישבור את קוד הלקוח הקיים, כלומר הוא לא יהיה תאימה לאחור עם גרסה קודמת שלו, אז שוב, החלק <major> יוגדל.

<minor> מוגדל אם נוספו תכונות חדשות אבל הגרסה החדשה עדיין תאימה לאחור. אז מ1.0.2 תעברו ל1.1.0.

<patch> מוגדל כאשר יש צורך להוציא שחרור חדש אף על פי שלא היו שינויים שוברים ולא נוספו תכונות חדשות. זה יכול לקרות, למשל, אם היה תיקון באג שהיה צריך להשחרר.

-<prerelease> היא תוספת אופציונלית. היא בדרך כלל מצורפת למספר גרסה בשלושה חלקים כאשר התוכנה צריכה להיות זמינה בשלבי בדיקת שחרור מוקדם כמו אלפא ובטה. למשל, לפני שחרור גרסה 1.0.2 כללית, ניתן להציע אותה לבודקי הבטא כ1.0.2-beta.

הרכיב <prerelease> יכול להיות כמעט כל מחרוזת שתבחרו והדרישה היחידה היא שזה יהיה מזהה אלפביתי-מספרי כגון beta או 12 או alpha2 (ללא תווים אחרים מלבד מספרים או אותיות של האלפבית) או מזהים אלפביתי-מספריים מרובים מופרדים בנקודה (.) לדוגמה development.version.

מספרי הגרסאות הרבים של אסם .NET

כפי שמוסבר במאמר של אנדרו לוק לגבי ניהול גרסאות ב.NET, לאסם .NET יש לא אחד אלא מספר גרסאות שונות:

  • AssemblyVersion: זה מספר גרסה בחלקים ארבעה, למשל, 1.0.2.0. הוא משמש את הרכיב בעת טעינת אסמים מקושרים.

  • FileVersion: זה מספר הגרסה שמודיע עבור קובץ .dll בתכנית חלונות בזמן שמקליק שמאלי על האסם ובוחר באפשרות תכונות.

  • גירסת מידע: גירסה נוספת ודומה ל-'FileVersion', וניתן לראותה בתיבת המאפיינים אם תלחצו על האסמבלי בחלונות ותבחרו 'מאפיינים'. זה יכול להכיל טקסטים ולא רק מספרים ונקודות כמו ב-'AssemblyVersion' ו-'FileVersion' שמוגבלים לזה.

  • גירסת חבילה: אם הפרויקט הוא חבילת Nuget, זה יהיה מספר הגירסה של החבילה שהאסמבלי הוא חלק ממנה.

כל מספרי הגירסה הללו מוחלפים לתוך האסמבלי בתהליך ההרכה כמטאדאטה. ניתן לראותם אם תבדקו את האסמבלי עם JetBrains dotPeek (חינם) או Red gate Reflector (לא חינם) או דומה.

'FileVersion' ו-'InformationalVersion' ניתן לראותם גם בטאב 'פרטים' של תיבת המאפיינים שמופיעה כשמלחצים ימנית על קובץ האסמבלי במסוף קבצים של חלונות ובוחרים 'מאפיינים':

בצילום המסך לעיל, "גירסת מוצר" היא הכותרת עבור InformationalVersion בעוד "גירסת קובץ" היא הכותרת עבור FIleVersion.

מתוך ארבעה סוגי מספרי גירסה שתוארו למעלה, רק שלושת הראשונים מתאימים לכל אסםבלי (כלומר, בין אם האסםבל הוא חלק מחבילת Nuget או לא).

מתוך שלושת אלה, AssemblyVersion תמיד הוסיף 0 במקום הרביעי אם תנסה להגדיר גירסת SemVer שיש לה שלושה מספרים (פלוס תחילה קדם יציאה אופציונלית). לדוגמה, אם תנסה להגדיר גירסת SemVer של 1.0.2-beta במהלך הבנייה ואז תקרא את ערך AssemblyVersion בזמן הריצה באסםבל, זה יהיה 1.0.2.0.

FileVersion עושה אותו דבר, כפי שנראה בצילום המסך לעיל.

InformationalVersion הוא המספר היחיד שיהיה מוגדר בדיוק לגירסה של השרת שהגדרת במהלך הבנייה, כפי שנראה בצילום המסך לעיל.

לכן, InformationalVersion הוא הגירסה שצריך לקרוא אותה בזמן הריצה כדי לשלוף את גירסת SemVer של האסםבל.

איך לקבוע מספר גירסת SemVer

יש שני דברים שאתה צריך לעשות כדי לקבוע מספר גירסת SemVer על אסםבל במהלך הבנייה.

ראשית, בתוך <PropertyGroup> בקובץ הפרויקט csproj, הוסף אלמנט <IncludeSourceRevisionInInformationalVersion>false</IncludeSourceRevisionInInformationalVersion>:

<PropertyGroup>
 ...
 <IncludeSourceRevisionInInformationalVersion>false</IncludeSourceRevisionInInformationalVersion> 
</PropertyGroup>

כפי שתואר בהבעיה הזו, זה מבטיח שהערך InformationalVersion מוגדר בדיוק למספר הגירסה SemVer שהגדרנו ולא יקבל +<קוד המילול> עוקב בסוף.

שנית, העביר את מספר הגירסה כערך של התכונה Version שמועברת לפקודה dotnet build למשל:

dotnet build --configuration Release -p Version=1.0.2-beta

זה יגדיר את InformationalVersion בקובץ המורכב (.exe או .dll) ל1.0.2-beta.

במקרה של צדקה, זה גם יגדיר את AssemblyVersion ו-FileVersion (יכול להתווסף 0 בסוף 1.0.2) אבל אנחנו לא מעוניינים באלה.

שים לב שבמקום להעביר את הארגומנט Version בשורת הפקודה, אתה יכול לקבוע תכונת MS Build <Version>1.0.2-beta</Version> בתוך אלמנט <PropertyGroup> בקובץ csproj. אולם העברת ערך של הפרמטר Version לפקודה dotnet build פשוטה יותר כי הקובץ csproj לא צריך להשתנות כל פעם שמספר הגירסה מוגדל. זה מועיל בתעלות CD. גם, כברירת מחדל, קבצי csproj לא מכילים תכונה כלשהי הקשורה לגירסום.

איך לקרוא את גירסת SemVer של האסמבלי בזמן הריצה

הקוד שקורא את InformationalVersion בזמן הריצה הוא כדלהלן:

string? version = Assembly.GetEntryAssembly()?.
  GetCustomAttribute<AssemblyInformationalVersionAttribute>()?.
  InformationalVersion;

בממשקי המינימליים שלי, להוספת נקודת קצה /version כפי שהראיתי בחלק ההקדמה לעיל, אני מקבע את הקטע לעיל בProgram.cs, ואז מוסיף את הקטע הבא מייד לאחר מכן. שים לב שכל הדבר צריך להופיע לפני שbuilder.Build() נקרא:

//האובייקט הזה של סוג אנונימי יהיה 
//מתוקן כ-JSON בגוף התשובה
//כאשר נחזר על ידי ההנדלר
var objVersion = new { Version = version ?? "" };

//קוד אחר
//var app = builder.Build()

אחרי שbuilder.Build() נקרא, אני יוצר הנדלר לנקודת הקצה /version:

app.MapGet("/version", () => objVersion);

עכשיו כשאני מריץ את פרויקט ה-API וקורא לנקודת הקצה /version, אני מקבל את מספר הגרסה חזרה באובייקט JSON בגוף תשובת HTTP:

{
  "version": "1.0.2-beta"
}

זה מה שתמונת Hoppscotch בהקדמה הראתה.

סיכום

מאמר זה הראה לך איך לקבוע מספר גרסה SemVer באסמבליות, ספריות או אפליקציות .NET שלך.

הוא גם הראה לך איך לקרוא את מספר הגרסה בזמן ריצה.