Archivo de bloqueo de Bazel

Informar un problema Ver fuente

La función del archivo de bloqueo en Bazel permite registrar versiones o dependencias específicas de paquetes o bibliotecas de software que requiere un proyecto. Para ello, almacena el resultado de la resolución del módulo y la evaluación de la extensión. El archivo de bloqueo promueve compilaciones reproducibles, lo que garantiza entornos de desarrollo coherentes. Además, mejora la eficiencia de la compilación, ya que permite que Bazel omita las partes del proceso de resolución que no se ven afectadas por los cambios en las dependencias del proyecto. Además, el archivo de bloqueo mejora la estabilidad, ya que evita actualizaciones inesperadas o cambios rotundos en las bibliotecas externas, lo que reduce el riesgo de introducir errores.

Generación de archivos de bloqueo

El archivo de bloqueo se genera en la raíz del lugar de trabajo con el nombre MODULE.bazel.lock. Se crea o actualiza durante el proceso de compilación, específicamente después de la resolución del módulo y la evaluación de la extensión. Es importante destacar que solo incluye dependencias que se incluyen en la invocación actual de la compilación.

Cuando se producen cambios en el proyecto que afectan sus dependencias, el archivo de bloqueo se actualiza automáticamente para reflejar el estado nuevo. Esto garantiza que el archivo de bloqueo se mantenga enfocado en el conjunto específico de dependencias requeridas para la compilación actual, lo que proporciona una representación precisa de las dependencias resueltas del proyecto.

Uso del archivo de bloqueo

El archivo de bloqueo se puede controlar con la marca --lockfile_mode para personalizar el comportamiento de Bazel cuando el estado del proyecto difiere del archivo de bloqueo. Los modos disponibles son los siguientes:

  • update (predeterminado): Usa la información que está presente en el archivo de bloqueo para omitir descargas de archivos de registro conocidos y evitar volver a evaluar las extensiones cuyos resultados estén actualizados. Si falta información, se agregará al archivo de bloqueo. En este modo, Bazel también evita actualizar la información mutable, como las versiones bloqueadas, para las dependencias que no se modificaron.
  • refresh: Es similar a update, pero la información mutable siempre se actualiza cuando se cambia a este modo y aproximadamente cada hora mientras se está en este modo.
  • error: Es similar a update, pero si falta información o está desactualizada, Bazel fallará y se mostrará un error. Este modo nunca cambia el archivo de bloqueo ni realiza solicitudes de red durante la resolución. Es posible que las extensiones de módulo que se marcaron como reproducible aún realicen solicitudes de red, pero se espera que siempre produzcan el mismo resultado.
  • off: El archivo de bloqueo no se verifica ni se actualiza.

Beneficios del archivo de bloqueo

El archivo de bloqueo ofrece varios beneficios y se puede usar de varias maneras:

  • Compilaciones reproducibles. Mediante la captura de las versiones o dependencias específicas de las bibliotecas de software, el archivo de bloqueo garantiza que las compilaciones sean reproducibles en diferentes entornos y con el tiempo. Los desarrolladores pueden confiar en resultados coherentes y predecibles cuando compilan sus proyectos.

  • Resoluciones incrementales rápidas. El archivo de bloqueo permite que Bazel evite la descarga de archivos de registro que ya se usaron en una compilación anterior. Esto mejora significativamente la eficiencia de la compilación, especialmente en situaciones en las que la resolución puede llevar mucho tiempo.

  • Estabilidad y reducción de riesgos. El archivo de bloqueo ayuda a mantener la estabilidad evitando actualizaciones inesperadas o cambios rotundos en las bibliotecas externas. Cuando bloqueas las dependencias en versiones específicas, se reduce el riesgo de introducir errores debido a actualizaciones incompatibles o no probadas.

Contenido del archivo de bloqueo

El archivo de bloqueo contiene toda la información necesaria para determinar si cambió el estado del proyecto. También incluye el resultado de compilar el proyecto en el estado actual. El archivo de bloqueo consta de dos partes principales:

  1. Hashes de todos los archivos remotos que son entradas para la resolución del módulo
  2. Para cada extensión de módulo, el archivo de bloqueo incluye entradas que la afectan, representadas por bzlTransitiveDigest, usagesDigest y otros campos, además de la salida de ejecutar esa extensión, conocida como generatedRepoSpecs.

A continuación, se muestra un ejemplo que muestra la estructura del archivo de bloqueo, junto con explicaciones para cada secció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",
            ...
          }
        }
      }
    }
  }
}

Hashes de archivos de registro

