與 Bazel 的日常互動主要是透過幾個指令:build
、test
和 run
。但有時候這類情況可能會相當有限:您可能會想將套件推送至存放區、向使用者發布說明文件,或是使用 Kubernetes 部署應用程式。但 Bazel 沒有 publish
或 deploy
指令,執行這些動作的位置為何?
bazel run 指令
Bazel 著重在歷史性、可重現性和成效增幅,因此 build
和 test
指令不適用於上述工作。這些動作可能會在網路存取權有限的沙箱中執行,而且不保證每次都會在每個 bazel build
中執行。
請改用 bazel run
,也就是您希望「想要」產生副作用的工作。Bazel 使用者習慣建立執行檔的規則,而規則作者可以遵循一組通用的模式來擴大實施至「自訂動詞」。
對外:rules_k8s
舉例來說,請考慮使用 rules_k8s
,這是 Bazel 的 Kubernetes 規則。假設您的目標如下:
# BUILD file in //application/k8s
k8s_object(
name = "staging",
kind = "deployment",
cluster = "testing",
template = "deployment.yaml",
)
當 staging
目標使用 bazel build
時,k8s_object
規則會建構標準 Kubernetes YAML 檔案。不過,其他目標也會由 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"],
)
在這個範例中,系統會建立「文件」目標,就像巨集是單一的 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
會根據 apply.sh.tpl
編寫非常簡單的 Bash 指令碼,而該指令碼會以主要目標的輸出內容執行 kubectl
。如此一來,您就能使用 bazel run :staging.apply
建構及執行這個指令碼,有效為 k8s_object
目標提供 k8s-apply
指令。