Bazel과의 일상적인 상호작용은 주로 몇 가지 명령어(build
, test
, run
)를 통해 발생합니다. 하지만 경우에 따라 제한으로 느껴질 수 있습니다. 즉, 저장소에 패키지를 푸시하거나, 최종 사용자를 위한 문서를 게시하거나, Kubernetes를 사용하여 애플리케이션을 배포해야 할 수 있습니다. 그러나 Bazel에는 publish
또는 deploy
명령어가 없습니다. 이러한 작업은 어디에 적합한가요?
bazel 실행 명령어
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
규칙은 bazel build
가 staging
대상에 사용되면 표준 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 규칙인 것처럼 '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
명령어를 효과적으로 제공할 수 있습니다.