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.
⚡ 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
Modo no-interactivo (CI/scripts):
gradle-analyzer-menu --quick sanity /ruta/proyecto
gradle-analyzer-menu --version
✨ Qué hace
🔍 Dependencias internasLee Salida · PlantUML · Mermaid · reporte de texto |
🌐 Llamadas externasDetecta qué módulos de fuera de tu feature lo están consumiendo. Útil para refactors seguros. Salida · PlantUML · Mermaid · reporte de texto |
🏥 Sanidad arquitectónicaMétricas Ca/Ce/I, detección de ciclos, violaciones SDP y score 0–100 con explicación. Salida · reporte detallado · JSON |
💥 Impacto de cambiosDado 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ásicoproject(":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 - 🤫
--quieten todos los CLIs para suprimir output de progreso - 📄
--jsonen todos los CLIs para salida JSON (ideal para CI/CD) - 🚦
--fail-on-cycle/--fail-on-score-below Nengradle-sanitypara 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.pumldiagrams/gradle-dependencies.mmddiagrams/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.pumlexternal-calls/<modulo>-external-calls.mmdexternal-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.pumlimpact/<modulo>-impact.mmdimpact/<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:
--config <path>explícitoanalyzer_config.jsonen el directorio actual (donde ejecutas el comando)- Defaults internos
Tip: si siempre analizas el mismo proyecto, pon el
analyzer_config.jsonen 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
- Si existe
settings.gradle.ktsosettings.gradleen la raíz, se usa como fuente de verdad (extrae losinclude(":module")). - Sino,
rglob()busca todos losbuild.gradle*como fallback. - Paths → nombres:
payments/home→payments:home.
Extracción de dependencias
- Lee cada
build.gradle/build.gradle.kts. - 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. - Para
.gradle: preprocesa el contenido (elimina comentarios//y/* */, colapsa declaraciones multilínea) y aplica regex. - Soporta formato clásico (
project(":foo:bar")) y type-safe accessors (projects.foo.barBaz). - Matching exacto contra la lista de módulos conocidos — sin heurísticas de prefijo.
Generación de diagramas
- Clasifica módulos por tipo (common, gateway, features).
- Aplica colores según clasificación.
- Genera PlantUML/Mermaid agrupados por categoría visual.
- 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 Android — Guide 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.
- DAGP — dependency-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.jsonbajosanity_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
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file android_gradle_analyzer-1.3.2.tar.gz.
File metadata
- Download URL: android_gradle_analyzer-1.3.2.tar.gz
- Upload date:
- Size: 68.1 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
e0b386ec1b7bfa50ab5b5be37269d425f98e9a0d85ce6234bca47e14ae97df64
|
|
| MD5 |
1c374e18eb806b97a1c30cb4b45c0ed0
|
|
| BLAKE2b-256 |
a96588cac440abc1bc9a4f96e5748e176c7df010d0eb71c700c9b1e82a26d50d
|
Provenance
The following attestation bundles were made for android_gradle_analyzer-1.3.2.tar.gz:
Publisher:
release.yml on pfranccino/android-gradle-analyzer
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
android_gradle_analyzer-1.3.2.tar.gz -
Subject digest:
e0b386ec1b7bfa50ab5b5be37269d425f98e9a0d85ce6234bca47e14ae97df64 - Sigstore transparency entry: 1742243366
- Sigstore integration time:
-
Permalink:
pfranccino/android-gradle-analyzer@df01a2ed94a0185f4fd539b01546ce48b5ba3a7d -
Branch / Tag:
refs/heads/main - Owner: https://github.com/pfranccino
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@df01a2ed94a0185f4fd539b01546ce48b5ba3a7d -
Trigger Event:
push
-
Statement type:
File details
Details for the file android_gradle_analyzer-1.3.2-py3-none-any.whl.
File metadata
- Download URL: android_gradle_analyzer-1.3.2-py3-none-any.whl
- Upload date:
- Size: 55.1 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
4e7d6088bddacb5939e34ba935a28b24fe2d6558306dede255da8d4d829fc1db
|
|
| MD5 |
aa0b32b0007f82ba622465a616092866
|
|
| BLAKE2b-256 |
57a9ef63a04e6b74099cdec05ad1320bda6e044b6975fb3d9861b3890ee1ac06
|
Provenance
The following attestation bundles were made for android_gradle_analyzer-1.3.2-py3-none-any.whl:
Publisher:
release.yml on pfranccino/android-gradle-analyzer
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
android_gradle_analyzer-1.3.2-py3-none-any.whl -
Subject digest:
4e7d6088bddacb5939e34ba935a28b24fe2d6558306dede255da8d4d829fc1db - Sigstore transparency entry: 1742243474
- Sigstore integration time:
-
Permalink:
pfranccino/android-gradle-analyzer@df01a2ed94a0185f4fd539b01546ce48b5ba3a7d -
Branch / Tag:
refs/heads/main - Owner: https://github.com/pfranccino
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@df01a2ed94a0185f4fd539b01546ce48b5ba3a7d -
Trigger Event:
push
-
Statement type: