הגדרת Git Submodules לספריות משותפות
אותה ספרייה — מנתח קבצי קונפיגורציה, לקוח של API פנימי, סט של רכיבי ממשק — מתקיימת כעותקים במספר מאגרים. כל עוד העותקים מתפצלים לאט, איש לא שם לב לכך. די לשחרר תיקון באחד מהם, והגרסאות מפסיקות להתאים: בשירות ההזדהות כבר חתימת קריאה חדשה, ובחיוב — הישנה.
לאחר מכן פועל אפקט "למי יש חדש יותר". המהנדס הראשון שמשך את השינויים מקבל כשל בנייה בקוד של אחרים, והשאר ממשיכים להיבנות על עותק מיושן ואינם רואים את הבעיה.
חוסר סנכרון בגרסאות צץ בסביבת הבדיקות או, גרוע מכך, בפרודקשן — ומחפשים אותו לא במקום שבו נוצר: קודם בקוד של עצמם, אחר כך בתלויות, ואחר כך בסביבת הבנייה.
הגדרת Git Submodules מסירה את הסיבה עצמה. הספרייה המשותפת נשארת מאגר נפרד, והפרויקט שומר לא עותק של הקוד שלה, אלא הפניה לקומיט מסוים. במאגר האב רואים איזו גרסה של הספרייה מוצמדת, העדכון הופך לפעולה מודעת עם רשומה נפרדת בהיסטוריה, ולא לתוצר לוואי של שכפול. ספרייה אחת במאגרים מפסיקה להיות כמה ספריות שונות.
להלן נפרט כיצד זה נראה בפועל: מה נכנס ל-.gitmodules, כיצד לבנות סדר עדכון כך שהעלאת גרסה בפרויקט אחד לא תשבור את הבנייה בשכן, ואילו בדיקות להציב בבנייה האוטומטית כדי שחוסר ההתאמה יתפס לפני מיזוג הענף.
מה משתנה בבנייה כאשר גרסאות הקוד המשותף נמצאות תחת שליטה
תת-מודול שומר במאגר האב לא את "הגרסה האחרונה" של הספרייה, אלא מצביע לרוויזיה מסוימת. שכפול עם אתחול תת-מודולים נותן בדיוק את הקוד שעבר בדיקות קבלה — גם במחשב המפתח וגם בסוכן הבנייה. לכן בנייה ניתנת לשחזור מפסיקה להיות תלויה במועד ובסדר שבהם מישהו סנכרן תלויות ידנית.
בעבר הקוד המשותף התקיים כעותקים: הספרייה הועברה לכל פרויקט, נערכה ממש במקום, ושכחו להחזיר את התיקון חזרה. הסחף הצטבר, ובניות זהות למראה נבדלו בהתנהגות. Git עם תת-מודולים מסיר את עצם האפשרות להבדל כזה: במאגר האב מונחת הפניה, וגוף הספרייה — רק במאגר שלה עצמה.
הצמדת גרסת הספרייה הופכת את העדכון לפעולה מפורשת: המצביע משתנה, השינוי נכנס להיסטוריה ועובר ביקורת כמו עריכה רגילה. החזרה לאחור — החזרת המצביע לרוויזיה הקודמת, בלי לבנות מחדש את שאר חלקי הפרויקט.
מקור קוד אחיד מסיר עריכות ידניות בקוד של אחרים: התיקון מוזן למאגר הספרייה ומגיע לכל הפרויקטים בעת עדכון תת-המודול.
זמן הבנייה של הפרויקט מתקצר בזכות כך שנעלמת הסנכרוניזציה הידנית של גרסאות והבירורים החוזרים של אי-התאמות. שלב אתחול תת-המודולים בסוכן אורך שניות, והתוצאה דטרמיניסטית: קלט זהה — ארטיפקט זהה. בדרך נעלם סוג שלם של כשלים מסוג "אצלי זה עובד" — פשוט אין מאיפה שייווצר חוסר התאמה.
שלוש דרכים לשימוש חוזר בקוד משותף: השוואת גישות
הדרך לשימוש חוזר בקוד משותף היא בראש ובראשונה שאלה של מי ובאיזו תדירות משנה את הספרייה. אם העריכות מגיעות מפרויקט אחד ומשוחררות יחד איתו, אין טעם לפזר את הקוד בין מאגרים; אם חמש צוותים צורכים את הספרייה בקצב שחרורים שונה — יש צורך בחוזה גרסה, ולא בתיקייה משותפת.
| אפשרות | מתי מתאימה | מגבלות |
|---|---|---|
| תת-מודולים או מאגר אחיד | מספר מאגרים, הספרייה מתקיימת בנפרד, נדרשת הצמדה קשיחה של הפרויקט לרוויזיה מסוימת | הפניה לקומיט במקום לענף: עדכון רק בפקודה מפורשת, הפניה מיושנת לא תשבור את הבנייה, אלא תמשוך בשקט קוד ישן |
| מאגר חבילות | הספרייה נצרכת על ידי פרויקטים שונים, שיש להם מחזור שחרורים משלהם ונדרשות גרסאות עם טווחי תאימות | דורש מאגר, כללי פרסום וגירסום סמנטי; העריכה נעשית זמינה רק לאחר שחרור גרסה חדשה |
| עותק של התלות בעץ | עדכונים נדירים ודרישה להיבנות בלי לפנות למקורות חיצוניים | אותה ספרייה מתפצלת בין גרסאות בין פרויקטים, תיקונים מקומיים הולכים לאיבוד בעדכון הבא |
בנפרד על מגבלות תת-המודולים, שצצות לרוב כבר תוך כדי התהליך. עותק העבודה לאחר שכפול ריק עד לביצוע מעבר רקורסיבי — זה שובר באופן קבוע את הבנייה במערכות אינטגרציה רציפה, אם השלב לא מוגדר במפורש.
בתוך תת-המודול המאגר נמצא במצב של ענף מנותק, ועריכה שנעשתה "במקום" נוסעת כקומיט לענף שהמהנדס לא חשב עליו. לכן כדאי לעגן את בחירת האפשרות בכללים: היכן מותר לשנות את גרסת ההפניה ומי מבצע ביקורת על השינוי הזה.
סדר ההגדרה והבדיקה של תת-מודולים בפרויקט
תת-מודול מצמיד לא ענף, אלא קומיט מסוים של מאגר חיצוני, ולכן "פשוט להוסיף" אותו אינו מספיק. בלי ביקורת תלויות וסכימת ענפים אחידה, הבנייה משוחזרת רק אצל מחבר העריכות: האחרים מקבלים רוויזיה אחרת של הספרייה המשותפת.
שישה שלבים: מהמלאי ועד למסירה
-
ביקורת תלויות. עורכים מלאי של הספריות המשותפות: מה מחובר כעותק, מה כמאגר מקונן, מה ידנית. עבור כל אחת מתעדים בעלים, תדירות עריכות ומספר פרויקטים צורכים.
-
סכימת ענפים. מסכימים מאיפה נלקחת הרוויזיה: צורכי שחרור מסתכלים על ענף התחזוקה, פיתוח פעיל — על הראשי. בתת-המודול מוצמד תג או קומיט, ולא קצה הענף.
-
חיבור. רושמים את הנתיב ואת אופן השגת כל תת-מודול: עותק נקי של המאגר צריך להיפרס בפקודה אחת, בלי התקנות השלמה ידניות.
-
תקנון עדכון תת-מודולים. העדכון מתבצע כשינוי נפרד עם ציון הסיבה, עובר ביקורת ונכנס לענף הראשי יחד עם עריכות הקוד. "למשוך את האחרון" אוטומטית — שלול.
-
בדיקת שחזוריות. בסביבה נקייה הבנייה מתבצעת רק מרוויזיות מוצמדות, דבר לא נמשך שלא דרך מקורות מוצהרים; חוסר ההתאמה נראה לפני השחרור, ולא אחריו.
-
בדיקה לפני שחרור ומסירה. לפני השחרור משווים רוויזיות מול מאגר התלויות ומריצים בנייה מעותק נקי — חוסר התאמה חוסם את השחרור. מוסרים מפת תלויות, כללי עדכון וסדר החזרות לאחור.
מה מוסרים לצוות לאחר ההטמעה
לאחר ההטמעה נשאר לצוות לא "מאגר מוגדר", אלא ערכה שלפיה מהנדס חדש מרים סביבה במעבר אחד, ועדכון הספרייה המשותפת מתפצל בין הפרויקטים בלי עריכות ידניות בכל אחד. להלן — הרכב הערכה ומה שבה נבדק בבניות אמיתיות, ולא תואר "מהזיכרון".
- קונפיגורציות של תת-מודולים — קובץ עם נתיבים, כתובות וענף לכל חיבור: מוצמד תג או ענף, שכפול חלקי מופעל, כללי התעלמות משינויים מקומיים מוגדרים. הצוות רואה איזה חיבור ציבורי ואיזה סגור.
- מפת תלויות — טבלה "פרויקט → ספרייה → גרסה מוצמדת → בעלים". לפיה תוך דקה מוצאים אילו פרויקטים תיגע עריכה בספרייה המשותפת ומי צריך להתריע לו.
- מדיניות עדכון — מי מזיז את מצביע הגרסה, באיזה אירוע (שחרור הספרייה, סגירת חולשה) ומה צריך להאיר בירוק בבדיקות לפני המיזוג.
- הרשאות גישה — מטריצה "תפקיד → קריאה/כתיבה" לכל מאגר: לקבלן מוענקת קריאה של הספרייה בלי זכות לשנות גרסאות בפרויקט הראשי.
- מדריך למפתחים — תרחישים קצרים: פריסה ראשונית, הוספת חיבור, העברת פרויקט לגרסה חדשה, שגיאות אופייניות והסימנים שלהן.
- סדר החזרה לאחור — כיצד להחזיר את הגרסה הקודמת של הספרייה בפרויקט אחד וכיצד להחזיר עדכון המוני, אם הבדיקות גילו רגרסיה.
- תקנון בדיקות בבנייה — כללים שמונעים מיזוג שינוי עם גרסה "צפה" או קונפליקטים לא סגורים בחיבורים.
הערכה נמצאת לצד הקוד ומתעדכנת יחד איתו: קונפיגורציות — במאגר, המפה והמדיניות — בתיעוד המשותף של הצוות. תארו את סכימת המאגרים שלכם — נחזור עם הערכה ותוכנית הטמעה.
מקרה בוחן: הסרנו חוסר סנכרון גרסאות בשלושה צוותים
כיצד היה: ספרייה משותפת בשלושה עותקים
שלושה צוותי מוצר החזיקו מודול משותף — טיפוסים, לקוח של שירות חיצוני, שכבת לוגינג. כל אחד העתיק אותו למאגר שלו וערך אותו לעצמו, ולכן לקראת השחרור הגרסאות התפצלו, והכישלון התרחש כבר בצינור.
כל בנייה רביעית נעצרה בגלל אי-התאמה של גרסת הספרייה, ועל איחוד ידני של עריכות התבזבזו עד שש שעות בשבוע לכל צוות: להשוות שלושה עותקים, להעביר עריכה של אחר, ולא לשבור תוספות מקומיות.
מה שינו
את הספרייה הוציאו למאגר נפרד, חיברו כ-Git Submodules והצמידו בכל מוצר לא ענף, אלא קומיט מסוים — הבנייה הפכה לשחזורית. העדכון מתבצע כמשימה נפרדת: המהנדס מרים את המצביע לרוויזיה חדשה, מריץ בדיקות, ורק אחר כך זה עובר לענף הראשי. שינויים שוברים מפורסמים בענף מג'ורי, המוצרים עוברים אליו בזה אחר זה, ולא כולם בבת אחת.
מדדים לאחר מכן
חלק הבניות שנפלו בגלל גרסת הספרייה ירד מרבע למקרים בודדים — בדרך כלל זה קונפליקט גלוי שרואים בשלב הבדיקה. האיחוד הידני של עריכות נעלם: עריכה מתקיימת במקום אחד ומגיעה למוצרים דרך עדכון המצביע. זמן הבנייה של המוצר התקצר, כי החלק המשותף אינו נבנה מחדש עוד בתוך כל מאגר.
במה תת-מודול שונה מחבילה ומה לעשות כשהמאגר מתנפח?
תת-מודול הוא הפניה קשיחה לקומיט מסוים של מאגר זר, הרשומה בעץ הפרויקט. Git לא מחליט בשבילכם אם הגרסאות תואמות: את המצביע מעדכנים ידנית ומצמידים בקומיט נפרד, והתלויות העקיפות של הספרייה לא מגיעות לעץ. חבילה בנויה אחרת — זהו ארטיפקט מפורסם עם גרסה, ומנהל התלויות מרכיב בעצמו סט עקבי.
הגבול עובר לפי קצב השחרורים ומספר הצורכים. כל עוד הספרייה משתנה יחד עם היישום, והצורך אחד, תת-המודול זול יותר: קוד המקור נראה בעץ, העריכות מתבצעות בסביבה אחת, ואין צורך לגרס מצבי ביניים.
ברגע שמופיע צורך שני-שלישי עם לוח זמנים עצמאי, תאימות לאחור ומספור גרסאות הופכים לחובה — זה האות שנדרשת חבילה.
מאגר שהתנפח מסגיר את עצמו בזמן הבנייה ובתורים למיזוג. העברה מלאה בשלב אחד כמעט תמיד שוברת את הבנייה: תחילה מוציאים את הספרייה למאגר נפרד ומפרסמים אותה כחבילה, ותת-המודול נשאר גשר מעבר למי שעוד לא עבר.
את הצורכים מעבירים בזה אחר זה, בליווי השוואת החוזה, ורק אחר כך מסירים את ההפניות ואת הקובץ .gitmodules.
בנפרד על גבולות האחריות: תת-מודול נותן לכולם זכויות זהות על מאגר אחד, ולכן צוותים חוסמים זה את זה. חבילה מפרידה בין תפקידים — צוות אחד משחרר גרסאות, והשאר רק מחברים אותן. לפני המיגרציה מצמידים מדיניות גירסום וחוזה יציב, אחרת מיגרציה חלקית תימשך לזמן בלתי מוגבל.
ספקות טכניים: ענפים, הרשאות, החזרות לאחור ועדכונים
תת-מודול מוצמד במאגר האב בהפניה לקומיט מסוים של הספרייה המשותפת, ולא לענף. בדיוק התכונה הזו קובעת כיצד הפרויקט מתנהג בעת החלפת ענפים, החזרות לאחור והפרדת גישות — ובדיוק סביבה מתעוררים רוב הספקות לפני שמסכימים לסכימה כזו.
עיקרון כללי: אתחול תת-מודולים הוא פעולה מפורשת (submodule update --init --recursive), ולא תוצר לוואי של שכפול. כל עוד לא ביצעו אותה, תיקיות הספריות נשארות ריקות, והבנייה נופלת מיד ובאופן מובן, ולא "בשקט" נוסעת על קוד מיושן.
מה יקרה בעת החלפת ענף?
עץ העבודה של תת-המודול אינו זז מעצמו — לאחר החלפת הענף של פרויקט האב יש לסנכרן את ההפניות, אחרת תבנו ספרייה ישנה עם קוד חדש. אנחנו סוגרים זאת בסקריפט hook: הסנכרון מתבצע אוטומטית, והפער בין ההפניה המוצמדת למצב בפועל נתפס בבדיקה באינטגרציה רציפה לפני הבנייה.
כיצד להגביל את הרשאות הקבלנים על הקוד המשותף?
גישה למאגר הספרייה מוענקת בנפרד מהגישה לפרויקט ורק לקריאה — באמצעות מפתח נפרד או טוקן בפעולה מוגבלת. הקבלן בונה ומנפה באגים עם הספרייה המשותפת, אך אינו יכול לכתוב אותה מחדש לעצמו; השינויים עוברים דרך בעל המאגר ובדיקה מחייבת.
כיצד להחזיר גרסת ספרייה בלי לבנות הכל מחדש?
ההפניה לתת-המודול היא שורה אחת בהיסטוריה של פרויקט האב. החזרה לקומיט הקודם של הספרייה נעשית בעריכת ההפניה הזו ומוצמדת בשינוי נפרד: שאר הקוד לא נגע. אנחנו מחזיקים מדיניות כך שהחזרה לאחור תימשך דקות, ולא בנייה מחדש של כל השירותים, ובודקים אותה בבדיקות לפני הפריסה.
נדון במשימה: ננתח את הסכימה ונחזור עם תוכנית
כדי לחזור עם הערכה ותוכנית הטמעה, אנחנו זקוקים לא להצהרת בעיה מופשטת, אלא לסכימת המאגרים בפועל: היכן נמצאות הספריות המשותפות, מי וכיצד מחבר אותן כעת, אילו ענפים נחשבים ליציבים.
לפי הנתונים הללו אנחנו מחשבים את היקף העבודה ומציעים סדר מעבר ל-Git Submodules — עם נקודות החזרה לאחור וללא עצירת השחרורים השוטפים.
מה שמועיל לתאר בשיחה או לשלוח מראש:
- רשימת מאגרים והבעלים שלהם: יישומים, שירותים, ספריות משותפות, כלי עזר;
- הדרך הנוכחית לחיבור מודולים משותפים: העתקת קוד, חבילה נפרדת, מאגרים מקוננים, סקריפטים לסנכרון;
- סכימת הענפים וכללי השחרור: אילו ענפים ארוכי חיים, כיצד עוברות העריכות וכיצד מוצמדות גרסאות;
- דרישות לגישות: לאילו צוותים נדרשת קריאה של ספריות, למי מותר לשנות את המצביע לקומיט מסוים;
- כללי עדכון: באיזו תדירות למשוך שינויים של הספרייה המשותפת לשירותים ומי מקבל את ההחלטה על החזרה לאחור;
- מערכת בדיקות בבנייה: מה מופעל בעת שינוי של מודול מקונן ומה חוסם את המיזוג;
- מגבלות הסביבות: רשתות סגורות, מראות (mirrors) של מאגרים, מאפיינים של בנייה בצד הלקוח.
עם הסט הזה אנחנו מצמידים את סכימת המאגרים, מפצלים את העבודות לשלבים ושולחים תוכנית הטמעה עם קריטריונים למוכנות בכל שלב. תארו את המשימה — נחזור עם הערכה והצעה לסדר הפעולות.







