استخدام وحدات الماكرو لإنشاء أفعال مخصصة

يحدث التفاعل اليومي مع Bazel عادةً من خلال بضعة أوامر: build وtest وrun. قد يكون هذا الأمر محدودًا في بعض الأحيان: قد ترغب في إرسال الحزم إلى مستودع أو نشر مستندات للمستخدمين أو نشر تطبيق باستخدام Kubernetes. أمّا Bazel، فلا يتضمّن أمر publish أو deploy، حيث يمكن الاستفادة من هذه الإجراءات؟

الأمر bazel قيد التشغيل

يركّز "بازيل" على الأسلوب المتجدّد والقابلية للتكرار والتزايد، ولا يعني استخدام الأمرَين build وtest للمهام المذكورة أعلاه. قد يتم تنفيذ هذه الإجراءات في وضع الحماية، مع إمكانية وصول محدودة إلى الشبكة، ولا يمكن إعادة تشغيلها مع كل bazel build.

بدلاً من ذلك، يمكنك الاعتماد على bazel run: ركيزة المهام التي تريد أن يكون لها تأثيرات جانبية. اعتاد مستخدمو Bazel على القواعد التي تنشئ ملفات تنفيذية، ويمكن لمؤلفي القواعد اتّباع مجموعة شائعة من الأنماط لتوسيعها إلى "الأفعال المخصصة".

على الشاشة: مؤتمر_قواعد_8

على سبيل المثال، ضع في اعتبارك rules_k8s، قواعد Kubernetes لـ Bazel. لنفترض أن لديك الهدف التالي:

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

تُنشئ قاعدة k8s_object ملف Kubernetes YAML عادي عند استخدام bazel build على استهداف staging. ومع ذلك، يتم أيضًا إنشاء استهدافات إضافية من خلال وحدة ماكرو k8s_object بأسماء مثل staging.apply و:staging.delete. وتعمل هذه النصوص البرمجية على إنشاء هذه الإجراءات، وعند تنفيذها باستخدام bazel run staging.apply، تعمل هذه الأوامر على غرار أوامر bazel k8s-apply أو bazel k8s-delete.

مثال آخر: ts_api_guardian_test

يمكن أيضًا رؤية هذا النمط في مشروع Angular. ينتج ماكرو 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.

يمكن أن يكون هذا النمط فعّالاً جدًا ويتبيّن أنه شائع جدًا بعد التعرّف عليه.

تعديل قواعدك الخاصة

وحدات الماكرو هي نمط هذا النمط. يتم استخدام وحدات الماكرو مثل القواعد، لكن يمكنها إنشاء استهدافات متعددة. في العادة، تنشئ الشركة هدفًا بالاسم المحدد الذي ينفذ إجراء الإصدار الأساسي: ربما تنشئ بنية ثنائية أو صورة Docker أو أرشيفًا لرمز المصدر. في هذا النمط، يتم إنشاء أهداف إضافية لإنتاج تأثيرات أداء برمجية جانبية بناءً على نتائج الهدف الأساسي، مثل نشر البرنامج الثنائي الناتج أو تحديث مخرجات الاختبار المتوقعة.

لتوضيح ذلك، عليك التفاف قاعدة تخيلية تنشئ موقعًا إلكترونيًا باستخدام 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"],
)

في هذا المثال، يتم إنشاء هدف "مستندات Google"، تمامًا كما كانت وحدة الماكرو عبارة عن قاعدة بازيل عادية واحدة. عندما يتم إنشاء القاعدة، تنشئ بعض الإعدادات وتُشغّل تطبيق Sphinx لإنتاج موقع HTML، تكون جاهزة للفحص اليدوي. ومع ذلك، يتم أيضًا إنشاء هدف "docs.publish" إضافي، مما يؤدي إلى إنشاء نص برمجي لنشر الموقع. بعد التحقّق من ناتج الهدف الأساسي، يمكنك استخدام bazel run :docs.publish لنشره للجميع، تمامًا مثل أمر تخيلي bazel publish.

لا يتضح على الفور الشكل الذي قد يبدو عليه تنفيذ قاعدة _sphinx_publisher. وغالبًا ما تكتب إجراءات مثل هذا النص البرمجي Launcher. تتضمن هذه الطريقة عادةً استخدام 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.