การโต้ตอบกับ Bazel ในแต่ละวันส่วนใหญ่จะเกิดขึ้นผ่านคำสั่ง 2-3 คำสั่ง ได้แก่
build
, test
และ run
แต่ในบางครั้ง คุณอาจรู้สึกว่าเครื่องมือเหล่านี้มีข้อจำกัด เช่น คุณอาจต้องการ
ส่งแพ็กเกจไปยังที่เก็บ เผยแพร่เอกสารสำหรับผู้ใช้ปลายทาง หรือ
ติดตั้งใช้งานแอปพลิเคชันด้วย Kubernetes แต่ Bazel ไม่มีคำสั่ง publish
หรือ
deploy
แล้วการดำเนินการเหล่านี้จะอยู่ที่ไหน
คำสั่ง bazel run
การที่ Bazel มุ่งเน้นที่ความไม่ขึ้นต่อกัน ความสามารถในการทำซ้ำ และการเพิ่มขึ้นทีละน้อยหมายความว่าคำสั่ง
build
และ test
ไม่เป็นประโยชน์สำหรับงานข้างต้น การดำเนินการเหล่านี้
อาจทำงานในแซนด์บ็อกซ์ โดยมีการเข้าถึงเครือข่ายแบบจำกัด และไม่รับประกันว่าจะ
เรียกใช้ซ้ำทุกครั้งที่มี bazel build
แต่ให้ใช้ bazel run
แทน ซึ่งเป็นฟังก์ชันที่เหมาะสำหรับงานที่คุณต้องการให้มีผลข้างเคียง ผู้ใช้ Bazel คุ้นเคยกับกฎที่สร้างไฟล์ที่เรียกใช้งานได้ และ
ผู้เขียนกฎสามารถทำตามชุดรูปแบบทั่วไปเพื่อขยายกฎนี้ไปยัง
"คำกริยาที่กำหนดเอง" ได้
ในสภาพแวดล้อมจริง: rules_k8s
ตัวอย่างเช่น ลองพิจารณา rules_k8s
กฎ Kubernetes สำหรับ Bazel สมมติว่าคุณมีเป้าหมายต่อไปนี้
# BUILD file in //application/k8s
k8s_object(
name = "staging",
kind = "deployment",
cluster = "testing",
template = "deployment.yaml",
)
k8s_object
กฎจะสร้างไฟล์ YAML ของ Kubernetes มาตรฐานเมื่อใช้ bazel build
ในเป้าหมาย staging
อย่างไรก็ตาม 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 รายการ อย่างแรกคือ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,
)
สุดท้าย ให้กำหนดมาโครสัญลักษณ์ต่อไปนี้ (พร้อมใช้งานใน Bazel 8 ขึ้นไป) เพื่อ สร้างเป้าหมายสำหรับกฎทั้ง 2 ข้อข้างต้นพร้อมกัน
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 เวอร์ชันเก่ากว่า Bazel 8 คุณจะต้อง กำหนดมาโครเดิมแทน
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"],
)
ในตัวอย่างนี้ ระบบจะสร้างเป้าหมาย "docs" เหมือนกับว่ามาโครเป็นกฎ Bazel มาตรฐานแบบเดียว เมื่อสร้างแล้ว กฎจะสร้างการกำหนดค่าบางอย่าง
และเรียกใช้ Sphinx เพื่อสร้างเว็บไซต์ HTML ซึ่งพร้อมสำหรับการตรวจสอบด้วยตนเอง อย่างไรก็ตาม
ระบบจะสร้างเป้าหมาย "docs.publish" เพิ่มเติมด้วย ซึ่งจะสร้างสคริปต์สำหรับ
การเผยแพร่เว็บไซต์ เมื่อตรวจสอบเอาต์พุตของเป้าหมายหลักแล้ว คุณจะ
ใช้ bazel run :docs.publish
เพื่อเผยแพร่ต่อสาธารณะได้ เช่นเดียวกับ
คำสั่ง bazel publish
ที่สมมติขึ้น
การใช้งาน_sphinx_publisher
กฎอาจดูไม่ชัดเจนในทันที โดยส่วนใหญ่แล้ว การดำเนินการเช่นนี้จะเขียนสคริปต์เชลล์ของตัวเรียกใช้
โดยปกติแล้ววิธีนี้จะเกี่ยวข้องกับการใช้
ctx.actions.expand_template
เพื่อเขียนสคริปต์เชลล์อย่างง่าย ในกรณีนี้คือการเรียกใช้ไบนารีของผู้เผยแพร่โฆษณา
พร้อมเส้นทางไปยังเอาต์พุตของเป้าหมายหลัก วิธีนี้จะช่วยให้การติดตั้งใช้งานของผู้เผยแพร่โฆษณายังคงเป็นแบบทั่วไปได้ _sphinx_site
กฎจะสร้าง HTML ได้เท่านั้น และสคริปต์ขนาดเล็กนี้เป็นสิ่งเดียวที่จำเป็นในการรวมทั้ง 2 อย่างเข้าด้วยกัน
ใน rules_k8s
.apply
จะทำดังนี้
expand_template
เขียนสคริปต์ Bash ที่เรียบง่ายมากโดยอิงตาม
apply.sh.tpl
ซึ่งเรียกใช้ kubectl
พร้อมเอาต์พุตของเป้าหมายหลัก จากนั้นจะสร้างและเรียกใช้สคริปต์นี้ด้วย bazel run :staging.apply
ได้ ซึ่งจะให้คำสั่ง k8s-apply
สำหรับเป้าหมาย k8s_object
ได้อย่างมีประสิทธิภาพ