שימוש בפקודות מאקרו ליצירת תגים בהתאמה אישית

האינטראקציה היומיומית עם Bazel מתרחשת בעיקר באמצעות כמה פקודות: build, test ו-run. עם זאת, לפעמים זה מרגיש מוגבל: מומלץ לדחוף חבילות למאגר, לפרסם מסמכים למשתמשי קצה או לפרוס אפליקציה ב-Kubernetes. אבל לבזל אין את פקודת publish או deploy – איפה ניתן ליישם את הפעולות האלה?

פקודת "Bazel"

המטרה של Bazel היא הרמיטות, שחזוריות ומצטברות, והמשמעות היא שהפקודות build ו-test לא עוזרות למשימות שצוינו. פעולות אלה יכולות להתבצע בארגז חול, עם גישה מוגבלת לרשת, ולא מובטח שהן יופעלו מחדש עם כל bazel build.

במקום זאת, הסתמך על bazel run: כוח העבודה למשימות שאתה רוצה שיהיו להן תופעות לוואי. משתמשי Bazel רגילים לכללים שיוצרים קובצי הפעלה, ומחברי כללים יכולים לפעול לפי קבוצה נפוצה של דפוסים כדי להרחיב את "פעלים מותאמים אישית".

בטבע: rules_k8s

לדוגמה, rules_k8s, כללי Kubernetes עבור Bazel. נניח שיש לך את היעד הבא:

# BUILD file in //application/k8s
k8s_object(
    name = "staging",
    kind = "deployment",
    cluster = "testing",
    template = "deployment.yaml",
)

הכלל k8s_object בונה קובץ YAML סטנדרטי של Kubernetes כשנעשה שימוש ב-bazel build ביעד staging. עם זאת, היעדים הנוספים נוצרים גם על ידי פקודת המאקרו k8s_object עם שמות כמו staging.apply ו:staging.delete. סקריפטים אלה לבניית ביצוע פעולות אלו, וכאשר בוצעו באמצעות bazel run staging.apply, הם יפעלו כמו פקודות bazel k8s-apply או bazel k8s-delete שלנו.

דוגמה נוספת: ts_api_guardian_test

אפשר לראות את הדפוס הזה גם בפרויקט האנגולי. ה- ts_api_guardian_test פקודת המאקרו מפיק שני יעדים. הראשון הוא יעד סטנדרטי של nodejs_test, שמשווה פלט מסוים שנוצר מול קובץ "זהב" (כלומר, קובץ שמכיל את הפלט הצפוי). ניתן לבנות ולהפעיל אותה עם הפעלה רגילה של bazel test. בangular-cli, אפשר להפעיל יעד אחד מסוג זה עם bazel test //etc/api:angular_devkit_core_api.

עם הזמן, ייתכן שיש צורך לעדכן את קובץ הזהב הזה מסיבות לגיטימיות. עדכון זה באופן ידני הוא מייגע ומוטה שגיאות, כך שהמאקרו הזה מספק גם יעד של nodejs_binary שמעדכן את הקובץ המוזהב, במקום להשוות אותו נגדו. למעשה, ניתן לכתוב את אותו סקריפט בדיקה כך שיפעל במצב "אימות" או "אישור", על סמך אופן ההפעלה שלו. לפי אותו דפוס שכבר למדת: אין פקודת bazel test-accept מקומית, אך ניתן להשיג את אותו האפקט באמצעות bazel run //etc/api:angular_devkit_core_api.accept.

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

התאמת הכללים שלכם

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

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

_sphinx_site = rule(
     implementation = _sphinx_impl,
     attrs = {"srcs": attr.label_list(allow_files = [".rst"])},
)

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

_sphinx_publisher = rule(
    implementation = _publish_impl,
    attrs = {
        "site": attr.label(),
        "_publisher": attr.label(
            default = "//internal/sphinx:publisher",
            executable = True,
        ),
    },
    executable = True,
)

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

def sphinx_site(name, srcs = [], **kwargs):
    # This creates the primary target, producing the Sphinx-generated HTML.
    _sphinx_site(name = name, srcs = srcs, **kwargs)
    # This creates the secondary target, which produces a script for publishing
    # the site generated above.
    _sphinx_publisher(name = "%s.publish" % name, site = name, **kwargs)

בקובצי ה-BUILD, משתמשים במאקרו כאילו הוא יוצר את היעד העיקרי:

sphinx_site(
    name = "docs",
    srcs = ["index.md", "providers.md"],
)

בדוגמה הזו, נוצר יעד מסוג "מסמכים", בדיוק כאילו המאקרו הוא כלל בזל סטנדרטי ויחיד. כאשר הוא בנוי, הכלל יוצר תצורה מסוימת ומריץ את Sphinx כדי ליצור אתר HTML, מוכן לבדיקה ידנית. עם זאת, נוצר יעד נוסף של "docs.publish", שיוצר סקריפט לפרסום האתר. אחרי שבודקים את הפלט של היעד הראשי, אפשר להשתמש ב-bazel run :docs.publish כדי לפרסם אותו לצריכה ציבורית, בדיוק כמו פקודת bazel publish מדומה.

לא ברור מיד איך ייראה יישום הכלל של _sphinx_publisher. לעתים קרובות, פעולות כמו כתיבה של סקריפט מעטפת של מרכז האפליקציות. השיטה הזו בדרך כלל כוללת שימוש ב-ctx.actions.expand_template כתיבת סקריפט מעטפת מאוד פשוט, במקרה זה מפעיל את הבינארי של בעל האתר עם נתיב לפלט של היעד הראשי הנתונים. באופן זה, היישום של בעל האתר יכול להיות כללי, הכלל _sphinx_site יכול רק ליצור HTML, והסקריפט הקטן הזה הוא כל מה שצריך כדי לשלב את שניהם.

בrules_k8s, זה באמת מה ש.apply עושה: expand_template כותבת סקריפט Bash פשוט מאוד, על בסיס apply.sh.tpl, המריץ את kubectl עם הפלט של היעד הראשי. ניתן לבנות סקריפט זה ולהפעיל אותו יחד עם bazel run :staging.apply, כדי לספק ביעילות פקודה שלk8s-apply עבור k8s_object יעדים.