Tempo de leitura : 11 minutos.

Introdução

Público-alvo: desenvolvedores Gradle que já tenham encontrado comportamentos de build 'mágicos' ou inesperados.

Na nossa aventura de criação do plugin`site-baker`, seguimos uma abordagem TDD rigorosa. Cada funcionalidade era testada, validada, e avançávamos com confiança. E então, um dia, o imprevisto chegou. As builds começaram a se comportar de maneira errática. Modificações na lógica do plugin ou nos arquivos de configuração pareciam ser ignoradas, e nossos testes funcionais, antes confiáveis, falhavam sem razão aparente.

Este tipo de problema pode ser incrivelmente frustrante. Ele põe em causa a fiabilidade da ferramenta e a validade do nosso código. Depois de uma sessão de depuração intensa, o culpado foi identificado: ocache de configuração do Gradle.

O Sintoma: Builds Fantasma

O problema se manifestava de várias maneiras :

  1. Eu estava modificando uma cadeia de caracteres em uma tarefa`println`, mas a antiga cadeia continuava a ser exibida em tempo de execução.

  2. Eu estava alterando um valor no meu arquivo`managed-jbake-context.yml`, mas o plugin agia como se o arquivo não tivesse sido modificado.

  3. Os testes funcionais, que criam projetos de teste on the fly, falhavam porque o plugin não parecia detectar os arquivos de configuração recém-criados.

Tudo estava acontecendo como se o Gradle estivesse executando uma \"versão fantasma\" do nosso build, ignorando nossas alterações mais recentes.

A Investigação: O que é o Cache de Configuração?

O cache de configuração é uma funcionalidade relativamente moderna e extremamente poderosa do Gradle, ativada por padrão nas novas versões. Seu objetivo é tornar os builds mais rápidos.

O princípio é simples:
  1. Na primeira execução, o Gradle executa a fase deConfiguração(leitura dos`build.gradle.kts`, criação das tarefas, resolução das dependências) e constrói um grafo de tarefas.

  2. No final desta fase, GradleSerializar este grafo de tarefase o coloca em cache.

  3. Nas execuções seguintes, se nada tiver mudado (scripts de compilação,gradle.properties, etc.), Gradlepula completamente a fase de configuraçãoe reutiliza o grafo de tarefas em cache

O ganho de tempo é espectacular nos projetos grandes. No entanto, esse desempenho tem um custo: impõe regras rígidas sobre a forma como os plugins devem ser escritos.

config cache lifecycle
Figure 1. O ciclo de vida do cache de configuração

A Causa do Problema: Um Plugin Não Conforme

Nosso plugin`site-baker`violou, sem saber, várias regras do cache de configuração. Para que um grafo de tarefas seja serializável, as tarefas não devem conter referências a objetos complexos como o objeto`Project`ou ler arquivos de forma arbitrária durante a fase de execução.

Nossa principal falha era ler o conteúdo do arquivo YAML diretamente dentro da lógica de execução da tarefa, usando uma referência ao caminho armazenado na nossa extensão. Essa abordagem é incompatível com o cache porque o Gradle não consegue saber se o conteúdo do arquivo mudou se essa leitura não for modelada como umaentrada de tarefa(Task Input)

A Solução Temporária: Desativar o Cache

Para nos desbloquear e retomar um comportamento de compilação previsível, a solução mais rápida foi desativar o cache de configuração. Basta adicionar a linha seguinte ao arquivo`gradle.properties`do projeto que usa o plugin (ou, no nosso caso, o projeto de teste)site-baker).

# site-baker/gradle.properties
org.gradle.configuration-cache=false

Instantaneamente, os builds recuperaram seu comportamento normal. Cada execução reiniciava a fase de configuração e nossas alterações foram levadas em conta.

No entanto, essa é uma solução de contorno, não uma solução duradoura. Ela sacrifica o desempenho e não resolve o problema de fundo do nosso plugin.

A Verdadeira Solução : Tornar o Plugin Compatível

Para que um plugin seja um bom cidadão do ecossistema Gradle moderno, ele deve ser compatível com o cache de configuração. Isso implica repensar a forma como os dados fluem para nossas tarefas.

A chave é usar asAPIs do provedordo Gradle. Em vez de passar valores diretos (como um`String` ou un File) às nossas tarefas, devemos passar de`Property<T>`ou alguns`Provider<T>`.

Aqui está o plano de refatoramento:
  1. Declarar as entradas de tarefas :A tarefa que analisa o arquivo YAML deve declarar esse arquivo como uma entrada. Usa-se para isso a anotação`@InputFile`.

  2. Utilizar as Property e Provider :O valor de`configFile`será conectada à propriedade`configPath`da nossa extensão DSL. O Gradle assim pode rastrear a origem do dado.

Articles connexes