Bazel との日々のやり取りは、主に build
、test
、run
などのコマンドで行います。しかし、リポジトリにパッケージを push したり、エンドユーザー向けのドキュメントを公開したり、Kubernetes を使用してアプリケーションをデプロイしたりする必要はあると考えるかもしれません。Bazel には publish
や deploy
コマンドがありませんが、これらのアクションをどこに位置付けるかを指定します。
bazel run コマンド
Bazel は密閉性、再現性、インクリメンタリティを重視しているため、build
コマンドと test
コマンドは上記のタスクには役立ちません。これらのアクションは、ネットワーク アクセスが制限されたサンドボックス内で実行される可能性があり、すべての bazel build
で再度実行される保証はありません。
代わりに、bazel run
を使用します。これは、副作用を発生させるタスクの主力です。Bazel のユーザーは実行可能ファイルを作成するルールに慣れています。ルールの作成者は共通のパターンセットに従ってこれを「カスタム動詞」に拡張できます。
野生: rule_k8s
たとえば、Bazel の Kubernetes ルール rules_k8s
について考えてみましょう。次のようなターゲットがあるとします。
# BUILD file in //application/k8s
k8s_object(
name = "staging",
kind = "deployment",
cluster = "testing",
template = "deployment.yaml",
)
staging
ターゲットで bazel build
を使用すると、k8s_object
ルールによって標準の Kubernetes YAML ファイルがビルドされます。ただし、追加のターゲットは、staging.apply
や :staging.delete
などの名前で k8s_object
マクロによっても作成されます。これらのビルド スクリプトは、これらのアクションを実行します。bazel run
staging.apply
で実行すると、独自の bazel k8s-apply
コマンドまたは bazel
k8s-delete
コマンドのように動作します。
別の例: ts_api_guardian_test
このパターンは、Angular プロジェクトでも見ることができます。ts_api_guardian_test
マクロは 2 つのターゲットを生成します。1 つ目は標準の nodejs_test
ターゲットです。生成された出力を「ゴールデン」ファイル(予想される出力を含むファイル)と比較します。これは、通常の bazel
test
呼び出しでビルドして実行できます。angular-cli
では、bazel test //etc/api:angular_devkit_core_api
を使用して、このようなターゲットを 1 つ実行できます。
今後、正当な理由で、このゴールデン ファイルの更新が必要になることがあります。手動で更新するのは手間がかかり、エラーが発生しやすいため、このマクロでは 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"],
)
この例では、ドキュメントが 1 つの Bazel ルールであるように、ドキュメントがターゲットとして作成されます。ルールを構築すると、いくつかの構成が生成され、Sphinx が実行されて手動で検査できる HTML サイトが作成されます。ただし、追加の「docs.publish」ターゲットも作成され、サイトを公開するためのスクリプトが作成されます。プライマリ ターゲットの出力を確認したら、架空の bazel publish
コマンドと同様に、bazel run :docs.publish
を使用して公開できます。
_sphinx_publisher
ルールの実装がどのようになるかは、すぐにはわかりません。このようなアクションは多くの場合、ランチャー シェル スクリプトを記述します。この方法では、ctx.actions.expand_template
を使用して非常に単純なシェル スクリプトを記述する必要があります。この場合、プライマリ ターゲットの出力へのパスを指定してパブリッシャー バイナリを呼び出します。このように、パブリッシャーの実装は汎用のままで、_sphinx_site
ルールによって HTML のみが生成されます。この小さなスクリプトは、この 2 つを組み合わせるために必要なものです。
rules_k8s
では、実際に .apply
が実行します。expand_template
は apply.sh.tpl
に基づいて非常に単純な Bash スクリプトを記述します。このスクリプトは、プライマリ ターゲットの出力で kubectl
を実行します。このスクリプトは、bazel run :staging.apply
を使用してビルドおよび実行することができ、実質的には、k8s_object
ターゲットのための k8s-apply
コマンドを提供します。