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:
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.
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:
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:
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:
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 nó 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.
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).
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.
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