רשומה זו היא רשומת אורח מאת Jeffrey Ryan Thalhammer ג'פרי רייאן תאלהמר המפתח של פינטו (Pinto) ושל Perl::Critic. ג'ף מנהל עסק קטן לייעוץ בסן פרנסיסקו והוא פעיל בקהילת הפרל במשך שנים רבות. ג'ף מנסה לגייס כעת מימון לפיתוח היכולות שיאפשרו לציין טווחים של מספרי גירסאות בפינטו.

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

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

במשך השנים בניתי מספר מאגרי CPAN מותאמים אישית באמצעות כלים כמו CPAN::Mini ו-CPAN::Site. אך הם תמיד נראו מגושמים ולא ממש הייתי מאושר איתם. לפני מספר שנים, לקוח שכר אותו לפתח עוד מערכת CPAN מותאמת ללקוח. אך הפעם הייתה לי הזדמנות להתחיל מאפס. פינטו (Pinto) הוא התוצאה של אותו פרוייקט.

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

התקנת פינטו

פינטו זמין ב-CPAN וניתן להתקינו כמו כל מודול אחר באמצעות הכלי cpan או cpanm. פינטו דומה יותר ליישום מאשר לספרייה. הוא כלי שמאפשר לך לנהל את קוד היישום שלך, אך הוא לא חלק מהיישום. לכן אני ממליץ להתקין את פינטו כיישום בפני עצמו באמצעות הפקודות הבאות:

curl -L http://getpinto.stratopan.com | bash
source ~/opt/local/pinto/etc/bashrc

פקודות אלו יתקינו את פינטו ב ~/opt/local/pinto ויוסיפו את התיקיות הדרושות למסלולים ב- PATH וב- MANPATH. ההתקנה כוללת את כל מה שפינטו צריך, כך שהתקנת פינטו לא משנה את שאר סביבת הפיתוח שלך, וכמו כן שינויים בסביבת הפיתוח אינם משפיעים על פינטו.

ללמוד להשתמש בפינטו

כמו כל כלי חדש, הדבר הראשון שצריך לדעת הוא איך לקבל עזרה:

pinto commands            # Show a list of available commands
pinto help <COMMAND>      # Show a summary of options and arguments for <COMMAND>
pinto manual <COMMAND>    # Show the complete manual for <COMMAND>

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

man Pinto::Manual::Introduction  # Explains basic Pinto concepts
man Pinto::Manual::Installing    # Suggestions for installing Pinto
man Pinto::Manual::Tutorial      # A narrative guide to Pinto
man Pinto::Manual::QuickStart    # A summary of common commands

יצירת מאגר

השלב הראשון בשימוש בפינטו הוא יצירת מאגר באמצעות הפקודה init:

pinto -r ~/repo init

פקודוה זו תיצור מאגר חדש בתיקייה ~/repo. אם התיקייה אינה קיימת, היא תיווצר עבורך. אם היא כבר קיימת אז היא חייבת להיות ריקה.

האופציה -r (או -root) מציינת את מיקום המאגר. יש לציין זאת בכל פקודת פינטו. אבל אם נמאס לך לחזור ולהקליד זאת שוב ושוב, אז תוכל להגדיר את מיקום המאגר במשתנה הסביבה PINTO_REPOSITORY_ROOT ואז אין צורך להשתמש עוד ב -r .

בדיקת המאגר

עכשיו שיש לנו מאגר,נבדוק ומראה מה יש בו. כדי לראות את תוכן המאגר, השתמש בפקודה "list":

pinto -r ~/repo list

בשלב זה הרשימה תהיה ריקה כיוון שאין עוד כלום במאגר. אך הפקודה "list" תופיע די הרבה בהמשך ההדרכה.

הוספת מודולים מ-CPAN

נניח שאתה עובד כעת על יישום שנקרא My-App המכיל מודל בדם My::App, והוא מסתמך על המודול URI. אתה יכול להכניס את המודול URI אל המאגר שלך באמצעות הפקודה pull:

pinto -r ~/repo pull URI

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

