Bazel과의 일상적인 상호작용은 주로 build
, test
, run
의 몇 가지 명령어를 통해 이루어집니다. 하지만 이러한 도구는 제한적일 수 있습니다. 패키지를 저장소에 푸시하거나, 최종 사용자용 문서를 게시하거나, 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
매크로는 두 개의 타겟을 생성합니다. 첫 번째는 생성된 출력을 '골든' 파일 (예상 출력이 포함된 파일)과 비교하는 표준 nodejs_test
타겟입니다. 이는 일반적인 bazel
test
호출로 빌드하고 실행할 수 있습니다. angular-cli
에서는 bazel test //etc/api:angular_devkit_core_api
를 사용하여 이러한 타겟 중 하나를 실행할 수 있습니다.
시간이 지남에 따라 정당한 이유로 이 골든 파일을 업데이트해야 할 수 있습니다.
이를 수동으로 업데이트하는 것은 지루하고 오류가 발생하기 쉬우므로 이 매크로는 골든 파일과 비교하는 대신 골든 파일을 업데이트하는 nodejs_binary
타겟도 제공합니다. 효과적으로 동일한 테스트 스크립트를 호출 방식에 따라 'verify' 또는 'accept' 모드에서 실행되도록 작성할 수 있습니다. 이는 이미 배운 것과 동일한 패턴을 따릅니다. 네이티브 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만 생성할 수 있으며 이 작은 스크립트만 있으면 두 가지를 함께 결합할 수 있습니다.
rules_k8s
에서 .apply
는 실제로 다음과 같이 작동합니다. expand_template
는 apply.sh.tpl
에 기반한 매우 간단한 Bash 스크립트를 작성하며, 이 스크립트는 기본 타겟의 출력으로 kubectl
를 실행합니다. 그러면 이 스크립트를 bazel run :staging.apply
로 빌드하고 실행하여 k8s_object
타겟에 k8s-apply
명령어를 효과적으로 제공할 수 있습니다.