Skip to main content

Analiza dependencias y mide la sanidad de proyectos Android multi-módulo.

Project description

📊 Android Gradle Dependency Analyzer

Herramientas para analizar, visualizar y medir la salud de las dependencias entre módulos en proyectos Android multi-módulo.

Python Tests Release License: MIT PlantUML

Vista previa del analizador en consola

⚡ Quick start

# Recomendado · instalación global con pipx desde PyPI
pipx install android-gradle-analyzer

# Con parser AST para .gradle.kts (maneja multilínea y comentarios correctamente)
pipx install "android-gradle-analyzer[kts]"

# Versión específica
pipx install android-gradle-analyzer==1.3.0

# Última versión de desarrollo (sin esperar al release en PyPI)
pipx install git+https://github.com/pfranccino/android-gradle-analyzer.git

# Ver versión instalada
gradle-analyzer-menu --version

# 1 · Dependencias internas de un módulo
gradle-analyzer /ruta/a/tu/proyecto/payments

# También puedes usar . si ya estás en el módulo
cd /ruta/a/tu/proyecto/payments
gradle-analyzer .

# 2 · Quién llama a un módulo desde fuera
gradle-externals /ruta/a/tu/proyecto payments

# 3 · Score de sanidad (Ca/Ce/I, ciclos, anti-patrones)
gradle-sanity /ruta/a/tu/proyecto/payments
gradle-sanity .   # equivalente si ya estás en el módulo

# 4 · Impacto de cambios — qué se rompe si cambio X
gradle-impact /ruta/a/tu/proyecto payments:common
Alternativa · clonar el repo (para desarrollo o contribuir)
git clone https://github.com/pfranccino/android-gradle-analyzer.git
cd android-gradle-analyzer
pip install -e ".[kts,yaml]"
gradle-analyzer /ruta/a/tu/proyecto/payments

🎛️ Modo interactivo

Un único comando con dashboard, autodetección de módulos, navegación con teclado y export a HTML / Markdown / ZIP.

gradle-analyzer-menu
Vista previa del menú interactivo

Modo no-interactivo (CI/scripts):

gradle-analyzer-menu --quick sanity /ruta/proyecto
gradle-analyzer-menu --version

✨ Qué hace

🔍 Dependencias internas

Lee build.gradle / build.gradle.kts recursivamente y dibuja cómo dependen los módulos entre sí.

Salida · PlantUML · Mermaid · reporte de texto

🌐 Llamadas externas

Detecta qué módulos de fuera de tu feature lo están consumiendo. Útil para refactors seguros.

Salida · PlantUML · Mermaid · reporte de texto

🏥 Sanidad arquitectónica

Métricas Ca/Ce/I, detección de ciclos, violaciones SDP y score 0–100 con explicación.

Salida · reporte detallado · JSON

💥 Impacto de cambios

Dado un módulo, muestra qué otros módulos se romperían si cambia (BFS sobre el grafo invertido de dependencias).

Salida · PlantUML · Mermaid · reporte de texto

Características destacadas

  • Detección recursiva sin importar la profundidad de los módulos
  • 📋 settings.gradle.kts / settings.gradle — si existe, se usa como fuente de verdad para los módulos
  • 🎯 Type-safe project accessors (projects.foo.barBaz, Gradle 7+) soportados junto al formato clásico project(":foo:bar")
  • 🌳 Parser AST para .kts (opcional) — usa tree-sitter-kotlin para manejar dependencias multilínea y comentados correctamente
  • 🎨 Colores por tipo (common, gateway, features)
  • ⚠️ Detección automática de ciclos
  • 🍃 Lógica compartida mal ubicada — detecta una hoja (feature/app) de la que otros dependen, sin depender de nombres ni plugins
  • 🔭 Scopes soportados: implementation, api, kapt, compileOnly, testImplementation, y más
  • ⚙️ Configuración personalizable via analyzer_config.json
  • 🤫 --quiet en todos los CLIs para suprimir output de progreso
  • 📄 --json en todos los CLIs para salida JSON (ideal para CI/CD)
  • 🚦 --fail-on-cycle / --fail-on-score-below N en gradle-sanity para integración con CI

📖 Uso

