Việc tương tác hằng ngày với Bazel chủ yếu diễn ra thông qua một vài lệnh:
build
, test
và run
. Tuy nhiên, đôi khi việc này có thể khá hạn chế: bạn có thể muốn đẩy các gói vào kho lưu trữ, phát hành tài liệu cho người dùng cuối hoặc triển khai một ứng dụng bằng Kubernetes. Nhưng Bazel không có lệnh publish
hoặc deploy
– các thao tác này phù hợp với vị trí nào?
Lệnh chạy bazel
Việc Bazel tập trung vào độ ma sát, khả năng tái tạo và mức độ gia tăng có nghĩa là các lệnh build
và test
không hữu ích cho các nhiệm vụ ở trên. Những thao tác này có thể chạy trong một hộp cát, bị giới hạn quyền truy cập vào mạng và không đảm bảo sẽ được chạy lại với mỗi bazel build
.
Thay vào đó, hãy dựa vào bazel run
: mã công việc cho các tác vụ mà bạn muốn có hiệu ứng phụ. Người dùng Bazel đã quen với các quy tắc tạo tệp thực thi và tác giả quy tắc có thể tuân theo một tập hợp các mẫu phổ biến để mở rộng quy tắc này thành "động từ tùy chỉnh".
Trong tự nhiên: Rules_k8
Ví dụ: hãy xem xét rules_k8s
, các quy tắc của Kubernetes dành cho Bazel. Giả sử bạn có mục tiêu sau:
# BUILD file in //application/k8s
k8s_object(
name = "staging",
kind = "deployment",
cluster = "testing",
template = "deployment.yaml",
)
Quy tắc k8s_object
tạo tệp Kubernetes YAML tiêu chuẩn khi bazel build
được sử dụng trên mục tiêu staging
. Tuy nhiên, các mục tiêu bổ sung cũng được tạo bằng macro k8s_object
với các tên như staging.apply
và :staging.delete
. Những tập lệnh xây dựng này để thực hiện các hành động đó và khi được thực thi bằng bazel run
staging.apply
, các tập lệnh này sẽ hoạt động như các lệnh bazel k8s-apply
hoặc bazel
k8s-delete
của riêng chúng ta.
Ví dụ khác: ts_api_guardian_test
Bạn cũng có thể thấy mẫu này trong dự án Angular. Macro ts_api_guardian_test
tạo ra hai mục tiêu. Đầu tiên là mục tiêu nodejs_test
tiêu chuẩn, so sánh một số kết quả đầu ra được tạo với tệp "vàng" (nghĩa là tệp chứa kết quả đầu ra dự kiến). Bạn có thể tạo và chạy lệnh gọi này với lệnh gọi bazel
test
thông thường. Trong angular-cli
, bạn có thể chạy một mục tiêu như vậy với bazel test //etc/api:angular_devkit_core_api
.
Theo thời gian, tệp vàng này có thể cần được cập nhật vì những lý do chính đáng.
Việc cập nhật thủ công này rất tẻ nhạt và dễ xảy ra lỗi, vì vậy macro này cũng cung cấp
mục tiêu nodejs_binary
cập nhật tệp vàng, thay vì so sánh
với tệp đó. Bạn nên viết cùng một tập lệnh thử nghiệm ở chế độ "xác minh" hoặc "chấp nhận", dựa trên cách thức tập lệnh đó được chạy. Điều này tuân theo cùng một mẫu
bạn đã tìm hiểu: không có lệnh bazel test-accept
gốc, nhưng
có thể đạt được hiệu ứng tương tự với
bazel run //etc/api:angular_devkit_core_api.accept
.
Mẫu này có thể khá mạnh mẽ và khá phổ biến khi bạn học cách nhận ra.
Điều chỉnh các quy tắc của riêng bạn
Macro là trọng tâm của mẫu này. Macro được sử dụng như các quy tắc, nhưng chúng có thể tạo ra một số mục tiêu. Thông thường, chúng sẽ tạo một mục tiêu có tên được chỉ định để thực hiện hành động xây dựng chính: có thể mục tiêu đó tạo một tệp nhị phân thông thường, hình ảnh Docker hoặc bản lưu trữ mã nguồn. Trong mẫu này, các mục tiêu bổ sung được tạo để tạo ra các hiệu ứng phụ dựa trên kết quả của mục tiêu chính, như phát hành tệp nhị phân thu được hoặc cập nhật kết quả thử nghiệm dự kiến.
Để minh hoạ điều này, hãy gói một quy tắc ảo để tạo một trang web có Sphinx có macro nhằm tạo thêm một mục tiêu cho phép người dùng xuất bản trang web đó khi đã sẵn sàng. Hãy xem xét quy tắc hiện tại sau đây để tạo trang web bằng Sphinx:
_sphinx_site = rule(
implementation = _sphinx_impl,
attrs = {"srcs": attr.label_list(allow_files = [".rst"])},
)
Tiếp theo, hãy xem xét một quy tắc như sau. Quy tắc này sẽ xây dựng một tập lệnh mà khi chạy sẽ xuất bản các trang được tạo:
_sphinx_publisher = rule(
implementation = _publish_impl,
attrs = {
"site": attr.label(),
"_publisher": attr.label(
default = "//internal/sphinx:publisher",
executable = True,
),
},
executable = True,
)
Cuối cùng, hãy xác định macro sau để tạo mục tiêu cho cả hai quy tắc ở trên cùng nhau:
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)
Trong các tệp BUILD
, hãy sử dụng macro như thể macro vừa tạo mục tiêu chính:
sphinx_site(
name = "docs",
srcs = ["index.md", "providers.md"],
)
Trong ví dụ này, mục tiêu "tài liệu" được tạo, giống như macro là một quy tắc đơn giản của Bazel. Khi được tạo, quy tắc sẽ tạo một số cấu hình và chạy Sphinx để tạo một trang web HTML, sẵn sàng để kiểm tra theo cách thủ công. Tuy nhiên, mục tiêu "docs.publish" bổ sung cũng được tạo, tập lệnh này sẽ tạo tập lệnh để phát hành trang web. Sau khi kiểm tra kết quả của mục tiêu chính, bạn có thể
sử dụng bazel run :docs.publish
để xuất bản mục tiêu công khai, giống như
một lệnh bazel publish
ảo.
Bạn sẽ không thấy ngay cách triển khai quy tắc _sphinx_publisher
. Thông thường, các thao tác như thế này sẽ viết một tập lệnh shell trình chạy.
Phương thức này thường liên quan đến việc sử dụng ctx.actions.expand_template
để viết tập lệnh shell rất đơn giản, trong trường hợp này, gọi tệp nhị phân của nhà xuất bản bằng một đường dẫn đến đầu ra của mục tiêu chính. Bằng cách này, nhà xuất bản
có thể duy trì việc triển khai, quy tắc _sphinx_site
chỉ có thể tạo HTML và tập lệnh nhỏ này là tất cả những gì cần thiết để kết hợp cả hai.
Trong rules_k8s
, đây thực sự là những gì .apply
thực hiện:
expand_template
viết tập lệnh Bash rất đơn giản, dựa trên
apply.sh.tpl
,
chạy kubectl
với đầu ra của mục tiêu chính. Sau đó, tập lệnh này có thể
được tạo và chạy với bazel run :staging.apply
, cung cấp
lệnh k8s-apply
hiệu quả cho các mục tiêu k8s_object
.