Compreendendo Dependências… Visualmente!

Levante a mão quantos de nós realmente entendem como a sua ferramenta de automação de compilação constrói seu árvore de dependências? Agora, abaixe a mão se você entende porque trabalha na construção de ferramentas de automação. Pensei assim!

Uma responsabilidade frustrante dos engenheiros de software é entender as dependências do seu projeto: quais dependências transitivas foram introduzidas e por quem; por que v1.3.1 é usado quando v1.2.10 foi declarado; o que resultou quando as dependências transitivas mudaram; como ocorreu múltiplas versões do mesmo artefato?

Todo engenheiro de software já passou uma árvore de dependências para um arquivo de texto, procurou por artefatos específicos e depois trabalhou para identificar sua origem. Para qualquer coisa além de projetos triviais, criar um mapa mental das dependências é extremamente difícil, senão impossível.

I faced this problem when starting a new job with a mature code base, presenting a challenge to assemble the puzzle pieces.  I’ve previously worked with graph databases and thought a graphical view of the dependency artifacts could be created using Neo4J, which resulted in DependencyLoader.

Nota: este não é um tutorial sobre bancos de dados de grafos, nem este tutorial requer um background em bancos de dados de grafos. Se estiver interessado, o Neo4J tem tutoriais e white papers para ajudá-lo a começar.

Configurar Ambiente

Instalar Java

É necessário Java 11 ou posterior. Se ainda não estiver disponível, instale sua versão favorita de OpenJDK.

Instale o Neo4J

O tutorial requer um banco de dados Neo4J no qual a informação de dependência é carregada, preferencialmente não compartilhada, pois o carregador limpa o banco de dados antes de cada execução.Atenção!

Neo4J fornece caixas de areia pessoais, ideais para projetos de curto prazo como este tutorial.

Como alternativa, instale o Neo4J localmente no seu desktop ou notebook. Homebrew simplifica as instalações no MacOS: 

Shell

 

brew install neo4j && brew services start neo4j

Antes de continuar, confirme o acesso ao seu banco de dados Neo4J usando o navegador, usando o link e credenciais para a caixa de areia Neo4J ou localmente em http://localhost:7474. As credenciais padrão para uma instalação local são neo4j/neo4j; após o login bem-sucedido, você é obrigado a alterar a senha.

Clonar Repositórios

O repositório neo4j-gradle-dependencies contém os arquivos para carregar as dependências no Neo4J. Este tutorial gerará um grafo de dependência para spring-boot. Você deve clonar esses dois repositórios.

Shell

 

Scott.Sosna@mymachine src% git clone [email protected]:scsosna99/neo4j-gradle-dependencies.git
Scott.Sosna@mymachine src% git clone [email protected]:spring-projects/spring-boot.git

Nota: Gradle local não é necessário, pois ambos os repositórios utilizam o Gradle Wrapper, que baixa todos os componentes necessários na primeira utilização do wrapper.

Gerar Dependências

DependencyLoader recebe a árvore de dependência gerada por Gradle como entrada. Embora várias configurações possam ser carregadas juntas — por exemplo, compileClasspath, runtimeClasspath, testCompileClasspath, testRuntimeClasspath — começar com uma única configuração é mais simples de navegar, especialmente para um tutorial.

Para gerar dependências para todas as configurações: 

  • gradle dependencies
  • ./gradlew dependencies

Para gerar dependências para uma única configuração

  • gradle dependencies --configuration <configuração>
  • ./gradlew dependencies --configuration <configuração>

Gerar Dependências do Spring Boot

Este tutorial cria um gráfico de dependências no Neo4J usando as dependências compileClasspath do Spring Boot. A partir do diretório onde os repositórios foram clonados, execute os seguintes comandos:

Shell

 

Scott.Sosna@mymachine src% cd spring-boot/spring-boot-project/spring-boot
Scott.Sosna@mymachine spring-boot% ./gradlew dependencies --configuration compileClasspath > dependencies.out

O arquivo dependencies.out contém as dependências da classpath em tempo de compilação para o Spring Boot.

Carregar Dependências

Primeiro, confirme a URL de conexão e as credenciais de autenticação no DependencyLoader.java e modifique-as se necessário.

Execute os seguintes comandos para carregar as dependências do Spring Boot no Neo4j:

Shell

 

Scott.Sosna@mymachine spring-boot% cd ../../../neo4j-gradle-dependencies
Scott.Sosna@mymachine neo4j-gradle-dependencies% ./gradlew clean run  --args="../spring-boot/spring-boot-project/spring-boot/dependencies.out"

Quando bem-sucedido, as linhas de saída do gradle são:

Shell

 

Scott.Sosna@PVHY32M6KG neo4j-gradle-dependencies % ./gradlew clean run  --args="../spring-boot/spring-boot-project/spring-boot/dependencies.out"