1. Analizar dependencias internas
gradle-analyzer <ruta_al_modulo>
Flag Descripción Default
--format plantuml|mermaid|all Formato de salida all
--output-dir <dir> Directorio de salida diagrams
--exclude <module> Excluir un módulo (puede repetirse)
--config <path> Ruta a analyzer_config.json personalizado auto-detect
--quiet Suprime output de progreso off
--json Salida JSON a stdout off

Ejemplos:

# Solo Mermaid
gradle-analyzer /ruta/proyecto/payments --format mermaid

# Excluir módulos de test
gradle-analyzer /ruta/proyecto/payments --exclude test-utils --exclude mocks

# Output personalizado
gradle-analyzer /ruta/proyecto/payments --output-dir docs/diagrams

Genera:

  • diagrams/gradle-dependencies.puml
  • diagrams/gradle-dependencies.mmd
  • diagrams/gradle-report.txt
2. Analizar llamadas externas
gradle-externals <ruta_proyecto> <nombre_modulo>
Flag Descripción Default
--format plantuml|mermaid|all Formato de salida all
--output-dir <dir> Directorio de salida external-calls
--config <path> Config personalizado auto-detect
--quiet Suprime output de progreso off
--json Salida JSON a stdout off

Genera:

  • external-calls/<modulo>-external-calls.puml
  • external-calls/<modulo>-external-calls.mmd
  • external-calls/<modulo>-external-report.txt
3. Analizar sanidad arquitectónica
gradle-sanity <ruta_al_modulo>
Flag Descripción Default
--output-dir <dir> Directorio de salida sanity
--config <path> Config personalizado auto-detect
--quiet Suprime output de progreso off
--json Salida JSON a stdout off
--fail-on-cycle exit 1 si se detecta algún ciclo off
--fail-on-score-below N exit 1 si el score es menor a N off

Ejemplo de reporte:

MÉTRICAS POR MÓDULO

  Módulo          Ca   Ce     I    Estado
  ──────────────  ───  ───  ────   ──────────────────────────
  common           3    0   0.00   🟢 Estable
  gateway          1    1   0.50   🟡 Moderadamente estable
  home             1    2   0.67   🟠 Moderadamente inestable
  ui               0    2   1.00   🔴 Inestable (módulo hoja)

VIOLACIONES DETECTADAS

🔴 CICLOS (0)            — sin ciclos ✅
🟠 VIOLACIONES SDP (0)   — sin violaciones ✅
🟡 API INNECESARIO (1)   — ui usa api pero Ca=0
🔵 VERSIONES HARD. (2)   — gateway, home

PUNTUACIÓN FINAL: 91 / 100  🟢 Excelente

¿Qué mide cada columna?

Columna Significado
Ca Cuántos módulos dependen de éste (fan-in). Alto en common, core.
Ce De cuántos depende éste (fan-out). Alto en app o features de alto nivel.
I Ce / (Ce + Ca). 0 = muy estable, 1 = muy inestable.

Pirámide de dependencias: los módulos de I alto (features, app) están "arriba" — usan a otros pero nadie debería depender de ellos (son las hojas del árbol). Los de I bajo (core, common) están "abajo": todos dependen de ellos. Las flechas deben apuntar de arriba (inestable) hacia abajo (estable).

¿Qué detecta?

Problema Penalización default Descripción
Ciclo −20 pts A depende de B y B depende de A
Violación SDP −10 pts Estable depende de inestable
api innecesario −5 pts Usa api pero Ca=0
Fan-out excesivo −3 pts Ce supera el umbral (default: 5)
Versión hardcodeada −2 pts "lib:x:1.2.3" en vez de Version Catalog
Lógica compartida mal ubicada informativo (configurable) Una hoja (feature o app, I alto) de la que otros dependen (Ca sobre el límite)

Los pesos son configurables en analyzer_config.json bajo sanity_weights y coupling_limits.

4. Analizar impacto de cambios
gradle-impact <ruta_proyecto> <modulo>

Responde: "¿qué módulos se rompen si cambio este módulo?"

Construye el grafo invertido de dependencias y hace BFS desde el módulo target, asignando un nivel a cada módulo impactado (1 = directo, 2 = transitivo, etc.).

