ระบบบิลด์ที่อิงตามอาร์ติแฟกต์

รายงานปัญหา ดูแหล่งที่มา

หน้านี้จะพูดถึงระบบการสร้างที่ใช้อาร์ติแฟกต์และปรัชญาที่อยู่เบื้องหลังการสร้างผลงาน Bazel เป็นระบบการสร้างที่ใช้วัตถุเป็นหลัก แม้ว่าระบบบิลด์ตามงานจะเป็นขั้นตอนที่ดีเหนือสคริปต์การสร้าง แต่ระบบก็ให้กำลังมากเกินไปแก่วิศวกรแต่ละคนด้วยการให้ทีมมอบหมายงานของตัวเอง

ระบบบิลด์ที่อิงตามอาร์ติแฟกต์จะมีงานจำนวนหนึ่งที่ระบบกำหนด ซึ่งวิศวกรกำหนดค่าได้อย่างจำกัด วิศวกรยังคงบอกระบบว่าต้องสร้างอะไร แต่ระบบบิลด์จะเป็นตัวกำหนดวิธีการสร้าง เช่นเดียวกับระบบบิลด์ตามงาน ระบบบิลด์ที่ใช้อาร์ติแฟกต์ เช่น Bazel ยังคงมีบิลด์ไฟล์ แต่เนื้อหาของบิลด์เหล่านั้นจะแตกต่างกันมาก นอกจากจะเป็นชุดคำสั่งที่สำคัญในภาษาสคริปต์ที่สมบูรณ์ของตุรกีซึ่งอธิบายถึงวิธีสร้างเอาต์พุตแล้ว Buildfile ใน Bazel คือไฟล์ประกาศที่ใช้อธิบายชุดอาร์ติแฟกต์ในการสร้าง ทรัพยากร Dependency และตัวเลือกชุดที่จำกัดที่ส่งผลต่อวิธีสร้างอาร์ติแฟกต์ เมื่อวิศวกรเรียกใช้ bazel ในบรรทัดคำสั่ง ก็จะระบุชุดเป้าหมายที่จะสร้าง (อะไร) และ Bazel มีหน้าที่ในการกำหนดค่า เรียกใช้ และกำหนดเวลาขั้นตอนการคอมไพล์ (วิธีการ) เพราะตอนนี้ระบบสร้างสามารถควบคุมเครื่องมือที่จะเรียกใช้ได้อย่างเต็มที่ ทำให้สามารถการันตีที่แข็งแกร่งขึ้นมากว่าจะทำให้มีประสิทธิภาพมากขึ้นขณะที่ยังคงรับประกันความถูกต้อง

มุมมองเกี่ยวกับฟังก์ชันการทำงาน

คุณสามารถเปรียบเทียบระหว่างระบบบิลด์ที่อิงตามอาร์ติแฟกต์กับการเขียนโปรแกรมด้านการทำงานได้ง่ายๆ ภาษาโปรแกรมพื้นฐานที่จำเป็น (เช่น Java, C และ Python) ระบุรายการคำสั่งให้ดำเนินการทีละคำสั่ง ในลักษณะเดียวกันกับระบบการสร้างแบบเฉพาะงานอนุญาตให้โปรแกรมเมอร์กำหนดชุดขั้นตอนในการดำเนินการ ภาษาโปรแกรมแบบฟังก์ชัน (เช่น Haskell และ ML) ในทางตรงข้ามจะมีโครงสร้างคล้ายกับชุดสมการทางคณิตศาสตร์ ในภาษาที่ใช้เขียนโปรแกรม โปรแกรมเมอร์จะอธิบายถึงการประมวลผล แต่มีรายละเอียดเกี่ยวกับเวลาและวิธีในการดำเนินการประมวลผลดังกล่าวกับคอมไพเลอร์

