Entendiendo las Dependencias… ¡Visualmente!

Levanten la mano cuántos de nosotros realmente entendemos cómo su herramienta de automatización de compilación construye su árbol de dependencias? Ahora, baje la mano si entiende porque trabaja en la construcción de herramientas de automatización. ¡Pensé que así era!

Una responsabilidad frustrante de los ingenieros de software es comprender las dependencias de su proyecto: cuáles dependencias transitivas se trajeron y por quién; por qué se usa v1.3.1 cuando se declaró v1.2.10; qué resultó cuando las dependencias transitivas cambiaron; cómo ocurrieron múltiples versiones del mismo artefacto?

Cada ingeniero de software ha canalizado un árbol de dependencias en un archivo de texto, ha buscado artefactos específicos y luego ha trabajado de regreso para identificar su origen. Para cualquier proyecto que no sea trivial, crear un mapa mental de las dependencias es extremadamente difícil, si no es imposible.

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 no es un tutorial sobre bases de datos de grafos, ni requiere este tutorial un trasfondo en bases de datos de grafos. Si está interesado, Neo4J tiene tutoriales y documentos blancos para ayudarlo a comenzar.

Configurar el entorno

Instalar Java

Se requiere Java 11 o posterior. Si aún no está disponible, instale su favorito OpenJDK sabor.

Instalar Neo4J

El tutorial requiere una base de datos Neo4J en la que se cargue la información de dependencia, preferiblemente no compartida, ya que el cargador purga la base de datos antes de cada ejecución. ¡Se lo han advertido!

Neo4J proporciona áreas de pruebas personales, ideales para proyectos a corto plazo como este tutorial.

Como alternativa, instale Neo4J localmente en su escritorio o portátil. Homebrew simplifica las instalaciones en MacOS:

Shell

 

brew install neo4j && brew services start neo4j

Antes de continuar, confirme el acceso a su base de datos Neo4J utilizando el navegador, usando ya sea el enlace y credenciales para la caja de arena Neo4J o localmente en http://localhost:7474. Las credenciales predeterminadas para una instalación local son neo4j/neo4j; tras un inicio de sesión exitoso, se le obligará a cambiar la contraseña.

Clonar Repositorios

El repositorio neo4j-gradle-dependencies contiene lo necesario para cargar las dependencias en Neo4J. Este tutorial generará un grafo de dependencias para spring-boot. Debes clonar estos dos repositorios.

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: no se requiere Gradle local ya que ambos repositorios utilizan el Gradle Wrapper, que descarga todos los componentes necesarios la primera vez que se usa el wrapper.

Generar Dependencias

DependencyLoader recibe como entrada el árbol de dependencias generado por Gradle. Aunque se pueden cargar múltiples configuraciones al mismo tiempo, como compileClasspath, runtimeClasspath, testCompileClasspath, testRuntimeClasspath, comenzar con una sola configuración es más sencillo de navegar, especialmente para un tutorial.

Para generar dependencias para todas las configuraciones: 

  • gradle dependencies
  • ./gradlew dependencies

Para generar dependencias para una sola configuración

  • gradle dependencies --configuration <configuration>
  • ./gradlew dependencies --configuration <configuration>

Generar Dependencias de Spring Boot

Este tutorial crea un gráfico de dependencias en Neo4J utilizando las dependencias de classpath de compilación de Spring Boot. Desde el directorio donde se clonaron los repositorios, ejecute los siguientes 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

El archivo dependencies.out contiene las dependencias de classpath de compilación para Spring Boot.

Cargar Dependencias

Primero, confirme la URL de conexión y las credenciales de autenticación en DependencyLoader.java y modifíquelas si es necesario.

Ejecute los siguientes comandos para cargar las dependencias de Spring Boot en 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"

Cuando tenga éxito, las líneas de salida de gradle son:

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

Ver Dependencias

Hay múltiples herramientas disponibles para mostrar gráficos de Neo4J, pero la herramienta de navegador incorporada es adecuada para este tutorial.

Mostrar el Árbol Completo

La consulta MATCH(a) RETURN a es el equivalente relacional de SELECT * FROM <tabla>

Ver Detalles de un Artefacto

Cada artefacto encontrado crea un nodo cuyas propiedades identifican el artefacto (groupId/artifactId) y su tipo, mostrado en el panel lateral derecho.

Ver Detalles de una Dependencia

Cada dependencia se crea como una relación cuyas propiedades identifican los detalles específicos de la dependencia: configuración, versión especificada y configuración. La dependencia seleccionada a continuación muestra que spring-security:spring-web depende de io.micrometer:micrometer-observation, pero la versión especificada 1.10.7 de spring-web se resolvió como versión 1.11.0.

Navegar Dependencias

Neo4J te permite explorar el nodo gráfico nodo por nodo, permitiéndote expandir manualmente el nodo gráfico nodo por nodo, proporcionando una manera de explorar áreas específicas del árbol de dependencias.

Supongamos que deseas comprender las dependencias para el artefacto io.projectreactor.netty:reactor-netty-http. Primero, consultaremos a Neo4J para ese nodo específico.

Cypher

 

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

Al hacer doble clic en el nodo se muestran sus nodos vecinos: los artefactos de los que depende y los artefactos que dependen de él.

Esta gráfica expandida muestra un artefacto que depende de él: la raíz del proyecto con un tipo de artefacto PROJECT y otras seis dependencias de las que depende.

A continuación, haga doble clic en io.netty:netty-code https://github.com/netty/netty/tree/4.1/codec-httpc-http para mostrar el siguiente nivel de dependencias. Tenga en cuenta que además de las relaciones (dependencias) del nodo seleccionado, pueden mostrarse relaciones adicionales para nodos ya en el gráfico.

Identificar Desadaptación de Versión

La salida de dependencia de Gradle indica dónde la versión especificada no era la versión resuelta por Gradle. Las propiedades en la dependencia (relación) pueden ser utilizadas en una consulta Neo4J, restringiendo las relaciones mostradas y los artefactos adjuntos (nodos).

Cypher

 

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

Neo4J puede devolver resultados en formato tabular para facilitar la revisión, si es necesario.

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

Información Adicional

mappings.out

El archivo mappings.out te permite personalizar el tipo de artefacto asignado a un nodo basado en el groupId del artefacto, comúnmente para identificar específicamente los artefactos creados por tu organización.

Directorio de Entrada

El argumento de línea de comandos para DependencyLoader puede ser un directorio que contiene múltiples Gradle árboles de dependencia cargados en la misma base de datos Neo4J. Esto ayuda a comprender las dependencias de proyectos relacionados con archivos build.gradle separados.

Constreñidas y Omitidas

Gradle identifica ciertas dependencias como Constreñidas y Omitidas. Actualmente, esas no se cargan pero sería fácil incluirlas, probablemente creando propiedades adicionales para las relaciones.

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