Flag Descripción Default
--format plantuml|mermaid|all Formato de salida all
--output-dir <dir> Directorio de salida impact
--config <path> Config personalizado auto-detect
--quiet Suprime output de progreso off
--json Salida JSON a stdout off

Ejemplo de reporte:

IMPACTO DE CAMBIOS EN: PAYMENTS:COMMON

Proyecto      : /ruta/proyecto
Módulo        : payments:common
Total módulos : 12

  Nivel 1 — dependientes directos (2):
    • payments:home
    • payments:checkout

  Nivel 2 — dependientes transitivos (2):
    • payments:summary
    • app

  🔥 Impacto total: 4 módulos (33% del proyecto)
     Cambiar payments:common requiere verificar 4 módulo(s).

Genera:

  • impact/<modulo>-impact.puml
  • impact/<modulo>-impact.mmd
  • impact/<modulo>-impact-report.txt
5. Generar imágenes desde PlantUML
# PNG
plantuml diagrams/gradle-dependencies.puml
plantuml diagrams/*.puml external-calls/*.puml

# SVG (escalable)
plantuml -tsvg diagrams/gradle-dependencies.puml

Instalar PlantUML:

brew install plantuml          # macOS
sudo apt install plantuml      # Ubuntu/Debian
choco install plantuml         # Windows

🎨 Configuración personalizada

Sin config, el analizador usa defaults genéricos para cualquier proyecto Android. La configuración es opt-in: la herramienta funciona sin ningún archivo de config.

Si quieres personalizar colores, íconos y estilos, crea un analyzer_config.json en el directorio desde donde ejecutas el comando:

Instalado con pipx:

# macOS / Linux
curl -o analyzer_config.json \
  https://raw.githubusercontent.com/pfranccino/android-gradle-analyzer/main/analyzer_config.example.json

# Windows (PowerShell)
Invoke-WebRequest `
  -Uri "https://raw.githubusercontent.com/pfranccino/android-gradle-analyzer/main/analyzer_config.example.json" `
  -OutFile "analyzer_config.json"

Con el repo clonado:

cp analyzer_config.example.json analyzer_config.json
Ejemplo de configuración
{
  "icons": {
    "payment": "💸",
    "cart":    "🛒",
    "auth":    "🔐"
  },
  "colors": {
    "cycle": "#FF0000"
  }
}

Solo incluye los campos que quieres cambiar — el resto usa defaults.

Orden de búsqueda:

  1. --config <path> explícito
  2. analyzer_config.json en el directorio actual (donde ejecutas el comando)
  3. Defaults internos

Tip: si siempre analizas el mismo proyecto, pon el analyzer_config.json en la raíz de ese proyecto y ejecuta el comando desde ahí. Si analizas varios proyectos, usa --config ~/mi-config.json.

Detector de "lógica compartida mal ubicada" (coupling_limits)

Detecta una hoja (feature o app: I alto, en la punta del grafo) de la que otros módulos dependen — señal de que código común quedó atrapado arriba en vez de bajar a core/shared. No depende de nombres ni de plugins: se basa en las métricas que el tool ya calcula. core/common quedan excluidos solos por tener I bajo.

Funciona sin configuración con estos defaults. Es advisory (penalty: 0 → solo aparece en el reporte, no afecta el score); súbelo para activar el gate en CI.

{
  "coupling_limits": {
    "leaf_instability": 0.70,
    "leaf_max_ca":      1,
    "leaf_penalty":     0,
    "app_max_ca":       0,
    "app_penalty":      0
  },
  "coupling_overrides": { "legacy:util": "ignore" }
}

El punto de entrada se detecta por el plugin com.android.application; coupling_overrides permite forzar ("app"/"leaf") o excluir ("ignore") módulos puntuales.


⚙️ Configuración por proyecto (analyzer.yml)

Si creas un archivo analyzer.yml en la raíz del proyecto Android analizado, las herramientas lo leerán automáticamente y usarán sus valores como defaults. Los flags de CLI siempre tienen prioridad sobre el yml.

sanity:
  fail_on_cycle: true
  fail_on_score_below: 70
  output_dir: reports/sanity

impact:
  default_module: app
  output_dir: reports/impact

analyzer:
  output_dir: reports/diagrams
  format: mermaid

externals:
  output_dir: reports/external-calls

Campos por sección:

Sección Campo CLI equivalente
sanity fail_on_cycle --fail-on-cycle
sanity fail_on_score_below --fail-on-score-below N
sanity output_dir --output-dir
impact default_module segundo argumento posicional
impact output_dir --output-dir
analyzer output_dir --output-dir
analyzer format --format
externals output_dir --output-dir

Reglas:

  • El yml se busca en la ruta que pasas como primer argumento, no en el CWD.
  • Los flags de CLI siempre ganan sobre el yml.
  • Requiere pyyaml (instalación opcional: pip install android-gradle-analyzer[yaml]). Si no está instalado, el yml se ignora sin error.

🚦 Integración CI/CD

gradle-sanity tiene flags para fallar la build si se detectan problemas:

# Falla si hay ciclos
gradle-sanity /ruta/proyecto --fail-on-cycle --quiet

# Falla si el score cae por debajo de 70
gradle-sanity /ruta/proyecto --fail-on-score-below 70 --quiet

# Salida JSON para parsear en el pipeline
gradle-sanity /ruta/proyecto --json > sanity-report.json

Ver el ejemplo completo en examples/github-actions-dependency-health.yml.


🔭 Scopes soportados

Scope Categoría visual
api, implementation, compileOnly Flecha sólida (compile)
kapt, annotationProcessor Flecha punteada (build)
testImplementation, androidTestImplementation, debugImplementation, releaseImplementation, runtimeOnly, testRuntimeOnly Flecha punteada con label (test/debug)

📋 Más info

Estructura del proyecto
android-gradle-analyzer/
├── README.md
├── CHANGELOG.md
├── LICENSE
├── CONTRIBUTING.md
├── pyproject.toml               ← fuente única de dependencias (pipx · pip install -e .)
├── menu.py                      ← wrapper: python3 menu.py
├── menu/                        ← paquete del menú interactivo
│   ├── actions.py
│   ├── branding.py
│   ├── exporter.py
│   ├── prompts.py
│   ├── state.py
│   └── ui.py
├── analyzer_utils.py            ← utilidades compartidas
├── analyzer_config.example.json ← config de ejemplo
├── gradle_analyzer.py           ← script 1: dependencias internas
├── external_callers.py          ← script 2: llamadas externas
├── gradle_sanity.py             ← script 3: sanidad + score
├── gradle_impact.py             ← script 4: impacto de cambios
├── examples/
│   └── github-actions-dependency-health.yml
└── scripts/
    └── bump_version.py          ← sincroniza versión y guía el release
Cómo funciona internamente

Detección de módulos

  1. Si existe settings.gradle.kts o settings.gradle en la raíz, se usa como fuente de verdad (extrae los include(":module")).
  2. Sino, rglob() busca todos los build.gradle* como fallback.
  3. Paths → nombres: payments/homepayments:home.

Extracción de dependencias

  1. Lee cada build.gradle / build.gradle.kts.
  2. Para .kts: usa tree-sitter (si está instalado) para obtener un AST real — maneja multilínea, comentarios y parámetros nombrados. Si no está disponible, cae en regex.
  3. Para .gradle: preprocesa el contenido (elimina comentarios // y /* */, colapsa declaraciones multilínea) y aplica regex.
  4. Soporta formato clásico (project(":foo:bar")) y type-safe accessors (projects.foo.barBaz).
  5. Matching exacto contra la lista de módulos conocidos — sin heurísticas de prefijo.

