ฟีเจอร์ Lockfile ใน Bazel ช่วยให้บันทึกเวอร์ชันหรือการขึ้นต่อกันที่เฉพาะเจาะจงของไลบรารีหรือแพ็กเกจซอฟต์แวร์ที่โปรเจ็กต์ต้องการได้ โดยจะจัดเก็บผลลัพธ์ของการแก้ปัญหาโมดูลและการประเมินส่วนขยาย Lockfile ช่วยให้บิลด์สามารถทำซ้ำได้ ซึ่งจะช่วยให้สภาพแวดล้อมการพัฒนาสอดคล้องกัน นอกจากนี้ ยังช่วยเพิ่มประสิทธิภาพของบิลด์ด้วยการอนุญาตให้ Bazel ข้ามส่วนต่างๆ ของกระบวนการแก้ปัญหาที่ไม่ได้รับผลกระทบจากการเปลี่ยนแปลงในทรัพยากร Dependency ของโปรเจ็กต์ ยิ่งไปกว่านั้น Lockfile ยังช่วยปรับปรุงความเสถียรด้วยการป้องกันการอัปเดตที่ไม่คาดคิดหรือการเปลี่ยนแปลงที่ทำให้เกิดข้อผิดพลาดในไลบรารีภายนอก ซึ่งจะช่วยลดความเสี่ยงในการเกิดข้อบกพร่อง
การสร้าง Lockfile
ระบบจะสร้าง Lockfile ไว้ที่รูทของพื้นที่ทำงานโดยใช้ชื่อ MODULE.bazel.lock และจะสร้างหรืออัปเดตระหว่างกระบวนการบิลด์ โดยเฉพาะอย่างยิ่งหลังจากแก้ปัญหาโมดูลและประเมินส่วนขยายแล้ว สิ่งสำคัญคือ Lockfile จะมีเฉพาะทรัพยากร Dependency ที่รวมอยู่ในการเรียกใช้บิลด์ปัจจุบันเท่านั้น
เมื่อมีการเปลี่ยนแปลงในโปรเจ็กต์ที่ส่งผลต่อการขึ้นต่อกัน ระบบจะอัปเดต Lockfile โดยอัตโนมัติเพื่อแสดงสถานะใหม่ ซึ่งจะช่วยให้ Lockfile มุ่งเน้นไปที่ชุดทรัพยากร Dependency ที่เฉพาะเจาะจงซึ่งจำเป็นสำหรับบิลด์ปัจจุบัน และแสดงทรัพยากร Dependency ที่แก้ปัญหาแล้วของโปรเจ็กต์ได้อย่างถูกต้อง
การใช้ Lockfile
คุณสามารถควบคุม Lockfile ได้โดยใช้แฟล็ก
--lockfile_mode เพื่อ
ปรับแต่งลักษณะการทำงานของ Bazel เมื่อสถานะโปรเจ็กต์แตกต่างจาก
Lockfile โหมดที่ใช้ได้มีดังนี้
update(ค่าเริ่มต้น): ใช้ข้อมูลที่มีอยู่ใน Lockfile เพื่อข้ามการดาวน์โหลดไฟล์รีจิสทรีที่ทราบ และหลีกเลี่ยงการประเมินส่วนขยายซ้ำซึ่งผลลัพธ์ยังคงเป็นปัจจุบัน หากไม่มีข้อมูล ระบบจะเพิ่มข้อมูลนั้นลงใน Lockfile ในโหมดนี้ Bazel จะหลีกเลี่ยงการรีเฟรชข้อมูลที่เปลี่ยนแปลงได้ เช่น เวอร์ชันที่ถูกยกเลิก สำหรับการขึ้นต่อกันที่ไม่มีการเปลี่ยนแปลงrefresh: เหมือนกับupdateแต่ระบบจะรีเฟรชข้อมูลที่เปลี่ยนแปลงได้เสมอเมื่อเปลี่ยนไปใช้โหมดนี้ และรีเฟรชทุกๆ ชั่วโมงโดยประมาณขณะอยู่ในโหมดนี้error: เหมือนกับupdateแต่หากไม่มีข้อมูลหรือข้อมูลล้าสมัย Bazel จะล้มเหลวและแสดงข้อผิดพลาด โหมดนี้จะไม่เปลี่ยนแปลง Lockfile หรือส่งคำขอเครือข่ายระหว่างการแก้ปัญหา ส่วนขยายโมดูลที่ทำเครื่องหมายตัวเองเป็นreproducibleอาจยังคงส่งคำขอเครือข่าย แต่คาดว่าจะให้ผลลัพธ์เดียวกันเสมอoff: ระบบจะไม่ตรวจสอบหรืออัปเดต Lockfile
ประโยชน์ของ Lockfile
Lockfile มีประโยชน์หลายประการและใช้ได้หลายวิธี ดังนี้
บิลด์ที่ทำซ้ำได้ Lockfile จะบันทึกเวอร์ชันหรือการขึ้นต่อกันที่เฉพาะเจาะจงของไลบรารีซอฟต์แวร์ ซึ่งจะช่วยให้บิลด์สามารถทำซ้ำได้ในสภาพแวดล้อมต่างๆ และเมื่อเวลาผ่านไป นักพัฒนาแอปสามารถมั่นใจได้ว่าจะได้รับผลลัพธ์ที่สอดคล้องและคาดการณ์ได้เมื่อสร้างโปรเจ็กต์
การแก้ปัญหาแบบเพิ่มทีละส่วนอย่างรวดเร็ว Lockfile ช่วยให้ Bazel หลีกเลี่ยงการดาวน์โหลดไฟล์รีจิสทรีที่ใช้ไปแล้วในบิลด์ก่อนหน้า ซึ่งจะช่วยเพิ่มประสิทธิภาพของบิลด์ได้อย่างมาก โดยเฉพาะอย่างยิ่งในสถานการณ์ที่การแก้ปัญหาอาจใช้เวลานาน
ความเสถียรและการลดความเสี่ยง Lockfile ช่วยรักษาความเสถียรด้วยการป้องกันการอัปเดตที่ไม่คาดคิดหรือการเปลี่ยนแปลงที่ทำให้เกิดข้อผิดพลาดในไลบรารีภายนอก การล็อกการขึ้นต่อกันไว้ที่เวอร์ชันที่เฉพาะเจาะจงจะช่วยลดความเสี่ยงในการเกิดข้อบกพร่องเนื่องจากการอัปเดตที่ไม่เข้ากันหรือยังไม่ได้ทดสอบ
เนื้อหาของ Lockfile
Lockfile มีข้อมูลที่จำเป็นทั้งหมดเพื่อพิจารณาว่าสถานะโปรเจ็กต์มีการเปลี่ยนแปลงหรือไม่ นอกจากนี้ ยังมีผลลัพธ์ของการสร้างโปรเจ็กต์ในสถานะปัจจุบันด้วย Lockfile ประกอบด้วย 2 ส่วนหลัก ได้แก่
- แฮชของไฟล์ระยะไกลทั้งหมดที่เป็นอินพุตสำหรับการแก้ปัญหาโมดูล
- สำหรับส่วนขยายโมดูลแต่ละรายการ Lockfile จะมีอินพุตที่ส่งผลต่อส่วนขยายนั้น
ซึ่งแสดงด้วย
bzlTransitiveDigest,usagesDigestและฟิลด์อื่นๆ รวมถึง เอาต์พุตของการเรียกใช้ส่วนขยายนั้น ซึ่งเรียกว่าgeneratedRepoSpecs
ตัวอย่างต่อไปนี้แสดงโครงสร้างของ Lockfile พร้อมคำอธิบายสำหรับแต่ละส่วน
{
"lockFileVersion": 10,
"registryFileHashes": {
"https://bcr.bazel.build/bazel_registry.json": "8a28e4af...5d5b3497",
"https://bcr.bazel.build/modules/foo/1.0/MODULE.bazel": "7cd0312e...5c96ace2",
"https://bcr.bazel.build/modules/foo/2.0/MODULE.bazel": "70390338... 9fc57589",
"https://bcr.bazel.build/modules/foo/2.0/source.json": "7e3a9adf...170d94ad",
"https://registry.mycorp.com/modules/foo/1.0/MODULE.bazel": "not found",
...
},
"selectedYankedVersions": {
"foo@2.0": "Yanked for demo purposes"
},
"moduleExtensions": {
"//:extension.bzl%lockfile_ext": {
"general": {
"bzlTransitiveDigest": "oWDzxG/aLnyY6Ubrfy....+Jp6maQvEPxn0pBM=",
"usagesDigest": "aLmqbvowmHkkBPve05yyDNGN7oh7QE9kBADr3QIZTZs=",
...,
"generatedRepoSpecs": {
"hello": {
"bzlFile": "@@//:extension.bzl",
...
}
}
}
},
"//:extension.bzl%lockfile_ext2": {
"os:macos": {
"bzlTransitiveDigest": "oWDzxG/aLnyY6Ubrfy....+Jp6maQvEPxn0pBM=",
"usagesDigest": "aLmqbvowmHkkBPve05y....yDNGN7oh7r3QIZTZs=",
...,
"generatedRepoSpecs": {
"hello": {
"bzlFile": "@@//:extension.bzl",
...
}
}
},
"os:linux": {
"bzlTransitiveDigest": "eWDzxG/aLsyY3Ubrto....+Jp4maQvEPxn0pLK=",
"usagesDigest": "aLmqbvowmHkkBPve05y....yDNGN7oh7r3QIZTZs=",
...,
"generatedRepoSpecs": {
"hello": {
"bzlFile": "@@//:extension.bzl",
...
}
}
}
}
}
}
แฮชของไฟล์รีจิสทรี
ส่วน registryFileHashes มีแฮชของไฟล์ทั้งหมดจากรีจิสทรีระยะไกลที่เข้าถึงระหว่างการแก้ปัญหาโมดูล เนื่องจากอัลกอริทึมการแก้ปัญหาเป็นแบบกำหนดได้ทั้งหมดเมื่อได้รับอินพุตเดียวกัน และระบบจะแฮชอินพุตระยะไกลทั้งหมด ซึ่งจะช่วยให้มั่นใจได้ว่าผลลัพธ์การแก้ปัญหาจะทำซ้ำได้ทั้งหมด พร้อมทั้งหลีกเลี่ยงการทำซ้ำข้อมูลระยะไกลมากเกินไปใน Lockfile โปรดทราบว่าการดำเนินการนี้ยังกำหนดให้ต้องบันทึกเมื่อรีจิสทรีหนึ่งๆ ไม่มีโมดูลที่เฉพาะเจาะจง แต่รีจิสทรีที่มีลำดับความสำคัญต่ำกว่ามี (ดูรายการ "ไม่พบ" ในตัวอย่าง) คุณอัปเดตข้อมูลที่เปลี่ยนแปลงได้โดยธรรมชาติได้ผ่าน bazel mod deps --lockfile_mode=refresh
Bazel ใช้แฮชจาก Lockfile เพื่อค้นหาไฟล์รีจิสทรีในแคชของที่เก็บก่อนที่จะดาวน์โหลด ซึ่งจะช่วยเร่งการแก้ปัญหาในครั้งต่อๆ ไป
เวอร์ชันที่ถูกยกเลิกที่เลือก
ส่วน selectedYankedVersions มีเวอร์ชันที่ถูกยกเลิกของโมดูลที่การแก้ปัญหาโมดูลเลือกไว้ เนื่องจากโดยปกติแล้วการดำเนินการนี้จะทำให้เกิดข้อผิดพลาดเมื่อพยายามสร้าง ดังนั้นส่วนนี้จึงไม่ว่างเปล่าก็ต่อเมื่อมีการอนุญาตเวอร์ชันที่ถูกยกเลิกอย่างชัดเจนผ่าน --allow_yanked_versions หรือ BZLMOD_ALLOW_YANKED_VERSIONS
เราจำเป็นต้องใช้ฟิลด์นี้เนื่องจากข้อมูลเวอร์ชันที่ถูกยกเลิกมีการเปลี่ยนแปลงได้โดยธรรมชาติเมื่อเทียบกับไฟล์โมดูล จึงไม่สามารถอ้างอิงได้ด้วยแฮช คุณอัปเดตข้อมูลนี้ได้ผ่าน bazel mod deps --lockfile_mode=refresh
ส่วนขยายโมดูล
ส่วน moduleExtensions เป็นแผนที่ที่มีเฉพาะส่วนขยายที่ใช้ในการเรียกใช้ปัจจุบันหรือที่เรียกใช้ก่อนหน้านี้ โดยไม่รวมส่วนขยายที่ไม่ได้ใช้อีกต่อไป กล่าวอีกนัยหนึ่งคือ หากไม่มีการใช้ส่วนขยายในกราฟการขึ้นต่อกันอีกต่อไป ระบบจะนำส่วนขยายนั้นออกจากแผนที่ moduleExtensions
หากส่วนขยายไม่ขึ้นอยู่กับระบบปฏิบัติการหรือประเภทสถาปัตยกรรม ส่วนนี้จะมีเฉพาะรายการ "ทั่วไป" รายการเดียว ไม่เช่นนั้น ระบบจะรวมรายการหลายรายการที่ตั้งชื่อตามระบบปฏิบัติการ สถาปัตยกรรม หรือทั้ง 2 อย่าง โดยแต่ละรายการจะสอดคล้องกับผลลัพธ์ของการประเมินส่วนขยายในรายละเอียดเหล่านั้น
แต่ละรายการในแผนที่ส่วนขยายจะสอดคล้องกับส่วนขยายที่ใช้และระบุด้วยไฟล์และชื่อที่มีส่วนขยายนั้น ค่าที่สอดคล้องกันสำหรับแต่ละรายการจะมีข้อมูลที่เกี่ยวข้องกับส่วนขยายนั้น ดังนี้
bzlTransitiveDigestคือไดเจสต์ของการใช้งานส่วนขยายและไฟล์ .bzl ที่โหลดแบบทรานซิทีฟโดยส่วนขยายนั้นusagesDigestคือไดเจสต์ของ การใช้งาน ส่วนขยายในกราฟการขึ้นต่อกัน ซึ่งรวมถึงแท็กทั้งหมด- ฟิลด์อื่นๆ ที่ไม่ได้ระบุซึ่งติดตามอินพุตอื่นๆ ไปยังส่วนขยาย เช่น เนื้อหาของไฟล์หรือไดเรกทอรีที่ส่วนขยายอ่านหรือตัวแปรสภาพแวดล้อมที่ส่วนขยายใช้
generatedRepoSpecsจะเข้ารหัสที่เก็บที่ส่วนขยายสร้างขึ้นด้วยอินพุตปัจจุบัน- ฟิลด์
moduleExtensionMetadataที่ไม่บังคับจะมีข้อมูลเมตาที่ส่วนขยายให้ไว้ เช่น โมดูลรูทควรนำเข้าที่เก็บบางรายการที่ส่วนขยายสร้างขึ้นผ่านuse_repoหรือไม่ ข้อมูลนี้จะขับเคลื่อนคำสั่งbazel mod tidy
ส่วนขยายโมดูลสามารถเลือกไม่ให้รวมอยู่ใน Lockfile ได้โดยตั้งค่าข้อมูลเมตาที่แสดงผลด้วย reproducible = True การดำเนินการนี้จะรับประกันว่าส่วนขยายจะสร้างที่เก็บเดียวกันเสมอเมื่อได้รับอินพุตเดียวกัน
แนวทางปฏิบัติแนะนำ
โปรดพิจารณาแนวทางปฏิบัติแนะนำต่อไปนี้เพื่อใช้ประโยชน์จากฟีเจอร์ Lockfile ให้ได้มากที่สุด
อัปเดต Lockfile เป็นประจำเพื่อแสดงการเปลี่ยนแปลงในการขึ้นต่อกันหรือการกำหนดค่าของโปรเจ็กต์ ซึ่งจะช่วยให้บิลด์ในครั้งต่อๆ ไปอิงตามชุดการขึ้นต่อกันที่ถูกต้องและเป็นปัจจุบันที่สุด หากต้องการล็อกส่วนขยายทั้งหมดพร้อมกัน ให้เรียกใช้
bazel mod deps --lockfile_mode=updateรวม Lockfile ไว้ในการควบคุมเวอร์ชันเพื่ออำนวยความสะดวกในการทำงานร่วมกัน และตรวจสอบว่าสมาชิกในทีมทุกคนมีสิทธิ์เข้าถึง Lockfile เดียวกัน ซึ่งจะช่วยให้สภาพแวดล้อมการพัฒนาสอดคล้องกันทั่วทั้งโปรเจ็กต์
ใช้
bazeliskเพื่อเรียกใช้ Bazel และรวมไฟล์.bazelversionไว้ในการควบคุมเวอร์ชันซึ่งระบุเวอร์ชัน Bazel ที่สอดคล้องกับ Lockfile เนื่องจาก Bazel เองเป็นทรัพยากร Dependency ของ บิลด์ Lockfile จึงเป็นเวอร์ชัน Bazel ที่เฉพาะเจาะจง และจะ เปลี่ยนแปลงแม้แต่ระหว่างการเผยแพร่ Bazel ที่เข้ากันได้แบบย้อนหลัง การใช้bazeliskจะช่วยให้มั่นใจได้ว่านักพัฒนาแอปทุกคนใช้ Bazel เวอร์ชันที่ตรงกับ Lockfile
การทำตามแนวทางปฏิบัติแนะนำเหล่านี้จะช่วยให้คุณใช้ฟีเจอร์ Lockfile ใน Bazel ได้อย่างมีประสิทธิภาพ ซึ่งจะนำไปสู่เวิร์กโฟลว์การพัฒนาซอฟต์แวร์ที่มีประสิทธิภาพ เชื่อถือได้ และทำงานร่วมกันได้มากขึ้น
การทับซ้อนในการผสาน
รูปแบบ Lockfile ได้รับการออกแบบมาเพื่อลดการทับซ้อนในการผสาน แต่ก็ยังอาจเกิดขึ้นได้
การแก้ปัญหาอัตโนมัติ
Bazel มีไดรเวอร์การผสาน Git ที่กำหนดเองเพื่อช่วยแก้ปัญหาการทับซ้อนเหล่านี้โดยอัตโนมัติ
ตั้งค่าไดรเวอร์โดยเพิ่มบรรทัดนี้ลงในไฟล์ .gitattributes ที่รูทของที่เก็บ Git
# A custom merge driver for the Bazel lockfile.
# https://bazel.build/external/lockfile#automatic-resolution
MODULE.bazel.lock merge=bazel-lockfile-merge
จากนั้นนักพัฒนาแอปแต่ละคนที่ต้องการใช้ไดรเวอร์จะต้องลงทะเบียนไดรเวอร์เพียงครั้งเดียวโดยทำตามขั้นตอนต่อไปนี้
- ติดตั้ง jq (1.5 ขึ้นไป)
- เรียกใช้คำสั่งต่อไปนี้
jq_script=$(curl https://raw.githubusercontent.com/bazelbuild/bazel/master/scripts/bazel-lockfile-merge.jq)
printf '%s\n' "${jq_script}" | less # to optionally inspect the jq script
git config --global merge.bazel-lockfile-merge.name "Merge driver for the Bazel lockfile (MODULE.bazel.lock)"
git config --global merge.bazel-lockfile-merge.driver "jq -s '${jq_script}' -- %O %A %B > %A.jq_tmp && mv %A.jq_tmp %A"
การแก้ปัญหาด้วยตนเอง
คุณสามารถแก้ปัญหาการทับซ้อนในการผสานอย่างง่ายในฟิลด์ registryFileHashes และ selectedYankedVersions ได้อย่างปลอดภัยโดยเก็บรายการทั้งหมดจากทั้ง 2 ด้านของการทับซ้อนไว้
คุณไม่ควรแก้ปัญหาการทับซ้อนในการผสานประเภทอื่นๆ ด้วยตนเอง แต่ให้ทำดังนี้แทน
- กู้คืนสถานะก่อนหน้าของ Lockfile
ผ่าน
git reset MODULE.bazel.lock && git checkout MODULE.bazel.lock - แก้ปัญหาการทับซ้อนในไฟล์
MODULE.bazel - เรียกใช้
bazel mod depsเพื่ออัปเดต Lockfile