Bazel との日常的な操作は、主に build
、test
、run
のいくつかのコマンドを使用して行います。ただし、パッケージをリポジトリに push したり、エンドユーザー向けのドキュメントを公開したり、Kubernetes でアプリケーションをデプロイしたりする必要がある場合は、制限を感じるかもしれません。しかし、Bazel には publish
コマンドや deploy
コマンドがありません。これらのアクションはどこに当てはまるのでしょうか。
bazel run コマンド
Bazel は完全性、再現性、増分性に重点を置いているため、build
コマンドと test
コマンドは上記のタスクには役立ちません。これらのアクションは、ネットワーク アクセスが制限されたサンドボックスで実行される場合があり、bazel build
ごとに再実行されるとは限りません。
代わりに、bazel run
を使用してください。これは、副作用を発生させるタスクのメインの処理です。Bazel ユーザーは実行可能ファイルを作成するルールに慣れています。ルール作成者は、一般的なパターンに沿ってこれを「カスタム動詞」に拡張できます。
実環境: rules_k8s
たとえば、Bazel の Kubernetes ルールである rules_k8s
について考えてみましょう。次のターゲットがあるとします。
# BUILD file in //application/k8s
k8s_object(
name = "staging",
kind = "deployment",
cluster = "testing",
template = "deployment.yaml",
)
k8s_object
ルールは、staging
ターゲットで bazel build
が使用されている場合に、標準の 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
マクロは 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,
)
最後に、次の記号マクロ(Bazel 8 以降で使用可能)を定義して、上記の両方のルールのターゲットを一緒に作成します。
def _sphinx_site_impl(name, visibility, srcs, **kwargs):
# This creates the primary target, producing the Sphinx-generated HTML. We
# set `visibility = visibility` to make it visible to callers of the
# macro.
_sphinx_site(name = name, visibility = visibility, srcs = srcs, **kwargs)
# This creates the secondary target, which produces a script for publishing
# the site generated above. We don't want it to be visible to callers of
# our macro, so we omit visibility for it.
_sphinx_publisher(name = "%s.publish" % name, site = name, **kwargs)
sphinx_site = macro(
implementation = _sphinx_site_impl,
attrs = {"srcs": attr.label_list(allow_files = [".rst"])},
# Inherit common attributes like tags and testonly
inherit_attrs = "common",
)
Bazel 8 より前の Bazel リリースをサポートする必要がある場合は、代わりにレガシー マクロを定義します。
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 ルールであるかのように、「docs」ターゲットが作成されます。ビルド時に、ルールはいくつかの構成を生成し、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
コマンドが効果的に提供されます。