Generación de diagramas

  1. Clasifica módulos por tipo (common, gateway, features).
  2. Aplica colores según clasificación.
  3. Genera PlantUML/Mermaid agrupados por categoría visual.
  4. Marca en rojo los módulos involucrados en ciclos.
Ajustar espaciado de diagramas

En gradle_analyzer.py, función generate_plantuml():

"skinparam nodesep 150",    # Horizontal
"skinparam ranksep 150",    # Vertical
"skinparam padding 30",     # Interno
Estilo nodesep / ranksep / padding
Compacto 60 / 60 / 10
Balanceado 100 / 100 / 20
Espacioso 150 / 150 / 30
Troubleshooting

"No se encontró gradle para: [módulo]" El módulo no tiene build.gradle ni build.gradle.kts. Verifica el path.

Diagrama apretado Sube los valores de espaciado (ver sección anterior).

No detecta algunas dependencias Para archivos .kts, instala el parser AST: pip install tree-sitter tree-sitter-kotlin. Para .gradle, el preprocesador ya maneja multilínea y comentarios. Si el problema persiste, revisa los patrones en analyzer_utils.py (constante DEPENDENCY_SCOPES).

El menú no arranca tras clonar Instala el paquete en modo editable: pip install -e .

Referencias del análisis de sanidad