> Task :compileJava
Note: /Users/Scott.Sosna/data/src/github/neo4j-gradle-dependencies/src/main/java/dev/scottsosna/neo4j/gradle/relationship/DependsOn.java uses unchecked or unsafe operations.
Note: Recompile with -Xlint:unchecked for details.

> Task :run
SLF4J: Failed to load class "org.slf4j.impl.StaticLoggerBinder".
SLF4J: Defaulting to no-operation (NOP) logger implementation
SLF4J: See http://www.slf4j.org/codes.html#StaticLoggerBinder for further details.
Jun 02, 2023 6:19:22 AM org.neo4j.driver.internal.logging.JULogger info
INFO: Direct driver instance 1606286799 created for server address localhost:7687
dependencies.out completed.
Jun 02, 2023 6:19:23 AM org.neo4j.driver.internal.logging.JULogger info
INFO: Closing driver instance 1606286799
Jun 02, 2023 6:19:23 AM org.neo4j.driver.internal.logging.JULogger info
INFO: Closing connection pool towards localhost:7687

Deprecated Gradle features were used in this build, making it incompatible with Gradle 8.0.

You can use '--warning-mode all' to show the individual deprecation warnings and determine if they come from your own scripts or plugins.

See https://docs.gradle.org/7.5.1/userguide/command_line_interface.html#sec:command_line_warnings

BUILD SUCCESSFUL in 3s

Visualizar Dependências

Várias ferramentas estão disponíveis para exibir gráficos Neo4J, mas a ferramenta de navegador interna é adequada para este tutorial.

Mostrar a Árvore Completa

A consulta MATCH(a) RETURN a é o equivalente relacional de SELECT * FROM <tabela>

Visualizar Detalhes de um Artefato

Cada artefato encontrado cria um cujas propriedades identificam o artefato (groupId/artifactId) e seu tipo, mostrado na janela lateral direita.

Visualizar Detalhes de uma Dependência

Cada dependência é criada como uma relação cujas propriedades identificam os detalhes específicos da dependência: configuração, versão especificada e configuração. A dependência selecionada abaixo mostra que spring-security:spring-web depende de io.micrometer:micrometer-observation, mas a versão especificada do spring-web, 1.10.7, foi resolvida como a versão 1.11.0.

Percorrer Dependências

O Neo4J permite explorar o grafo nó a nó, permitindo expandir manualmente o grafo nó a nó, fornecendo uma maneira de explorar áreas específicas da árvore de dependências.

Suponha que você deseja entender as dependências para o artefato io.projectreactor.netty:reactor-netty-http. Primeiro, vamos consultar o Neo4J para aquele nó específico.

Cypher

 

MATCH(a:Artifact {groupId: 'io.projectreactor.netty', artifactId: 'reactor-netty-http'}) RETURN a

Duplo clique no nó mostra seus nós vizinhos — os artefatos que dependem dele e os artefatos que ele depende.

Esta árvore expandida mostra um artefato que depende dele — a raiz do projeto com um tipo de artefato PROJETO e mais seis outras dependências das quais depende.

Em seguida, duplique clique em io.netty:netty-code https://github.com/netty/netty/tree/4.1/codec-httpc-http para mostrar o próximo nível de dependências. Observe que, além das relações (dependências) do nó selecionado, podem ser mostradas relações adicionais para nós já presentes no gráfico.

Identificar Descompasso de Versão

A saída de dependência do Gradle indica onde a versão especificada não foi a versão resolvida pelo Gradle. As propriedades na dependência (relação) podem ser usadas em uma consulta Neo4J, restringindo as relações mostradas e os artefatos anexados (nós).

Cypher

 

MATCH (a:Artifact)-[d:DEPENDS_ON]->(b:Artifact) WHERE d.specifiedVersion<>d.resolvedVersion RETURN a,b,d

O Neo4J pode retornar resultados em formato tabular para facilitar a revisão, se necessário.

Cypher

 

MATCH (a:Artifact)-[d:DEPENDS_ON]->(b:Artifact) WHERE d.specifiedVersion<>d.resolvedVersion RETURNa.name AS source, b.name AS dependency, d.specifiedVersion AS specified, d.resolvedVersion AS resolved

Informações Adicionais

mappings.out

O arquivo mappings.out permite que você personalize o tipo de artefato atribuído a um nó com base no groupId do artefato, geralmente para identificar especificamente os artefatos criados pela sua organização.

Diretório de Entrada

O argumento de linha de comando para DependencyLoader pode ser um diretório contendo múltiplas árvores de dependência Gradle carregadas no mesmo banco de dados Neo4J. Isso ajuda a compreender as dependências de projetos relacionados com arquivos build.gradle separados.

Restritas e Omitidas

O Gradle identifica certas dependências como Restritas e Omitidas. Atualmente, essas não são carregadas, mas seria fácil incluí-las, provavelmente criando propriedades adicionais para as relações.

Source:
https://dzone.com/articles/understanding-dependenciesvisually