Tính năng lockfile trong Bazel cho phép ghi lại các phiên bản hoặc phần phụ thuộc cụ thể của các thư viện phần mềm hoặc gói mà một dự án yêu cầu. Việc này được thực hiện bằng cách lưu trữ kết quả của quá trình phân giải mô-đun và đánh giá tiện ích. Tệp khoá giúp tạo các bản dựng có thể tái tạo, đảm bảo môi trường phát triển nhất quán. Ngoài ra, tính năng này còn nâng cao hiệu quả của bản dựng bằng cách cho phép Bazel bỏ qua những phần của quy trình phân giải không bị ảnh hưởng bởi các thay đổi trong phần phụ thuộc của dự án. Hơn nữa, tệp khoá giúp cải thiện độ ổn định bằng cách ngăn chặn các bản cập nhật không mong muốn hoặc các thay đổi gây lỗi trong thư viện bên ngoài, từ đó giảm nguy cơ xuất hiện lỗi.
Tạo tệp khoá
Lockfile được tạo trong thư mục gốc của không gian làm việc với tên MODULE.bazel.lock
. Tệp này được tạo hoặc cập nhật trong quá trình xây dựng, cụ thể là sau khi phân giải mô-đun và đánh giá tiện ích. Điều quan trọng là nó chỉ bao gồm các phần phụ thuộc có trong lệnh gọi hiện tại của bản dựng.
Khi có thay đổi trong dự án ảnh hưởng đến các phần phụ thuộc của dự án, lockfile sẽ tự động cập nhật để phản ánh trạng thái mới. Điều này đảm bảo rằng lockfile vẫn tập trung vào một nhóm phần phụ thuộc cụ thể cần thiết cho bản dựng hiện tại, cung cấp thông tin chính xác về các phần phụ thuộc đã phân giải của dự án.
Sử dụng tệp khoá
Bạn có thể kiểm soát lockfile bằng cờ --lockfile_mode
để tuỳ chỉnh hành vi của Bazel khi trạng thái dự án khác với lockfile. Các chế độ hiện có là:
update
(Mặc định): Sử dụng thông tin có trong tệp khoá để bỏ qua việc tải các tệp đăng ký đã biết xuống và tránh đánh giá lại các tiện ích có kết quả vẫn còn mới nhất. Nếu thiếu thông tin, thông tin đó sẽ được thêm vào tệp khoá. Ở chế độ này, Bazel cũng tránh làm mới thông tin có thể thay đổi (chẳng hạn như các phiên bản bị rút lại) cho những phần phụ thuộc chưa thay đổi.refresh
: Tương tự nhưupdate
, nhưng thông tin có thể thay đổi luôn được làm mới khi chuyển sang chế độ này và cứ khoảng mỗi giờ một lần khi ở chế độ này.error
: Giống nhưupdate
, nhưng nếu thiếu hoặc hết hạn thông tin, Bazel sẽ gặp lỗi. Chế độ này không bao giờ thay đổi lockfile hoặc thực hiện các yêu cầu mạng trong quá trình phân giải. Các tiện ích mô-đun tự đánh dấu làreproducible
vẫn có thể thực hiện các yêu cầu mạng, nhưng dự kiến sẽ luôn tạo ra cùng một kết quả.off
: Lockfile không được kiểm tra cũng như không được cập nhật.
Lợi ích của tệp khoá
Tệp khoá mang lại một số lợi ích và có thể được sử dụng theo nhiều cách:
Bản dựng có thể mô phỏng. Bằng cách ghi lại các phiên bản hoặc phần phụ thuộc cụ thể của thư viện phần mềm, tệp khoá đảm bảo rằng các bản dựng có thể tái tạo trên nhiều môi trường và theo thời gian. Nhà phát triển có thể dựa vào kết quả nhất quán và có thể dự đoán được khi tạo dự án.
Giải pháp gia tăng nhanh chóng. Tệp khoá cho phép Bazel tránh tải các tệp đăng ký đã được dùng trong bản dựng trước đó xuống. Điều này giúp cải thiện đáng kể hiệu suất bản dựng, đặc biệt là trong những trường hợp có thể mất nhiều thời gian để phân giải.
Tính ổn định và giảm rủi ro. Lockfile giúp duy trì tính ổn định bằng cách ngăn chặn các bản cập nhật không mong muốn hoặc các thay đổi gây lỗi trong thư viện bên ngoài. Bằng cách khoá các phần phụ thuộc vào các phiên bản cụ thể, nguy cơ xuất hiện lỗi
do các bản cập nhật không tương thích hoặc chưa được kiểm thử.
Tệp khoá ẩn
Bazel cũng duy trì một lockfile khác tại "$(bazel info output_base)"/MODULE.bazel.lock
. Định dạng và nội dung của tệp khoá này không được chỉ định rõ ràng. Tính năng này chỉ được dùng để tối ưu hoá hiệu suất. Mặc dù có thể xoá cùng với cơ sở đầu ra thông qua bazel clean --expunge
, nhưng mọi nhu cầu làm như vậy đều là một lỗi trong chính Bazel hoặc một tiện ích mô-đun.
Nội dung của tệp khoá
Tệp khoá chứa tất cả thông tin cần thiết để xác định xem trạng thái dự án có thay đổi hay không. Báo cáo này cũng bao gồm kết quả của việc tạo dự án ở trạng thái hiện tại. Tệp khoá bao gồm hai phần chính:
- Mã băm của tất cả các tệp từ xa là đầu vào cho quá trình phân giải mô-đun.
- Đối với mỗi tiện ích mô-đun, lockfile bao gồm các đầu vào ảnh hưởng đến tiện ích đó, được biểu thị bằng
bzlTransitiveDigest
,usagesDigest
và các trường khác, cũng như đầu ra của việc chạy tiện ích đó, được gọi làgeneratedRepoSpecs
Sau đây là ví dụ minh hoạ cấu trúc của tệp khoá, cùng với nội dung giải thích cho từng phần:
{
"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",
...
}
}
}
}
}
}
Băm tệp đăng ký
Phần registryFileHashes
chứa các hàm băm của tất cả tệp từ các sổ đăng ký từ xa được truy cập trong quá trình phân giải mô-đun. Vì thuật toán phân giải hoàn toàn xác định được khi có cùng đầu vào và tất cả đầu vào từ xa đều được băm, nên điều này đảm bảo kết quả phân giải hoàn toàn có thể tái tạo trong khi tránh trùng lặp quá mức thông tin từ xa trong tệp khoá. Xin lưu ý rằng điều này cũng yêu cầu ghi lại thời điểm một sổ đăng ký cụ thể không chứa một mô-đun nhất định, nhưng một sổ đăng ký có mức độ ưu tiên thấp hơn thì có (xem mục "không tìm thấy" trong ví dụ). Bạn có thể cập nhật thông tin vốn có thể thay đổi này thông qua bazel mod deps --lockfile_mode=refresh
.
Bazel sử dụng các hàm băm từ tệp khoá để tra cứu các tệp đăng ký trong bộ nhớ đệm của kho lưu trữ trước khi tải xuống. Điều này giúp tăng tốc độ phân giải sau này.
Các phiên bản bị rút lại đã chọn
Mục selectedYankedVersions
chứa các phiên bản bị loại bỏ của những mô-đun được chọn theo độ phân giải mô-đun. Vì điều này thường dẫn đến lỗi khi cố gắng tạo, nên phần này chỉ không trống khi các phiên bản bị rút lại được cho phép rõ ràng thông qua --allow_yanked_versions
hoặc BZLMOD_ALLOW_YANKED_VERSIONS
.
Bạn cần có trường này vì so với các tệp mô-đun, thông tin phiên bản bị loại bỏ vốn có thể thay đổi và do đó không thể tham chiếu bằng một hàm băm. Bạn có thể cập nhật thông tin này thông qua bazel mod deps --lockfile_mode=refresh
.
Tiện ích mô-đun
Phần moduleExtensions
là một bản đồ chỉ bao gồm các tiện ích được dùng trong lệnh gọi hiện tại hoặc lệnh gọi trước đó, đồng thời loại trừ mọi tiện ích không còn được sử dụng. Nói cách khác, nếu một tiện ích không còn được dùng nữa trong biểu đồ phần phụ thuộc, thì tiện ích đó sẽ bị xoá khỏi bản đồ moduleExtensions
.
Nếu một tiện ích độc lập với hệ điều hành hoặc loại cấu trúc, thì phần này chỉ có một mục "chung". Nếu không, nhiều mục sẽ được đưa vào, được đặt tên theo hệ điều hành, cấu trúc hoặc cả hai, trong đó mỗi mục tương ứng với kết quả đánh giá tiện ích trên những thông số cụ thể đó.
Mỗi mục trong bản đồ tiện ích tương ứng với một tiện ích đã dùng và được xác định bằng tệp và tên chứa tiện ích đó. Giá trị tương ứng cho mỗi mục chứa thông tin liên quan đến tiện ích đó:
bzlTransitiveDigest
là bản tóm tắt của việc triển khai tiện ích và các tệp .bzl được tải một cách gián tiếp bởi tiện ích đó.usagesDigest
là bản tóm tắt về các cách sử dụng của tiện ích trong biểu đồ phần phụ thuộc, bao gồm tất cả các thẻ.- Các trường khác không được chỉ định để theo dõi những dữ liệu đầu vào khác cho tiện ích, chẳng hạn như nội dung của các tệp hoặc thư mục mà tiện ích đọc hoặc các biến môi trường mà tiện ích sử dụng.
generatedRepoSpecs
mã hoá các kho lưu trữ do tiện ích tạo bằng dữ liệu đầu vào hiện tại.- Trường
moduleExtensionMetadata
không bắt buộc chứa siêu dữ liệu do tiện ích cung cấp, chẳng hạn như liệu mô-đun gốc có nên nhập một số kho lưu trữ mà tiện ích đã tạo thông quause_repo
hay không. Thông tin này hỗ trợ lệnhbazel mod tidy
.
Các tiện ích mô-đun có thể chọn không được đưa vào tệp khoá bằng cách thiết lập siêu dữ liệu trả về bằng reproducible = True
. Khi đó, chúng hứa rằng sẽ luôn tạo ra các kho lưu trữ giống nhau khi nhận được các đầu vào giống nhau.
Các phương pháp hay nhất
Để tối đa hoá lợi ích của tính năng lockfile, hãy cân nhắc các phương pháp hay nhất sau đây:
Thường xuyên cập nhật tệp khoá để phản ánh những thay đổi trong cấu hình hoặc các phần phụ thuộc của dự án. Điều này đảm bảo rằng các bản dựng tiếp theo dựa trên bộ phần phụ thuộc mới nhất và chính xác nhất. Để khoá tất cả các tiện ích cùng một lúc, hãy chạy
bazel mod deps --lockfile_mode=update
.Đưa tệp khoá vào hệ thống kiểm soát phiên bản để tạo điều kiện cộng tác và đảm bảo rằng tất cả thành viên trong nhóm đều có quyền truy cập vào cùng một tệp khoá, từ đó thúc đẩy môi trường phát triển nhất quán trong toàn dự án.
Sử dụng
bazelisk
để chạy Bazel và thêm một tệp.bazelversion
vào hệ thống kiểm soát phiên bản. Tệp này chỉ định phiên bản Bazel tương ứng với tệp khoá. Vì bản thân Bazel là một phần phụ thuộc của bản dựng, nên tệp khoá dành riêng cho phiên bản Bazel và sẽ thay đổi ngay cả giữa các bản phát hành Bazel tương thích ngược. Việc sử dụngbazelisk
đảm bảo rằng tất cả nhà phát triển đều đang sử dụng một phiên bản Bazel khớp với tệp khoá.
Bằng cách làm theo các phương pháp hay nhất này, bạn có thể sử dụng hiệu quả tính năng lockfile trong Bazel, nhờ đó có quy trình phát triển phần mềm hiệu quả, đáng tin cậy và mang tính cộng tác hơn.
Xung đột hợp nhất
Định dạng lockfile được thiết kế để giảm thiểu xung đột hợp nhất, nhưng vẫn có thể xảy ra.
Độ phân giải tự động
Bazel cung cấp một trình điều khiển hợp nhất git tuỳ chỉnh để giúp tự động giải quyết những xung đột này.
Thiết lập trình điều khiển bằng cách thêm dòng này vào tệp .gitattributes
trong thư mục gốc của kho lưu trữ git:
# A custom merge driver for the Bazel lockfile.
# https://bazel.build/external/lockfile#automatic-resolution
MODULE.bazel.lock merge=bazel-lockfile-merge
Sau đó, mỗi nhà phát triển muốn sử dụng trình điều khiển này phải đăng ký một lần bằng cách làm theo các bước sau:
- Cài đặt jq (1.5 trở lên).
- Chạy các lệnh sau:
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"
Độ phân giải thủ công
Bạn có thể giải quyết một cách an toàn các xung đột hợp nhất đơn giản trong các trường registryFileHashes
và selectedYankedVersions
bằng cách giữ lại tất cả các mục nhập từ cả hai phía của xung đột.
Bạn không nên giải quyết các loại xung đột hợp nhất khác theo cách thủ công. Thay vào đó:
- Khôi phục trạng thái trước đó của lockfile thông qua
git reset MODULE.bazel.lock && git checkout MODULE.bazel.lock
. - Giải quyết mọi xung đột trong tệp
MODULE.bazel
. - Chạy
bazel mod deps
để cập nhật lockfile.