Las métricas de gradle_sanity.py están basadas en fuentes verificadas:

  • Low coupling / High cohesion en AndroidGuide to Android app modularization · Common modularization patterns
  • Métricas Ca, Ce, I y principios de acoplamiento de paquetes — formulados por Robert C. Martin (Uncle Bob) en Agile Software Development: Principles, Patterns, and Practices (2002), capítulo sobre Package Design Principles. Los tres principios de acoplamiento son:
    • ADP (Acyclic Dependencies Principle) — el grafo de dependencias entre módulos no debe tener ciclos.
    • SDP (Stable Dependencies Principle) — un módulo solo debe depender de módulos más estables que él mismo.
    • SAP (Stable Abstractions Principle) — los módulos más estables deben ser los más abstractos.
    • La fórmula I = Ce / (Ce + Ca) cuantifica estabilidad: 0 = imposible de desestabilizar, 1 = completamente inestable.
    • Ver también: Efferent coupling — Wikipedia · Software Coupling Metrics — entrofi.net
  • Aplicación a módulos Android — Martin definió estas métricas para packages en Java/C++. En proyectos Android multi-módulo, cada módulo Gradle cumple el mismo rol que un package en su modelo (unidad de compilación y encapsulamiento), por lo que la semántica es directamente trasladable.
  • DAGPdependency-analysis-gradle-plugin. Inspiró la detección de scopes mal declarados.
  • Detección de ciclos — DFS con coloreo de nodos (blanco/gris/negro).

El score 0–100 no es un estándar externo. Es orientativo con pesos razonables como punto de partida, ajustables en analyzer_config.json bajo sanity_weights. El umbral SDP de 0.3 tampoco es parte del principio original — es un parámetro configurable.


🤝 Contribuir

Las contribuciones son bienvenidas. Forkea, crea una rama, commitea y abre un PR. Ver CONTRIBUTING.md para detalles.

📄 Licencia

MIT — ver LICENSE.


made with care · pfranccino.dev

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

android_gradle_analyzer-1.3.1.tar.gz (67.2 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

android_gradle_analyzer-1.3.1-py3-none-any.whl (55.0 kB view details)

Uploaded Python 3

File details

Details for the file android_gradle_analyzer-1.3.1.tar.gz.

File metadata

  • Download URL: android_gradle_analyzer-1.3.1.tar.gz
  • Upload date:
  • Size: 67.2 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for android_gradle_analyzer-1.3.1.tar.gz
Algorithm Hash digest
SHA256 7a6e5bd35c60cb7dd5e7c9fbcbed1b9ba6b667c220456656a31b811615898c7b
MD5 7a8a73b7363c225d99e313527a5e6b0a
BLAKE2b-256 0987f504cc1d97233502c0dba1f7beb59f1439094e65ef7226c52cc616f8b3f1

See more details on using hashes here.

Provenance

The following attestation bundles were made for android_gradle_analyzer-1.3.1.tar.gz:

Publisher: release.yml on pfranccino/android-gradle-analyzer

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file android_gradle_analyzer-1.3.1-py3-none-any.whl.

File metadata

File hashes

Hashes for android_gradle_analyzer-1.3.1-py3-none-any.whl
Algorithm Hash digest
SHA256 ca33e6d8b24e1dd18266f4eb4b9b9fe0cb8f2ee7f9f9197feadfc8c13148e794
MD5 8666b6cbb72df21b4acc42e8579b2b9f
BLAKE2b-256 16d554496c4d16cd681304e90c8def4df63ca80992e929cd074d885a2d4c9d97

See more details on using hashes here.

Provenance

The following attestation bundles were made for android_gradle_analyzer-1.3.1-py3-none-any.whl:

Publisher: release.yml on pfranccino/android-gradle-analyzer

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page