ซึ่งสอดคล้องกับแนวคิดของการประกาศไฟล์ Manifest ในระบบบิลด์ที่อิงตามอาร์ติแฟกต์และปล่อยให้ระบบหาวิธีดำเนินการสร้าง ปัญหาหลายอย่างไม่สามารถแสดงออกได้ง่ายๆ โดยใช้การเขียนโปรแกรมที่ใช้งานได้ แต่ปัญหาที่มีข้อดีคือ ภาษามักจะสามารถจับคู่โปรแกรมเหล่านี้ไปเพียงเล็กน้อย และให้การรับประกันอย่างแข็งขันเกี่ยวกับความถูกต้องที่ยากที่จะดูเป็นไปไม่ได้ในภาษาที่จำเป็นต้องใช้ ปัญหาที่ง่ายที่สุดที่จะแสดงโดยใช้การเขียนโปรแกรมเชิงฟังก์ชันการทำงานก็คือ การแปลงข้อมูลชิ้นหนึ่งเป็นอีกข้อมูลหนึ่งโดยใช้ชุดกฎหรือฟังก์ชัน นั่นเป็นระบบของบิลด์ ทั้งระบบเป็นฟังก์ชันทางคณิตศาสตร์ที่มีประสิทธิภาพที่ใช้ไฟล์ต้นฉบับ (และเครื่องมืออย่างคอมไพเลอร์) เป็นอินพุตและสร้างไบนารีเป็นเอาต์พุต จึงไม่น่าแปลกใจที่โมเดลนี้สามารถวางระบบงานสร้างโดยอิงตามหลักการของการเขียนโปรแกรมเชิงฟังก์ชันการทำงานได้ดี

ทำความเข้าใจระบบบิลด์ที่ใช้อาร์ติแฟกต์

ระบบสร้างของ Google ที่ชื่อว่า Blaze เป็นระบบการสร้างที่ใช้วัตถุเป็นหลักระบบแรก Bazel เป็น Blaze เวอร์ชันโอเพนซอร์ส

ต่อไปนี้คือลักษณะของไฟล์บิลด์ (โดยทั่วไปชื่อ BUILD) ใน Bazel

java_binary(
    name = "MyBinary",
    srcs = ["MyBinary.java"],
    deps = [
        ":mylib",
    ],
)
java_library(
    name = "mylib",
    srcs = ["MyLibrary.java", "MyHelper.java"],
    visibility = ["//java/com/example/myproduct:__subpackages__"],
    deps = [
        "//java/com/example/common",
        "//java/com/example/myproduct/otherlib",
    ],
)

ใน Bazel ไฟล์ BUILD จะกำหนดเป้าหมาย เป้าหมาย 2 ประเภทในที่นี้คือ java_binary และ java_library เป้าหมายทุกรายการสอดคล้องกับอาร์ติแฟกต์ที่ระบบสร้างได้ นั่นคือเป้าหมายไบนารีจะสร้างไบนารีที่เรียกใช้ได้โดยตรง และเป้าหมายไลบรารีจะสร้างไลบรารีที่ไบนารีหรือไลบรารีอื่นๆ ใช้ได้ ทุกเป้าหมายมี:

  • name: วิธีอ้างอิงเป้าหมายในบรรทัดคำสั่งและเป้าหมายอื่นๆ
  • srcs: ไฟล์ต้นฉบับที่จะรวบรวมเพื่อสร้างอาร์ติแฟกต์สำหรับเป้าหมาย
  • deps: เป้าหมายอื่นๆ ที่ต้องสร้างขึ้นก่อนเป้าหมายนี้ และลิงก์กับเป้าหมายนี้

