Bazel Lockfile

7.3 · 7.2 · 7.1 · 7.0 · 6.5

Bazel 的鎖定檔案功能可記錄專案所需軟體程式庫或套件的特定版本或依附元件。方法是儲存模組解析和擴充功能評估的結果。鎖定檔案可促進可重現的建構作業,確保開發環境一致。此外,它還可讓 Bazel 略過解析程序中不受專案依附元件變更影響的部分,進而提升建構效率。此外,鎖定檔案可防止外部程式庫發生意外更新或破壞性變更,進而提升穩定性,降低引入錯誤的風險。

產生鎖定檔案

這個 Lockfile 會在工作區根目錄下產生,名為 MODULE.bazel.lock。這項資訊會在建構程序期間建立或更新,特別是在模組解析和擴充功能評估之後。重要的是,它只會納入目前建構作業呼叫中包含的依附元件。

當專案發生變更,並影響其依附元件時,系統會自動更新鎖定檔案,以反映新的狀態。這麼做可確保鎖定檔案持續專注於目前版本所需的特定依附元件組合,並準確呈現專案已解析的依附元件。

鎖定檔案使用方式

鎖定檔案可透過標記 --lockfile_mode 進行控制,在專案狀態與鎖定檔案不同時,可自訂 Bazel 的行為。可用的模式如下:

  • update (預設):使用鎖定檔案中的資訊,略過已知登錄檔的下載作業,並避免重新評估結果仍為最新狀態的擴充功能。如果缺少資訊,系統會將資訊新增至鎖定檔案。在這個模式中,Bazel 也會避免針對未變更的依附元件,重新整理可變更的資訊 (例如已撤銷的版本)。
  • refresh:與 update 類似,但在切換至此模式時,以及在這個模式下大約每小時,變更資訊一律會重新整理。
  • error:類似 update,但如果缺少任何資訊或資訊過時,Bazel 就會失敗並顯示錯誤。這個模式絕不會變更鎖定檔案,也不會在解析期間執行網路要求。標示為 reproducible 的模組擴充功能仍可執行網路要求,但預期一律會產生相同結果。
  • off:系統未檢查或更新鎖定檔案。

鎖定檔案的優點

鎖定檔案有許多優點,而且可以透過各種方式使用:

  • 可重現的建構作業:鎖定檔會擷取軟體程式庫的特定版本或依附元件,確保建構作業可在不同環境中重現,並且隨時間推移。開發人員在建構專案時,可以依賴一致且可預測的結果。

  • 快速增加解析度。鎖定檔可讓 Bazel 避免下載先前建構中已使用的登錄檔案。這可大幅提升建構效率,特別是在解析度可能耗時的情況下。

  • 穩定性和風險降低。鎖定檔案可防止外部程式庫發生意外更新或破壞性變更,進而維持穩定性。將依附元件鎖定至特定版本,可降低因不相容或未經測試的更新而引入錯誤的風險。

鎖定檔案內容

鎖定檔案包含判斷專案狀態是否已變更所需的所有必要資訊。同時也包括在目前狀態下建構專案的結果。鎖定檔案包含兩個主要部分:

  1. 所有屬於模組解析度的遠端檔案,其雜湊值。
  2. 針對每個模組擴充功能,鎖定檔案都包含影響模組的輸入項目 (以 bzlTransitiveDigestusagesDigest 和其他欄位表示),以及執行該擴充功能的輸出內容 (稱為 generatedRepoSpecs)

以下範例說明瞭鎖定檔案的結構,以及各個部分的說明:

{
  "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 中過度重複遠端資訊。請注意,當特定登錄不含特定模組,但優先順序較低的登錄含有該模組時,也需要記錄這項資訊 (請參閱範例中的「not found」項目)。您可以透過 bazel mod deps --lockfile_mode=refresh 更新原本可變動的資訊。

Bazel 會使用鎖定檔案中的雜湊值,在下載之前查詢存放區快取中的登錄檔案,藉此加快後續的解析作業。

已選取的已撤銷版本

selectedYankedVersions 區段包含模組解析所選取的模組版本。由於這通常會導致建構時發生錯誤,因此只有在透過 --allow_yanked_versionsBZLMOD_ALLOW_YANKED_VERSIONS 明確允許撤銷版本時,這個部分才會非空白。

與模組檔案相比,客層檔案本身可變動的版本資訊,因此需要使用這個欄位,因此無法由雜湊參照。您可以透過 bazel mod deps --lockfile_mode=refresh 更新這項資訊。

模組擴充功能

moduleExtensions 區段是地圖,只包含目前叫用或先前叫用時使用的擴充功能,而排除所有不再使用的擴充功能。換句話說,如果依附元件圖表中不再使用某個擴充功能,就會從 moduleExtensions 地圖中移除。

如果擴充功能不受作業系統或架構類型影響,這個部分只會顯示單一「一般」項目。否則,系統會納入多個項目,並以作業系統、架構或兩者命名,每個項目都會對應到針對這些特定項目評估擴充功能的結果。

擴充功能對應表中的每個項目都對應至已使用的擴充功能,並以包含的檔案和名稱識別。每個項目的對應值都包含與該擴充功能相關的相關資訊:

  1. bzlTransitiveDigest 是擴充功能實作項目的摘要,以及由擴充功能間接載入的 .bzl 檔案。
  2. usagesDigest 是依附元件圖表中擴充功能的「用途」摘要,其中包含所有標記。
  3. 其他未指定的欄位,可追蹤擴充功能的其他輸入內容,例如所讀取檔案或目錄的內容,或是所使用的環境變數。
  4. generatedRepoSpecs 會使用目前的輸入內容將擴充功能建立的存放區進行編碼。
  5. 選用的 moduleExtensionMetadata 欄位包含擴充功能提供的中繼資料,例如根模組是否應透過 use_repo 匯入所建立的特定存放區。這項資訊是 bazel mod tidy 指令的驅動力。

模組擴充功能可以使用 reproducible = True 設定傳回中繼資料,選擇不納入鎖定檔案。這樣一來,他們就保證在收到相同輸入內容時,一律會建立相同的存放區。

最佳做法

如要充分發揮鎖定檔案功能的優點,請考慮採用下列最佳做法:

  • 請定期更新鎖定檔案,反映專案依附元件或設定的變更。這麼做可確保後續建構作業以最新且正確的依附元件為基礎。如要一次鎖定所有擴充功能,請執行 bazel mod deps --lockfile_mode=update

  • 將 Lockfile 納入版本管控,以利協同合作,並確保所有團隊成員都能存取相同的 Lockfile,促進專案中一致的開發環境。

  • 請使用 bazelisk 執行 Bazel,並在版本控制中加入 .bazelversion 檔案,指定與鎖定檔案相對應的 Bazel 版本。由於 Bazel 本身是建構作業的依附元件,因此鎖定檔案會依 Bazel 版本而異,即使在向下相容的 Bazel 版本之間,也會有所變動。使用 bazelisk 可確保所有開發人員都使用與鎖定檔案相符的 Bazel 版本。

只要遵循這些最佳做法,就能有效運用 Bazel 的鎖定檔案功能,打造更有效率、可靠且可協作的軟體開發工作流程。