La función de archivo de bloqueo en Bazel permite registrar versiones o dependencias específicas de bibliotecas de software o paquetes 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 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 espacio 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 las 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 nuevo estado. Esto garantiza que el archivo de bloqueo se centre en el conjunto específico de dependencias necesarias para la compilación actual, lo que proporciona una representación precisa de las dependencias resueltas del proyecto.
Uso de archivos 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 siguientes son los modos disponibles:
update(predeterminado): Usa la información que está presente en el archivo de bloqueo para omitir las descargas de archivos de registro conocidos y evitar volver a evaluar las extensiones cuyos resultados aún 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 retiradas, para las dependencias que no cambiaron.refresh: Al igual queupdate, pero la información mutable siempre se actualiza cuando se cambia a este modo y, aproximadamente, cada hora mientras está en este modo.error: Al igual queupdate, pero si falta información o está desactualizada, Bazel fallará con un error. Este modo nunca cambia el archivo de bloqueo ni realiza solicitudes de red durante la resolución. Las extensiones de módulo que se marcaron comoreproducibleaún pueden realizar solicitudes de red, pero se espera que siempre produzcan el mismo resultado.off: No se verifica ni se actualiza el archivo de bloqueo.
Beneficios de los archivos de bloqueo
El archivo de bloqueo ofrece varios beneficios y se puede utilizar de varias maneras:
Compilaciones reproducibles. Al capturar 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 descargar 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, ya que evita actualizaciones inesperadas o cambios rotundos en bibliotecas externas. Al bloquear las dependencias en versiones específicas, se reduce el riesgo de introducir errores debido a actualizaciones incompatibles o no probadas.
Contenido de los archivos 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 la compilación del proyecto en el estado actual. El archivo de bloqueo consta de dos partes principales:
- Hashes de todos los archivos remotos que son entradas para la resolución del módulo
- Para cada extensión de módulo, el archivo de bloqueo incluye entradas que la afectan,
representadas por
bzlTransitiveDigest,usagesDigesty otros campos, así como el resultado de ejecutar esa extensión, denominadogeneratedRepoSpecs
Este es 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
La sección registryFileHashes contiene los hashes de todos los archivos de los 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 proporcionan las mismas entradas y todas las entradas remotas tienen un hash, esto garantiza un resultado de resolución completamente reproducible y, al mismo tiempo, evita la duplicación excesiva de información remota en el archivo de bloqueo. Ten en cuenta que esto también requiere registrar cuando un registro en particular no contenía un módulo determinado, pero sí un registro con menor precedencia (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 hashes del archivo de bloqueo para buscar archivos de registro en la caché del repositorio antes de descargarlos, lo que acelera las resoluciones posteriores.
Versiones retiradas seleccionadas
La sección selectedYankedVersions contiene las versiones retiradas de los módulos que se seleccionaron mediante la resolución del módulo. Dado que esto suele generar un error cuando se intenta compilar, esta sección solo no está vacía cuando las versiones retiradas se permiten de forma explícita a través de --allow_yanked_versions o BZLMOD_ALLOW_YANKED_VERSIONS.
Este campo es necesario, ya que, en comparación con los archivos de módulo, la información de la versión retirada es inherentemente mutable y, por lo tanto, no se puede hacer referencia a ella con un hash. 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 que se usan en la invocación actual o que se invocaron anteriormente, y excluye cualquier extensión que ya no se utilice. En otras palabras, si ya no se usa una extensión en el gráfico de dependencias, se quita del mapa moduleExtensions.
Si una extensión es independiente del sistema operativo o del tipo de arquitectura, esta sección solo incluye una entrada "general". De lo contrario, se incluyen varias entradas, con el nombre del SO, la arquitectura o ambos, y cada una corresponde al resultado de evaluar la extensión en esas especificaciones.
Cada entrada del mapa de extensión corresponde a una extensión usada y se identifica por su archivo y nombre contenedores. El valor correspondiente para cada entrada contiene la información pertinente asociada con esa extensión:
bzlTransitiveDigestes el resumen de la implementación de la extensión y los archivos .bzl que se cargan de forma transitiva.usagesDigestes el resumen de los usos de la extensión en el gráfico de dependencias, que incluye todas las etiquetas.- Otros campos no especificados que hacen 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.
generatedRepoSpecscodifica los repositorios creados por la extensión con la entrada actual.- El campo opcional
moduleExtensionMetadatacontiene metadatos proporcionados por la extensión, como si ciertos repositorios que creó deben importarse a través deuse_repopor el módulo raíz. Esta información impulsa el comandobazel mod tidy.
Las extensiones de módulo pueden inhabilitar su inclusión en el archivo de bloqueo si configuran los metadatos que muestran con reproducible = True. De esta manera, prometen que siempre crearán los mismos repositorios cuando se 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 periódicamente 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 versiones para facilitar la colaboración y garantizar 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
bazeliskpara ejecutar Bazel y, luego, incluye un.bazelversionarchivo en el control de versiones que especifique la versión de Bazel correspondiente al archivo de bloqueo. Debido a que Bazel es una dependencia de tu compilación, el archivo de bloqueo es específico de la versión de Bazel y cambiará incluso entre versiones de Bazel compatibles con versiones anteriores. El uso debazeliskgarantiza 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 de archivo de bloqueo en Bazel, lo que genera flujos de trabajo de desarrollo de software más eficientes, confiables y colaborativos.
Conflictos de combinación
El formato de archivo de bloqueo está diseñado para minimizar los conflictos de combinación, pero aún pueden ocurrir.
Resolución automática
Para configurar el controlador, agrega esta línea a un archivo .gitattributes en la raíz de tu repositorio de Git:
# A custom merge driver for the Bazel lockfile.
# https://bazel.build/external/lockfile#automatic-resolution
MODULE.bazel.lock merge=bazel-lockfile-merge
Luego, cada desarrollador que quiera usar el controlador debe registrarlo una vez siguiendo estos pasos:
- Instala jq (1.5 o versiones posteriores).
- Ejecuta los siguientes comandos:
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"
Resolución manual
Los conflictos de combinación simples en los campos registryFileHashes y selectedYankedVersions se pueden resolver de forma segura si se conservan todas las entradas de ambos lados del conflicto.
No se deben resolver manualmente otros tipos de conflictos de combinación. En su lugar, siga estos pasos:
- Restablece el estado anterior del archivo de bloqueo
a través de
git reset MODULE.bazel.lock && git checkout MODULE.bazel.lock. - Resuelve cualquier conflicto en el archivo
MODULE.bazel. - Ejecuta
bazel mod depspara actualizar el archivo de bloqueo.