En la sección registryFileHashes, se incluyen los hash de todos los archivos de registros remotos a los que se accedió durante la resolución del módulo. Dado que el algoritmo de resolución es completamente determinista cuando se le proporcionan las mismas entradas y todas las entradas remotas tienen un hash, esto garantiza un resultado de resolución completamente reproducible y se evita la duplicación excesiva de información remota en el archivo de bloqueo. Ten en cuenta que esto también requiere el registro cuando un registro en particular no contenía un módulo determinado, pero un registro con menor precedencia sí lo hacía (consulta la entrada "no encontrado" en el ejemplo). Esta información inherentemente mutable se puede actualizar a través de bazel mod deps --lockfile_mode=refresh.

Bazel usa los hash del archivo de bloqueo para buscar archivos de registro en la caché del repositorio antes de descargarlos, lo que acelera las resoluciones posteriores.

Versiones yanks seleccionadas

La sección selectedYankedVersions contiene las versiones transferidas de módulos que se seleccionaron según la resolución del módulo. Dado que esto suele generar un error cuando se intenta realizar la compilación, esta sección solo no está vacía cuando se permiten explícitamente las versiones de prueba a través de --allow_yanked_versions o BZLMOD_ALLOW_YANKED_VERSIONS.

Este campo es necesario, ya que, en comparación con los archivos del módulo, la información de la versión generada es inherentemente mutable y, por lo tanto, un hash no puede hacer referencia a este. Esta información se puede actualizar a través de bazel mod deps --lockfile_mode=refresh.

Extensiones de módulo

La sección moduleExtensions es un mapa que incluye solo las extensiones utilizadas en la invocación actual o anteriormente invocadas, y excluye las extensiones que ya no se usan. En otras palabras, si una extensión ya no se usa en el gráfico de dependencia, se quita del mapa moduleExtensions.

Si una extensión es independiente del sistema operativo o del tipo de arquitectura, esta sección presenta solo una entrada “general”. De lo contrario, se incluyen varias entradas, que llevan el nombre del SO, la arquitectura o ambos, y cada una corresponde al resultado de evaluar la extensión en esos detalles.

Cada entrada en el mapa de la extensión corresponde a una extensión utilizada y se identifica por el archivo y el nombre que la contiene. El valor correspondiente para cada entrada contiene la información relevante asociada con esa extensión:

  1. El bzlTransitiveDigest es el resumen de la implementación de la extensión y los archivos .bzl que cargó de forma transitiva.
  2. El usagesDigest es el resumen de los usos de la extensión en el gráfico de dependencia, que incluye todas las etiquetas.
  3. Otros campos sin especificar que realizan un seguimiento de otras entradas a la extensión, como el contenido de los archivos o directorios que lee, o las variables de entorno que usa.
  4. generatedRepoSpecs codifica los repositorios creados por la extensión con la entrada actual.
  5. El campo opcional moduleExtensionMetadata contiene metadatos que proporciona la extensión, por ejemplo, si el módulo raíz debe importar ciertos repositorios que creó a través de use_repo. Esta información impulsa el comando bazel mod tidy.

Las extensiones de módulo pueden inhabilitar su inclusión en el archivo de bloqueo configurando los metadatos que se muestran con reproducible = True. De esta manera, prometen que siempre crearán los mismos repositorios cuando se les proporcionen las mismas entradas.

Prácticas recomendadas

Para maximizar los beneficios de la función de archivo de bloqueo, considera las siguientes prácticas recomendadas:

  • Actualiza con regularidad el archivo de bloqueo para reflejar los cambios en las dependencias o la configuración del proyecto. Esto garantiza que las compilaciones posteriores se basen en el conjunto de dependencias más actualizado y preciso. Para bloquear todas las extensiones a la vez, ejecuta bazel mod deps --lockfile_mode=update.

  • Incluye el archivo de bloqueo en el control de versión para facilitar la colaboración y asegurarte de que todos los miembros del equipo tengan acceso al mismo archivo de bloqueo, lo que promueve entornos de desarrollo coherentes en todo el proyecto.

  • Usa bazelisk para ejecutar Bazel y, luego, incluye un archivo .bazelversion en el control de versiones que especifique la versión de Bazel correspondiente al archivo de bloqueo. Debido a que Bazel en sí es una dependencia de tu compilación, el archivo de bloqueo es específico de la versión de Bazel y cambiará incluso entre las versiones de Bazel retrocompatibles. El uso de bazelisk garantiza que todos los desarrolladores usen una versión de Bazel que coincida con el archivo de bloqueo.

Si sigues estas prácticas recomendadas, puedes usar de manera eficaz la función del archivo de bloqueo en Bazel, lo que genera flujos de trabajo de desarrollo de software más eficientes, confiables y colaborativos.