Tệp Bazel Lockfile

Báo cáo vấn đề Xem nguồn Nightly · 8.0 . 7.4 . 7.3 · 7.2 · 7.1 · 7.0 · 6.5

Tính năng tệp khoá 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 thư viện phần mềm hoặc gói mà dự án yêu cầu. Thư viện này đạt được điều này bằng cách lưu trữ kết quả của việc phân giải mô-đun và đánh giá tiện ích. Tệp khoá thúc đẩy 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ả xây dựng bằng cách cho phép Bazel bỏ qua các 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ơ xảy ra lỗi.

Tạo tệp khoá

Tệp khoá được tạo trong thư mục gốc của không gian làm việc có tên là 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á phần mở rộng. Điều quan trọng là tệp này 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, tệp khoá 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 tệp khoá vẫn tập trung vào tập hợp 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 đã được phân giải của dự án.

Mức sử dụng tệp khoá

Bạn có thể kiểm soát tệp khoá 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 tệp khoá. 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 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ị xoá, đối với các phần phụ thuộc chưa thay đổi.
  • refresh: Giống 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ột giờ một lần khi ở chế độ này.
  • error: Giống như update, nhưng nếu thiếu hoặc lỗi thời bất kỳ thông tin nào, Bazel sẽ không thành công và báo lỗi. Chế độ này không bao giờ thay đổi tệp khoá 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: Tệp khoá 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ể tái tạo. 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à dễ dự đoán khi xây dựng dự án.

  • Độ phân giải gia tăng nhanh. Tệp khoá cho phép Bazel tránh tải các tệp đăng ký đã được sử dụng trong một bản dựng trước đó. Điều này giúp cải thiện đáng kể hiệu quả xây dựng, đặc biệt là trong các trường hợp độ phân giải có thể tốn nhiều thời gian.

  • Ổn định và giảm rủi ro. Tệp khoá giúp duy trì sự ổ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ơ phát sinh 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ử sẽ giảm.

Nội dung 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:

  1. Hàm băm của tất cả tệp từ xa là dữ liệu đầu vào cho độ phân giải mô-đun.
  2. Đối với mỗi phần mở rộng mô-đun, tệp khoá bao gồm các dữ liệu đầu vào ảnh hưởng đến phần mở rộng đó, được biểu thị bằng bzlTransitiveDigest, usagesDigest và các trường khác, cũng như kết quả của việc chạy phần mở rộng đó, được gọi là generatedRepoSpecs

Dưới đây là ví dụ minh hoạ cấu trúc của tệp khoá, cùng với 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",
            ...
          }
        }
      }
    }
  }
}

Hàm băm tệp đăng ký

Phần registryFileHashes chứa 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 là hoàn toàn xác định khi được cung cấp cùng một dữ liệu đầu vào và tất cả dữ liệu đầ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 có thể tái tạo hoàn toàn trong khi tránh tình trạng trùng lặp quá mức thông tin từ xa trong tệp khoá. Xin lưu ý rằng việc 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 lại chứa mô-đun đó (xem mục "not found" (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 hàm băm từ tệp khoá để tra cứu các tệp đăng ký trong bộ nhớ đệm kho lưu trữ trước khi tải các tệp đó xuống, giúp tăng tốc độ phân giải tiếp theo.

Các phiên bản đã bị xoá đã chọn

Phần selectedYankedVersions chứa các phiên bản mô-đun đã bị loại bỏ được chọn theo độ phân giải mô-đun. Vì việc này thường dẫn đến lỗi khi cố gắng tạo bản dựng, nên phần này chỉ không trống khi các phiên bản đã bị loại bỏ được cho phép rõ ràng thông qua --allow_yanked_versions hoặc BZLMOD_ALLOW_YANKED_VERSIONS.

Bạn cần trường này vì so với tệp mô-đun, thông tin phiên bản đã bị xoá bản chất là có thể thay đổi và do đó không thể được tham chiếu bằng 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 sử dụng trong lệnh gọi hiện tại hoặc đã được 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 sử dụng trên 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 nhập sẽ được đưa vào, được đặt tên theo hệ điều hành, cấu trúc hoặc cả hai, với mỗi mục tương ứng với kết quả đánh giá tiện ích trên các thông số cụ thể đó.

Mỗi mục nhập trong bản đồ tiện ích tương ứng với một tiện ích đã sử dụng và được xác định bằng tên và tệp chứa tiện ích đó. Giá trị tương ứng cho mỗi mục nhập chứa thông tin liên quan được liên kết với tiện ích đó:

  1. bzlTransitiveDigest là thông báo tóm tắt về việc triển khai tiện ích và các tệp .bzl được tải chuyển tiếp bằng tiện ích đó.
  2. 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ẻ.
  3. Các trường chưa chỉ định khác theo dõi các dữ liệu đầu vào khác cho tiện ích, chẳng hạn như nội dung của tệp hoặc thư mục mà tiện ích đọc hoặc biến môi trường mà tiện ích sử dụng.
  4. 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.
  5. Trường moduleExtensionMetadata không bắt buộc chứa siêu dữ liệu do phần mở rộng 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ữ nhất định do phần mở rộng tạo qua use_repo hay không. Thông tin này hỗ trợ lệnh bazel mod tidy.

Tiện ích mô-đun có thể chọn không đưa vào tệp khoá bằng cách đặt siêu dữ liệu trả về bằng reproducible = True. Bằng cách đó, các hàm này hứa hẹn rằng chúng sẽ luôn tạo cùng một kho lưu trữ khi được cung cấp cùng một dữ liệu đầu vào.

Các phương pháp hay nhất

Để khai thác tối đa lợi ích của tính năng tệp khoá, 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 các thay đổi trong phần phụ thuộc hoặc cấu hình 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 nhóm phần phụ thuộc mới nhất và chính xác nhất. Để khoá tất cả tiện ích cùng một lúc, hãy chạy bazel mod deps --lockfile_mode=update.

  • Thêm tệp khoá vào tính nă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á, thúc đẩy môi trường phát triển nhất quán trên toàn dự án.

  • Sử dụng bazelisk để chạy Bazel và đưa tệp .bazelversion vào phần quản lý phiên bản để 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ụng bazelisk đảm bảo rằng tất cả nhà phát triển đều đang sử dụng 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 tệp khoá trong Bazel, nhờ đó tạo ra quy trình phát triển phần mềm cộng tác, hiệu quả và đáng tin cậy hơn.

Xung đột khi hợp nhất

Định dạng tệp khoá đượ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 để tự động giải quyết các 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 phải đăng ký một lần bằng cách làm theo các bước sau:

  1. Cài đặt jq (1.5 trở lên).
  2. 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 an toàn các xung đột hợp nhất đơn giản trong các trường registryFileHashesselectedYankedVersions bằng cách giữ lại tất cả các mục nhập từ cả hai bên 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 đó:

  1. Khôi phục trạng thái trước đó của tệp khoá thông qua git reset MODULE.bazel.lock && git checkout MODULE.bazel.lock.
  2. Giải quyết mọi xung đột trong tệp MODULE.bazel.
  3. Chạy bazel mod deps để cập nhật tệp khoá.