O recurso de arquivo de bloqueio no Bazel permite o registro de versões ou dependências específicas de bibliotecas ou pacotes de software exigidos por um projeto. Para isso, ele armazena o resultado da resolução do módulo e da avaliação da extensão. O arquivo de bloqueio promove builds reproduzíveis, garantindo ambientes de desenvolvimento consistentes. Além disso, ele aumenta a eficiência do build, permitindo que o Bazel pule as partes do processo de resolução que não são afetadas por mudanças nas dependências do projeto. Além disso, o arquivo de bloqueio melhora a estabilidade impedindo atualizações inesperadas ou mudanças em bibliotecas externas, reduzindo o risco de introdução de bugs.
Geração de arquivos de bloqueio
O arquivo de bloqueio é gerado na raiz do espaço de trabalho com o nome
MODULE.bazel.lock
. Ela é criada ou atualizada durante o processo de build,
especificamente após a resolução do módulo e a avaliação da extensão. É importante ressaltar que ele
inclui apenas as dependências incluídas na invocação atual do
build.
Quando ocorrem mudanças no projeto que afetam as dependências, o arquivo de bloqueio é atualizado automaticamente para refletir o novo estado. Isso garante que o arquivo de bloqueio continue focado no conjunto específico de dependências necessárias para o build atual, fornecendo uma representação precisa das dependências resolução do projeto.
Uso de arquivos de bloqueio
O lockfile pode ser controlado pela flag
--lockfile_mode
para
personalizar o comportamento do Bazel quando o estado do projeto é diferente do
lockfile. Os modos disponíveis são:
update
(padrão): use as informações presentes no arquivo de bloqueio para pular downloads de arquivos de registro conhecidos e evitar reavaliar extensões cujos resultados ainda estão atualizados. Se alguma informação estiver faltando, ela será adicionada ao arquivo de bloqueio. Nesse modo, o Bazel também evita a atualização de informações mutáveis, como versões puxadas, para dependências que não mudaram.refresh
: semelhante aupdate
, mas as informações mutáveis são sempre atualizadas ao mudar para esse modo e aproximadamente a cada hora enquanto estiver nesse modo.error
: semelhante aupdate
, mas se alguma informação estiver ausente ou desatualizada, o Bazel vai falhar com um erro. Esse modo nunca muda o arquivo de bloqueio nem executa solicitações de rede durante a resolução. As extensões de módulo que se automarcaram comoreproducible
ainda podem executar solicitações de rede, mas precisam sempre produzir o mesmo resultado.off
: o arquivo de bloqueio não é verificado nem atualizado.
Benefícios do arquivo de bloqueio
O lockfile oferece vários benefícios e pode ser usado de várias maneiras:
Builds reproduzíveis. Ao capturar as versões ou dependências específicas de bibliotecas de software, o arquivo de bloqueio garante que as versões sejam reproduzíveis em diferentes ambientes e ao longo do tempo. Os desenvolvedores podem contar com resultados consistentes e previsíveis ao criar projetos.
Resoluções incrementais rápidas. O arquivo de bloqueio permite que o Bazel evite o download de arquivos de registro que já foram usados em um build anterior. Isso melhora significativamente a eficiência do build, especialmente em cenários em que a resolução pode levar muito tempo.
Estabilidade e redução de riscos. O arquivo de bloqueio ajuda a manter a estabilidade impedindo atualizações inesperadas ou mudanças interruptivas em bibliotecas externas. Ao bloquear as dependências a versões específicas, o risco de introduzir bugs devido a atualizações incompatíveis ou não testadas é reduzido.
Conteúdo do arquivo de bloqueio
O lockfile contém todas as informações necessárias para determinar se o estado do projeto foi alterado. Ele também inclui o resultado da criação do projeto no estado atual. O arquivo de bloqueio consiste em duas partes principais:
- Hashes de todos os arquivos remotos que são entradas para a resolução do módulo.
- Para cada extensão de módulo, o arquivo de bloqueio inclui entradas que a afetam,
representadas por
bzlTransitiveDigest
,usagesDigest
e outros campos, além da saída da execução dessa extensão, chamada degeneratedRepoSpecs
.
Confira um exemplo que demonstra a estrutura do arquivo de bloqueio, além de explicações para cada seção:
{
"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",
...
}
}
}
}
}
}
Registros de hash de arquivos
A seção registryFileHashes
contém os hashes de todos os arquivos de
registros remotos acessados durante a resolução do módulo. Como o algoritmo de resolução
é totalmente determinístico quando recebe as mesmas entradas e todas as entradas
remotas são hash, isso garante um resultado de resolução totalmente reproduzível, evitando
a duplicação excessiva de informações remotas no arquivo de bloqueio. Isso
também requer gravação quando um registro específico não continha um determinado
módulo, mas um registro com precedência menor sim. Consulte a entrada "não encontrado" no
exemplo. Essas informações mutáveis podem ser atualizadas por
bazel mod deps --lockfile_mode=refresh
.
O Bazel usa os hashes do arquivo de bloqueio para procurar arquivos de registro no cache do repositório antes de fazer o download deles, o que acelera as resoluções posteriores.
Versões selecionadas
A seção selectedYankedVersions
contém as versões retiradas de módulos
que foram selecionadas pela resolução de módulo. Como isso geralmente resulta em um erro
ao tentar criar, essa seção só não fica vazia quando as versões retiradas são
explicitamente permitidas por --allow_yanked_versions
ou
BZLMOD_ALLOW_YANKED_VERSIONS
.
Esse campo é necessário porque, em comparação com os arquivos do módulo, as informações da versão extraída
são mutáveis e, portanto, não podem ser referenciadas por um hash. Essas informações
podem ser atualizadas usando bazel mod deps --lockfile_mode=refresh
.
Extensões de módulo
A seção moduleExtensions
é um mapa que inclui apenas as extensões usadas
na invocação atual ou invocadas anteriormente, excluindo as extensões
que não são mais utilizadas. Em outras palavras, se uma extensão não estiver mais sendo usada
no gráfico de dependências, ela será removida do mapa
moduleExtensions
.
Se uma extensão for independente do sistema operacional ou do tipo de arquitetura, esta seção apresentará apenas uma única entrada "geral". Caso contrário, várias entradas são incluídas, nomeadas com base no SO, na arquitetura ou em ambos, e cada uma corresponde ao resultado da avaliação da extensão nessas especificações.
Cada entrada no mapa de extensão corresponde a uma extensão usada e é identificada pelo arquivo e nome que contém. O valor correspondente de cada entrada contém as informações relevantes associadas a essa extensão:
- O
bzlTransitiveDigest
é o resumo da implementação da extensão e dos arquivos .bzl carregados transitivamente por ela. - O
usagesDigest
é o resumo dos usos da extensão no gráfico de dependência, que inclui todas as tags. - Outros campos não especificados que rastreiam outras entradas para a extensão, como o conteúdo de arquivos ou diretórios que ela lê ou as variáveis de ambiente que ela usa.
- O
generatedRepoSpecs
codifica os repositórios criados pela extensão com a entrada atual. - O campo
moduleExtensionMetadata
opcional contém metadados fornecidos pela extensão, como se determinados repositórios criados por ela precisam ser importados poruse_repo
pelo módulo raiz. Essas informações alimentam o comandobazel mod tidy
.
As extensões de módulo podem desativar a inclusão no lockfile definindo os
metadados retornados com reproducible = True
. Ao fazer isso, eles garantem que
sempre vão criar os mesmos repositórios quando receberem as mesmas entradas.
Práticas recomendadas
Para maximizar os benefícios do recurso de arquivo de bloqueio, considere as seguintes práticas recomendadas:
Atualize o arquivo de bloqueio regularmente para refletir as mudanças nas dependências ou na configuração do projeto. Isso garante que os builds subsequentes sejam baseados no conjunto de dependências mais atualizado e preciso. Para bloquear todas as extensões de uma só vez, execute
bazel mod deps --lockfile_mode=update
.Inclua o arquivo de bloqueio no controle de versão para facilitar a colaboração e garantir que todos os membros da equipe tenham acesso ao mesmo arquivo, promovendo ambientes de desenvolvimento consistentes em todo o projeto.
Use
bazelisk
para executar o Bazel e inclua um arquivo.bazelversion
no controle de versão que especifique a versão do Bazel correspondente ao arquivo de bloqueio. Como o próprio Bazel é uma dependência da sua versão, o arquivo de bloqueio é específico dessa versão e é alterado até mesmo entre versões do Bazel compatíveis com versões anteriores. O uso debazelisk
garante que todos os desenvolvedores usem uma versão do Bazel que corresponda ao arquivo de bloqueio.
Ao seguir essas práticas recomendadas, você pode usar o recurso de arquivo de bloqueio no Bazel, o que leva a fluxos de trabalho de desenvolvimento de software mais eficientes, confiáveis e colaborativos.