Bazel の日常的な操作は、主に build、test、run のコマンドで行います。ただし、これらのコマンドでは、パッケージをリポジトリに push したり、エンドユーザー向けのドキュメントを公開したり、Kubernetes でアプリケーションをデプロイしたりすることはできません。Bazel には publish コマンドや deploy コマンドがないため、これらのアクションをどのように実行すればよいかわからないかもしれません。
bazel run コマンド
Bazel は、密閉性、再現性、増分性に重点を置いているため、上記のタスクには build コマンドと test
コマンドは役に立ちません。これらのアクションは、ネットワーク アクセスが制限されたサンドボックスで実行される可能性があり、bazel build を実行するたびに再実行されるとは限りません。
代わりに、bazel run を使用します。これは、副作用を発生させたいタスクの主力となります。 Bazel
ユーザーは実行可能ファイルを作成するルールに慣れており、ルール作成者は一般的なパターンに従って、これを「カスタム動詞」に拡張できます。
実際の使用例: rules_k8s
たとえば、rules_k8sBazel 用の Kubernetes ルールである
を考えてみましょう。次のようなターゲットがあるとします。
# 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,
)
最後に、次のマクロを定義して、上記の両方のルールのターゲットをまとめて作成します。
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 コマンドが提供されます。