ทรัพยากร Dependency อาจอยู่ภายในแพ็กเกจเดียวกัน (เช่น ทรัพยากร Dependency ของ MyBinary ใน :mylib) หรือแพ็กเกจอื่นในลำดับชั้นของต้นทางเดียวกันก็ได้ (เช่น ทรัพยากร Dependency ของ mylib ใน //java/com/example/common)

คุณจะสร้างบิลด์โดยใช้เครื่องมือบรรทัดคำสั่งของ Bazel ได้เช่นเดียวกับระบบบิลด์ตามงาน หากต้องการสร้างเป้าหมาย MyBinary ให้เรียกใช้ bazel build :MyBinary หลังจากป้อนคำสั่งนั้นเป็นครั้งแรกในที่เก็บเปล่า Bazel ทำดังนี้

  1. แยกวิเคราะห์ไฟล์ BUILD ทุกไฟล์ในพื้นที่ทำงานเพื่อสร้างกราฟทรัพยากร Dependency ในอาร์ติแฟกต์ต่างๆ
  2. ใช้กราฟเพื่อกำหนดทรัพยากร Dependency แบบทรานซิทีฟของ MyBinary กล่าวคือ ทุกเป้าหมายที่ MyBinary อิงตามและทุกเป้าหมายที่เป้าหมายเหล่านั้นอ้างอิงแบบซ้ำๆ
  3. สร้างทรัพยากร Dependency แต่ละรายการตามลำดับ Bazel เริ่มต้นด้วยการสร้างแต่ละเป้าหมายที่ไม่มีทรัพยากร Dependency อื่นๆ และติดตามว่าทรัพยากร Dependency ใดยังคงต้องสร้างสำหรับแต่ละเป้าหมาย ทันทีที่การอ้างอิงของเป้าหมายทั้งหมดเกิดขึ้นแล้ว Bazel ก็เริ่มสร้างเป้าหมายนั้น ขั้นตอนนี้จะดำเนินไปจนกว่าจะมีการสร้างทรัพยากร Dependency แบบทรานซิทีฟของ MyBinary ทั้งหมด
  4. สร้าง MyBinary เพื่อสร้างไบนารีที่ดำเนินการได้ขั้นสุดท้ายซึ่งลิงก์ในทรัพยากร Dependency ทั้งหมดที่สร้างไว้ในขั้นตอนที่ 3

โดยพื้นฐานแล้ว อาจดูเหมือนว่าสิ่งที่เกิดขึ้นที่นี่ไม่ได้แตกต่างจากสิ่งที่เกิดขึ้นเมื่อใช้ระบบบิลด์ตามงาน ผลลัพธ์สุดท้ายก็คือไบนารีเดียวกัน และกระบวนการในการสร้างผลลัพธ์จะเกี่ยวข้องกับการวิเคราะห์ขั้นตอนจำนวนมากเพื่อค้นหาทรัพยากร Dependency ต่างๆ จากนั้นจึงใช้ขั้นตอนเหล่านั้นตามลำดับ แต่มีความแตกต่างที่สำคัญมาก รายการแรกจะปรากฏในขั้นตอนที่ 3 เนื่องจาก Bazel ทราบว่าแต่ละเป้าหมายสร้างเฉพาะไลบรารี Java จึงทราบว่าสิ่งที่ต้องทำคือเรียกใช้คอมไพเลอร์ Java แทนสคริปต์ที่กำหนดโดยผู้ใช้เอง เพื่อให้รู้ว่าสามารถเรียกใช้ขั้นตอนเหล่านี้พร้อมกันได้อย่างปลอดภัย ซึ่งจะทำให้มีลำดับการปรับปรุงประสิทธิภาพมากกว่าการสร้างเป้าหมายครั้งละ 1 รายการในเครื่องแบบมัลติคอร์ และเป็นไปได้ก็เนื่องจากแนวทางที่ใช้อาร์ติแฟกต์จะทำให้ระบบบิลด์มีหน้าที่รับผิดชอบกลยุทธ์การดำเนินการของตนเอง เพื่อให้การรับประกันที่มีประสิทธิภาพมากขึ้นเกี่ยวกับการทำงานควบคู่กัน

แต่ประโยชน์ที่ได้มากกว่าการทำงานพร้อมกัน สิ่งต่อไปที่วิธีการนี้ช่วยให้เราเห็นได้ชัดเจนเมื่อนักพัฒนาซอฟต์แวร์พิมพ์ bazel build :MyBinary เป็นครั้งที่ 2 โดยไม่ทำการเปลี่ยนแปลงใดๆ ก็คือ Bazel ออกไปภายในเวลาไม่ถึง 1 วินาทีพร้อมข้อความที่บอกว่าเป้าหมายเป็นปัจจุบัน ซึ่งเป็นไปได้เนื่องจากกระบวนทัศน์การเขียนโปรแกรมเชิงฟังก์ชันการทำงานที่เราพูดถึงก่อนหน้านี้ Bazel ทราบว่าแต่ละเป้าหมายเป็นผลลัพธ์จากการเรียกใช้ Java Compiler เท่านั้น และทราบว่าเอาต์พุตจาก Java คอมไพเลอร์จะขึ้นอยู่กับอินพุตเท่านั้น ตราบใดที่อินพุตไม่มีการเปลี่ยนแปลง เอาต์พุตก็จะสามารถนำกลับมาใช้ใหม่ได้ การวิเคราะห์นี้ใช้งานได้ในทุกระดับ หาก MyBinary.java เปลี่ยนแปลง Bazel รู้ว่าจะสร้าง MyBinary ขึ้นใหม่ แต่ใช้ mylib ซ้ำ หากไฟล์ต้นฉบับของ //java/com/example/common มีการเปลี่ยนแปลง Bazel จะต้องสร้างไลบรารี mylib และ MyBinary นั้นอีกครั้ง แต่ใช้ //java/com/example/myproduct/otherlib ซ้ำ เนื่องจาก Bazel รู้คุณสมบัติของเครื่องมือที่ทำงานในทุกขั้นตอน จึงสามารถสร้างชุดอาร์ติแฟกต์ขั้นต่ำได้ในแต่ละครั้ง ขณะเดียวกันก็รับประกันได้ว่าแอปจะไม่สร้างบิลด์ที่เก่าเกินไป

การกำหนดกรอบใหม่ให้กับกระบวนการบิลด์ในแง่ของอาร์ติแฟกต์แทนที่จะเป็นงานนั้นทั้งซับซ้อนแต่มีประสิทธิภาพ การลดความยืดหยุ่นที่แสดงต่อโปรแกรมเมอร์จะทำให้ระบบบิลด์รู้มากขึ้นเกี่ยวกับสิ่งที่กำลังดำเนินการในทุกขั้นตอนของบิลด์ ซึ่งสามารถใช้ความรู้นี้เพื่อทำให้การสร้างมีประสิทธิภาพมากขึ้นโดยผสานรวมกระบวนการบิลด์พร้อมกันและนำเอาต์พุตกลับมาใช้ใหม่ แต่นี่เป็นเพียงขั้นตอนแรก องค์ประกอบที่ใช้สร้างสรรค์พร้อมกันและการนำกลับมาใช้ใหม่เป็นพื้นฐานสำหรับระบบบิลด์ที่มีการกระจายและรองรับการปรับขนาดได้สูง

เคล็ดลับเด็ดอื่นๆ ของ Bazel

โดยพื้นฐานแล้ว ระบบบิลด์ที่อิงตามอาร์ติแฟกต์จะแก้ปัญหาการทำงานพร้อมกัน และการใช้ซ้ำในระบบบิลด์ที่อิงตามงาน แต่ก็ยังมีปัญหาอีก 2-3 อย่างที่เกิดขึ้นก่อนหน้านี้ซึ่งเรายังไม่ได้แก้ไข Bazel มีวิธีที่ชาญฉลาดในการแก้ปัญหา แต่ละอย่างและเราควรพูดคุยกันก่อนที่จะไปต่อ

เครื่องมือที่เป็นทรัพยากร Dependency

ปัญหาหนึ่งที่เราเจอก่อนหน้านี้คือ เวอร์ชันต่างๆ ขึ้นอยู่กับเครื่องมือที่ติดตั้งในเครื่องของเรา การสร้างบิลด์ซ้ำในระบบต่างๆ อาจเป็นเรื่องยากเนื่องจากเครื่องมือเวอร์ชันหรือสถานที่แตกต่างกัน ปัญหาจะยากยิ่งขึ้นเมื่อโปรเจ็กต์ใช้ภาษาที่ต้องใช้เครื่องมือที่แตกต่างกันตามแพลตฟอร์มที่สร้างหรือคอมไพล์ (เช่น Windows กับ Linux) และแต่ละแพลตฟอร์มเหล่านั้นต้องใช้ชุดเครื่องมือที่ต่างกันเล็กน้อยเพื่อทำงานเดียวกัน

Bazel แก้ปัญหาส่วนแรกของโจทย์นี้โดยถือว่าเครื่องมือเป็นทรัพยากร Dependency ของแต่ละเป้าหมาย java_library ทุกรายการในพื้นที่ทำงานขึ้นอยู่กับคอมไพเลอร์ Java ซึ่งกำหนดค่าเริ่มต้นเป็นคอมไพเลอร์ที่รู้จักกันดี เมื่อใดก็ตามที่ Bazel สร้าง java_library ระบบจะตรวจสอบว่าคอมไพเลอร์ที่ระบุพร้อมใช้งานในตำแหน่งที่รู้จัก เช่นเดียวกับทรัพยากร Dependency อื่นๆ หากคอมไพเลอร์ Java มีการเปลี่ยนแปลง ระบบจะสร้างอาร์ติแฟกต์ทุกรายการที่ขึ้นอยู่กับทรัพยากรดังกล่าวขึ้นมาใหม่

Bazel แก้ปัญหาในส่วนที่ 2 ซึ่งก็คือความเป็นอิสระของแพลตฟอร์ม โดยตั้งการกำหนดค่าการสร้าง โดยจะขึ้นอยู่กับประเภทของการกำหนดค่า ซึ่งแทนที่จะกำหนดเป้าหมายจะขึ้นอยู่กับเครื่องมือโดยตรง เช่น

  • การกำหนดค่าโฮสต์: เครื่องมือสร้างที่ทำงานระหว่างบิลด์
  • การกำหนดค่าเป้าหมาย: การสร้างไบนารีที่คุณร้องขอในท้ายที่สุด

การขยายระบบบิลด์

Bazel มีเป้าหมายสำหรับภาษาโปรแกรมยอดนิยมต่างๆ ที่พร้อมใช้งานทันที แต่วิศวกรต้องการที่จะทำมากกว่านี้เสมอ ข้อดีของระบบการทำงานแบบเฉพาะงานคือความยืดหยุ่นในการรองรับกระบวนการสร้างทุกประเภท และคงจะดีกว่าถ้าไม่ใช้ระบบบิลด์ที่อิงกับอาร์ติแฟกต์ โชคดีที่ Bazel อนุญาตให้ขยายประเภทเป้าหมายที่รองรับด้วยการเพิ่มกฎที่กำหนดเอง

หากต้องการกำหนดกฎใน Bazel ผู้สร้างกฎจะประกาศอินพุตที่กฎต้องการ (ในรูปแบบของแอตทริบิวต์ที่ส่งในไฟล์ BUILD) และชุดเอาต์พุตแบบคงที่ที่กฎสร้างขึ้น และผู้เขียนยังกำหนดการดำเนินการ ที่จะเกิดจากกฎนั้นด้วย การดำเนินการแต่ละรายการจะประกาศอินพุตและเอาต์พุต เรียกใช้ไฟล์ปฏิบัติการนั้นๆ หรือเขียนสตริงที่เฉพาะเจาะจงไปยังไฟล์ และอาจเชื่อมต่อกับการดำเนินการอื่นๆ ผ่านอินพุตและเอาต์พุตได้ ซึ่งหมายความว่าการทำงานคือหน่วย Composable ระดับล่างสุดในระบบบิลด์ การทำงานจะทำอะไรก็ได้ตามที่ต้องการ ตราบใดที่ใช้เฉพาะอินพุตและเอาต์พุตที่ประกาศไว้ และ Bazel จะดูแลการกำหนดเวลาการดำเนินการและแคชผลลัพธ์ตามความเหมาะสม

ระบบไม่ได้เด็ดขาดเนื่องจากไม่มีวิธีหยุดนักพัฒนาการดำเนินการบางอย่างไม่ให้ดำเนินการบางอย่าง เช่น นำกระบวนการที่ไม่เจาะจงเข้ามาเป็นส่วนหนึ่งของการดำเนินการ แต่วิธีนี้ไม่ได้เกิดขึ้นบ่อยนักในทางปฏิบัติ และผลักดันให้เกิดการละเมิดได้จนถึงระดับการดำเนินการจึงลดโอกาสในการเกิดข้อผิดพลาดลงอย่างมาก กฎที่รองรับภาษาและเครื่องมือทั่วไปจำนวนมากพร้อมให้ใช้งานอย่างทั่วถึงทางออนไลน์ และโปรเจ็กต์ส่วนใหญ่ไม่จำเป็นต้องกำหนดกฎของตัวเองเลย แม้จะเป็นกฎที่กำหนด แต่คุณจะต้องกำหนดคำจำกัดความของกฎไว้ที่ส่วนกลางเพียงที่เดียวในที่เก็บ ซึ่งหมายความว่าวิศวกรส่วนใหญ่จะใช้กฎเหล่านั้นได้โดยไม่ต้องกังวลเกี่ยวกับการใช้งาน

แยกสภาพแวดล้อม

การทำงานอาจดูเหมือนพบปัญหาเดียวกันกับงานในระบบอื่น จะเป็นไปได้ไหมที่จะเขียนการทำงานที่ทั้งเขียนไปยังไฟล์เดียวกันและลงเอยด้วยความขัดแย้งกัน ที่จริงแล้ว Bazel ทำให้ความขัดแย้งเหล่านี้เป็นไปไม่ได้โดยใช้แซนด์บ็อกซ์ ในระบบที่รองรับ การดำเนินการทุกอย่างจะแยกออกจากการดำเนินการอื่นๆ ทั้งหมดผ่านแซนด์บ็อกซ์ของระบบไฟล์ การดำเนินการแต่ละรายการจะดูได้เฉพาะมุมมองแบบจำกัดของระบบไฟล์ซึ่งมีอินพุตที่ประกาศและเอาต์พุตที่สร้างขึ้น ซึ่งบังคับใช้โดยระบบต่างๆ เช่น LXC บน Linux ซึ่งเป็นเทคโนโลยีที่อยู่เบื้องหลัง Docker ซึ่งหมายความว่าคุณไม่สามารถที่จะดำเนินการขัดแย้งกันเองได้เนื่องจากไม่สามารถอ่านไฟล์ที่ไม่ได้ประกาศ และไฟล์ที่เขียนแต่ไม่ได้ประกาศจะทิ้งไปเมื่อการดำเนินการเสร็จสิ้น นอกจากนี้ Bazel ยังใช้แซนด์บ็อกซ์เพื่อจำกัดการดำเนินการจากการสื่อสารผ่านเครือข่ายอีกด้วย

การกำหนดทรัพยากร Dependency ภายนอก

ยังเหลืออีก 1 ปัญหาคือ ระบบมักจะต้องดาวน์โหลดทรัพยากร Dependency ต่างๆ (ไม่ว่าจะเป็นเครื่องมือหรือไลบรารี) จากแหล่งที่มาภายนอก แทนที่จะสร้างขึ้นโดยตรง ดูได้ในตัวอย่างผ่านทรัพยากร Dependency @com_google_common_guava_guava//jar ซึ่งดาวน์โหลดไฟล์ JAR จาก Maven

ทั้งนี้ขึ้นอยู่กับไฟล์ที่อยู่นอกพื้นที่ทำงานปัจจุบัน ไฟล์เหล่านี้อาจมีการเปลี่ยนแปลงได้ทุกเมื่อ ซึ่งอาจทำให้ระบบบิลด์ต้องคอยตรวจสอบอย่างต่อเนื่องว่าไฟล์นั้นเป็นปัจจุบันหรือไม่ หากไฟล์ระยะไกลเปลี่ยนแปลงโดยไม่มีการเปลี่ยนแปลงที่สอดคล้องกันในซอร์สโค้ดของพื้นที่ทำงาน ก็อาจทำให้บิลด์ที่ไม่สามารถทำซ้ำได้ โดยบิลด์อาจทำงานได้ใน 1 วัน และล้มเหลวครั้งถัดไปโดยไม่มีเหตุผลที่ชัดเจนเนื่องจากการเปลี่ยนแปลงการขึ้นต่อกันที่มองไม่เห็น สุดท้าย ทรัพยากร Dependency ภายนอกอาจทำให้เกิดความเสี่ยงด้านความปลอดภัยที่ร้ายแรงเมื่อบุคคลที่สามเป็นเจ้าของ หากผู้โจมตีสามารถแฝงตัวเข้าไปในเซิร์ฟเวอร์ของบุคคลที่สาม ผู้โจมตีก็สามารถแทนที่ไฟล์ทรัพยากร Dependency ด้วยการออกแบบของตัวเองได้ ซึ่งอาจทำให้ควบคุมสภาพแวดล้อมและเอาต์พุตของบิลด์ได้อย่างเต็มที่

ปัญหาพื้นฐานคือเราต้องการให้ระบบบิลด์ทราบเกี่ยวกับไฟล์เหล่านี้โดยไม่ต้องตรวจสอบไฟล์เพื่อควบคุมแหล่งที่มา การอัปเดตทรัพยากร Dependency ควรเป็นทางเลือกที่ควรคำนึงถึง แต่การเลือกดังกล่าวควรดำเนินการเพียงครั้งเดียวจากที่ส่วนกลาง แทนที่จะต้องจัดการโดยวิศวกรหรือให้ระบบทำงานโดยอัตโนมัติ ทั้งนี้เพราะถึงแม้ว่าจะมีโมเดล “Live at Head” แล้ว เรายังคงต้องการให้บิลด์มีการกำหนดทิศทาง ซึ่งบอกเป็นนัยว่าหากคุณตรวจสอบคอมมิตจากสัปดาห์ที่แล้ว คุณควรจะเห็นทรัพยากร Dependency อย่างที่เป็นอยู่ในขณะนั้น ไม่ใช่อย่างที่เป็นอยู่ในปัจจุบัน

Bazel และระบบบิลด์อื่นๆ บางระบบแก้ปัญหานี้โดยกำหนดให้มีไฟล์ Manifest ทั่วทั้งพื้นที่ทำงานที่แสดงแฮชแบบเข้ารหัสสำหรับการขึ้นต่อกันภายนอกทุกรายการในพื้นที่ทำงาน แฮชเป็นวิธีที่กระชับในการแสดงไฟล์แบบไม่ซ้ำ โดยไม่ต้องตรวจสอบทั้งไฟล์ในการควบคุมแหล่งที่มา เมื่อใดก็ตามที่มีการอ้างอิงทรัพยากรภายนอกใหม่จากพื้นที่ทำงาน ระบบจะเพิ่มแฮชของทรัพยากร Dependency นั้นลงในไฟล์ Manifest ไม่ว่าจะด้วยตนเองหรือโดยอัตโนมัติ เมื่อ Bazel เรียกใช้บิลด์ จะมีการตรวจสอบแฮชจริงของทรัพยากร Dependency ที่แคชเทียบกับแฮชที่คาดไว้ซึ่งกำหนดไว้ในไฟล์ Manifest และดาวน์โหลดไฟล์อีกครั้งเฉพาะในกรณีที่แฮชต่างกันเท่านั้น

หากอาร์ติแฟกต์ที่เราดาวน์โหลดมีแฮชต่างจากที่ประกาศในไฟล์ Manifest บิลด์จะล้มเหลวเว้นแต่จะมีการอัปเดตแฮชในไฟล์ Manifest การดำเนินการนี้จะเกิดขึ้นโดยอัตโนมัติ แต่การเปลี่ยนแปลงดังกล่าวต้องได้รับอนุมัติและตรวจสอบในการควบคุมแหล่งที่มาก่อนที่บิลด์จะยอมรับทรัพยากร Dependency ใหม่ ซึ่งหมายความว่าจะมีการบันทึกเวลาที่อัปเดตทรัพยากร Dependency ไว้เสมอ และการขึ้นต่อกันภายนอกจะเปลี่ยนแปลงไม่ได้หากไม่มีการเปลี่ยนแปลงที่สอดคล้องกันในแหล่งที่มาของพื้นที่ทำงาน นอกจากนี้ เมื่อดูซอร์สโค้ดเวอร์ชันเก่า เวอร์ชันดังกล่าวได้รับการรับประกันว่าจะใช้ทรัพยากร Dependency เดียวกันกับที่ใช้ ณ ช่วงเวลานั้นเมื่อตรวจสอบเวอร์ชันดังกล่าว (ไม่เช่นนั้นก็จะล้มเหลวหากทรัพยากร Dependency เหล่านั้นไม่พร้อมใช้งานอีกต่อไป)

แน่นอนว่าอาจยังคงมีปัญหาหากเซิร์ฟเวอร์ระยะไกลไม่พร้อมใช้งานหรือเริ่มแสดงข้อมูลที่เสียหาย ซึ่งอาจทำให้บิลด์ทั้งหมดเริ่มล้มเหลวหากคุณไม่มีสำเนาทรัพยากร Dependency ดังกล่าวอีกสำเนาหนึ่ง เพื่อหลีกเลี่ยงปัญหานี้ เราขอแนะนำว่าสำหรับโปรเจ็กต์ที่ไม่สำคัญ ให้มิเรอร์การขึ้นต่อกันทั้งหมดของโปรเจ็กต์ไปยังเซิร์ฟเวอร์หรือบริการที่คุณเชื่อถือและควบคุม มิฉะนั้น คุณจะต้องขอความช่วยเหลือจากบุคคลที่สามเพื่อความพร้อมใช้งานของระบบบิลด์ของคุณ แม้ว่าแฮชเช็คอินจะรับประกันความปลอดภัยก็ตาม