כעת מודול ה-URI אמור להיות במאגר הפינטו שלך. חזור שוב על הפקודה list כדי לראות את תוכן המאגר:

pinto -r ~/repo list

הפעם הרשימה תיראה דומה לרשימה זו:

rf  URI                            1.60  GAAS/URI-1.60.tar.gz
rf  URI::Escape                    3.31  GAAS/URI-1.60.tar.gz
rf  URI::Heuristic                 4.20  GAAS/URI-1.60.tar.gz
...

ניתן לראות שהמודול URI נוסף למאגר, וכמו כן גם כל המודולים הדרושים כדרישות קדם למודול URI וכל דרישות הקדם שלהם, וכו'.

הוספת מודולים פרטיים

כעת נניח שסיימת את העבודה של My-App ואתה מוכן להפיץ את הגירסה הראשונה. ארוז את המודול כמודול להפצה My-App-1.0.tar.gz באמצעות הכלי המועדף עליך (למשל: ExtUtils::MakםeMaker, Module::Build, Module::Install וכו'). הוסף את קובץ ההפצה שלך למאגר הפינטו שלך באמצעות הפקודה add:

$> pinto -r ~/repo add path/to/My-App-1.0.tar.gz

גם במקרה זה תתבקש להכניס הודעה שמתארת את השינוי. כשתציג את תוכן המאגר כעת, הוא יכלול גם את המודול My::App ויציג אותך כיוצר ההפצה:

rl  My::App                         1.0  JEFF/My-App-1.0.tar.gz
rf  URI                            1.60  GAAS/URI-1.60.tar.gz
rf  URI::Escape                    3.31  GAAS/URI-1.60.tar.gz
rf  URI::Heuristic                 4.20  GAAS/URI-1.60.tar.gz
...

התקנת המודולים

לאחר שכל המודולים שלך נמצאים במאגר הפינטו שלך, השלב הוא בא הוא להתקין אותם איפשהו. בתוך הקופסה, מאגר פינטו מנוהל בדיוק כמו מאגר CPAN, לכן הוא תואם לגמרי ל-cpanm ולכל כלי אחר להתקנת מודולי פרל. כל מה שנדרש הוא להפנות את כלי ההתקנה למאגר הפינטו שלך:

cpanm --mirror file://$HOME/repo --mirror-only My::App

פקודה זו תבנה ותתקין את My::App *אך ורק* בשימוש במודולים שנמצאים במאגר הפינטו שלך. כך תקבל בדיוק את אותן גרסאות של המודולים עם כל התקנה, אפילו אם המודול שודרג במאגר CPAN הפומבי או הוסר ממנו.

עם cpanm האופציה --mirror-only היא אופציה חשובה, כיוון שהיא מונעת מ-cpanm לפנות אל מאגר ה-CPAN הפומבי אם המודול אינו נמצא במאגר שלך. אם זה קורה, אז כנראה יש שגיאה בהגדרת דרישות הקדם בקובץ ה-META של אחד המודולים במאגר שלך. ניתן לפתור את הבעיה על ידי שימוש בפקודה pull כדי להוסיף את המודולים החסרים.

שידרוג מודולים

נניח שעברו מספר שבועות מאז שהפצת לראשונה את My-App ומודול URI עודכן על CPAN 1.62. בגירסה החדשה יש מספר תיקוני באגים חיוניים שאתה רוצה לקבל. שוב, נוכל להכניס את המודול אל המאגר באמצעות הפקודה pull. אך כיוון שבמאגר שלך כבר יש גירסה של מודול URI, עליך לציין שברצונך להכניס גירסה חדשה יותר על ידי מספר הגירסה המינימלי שרצוי לך:

pinto -r ~/repo pull URI~1.62

אם תסתכל שוב על רשימת תוכן המאגר תראה הפעם את הגרסה החדשה יותר של המודול URI (וייתכן שגם תראה מודולים נוספים):

rl  My::App                         1.0  JEFF/My-App-1.0.tar.gz
rf  URI                            1.62  GAAS/URI-1.62.tar.gz
rf  URI::Escape                    3.38  GAAS/URI-1.62.tar.gz
rf  URI::Heuristic                 4.20  GAAS/URI-1.62.tar.gz
...

אם הגירסה החדשה מסתמכת על מודולים נוספים או על מודולים אחרים ששודרגו, גם הם יהיו במאגר שלך. כשתתקין את My::App, תקבל גם את גירסה 1.62 של URI.

שימוש במחסניות (Stacks)

בינתיים התייחסנו אל המאגר כאל משאב יחיד. כך כששידרגנו את URI בחלק הקודם, הפעולה השפיעה על כל משתמש ועל כל יישום שהשתמשו במאגר. השפעה כל כך גורפת אינה רצויה. עדיף לעשות את שינויים בסביבה מבודדת ולבדוק אותם לפני שמחייבים את כולם לשדרג. בשביל זה נועדו המחסניות (Stacks).

לכל מאגר דמוי CPAN יש אינדקס שמקשר את הגירסה העדכנית ביותר של כל מודול לארכיון שמכיל אותה. לרוב יש אינדקס אחד לכל מאגר. במאגר פינטו יכול להיות יותר מאינדקס אחד. כל אינדקס נקרה "stack". זה מאפשר יצירת מחסניות שונות עם תלויות בדרישות קדם שונות בתוך מאגר. ניתן לנהל מחסנית "development" לפיתוח ובנוסף מחסנית "production" לגרסאות להפצה, או מחסנית "perl-5.8" ומחסנית "perl-5.16". כל הוספת מודול או עידכונו משפיעים רק על מחסנית אחת.

לפני שתמשיך צריך להכיר את מחסנית ברירת המחדל (default stack). ברוב הפעולות אין צורך לציין את שם המחסנית. אם לא מציינים במפורש שם מחסנית אז הפקודה מופעלת על המחסנית שמסומנת כמחסנית ברירת המחדל.

בכל מאגר, בכל רגע נתון, יש תמיד לכל היותר מחסנית ברירת מחדל אחת. כשיצרנו את המאגר נוצרה גם מחסנית בשם "master" והיא סומנה כמחסנית ברירת המחדל. ניתן לשנות את מחסנית ברירת המחדל, או את שם המחסנית, אבל לא ניכנס לזה כאן. פשוט יש לזכור ש-"master" הוא שמה של המחסנית שנוצרה כשיצרנו את המאגר.

יצירת מחסנית (Stack)

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

עד כה, כל מה שהוספת או הכנסת למאגר נכנס למחסנית "master". אז קודם כל ניצור העתק של המחסנית בעזרת הפקודה copy:

pinto -r ~/repo copy master uri_upgrade

פקודה זו יוצרת מחסנית חדשה בשם "uri_upgrade". אם תרצה לראות את תוכן המחסנית החדשה, השתמש בפקודה list עם האופציה "--stack":

pinto -r ~/repo list --stack uri_upgrade

הרשימה צריכה להיות זהה לרשימה במחסנית "master":

rl  My::App                         1.0  JEFF/My-App-1.0.tar.gz
rf  URI                            1.60  GAAS/URI-1.60.tar.gz
...

שידרוג מחסנית (Stack)

עכשיו כשיש לך מחסנית נפרדת, אתה יכול לנסות לשדרג את URI. כמו קודם, תשתמש בפקודה pull . רק הפעם תאמר לפינטו להכניס את המודולים אל המחסנית "uri_upgrade" :

pinto -r ~/repo pull --stack uri_upgrade URI~1.62

עכשיו ניתן להשוות את המחסניות "master" ו- "uri_upgrade" באמצעות הפקודה "diff":

pinto -r ~/repo diff master uri_upgrade

+rf URI                                              1.62 GAAS/URI-1.62.tar.gz
+rf URI::Escape                                      3.31 GAAS/URI-1.62.tar.gz
+rf URI::Heuristic                                   4.20 GAAS/URI-1.62.tar.gz
...
-rf URI                                              1.60 GAAS/URI-1.60.tar.gz
-rf URI::Escape                                      3.31 GAAS/URI-1.60.tar.gz
-rf URI::Heuristic                                   4.20 GAAS/URI-1.60.tar.gz

הקלט דומה לקלט של הפקודה diff(1). רשומות שמתחילות ב "+" הן רשומות שנוספו ורשומות שמתחילות ב "-" הן רשומות שהוסרו. ניתן לראות שמודולים מההפצה של URI-1.60 הוחלפו במודולים מההפצה URI-1.62.

התקנה ממחסנית (Stack)

לאחר שהמודולים החדשים הותקנו במחסנית "uri_upgrade" תוכל לנסות לבנות ולהתקין את היישום שלך על ידי כך שתפנה את cpanm אל המחסנית. מחסנית היא בסך הכל תת-תיקייה בתוך המאגר, כך שכל מה שיש לעשות הוא להוסיף אותה אל ה-URL:

cpanm --mirror file://$HOME/repo/stacks/uri_upgrade --mirror-only My::App

אם כל הבדיקות עברו בהצלחה, תוכל לשדרג בביטחה את מודול URI לגירסה 1.62 גם במחסנית "master" בעזרת הפקודה pull. כיוון שמחסנית "master" היא מחסנית ברירת המחדל, אין צורך לציין את שם המחסנית:

pinto -r ~/repo pull URI~1.62

שימוש בהצמדות (Pins)

מחסניות (Stacks) הן כלי מצוין לבדיקת ההשפעות של החלפת מודולים שהיישום שלך מסתמך עליהם. אבל מה עושים אם המודולים לא עוברים את הבדיקות? אם הבעיה היא ב- My-App ואתה יכול לתקן אותה במהירות, אז אתה יכול לתקן את הקוד שלך, להפיץ את גירסה 2.0 של My-App ואז להמשיך ולהעדכן את המודול URI על המחסנית "master".

אבל אם הבעיה היא באג במודול URI או שייקח הרבה זמן לתקן את My-App, אז יש לך בעיה. אתה לא רוצה שמישהו אחר ישדרג את URI, ואתה גם לא רוצה שהמודול ישודרג במקרה כדי לקיים דרישת קדם אחרת שאולי יש ל- My-App. עד שתדע שהבעיה תוקנה, תצטרך למנוע את השידרוג של המודול URI. בשביל זה נועדו הצמדות (Pins).

הצמדת מודול

כשאתה מצמיד מודול, אתה מאלץ את המודול להשאר בגירסה המותקנת. כל נסיון לשדרג את המודול (בין אם ישירות ובין אם דרך דרישות קדם של מודול אחר) ייכשלו. להצמדת מודול יש להשתמש בפקודה pin:

pinto -r ~/repo pin URI

אם תסתכל שוב בתוכן המחסנית "master" תראה משהו כזה:

...
rl  My::App                         1.0  JEFF/My-App-1.0.tar.gz
rf! URI                            1.60  GAAS/URI-1.60.tar.gz
rf! URI::Escape                    3.31  GAAS/URI-1.60.tar.gz
...

הסימן "!" בתחילת הרשומה מציין שהמודול המסומן הוצמד. אם מישהו ינסה לעדכן את מודול URI או להוסיף הפצה שדורשת גירסה חדשה יותר של URI פינטו יציג אזהרה ויסרב לקבל את ההפצות החדשות. שים לב שכל המודולים בהפצה URI-1.60 סומנו, כך שאי אפשר לשדרג מודול שידרוג חלקי (מצב כזה יכול לקרות כשמודול מועבר אל תוך הפצה אחרת).

ביטול הצמדה מודול

נניח שלאחר זמן מה תיקנת את הבעיה ב- My-App או שהופצה גירסה חדשה של מודול URI עם תיקון של הבאג. ניתן לבטל את ההצמדה של מודול URI מהמחסנית בעזרת הפקודה unpin:

pinto -r ~/repo unpin URI

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

שימוש משולב בהצמדות (Pins) ובמחסניות (Stacks)

במקרים רבים משתמשים בהצמדות ובמחסניות כדי לסייע לניהול שינויים בחלק מתהליך הפיתוח. למשל ניתן ליצור מחסנית בשם "prod" שכוללת את המודולים הנסמכים(מודולים הדרושים כדרישות קדם) שידוע שהם טובים. בו בזמן ניתן ליצור גם מחסנית בשם "dev" שמכילה מודולים נסמכים נוספים להפצה הבאה שלך. בהתחלה המחסנית "dev" היא פשוט העתק של המחסנית "prod".

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

הצמדות (Pins) וטלאים (Patches)

לפעמים מגלים שבגירסה חדשה של הפצה ב-CPAN יש באג אבל המתכנת של ההפצה אינו זמין או אינו מוכן לתקן את הבאג (לפחות לא לפני שמגיע הזמן להפיץ את הגירסה הבאה שלך). במקרה כזה ייתכן שתחליט לעשות טלאי מקומי של הפצת ה-CPAN.

נניח שיצרת פיצול (fork) של הקוד למודול URI ויצרת גרסה מקומית של ההפצה שנקראת URI-1.60_PATCHED.tar.gz. ניחן להוסיף אותה למאגר עם הפקודה add:

pinto -r ~/repo add path/to/URI-1.60_PATCHED.tar.gz

במקרה זה רצוי גם להצמיד את המודול כיוון שאינך רוצה שהוא יעודכן עד שאתה בטוח שהגרסה החדשה מ-CPAN כוללת את כל הטלאי שלך או שהמתכנת של המודול תיקן את הבאג הדרך אחרת.

pinto -r ~/repo pin URI

כשהמתכנת של המודול URI מפיץ את גירסה 1.62 וודאי תרצה לבדוק אותה לפני שתבטל את הצמדתה של הגירסה המוטלאת המקומית שלך. בדיוק כמו קודם, ניתן לבצע זאת על ידי יצירת עותק של המחסנית עם הפקודה copy. בוא נקרה הפעם למחסנית החדשה "trial":

pinto -r ~/repo copy master trial

לפני שתוכל לשדרג את URI על המחסנית "trial", תצטרך לבטל שם את ההצמדה:

pinto -r ~/repo unpin --stack trial URI

עכשיו אפשר לשדרג את URI במחסנית ולנסות לבנות את My::App כך:

pinto -r ~/repo pull --stack trial URI~1.62
cpanm --mirror file://$HOME/repo/stacks/trial --mirror-only My::App

אם הכל עובד כשורה, בטל את ההצמדה במחסנית "master" ומשוך אליה את הגירסה החדשה של המודול URI.

pinto -r ~/repo unpin URI
pinto -r ~/repo pull URI~1.62

סקירת שינויים קודמים

כפי שראית עד כה, כל פקודה שמשנה את מצב המחסנית דורשת הכנסת הודעה ללוג שמתארת את השינוי. ניתן לסקור את ההודעות באמצעות הפקודה log :

pinto -r ~/repo log

התצוגה אמורה להראות בערך כך:

revision 4a62d7ce-245c-45d4-89f8-987080a90112
Date: Mar 15, 2013 1:58:05 PM
User: jeff

     Pin GAAS/URI-1.59.tar.gz

     Pinning URI because it is not causes our foo.t script to fail

revision 4a62d7ce-245c-45d4-89f8-987080a90112
Date: Mar 15, 2013 1:58:05 PM
User: jeff

     Pull GAAS/URI-1.59.tar.gz

     URI is required for HTTP support in our application

...

הכותרת של כל הודעה מראה מי ביצע את השינוי ומתי זה קרה. כמו כן יש לה מזהה ייחודי בדומה לתמצית (digest) SHA-1 של Git. ניתן להשתמש במזהים כדי לראות את ההבדלים בין גרסאות שונות או כדי להחזיר את המחסנית למצב קודם [הערה: אפשרות זועדיין אינה מיושמת]

סיכום

מדריך זה הסביר את הפקודות הבסיסיות ליצירת מאגר פינטו ואיכלוס המאגר במודולים. כמו כן ראינו איך להשתמש במחסניות (Stacks) ובהצמדות (Pins) כדי לאפשר ניהול תלויות ונסמכים ועל ידי כך להתמודד עם מכשולים שכיחים בתהליכי פיתוח.

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