تعامل روزانه با 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
یک فایل استاندارد Kubernetes YAML را زمانی که از bazel build
bazel در هدف 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 در نظر بگیرید:
_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"],
)
در این مثال، یک هدف "docs" ایجاد می شود، درست مثل اینکه ماکرو یک قانون استاندارد و واحد Bazel است. زمانی که این قانون ساخته شد، برخی از تنظیمات را ایجاد میکند و 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
می کند.