Sử dụng macro để tạo động từ tuỳ chỉnh

Báo cáo vấn đề Xem nguồn Hằng đêm · 7.3 · 7.2 · 7.1 · 7 · 6,5

Hoạt động tương tác hằng ngày với Bazel chủ yếu diễn ra thông qua một số lệnh: build, testrun. Mặc dù vậy, đôi khi những yếu tố này có giới hạn: bạn có thể muốn đẩy các gói vào một kho lưu trữ, xuất bản tài liệu cho người dùng cuối, hoặc triển khai ứng dụng bằng Kubernetes. Nhưng Bazel không có publish hoặc Lệnh deploy – những thao tác này phù hợp ở đâu?

Lệnh chạy bazel

Bazel chú trọng vào tính ẩn giấu, khả năng tái tạo và gia tăng Các lệnh buildtest không hữu ích cho những nhiệm vụ trên. Các hành động này có thể chạy trong một hộp cát, có quyền truy cập mạng bị giới hạn và không được đảm bảo là chạy lại với mỗi bazel build.

Thay vào đó, hãy dựa vào bazel run: quy trình làm việc cho các tác vụ mà bạn muốn có các tác dụng phụ. Người dùng Bazel đã quen thuộc với các quy tắc tạo tệp thực thi, và người tạo quy tắc có thể làm theo một nhóm mẫu phổ biến để mở rộng quy tắc này "động từ tuỳ chỉnh".

Trong tự nhiên: rules_k8s

Ví dụ: hãy xem xét rules_k8s, các quy tắc của Kubernetes đối với Bazel. Giả sử bạn có mục tiêu sau đây:

# 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 YAML của Kubernetes khi sử dụng bazel build trên staging . Tuy nhiên, các mục tiêu khác cũng được k8s_object tạo có các tên như staging.apply:staging.delete. Bản dựng này để thực hiện những 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 giống như lệnh bazel k8s-apply hoặc bazel k8s-delete của chúng ta.

Ví dụ khác: ts_api_guardian_test

Mẫu này cũng có thể xem được trong dự án Angular. Chiến lược phát hành đĩa đơn Macro ts_api_guardian_test tạo ra hai mục tiêu. Mục tiêu đầu tiên là mục tiêu nodejs_test chuẩn để so sánh một số đầu ra được tạo dựa trên "vàng" (tức là một tệp chứa kết quả dự kiến). Bạn có thể tạo và chạy ứng dụng này bằng lệnh gọi bazel test thông thường. Trong angular-cli, bạn có thể chạy một như vậy mục tiêu cùng với bazel test //etc/api:angular_devkit_core_api.

Theo thời gian, chúng tôi có thể cần cập nhật tệp quan trọng này vì các 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 Golden, thay vì so sánh dựa vào nội dung đó. Trên thực tế, bạn có thể viết cùng một tập lệnh kiểm thử để chạy trong tuỳ chọn "xác minh" hoặc "chấp nhận" dựa vào cách gọi được gọi. Việc này tuân theo cùng một mẫu mà bạn đã biết: không có lệnh bazel test-accept gốc, nhưng có thể đạt được hiệu quả 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à trở nên khá phổ biến khi bạn học cách nhận ra thương hiệu đó.

Điều chỉnh các quy tắc của riêng bạn

Macro là thành phần chính của mẫu này. Macro được sử dụng như nhưng chúng có thể tạo một vài 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 tạo chính: có thể nó sẽ tạo một tệp nhị phân thông thường, một hình ảnh Docker hoặc một kho lưu trữ mã nguồn. Trong mẫu này, các mục tiêu khác sẽ được tạo để tạo ra khía cạnh hoạt động của tập lệnh những tác động dựa trên kết quả của mục tiêu chính, chẳng hạn như xuất bản tệp nhị phân hoặc cập nhật kết quả đầu ra kiểm thử dự kiến.

Để minh hoạ điều này, hãy gói một quy tắc ảo tạo ra một trang web Sphinx với macro để tạo thêm cho phép người dùng xuất bản khi đã sẵn sàng. Hãy cân nhắc những điều sau quy tắc hiện tại để 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 quy tắc như sau. Quy tắc này sẽ tạo tập lệnh mà khi chạy xuất bản các trang đã 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, xác định macro sau đây để tạo mục tiêu cho cả hai hoạt động trên quy tắc kết hợp:

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 dùng macro như thể macro này chỉ tạo mục tiêu:

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

Trong ví dụ này là "tài liệu" được tạo, giống như macro là quy tắc Bazel chuẩn và duy nhất. Khi được tạo, quy tắc này sẽ tạo ra một số cấu hình và chạy Sphinx để tạo một trang web HTML, sẵn sàng cho việc kiểm tra thủ công. Tuy nhiên, tệp "docs.publish" bổ sung nhắm mục tiêu cũng được tạo. Thao tác này sẽ tạo tập lệnh cho xuất bản 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 để công khai, tương tự như một lệnh bazel publish giả tưởng.

Không thể nhận thấy ngay việc triển khai _sphinx_publisher có thể trông giống như thế nào. Thông thường, các thao tác như thế này sẽ ghi tập lệnh shell trình chạy. Phương pháp này thường bao gồm việc sử dụng ctx.actions.expand_template để viết mộ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 có 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 Phương thức triển khai vẫn có thể chung chung, 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 khi kết hợp cùng nhau.

Trong rules_k8s, đây thực sự là những gì .apply thực hiện: expand_template viết một tập lệnh Bash rất đơn giản, dựa trên apply.sh.tpl, chạy kubectl với dữ liệu đầu ra của mục tiêu chính. Tập lệnh này có thể sau đó tạo và chạy bằng bazel run :staging.apply, cung cấp một cách hiệu quả Lệnh k8s-apply cho các mục tiêu k8s_object.