android-gradle-analyzer 1.3.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (92) hide show
  1. android_gradle_analyzer-1.3.1/.github/workflows/release.yml +132 -0
  2. android_gradle_analyzer-1.3.1/.gitignore +63 -0
  3. android_gradle_analyzer-1.3.1/CHANGELOG.md +162 -0
  4. android_gradle_analyzer-1.3.1/CONTRIBUTING.md +153 -0
  5. android_gradle_analyzer-1.3.1/EXAMPLES.md +290 -0
  6. android_gradle_analyzer-1.3.1/LICENSE +21 -0
  7. android_gradle_analyzer-1.3.1/PKG-INFO +668 -0
  8. android_gradle_analyzer-1.3.1/README.md +618 -0
  9. android_gradle_analyzer-1.3.1/analyzer_config.example.json +80 -0
  10. android_gradle_analyzer-1.3.1/analyzer_utils.py +687 -0
  11. android_gradle_analyzer-1.3.1/docs/preview-menu.svg +62 -0
  12. android_gradle_analyzer-1.3.1/docs/preview.svg +58 -0
  13. android_gradle_analyzer-1.3.1/examples/github-actions-dependency-health.yml +93 -0
  14. android_gradle_analyzer-1.3.1/external_callers.py +317 -0
  15. android_gradle_analyzer-1.3.1/gradle_analyzer.py +585 -0
  16. android_gradle_analyzer-1.3.1/gradle_impact.py +281 -0
  17. android_gradle_analyzer-1.3.1/gradle_sanity.py +622 -0
  18. android_gradle_analyzer-1.3.1/impact/core-impact-report.txt +13 -0
  19. android_gradle_analyzer-1.3.1/impact/core-impact.mmd +13 -0
  20. android_gradle_analyzer-1.3.1/impact/core-impact.puml +20 -0
  21. android_gradle_analyzer-1.3.1/menu/__init__.py +447 -0
  22. android_gradle_analyzer-1.3.1/menu/actions.py +208 -0
  23. android_gradle_analyzer-1.3.1/menu/branding.py +15 -0
  24. android_gradle_analyzer-1.3.1/menu/exporter.py +233 -0
  25. android_gradle_analyzer-1.3.1/menu/prompts.py +307 -0
  26. android_gradle_analyzer-1.3.1/menu/state.py +82 -0
  27. android_gradle_analyzer-1.3.1/menu/ui.py +198 -0
  28. android_gradle_analyzer-1.3.1/menu.py +9 -0
  29. android_gradle_analyzer-1.3.1/pyproject.toml +61 -0
  30. android_gradle_analyzer-1.3.1/scripts/bump_version.py +60 -0
  31. android_gradle_analyzer-1.3.1/scripts/extract_changelog.py +45 -0
  32. android_gradle_analyzer-1.3.1/tests/conftest.py +1 -0
  33. android_gradle_analyzer-1.3.1/tests/fixtures/accessors/app/build.gradle.kts +12 -0
  34. android_gradle_analyzer-1.3.1/tests/fixtures/accessors/core/network_api/build.gradle.kts +6 -0
  35. android_gradle_analyzer-1.3.1/tests/fixtures/accessors/feature/payments-common/build.gradle.kts +6 -0
  36. android_gradle_analyzer-1.3.1/tests/fixtures/accessors/legacy/plain-lib/build.gradle.kts +6 -0
  37. android_gradle_analyzer-1.3.1/tests/fixtures/accessors/settings.gradle.kts +6 -0
  38. android_gradle_analyzer-1.3.1/tests/fixtures/ambiguous/payments/build.gradle +7 -0
  39. android_gradle_analyzer-1.3.1/tests/fixtures/ambiguous/payments/common/build.gradle +6 -0
  40. android_gradle_analyzer-1.3.1/tests/fixtures/ambiguous/quick-payments/build.gradle +7 -0
  41. android_gradle_analyzer-1.3.1/tests/fixtures/commented_version/module/build.gradle +8 -0
  42. android_gradle_analyzer-1.3.1/tests/fixtures/cycle/a/build.gradle +7 -0
  43. android_gradle_analyzer-1.3.1/tests/fixtures/cycle/b/build.gradle +7 -0
  44. android_gradle_analyzer-1.3.1/tests/fixtures/external/app/build.gradle +7 -0
  45. android_gradle_analyzer-1.3.1/tests/fixtures/external/payments/build.gradle +6 -0
  46. android_gradle_analyzer-1.3.1/tests/fixtures/external/payments/gateway/build.gradle +6 -0
  47. android_gradle_analyzer-1.3.1/tests/fixtures/external/payments-extra/build.gradle +7 -0
  48. android_gradle_analyzer-1.3.1/tests/fixtures/external_ambiguous/caller/build.gradle.kts +10 -0
  49. android_gradle_analyzer-1.3.1/tests/fixtures/external_ambiguous/caller-using-accessor/build.gradle.kts +7 -0
  50. android_gradle_analyzer-1.3.1/tests/fixtures/external_ambiguous/common/build.gradle.kts +3 -0
  51. android_gradle_analyzer-1.3.1/tests/fixtures/external_ambiguous/settings.gradle.kts +7 -0
  52. android_gradle_analyzer-1.3.1/tests/fixtures/external_ambiguous/target/build.gradle.kts +3 -0
  53. android_gradle_analyzer-1.3.1/tests/fixtures/external_ambiguous/target/common/build.gradle.kts +3 -0
  54. android_gradle_analyzer-1.3.1/tests/fixtures/groovy_multiline/also-ignored/build.gradle +3 -0
  55. android_gradle_analyzer-1.3.1/tests/fixtures/groovy_multiline/app/build.gradle +14 -0
  56. android_gradle_analyzer-1.3.1/tests/fixtures/groovy_multiline/core/build.gradle +3 -0
  57. android_gradle_analyzer-1.3.1/tests/fixtures/groovy_multiline/ignored/build.gradle +3 -0
  58. android_gradle_analyzer-1.3.1/tests/fixtures/groovy_multiline/shared/build.gradle +3 -0
  59. android_gradle_analyzer-1.3.1/tests/fixtures/kts_commented/app/build.gradle.kts +9 -0
  60. android_gradle_analyzer-1.3.1/tests/fixtures/kts_commented/core/build.gradle.kts +3 -0
  61. android_gradle_analyzer-1.3.1/tests/fixtures/kts_commented/network/build.gradle.kts +3 -0
  62. android_gradle_analyzer-1.3.1/tests/fixtures/kts_commented/shared/build.gradle.kts +3 -0
  63. android_gradle_analyzer-1.3.1/tests/fixtures/kts_multiline/app/build.gradle.kts +15 -0
  64. android_gradle_analyzer-1.3.1/tests/fixtures/kts_multiline/core/build.gradle.kts +3 -0
  65. android_gradle_analyzer-1.3.1/tests/fixtures/kts_multiline/shared/build.gradle.kts +3 -0
  66. android_gradle_analyzer-1.3.1/tests/fixtures/leaf_coupling/app/build.gradle +9 -0
  67. android_gradle_analyzer-1.3.1/tests/fixtures/leaf_coupling/cart/build.gradle +7 -0
  68. android_gradle_analyzer-1.3.1/tests/fixtures/leaf_coupling/checkout/build.gradle +7 -0
  69. android_gradle_analyzer-1.3.1/tests/fixtures/leaf_coupling/core/build.gradle +6 -0
  70. android_gradle_analyzer-1.3.1/tests/fixtures/leaf_coupling/database/build.gradle +7 -0
  71. android_gradle_analyzer-1.3.1/tests/fixtures/leaf_coupling/legacy/build.gradle +7 -0
  72. android_gradle_analyzer-1.3.1/tests/fixtures/leaf_coupling/model/build.gradle +7 -0
  73. android_gradle_analyzer-1.3.1/tests/fixtures/leaf_coupling/network/build.gradle +7 -0
  74. android_gradle_analyzer-1.3.1/tests/fixtures/leaf_coupling/payments/build.gradle +11 -0
  75. android_gradle_analyzer-1.3.1/tests/fixtures/leaf_coupling/settings.gradle.kts +12 -0
  76. android_gradle_analyzer-1.3.1/tests/fixtures/leaf_coupling/util/build.gradle +7 -0
  77. android_gradle_analyzer-1.3.1/tests/fixtures/orphan/connected/build.gradle +7 -0
  78. android_gradle_analyzer-1.3.1/tests/fixtures/orphan/island/build.gradle +6 -0
  79. android_gradle_analyzer-1.3.1/tests/fixtures/orphan/shared/build.gradle +6 -0
  80. android_gradle_analyzer-1.3.1/tests/fixtures/simple/app/build.gradle +7 -0
  81. android_gradle_analyzer-1.3.1/tests/fixtures/simple/core/build.gradle +7 -0
  82. android_gradle_analyzer-1.3.1/tests/fixtures/with_analyzer_yml/analyzer.yml +12 -0
  83. android_gradle_analyzer-1.3.1/tests/fixtures/with_settings/app/build.gradle +7 -0
  84. android_gradle_analyzer-1.3.1/tests/fixtures/with_settings/core/build.gradle +3 -0
  85. android_gradle_analyzer-1.3.1/tests/fixtures/with_settings/settings.gradle.kts +4 -0
  86. android_gradle_analyzer-1.3.1/tests/fixtures/with_settings_no_colon/settings.gradle +6 -0
  87. android_gradle_analyzer-1.3.1/tests/test_analyzer_utils.py +433 -0
  88. android_gradle_analyzer-1.3.1/tests/test_external_callers.py +94 -0
  89. android_gradle_analyzer-1.3.1/tests/test_gradle_analyzer.py +130 -0
  90. android_gradle_analyzer-1.3.1/tests/test_gradle_impact.py +62 -0
  91. android_gradle_analyzer-1.3.1/tests/test_gradle_sanity.py +101 -0
  92. android_gradle_analyzer-1.3.1/tests/test_project_config.py +69 -0
@@ -0,0 +1,132 @@
1
+ name: Release on main
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ pull_request:
7
+ branches: [main]
8
+
9
+ permissions:
10
+ contents: write
11
+
12
+ jobs:
13
+ test:
14
+ runs-on: ubuntu-latest
15
+ strategy:
16
+ matrix:
17
+ python-version: ["3.10", "3.11", "3.12"]
18
+
19
+ steps:
20
+ - uses: actions/checkout@v6
21
+
22
+ - name: Setup Python ${{ matrix.python-version }}
23
+ uses: actions/setup-python@v6
24
+ with:
25
+ python-version: ${{ matrix.python-version }}
26
+
27
+ - name: Install dependencies
28
+ run: |
29
+ python -m pip install --upgrade pip
30
+ pip install -e ".[yaml]"
31
+ pip install pytest
32
+
33
+ - name: Run tests
34
+ run: pytest -q
35
+
36
+ release:
37
+ needs: test
38
+ if: github.event_name == 'push' && github.ref == 'refs/heads/main'
39
+ runs-on: ubuntu-latest
40
+ outputs:
41
+ version: ${{ steps.version.outputs.version }}
42
+ tag_created: ${{ steps.tag_check.outputs.exists == 'false' }}
43
+
44
+ steps:
45
+ - uses: actions/checkout@v6
46
+ with:
47
+ fetch-depth: 0
48
+
49
+ - name: Setup Python
50
+ uses: actions/setup-python@v6
51
+ with:
52
+ python-version: "3.10"
53
+
54
+ - name: Read version from pyproject.toml
55
+ id: version
56
+ run: |
57
+ VERSION=$(python -c "import re; print(re.search(r'(?m)^version\s*=\s*\"([^\"]+)\"', open('pyproject.toml').read()).group(1))")
58
+ echo "version=$VERSION" >> "$GITHUB_OUTPUT"
59
+ echo "Detected version: $VERSION"
60
+
61
+ - name: Check if tag already exists
62
+ id: tag_check
63
+ run: |
64
+ if git rev-parse "v${{ steps.version.outputs.version }}" >/dev/null 2>&1; then
65
+ echo "exists=true" >> "$GITHUB_OUTPUT"
66
+ echo "Tag v${{ steps.version.outputs.version }} already exists — skipping release"
67
+ else
68
+ echo "exists=false" >> "$GITHUB_OUTPUT"
69
+ echo "Tag v${{ steps.version.outputs.version }} does not exist — will create"
70
+ fi
71
+
72
+ - name: Extract release notes from CHANGELOG
73
+ if: steps.tag_check.outputs.exists == 'false'
74
+ run: |
75
+ if python scripts/extract_changelog.py "${{ steps.version.outputs.version }}" > release_notes.md; then
76
+ echo "Release notes extracted:"
77
+ cat release_notes.md
78
+ else
79
+ echo "Sin entrada en CHANGELOG.md para v${{ steps.version.outputs.version }}." > release_notes.md
80
+ fi
81
+
82
+ - name: Create and push tag
83
+ if: steps.tag_check.outputs.exists == 'false'
84
+ run: |
85
+ git config user.name "github-actions[bot]"
86
+ git config user.email "github-actions[bot]@users.noreply.github.com"
87
+ git tag -a "v${{ steps.version.outputs.version }}" -m "Release v${{ steps.version.outputs.version }}"
88
+ git push origin "v${{ steps.version.outputs.version }}"
89
+
90
+ - name: Create GitHub release
91
+ if: steps.tag_check.outputs.exists == 'false'
92
+ env:
93
+ GH_TOKEN: ${{ github.token }}
94
+ run: |
95
+ gh release create "v${{ steps.version.outputs.version }}" \
96
+ --title "v${{ steps.version.outputs.version }}" \
97
+ --notes-file release_notes.md
98
+
99
+ publish-pypi:
100
+ name: Publicar en PyPI
101
+ needs: release
102
+ # Solo publica cuando el job 'release' creó un tag nuevo (versión nueva).
103
+ # En pushes sin bump de versión, tag_created es 'false' y este job se salta,
104
+ # evitando reintentos de subir una versión ya existente (que PyPI rechaza).
105
+ if: needs.release.outputs.tag_created == 'true'
106
+ runs-on: ubuntu-latest
107
+
108
+ # El environment debe coincidir con el configurado en el Trusted Publisher de PyPI.
109
+ environment:
110
+ name: pypi
111
+ url: https://pypi.org/p/android-gradle-analyzer
112
+
113
+ permissions:
114
+ id-token: write # Obligatorio para Trusted Publishing (OIDC) — sin token ni password
115
+ contents: read
116
+
117
+ steps:
118
+ - uses: actions/checkout@v6
119
+
120
+ - name: Setup Python
121
+ uses: actions/setup-python@v6
122
+ with:
123
+ python-version: "3.10"
124
+
125
+ - name: Instalar dependencias de build
126
+ run: python -m pip install --upgrade pip build
127
+
128
+ - name: Construir paquete
129
+ run: python -m build
130
+
131
+ - name: Publicar en PyPI
132
+ uses: pypa/gh-action-pypi-publish@release/v1
@@ -0,0 +1,63 @@
1
+ # Python
2
+ __pycache__/
3
+ *.py[cod]
4
+ *$py.class
5
+ *.so
6
+ .Python
7
+ build/
8
+ develop-eggs/
9
+ dist/
10
+ downloads/
11
+ eggs/
12
+ .eggs/
13
+ lib/
14
+ lib64/
15
+ parts/
16
+ sdist/
17
+ var/
18
+ wheels/
19
+ *.egg-info/
20
+ .installed.cfg
21
+ *.egg
22
+
23
+ # Virtual environments
24
+ venv/
25
+ ENV/
26
+ env/
27
+
28
+ # IDEs
29
+ .vscode/
30
+ .idea/
31
+ *.swp
32
+ *.swo
33
+ *~
34
+ .DS_Store
35
+
36
+ # Output directories
37
+ diagrams/
38
+ external-calls/
39
+ sanity/
40
+ *.png
41
+ *.pdf
42
+
43
+ # Claude Code internal files
44
+ .claude/
45
+
46
+ # SVG generados (excluir diagrams de PlantUML pero no los assets del repo)
47
+ *.svg
48
+ !docs/*.svg
49
+
50
+ # Logs
51
+ *.log
52
+
53
+ # PlantUML cache
54
+ .plantuml
55
+
56
+ # Testing
57
+ .pytest_cache/
58
+ .coverage
59
+ htmlcov/
60
+
61
+ # Temporary files
62
+ *.tmp
63
+ *.bak
@@ -0,0 +1,162 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project will be documented in this file.
4
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
5
+
6
+ ## [Unreleased]
7
+
8
+ ## [1.3.1] - 2026-06-06
9
+
10
+ ### Changed
11
+ - **Distribución por PyPI**: la herramienta ahora se instala con `pipx install android-gradle-analyzer` (antes solo `git+https://...`). El install desde `git+` se mantiene documentado como vía de desarrollo
12
+ - README y CONTRIBUTING actualizados: el flujo de desarrollo usa `pip install -e ".[kts,yaml]"` y el de release ya no crea el tag a mano (lo hace el CI)
13
+
14
+ ### Added
15
+ - **Publicación automática a PyPI** vía Trusted Publishers (OIDC) en `release.yml`: el job `publish-pypi` sube el paquete sin tokens ni contraseñas, gateado a que el job `release` haya creado un tag nuevo (no publica en pushes sin bump de versión)
16
+
17
+ ### Removed
18
+ - `setup.sh` y `requirements.txt`: `pyproject.toml` queda como fuente única de dependencias
19
+
20
+ ## [1.3.0] - 2026-06-06
21
+
22
+ ### Added
23
+ - **Detector de "lógica compartida mal ubicada"** en `gradle-sanity`: identifica un módulo *hoja* (feature o app — `I` alto, en la punta del grafo de dependencias) del que sin embargo **otros módulos dependen** (`Ca` por encima del límite). Suele indicar código común atrapado en un feature en vez de estar en `core`/`shared`. Se basa solo en las métricas que el tool ya calcula (`I` y `Ca`) — **no depende de nombres de módulo ni de plugins**, y `core`/`common` quedan excluidos automáticamente por tener `I` bajo (su `Ca` alto es esperado)
24
+ - **Advisory por default**: con `leaf_penalty`/`app_penalty` en `0` el detector solo aparece en el reporte y en el JSON (`coupling_issues`) sin afectar el score → cero regresión para proyectos existentes. Subir las penalizaciones activa el gate en CI
25
+ - Nueva config `coupling_limits` (`leaf_instability`, `leaf_max_ca`, `leaf_penalty`, `app_max_ca`, `app_penalty`) — funciona sin `analyzer_config.json` usando los defaults internos
26
+ - El punto de entrada (app) se detecta por el plugin `com.android.application` (única señal de plugin confiable; `com.android.library` no distingue rol). `coupling_overrides` permite forzar (`"app"`/`"leaf"`) o excluir (`"ignore"`) módulos puntuales cuando la métrica se equivoca
27
+ - Nueva clase de test `TestLeafCoupling` y fixture `leaf_coupling`
28
+
29
+ ### Known limitations
30
+ - La detección del plugin de app no resuelve `alias(libs.plugins.android.application)` (requiere leer el version catalog); solo el `id("com.android.application")` literal. El detector funciona igual sin esa señal — solo pierde el umbral más estricto del punto de entrada
31
+
32
+ ## [1.2.2] - 2026-05-27
33
+
34
+ ### Added
35
+ - **Parser tree-sitter para `.gradle.kts`**: cuando `tree-sitter-kotlin` está instalado (`pip install "android-gradle-analyzer[kts]"`), los archivos KTS se parsean con un AST real en lugar de regex. Maneja correctamente: dependencias multilínea, comentarios de línea `//` y de bloque `/* */`, y parámetros nombrados `project(path = ":module")`. El fallback a regex es automático si la librería no está disponible
36
+ - **Preprocesador Groovy** para archivos `.gradle`: antes de aplicar regex, se eliminan comentarios `//` y `/* */` y se colapsan declaraciones multilínea (e.g. `implementation(\n project(":core")\n)`). Elimina falsos negativos por comentarios y falsos positivos desde dependencias comentadas
37
+ - Nueva función `_strip_comments` (interna): limpia comentarios sin colapso de paréntesis — usada en el path KTS para los accessors `projects.*`
38
+ - Nuevos fixtures de test: `kts_multiline`, `kts_commented`, `groovy_multiline`
39
+ - Nuevas clases de test: `TestPreprocessGroovy` y `TestKtsTreeSitter` (se saltan automáticamente si `tree-sitter-kotlin` no está instalado)
40
+ - Nueva dependencia opcional `kts`: `pip install "android-gradle-analyzer[kts]"` instala `tree-sitter>=0.22` y `tree-sitter-kotlin>=0.3`
41
+
42
+ ## [1.2.1] - 2026-05-26
43
+
44
+ ### Changed
45
+ - `pyproject.toml`: `requires-python` actualizado de `>=3.8` a `>=3.10`. El código usa sintaxis PEP 604 (`str | None`, `list[str]`) que requiere Python 3.10 o superior. Antes el paquete se instalaba en 3.8/3.9 pero crashaba al primer import — ahora pip se niega antes con un mensaje claro
46
+ - README: badge de Python actualizado a `3.10+` (antes mentía con `3.7+`)
47
+
48
+ ### Added
49
+ - README: badges de **Tests** (estado del último workflow) y **Release** (última versión publicada) vía shields.io
50
+
51
+ ## [1.2.0] - 2026-05-26
52
+
53
+ ### Added
54
+ - Soporte para **type-safe project accessors** (`projects.foo.barBaz`, Gradle 7+) en los 4 analizadores. El parser construye un mapa accessor → módulo a partir de `settings.gradle(.kts)` y resuelve referencias en Kotlin DSL (`implementation(projects.feature.paymentsCommon)`) y Groovy (`implementation projects.feature.paymentsCommon`). Análisis 100% estático, sin compilar
55
+ - `module_to_accessor()` y `build_accessor_map()` expuestos en `analyzer_utils` para uso externo o testing
56
+ - Detección de colisiones de accessor (raro en proyectos reales): si dos módulos mapean al mismo accessor, se emite un warning y gana el primero
57
+
58
+ ### Fixed
59
+ - **Falsos positivos en `external_callers` y `gradle_analyzer`**: el matcher anterior usaba `endswith(':' + path)` bidireccional, lo que hacía que `project(":common")` matchera erróneamente cualquier submódulo `:foo:common`. Ahora el matcheo es exacto sobre el listado de módulos conocidos. Caso real afectado: cualquier proyecto con módulos raíz que comparten leaf-name con submódulos del target
60
+
61
+ ## [1.1.1] - 2026-05-26
62
+
63
+ ### Fixed
64
+ - `generate_ascii`: usaba `module_set` (solo los módulos del scope analizado) como filtro de dependencias, mostrando "(sin dependencias internas)" cuando las deps apuntan a módulos del proyecto raíz fuera del subdirectorio. Ahora usa `known_modules`
65
+ - `generate_report`: la sección "Módulos no utilizados por otros" se oculta cuando se analiza un subconjunto del proyecto (el Ca no puede calcularse sin analizar todos los módulos). En su lugar muestra una nota que apunta a "Llamadas externas"
66
+
67
+ ## [1.1.0] - 2026-05-26
68
+
69
+ ### Added
70
+ - Auto-detección de raíz del proyecto: `find_project_root()` sube directorios desde el path analizado hasta encontrar `settings.gradle(.kts)`, permitiendo resolver dependencias `implementation(project(":..."))` a módulos fuera del subdirectorio analizado (ej. analizar `customer/` y ver dependencias a `view`, `mach-foundation:util`, etc.)
71
+ - `known_modules` separado de `modules` en `GradleDependencyAnalyzer`: el registry completo del proyecto raíz se usa para el matching de dependencias, mientras `modules` sigue controlando qué módulos se analizan
72
+ - Barra de progreso con porcentaje en el menú interactivo para proyectos con más de 10 módulos (`BarColumn` + `MofNCompleteColumn`); proyectos pequeños mantienen el spinner simple
73
+ - `analyze_gradle_dependencies` acepta callback `on_progress(done, total)` para actualizar la UI; usa `as_completed` para progreso fluido conforme terminan los workers paralelos
74
+
75
+ ## [1.0.9] - 2026-05-25
76
+
77
+ ### Fixed
78
+ - Mermaid: grafos grandes (>500 edges) fallaban silenciosamente por el límite default del parser. Se agrega header `%%{init}%%` con `maxEdges: 10000`, `maxTextSize: 200000`, `htmlLabels: true`, `curve: basis` y `wrappingWidth: 200`
79
+
80
+ ## [1.0.8] - 2026-05-25
81
+
82
+ ### Fixed
83
+ - Mermaid: edge label `use` renombrado a `uses` — `use` es keyword reservado en Mermaid v11+ y causaba error de parsing
84
+ - Mermaid: `base_path` resuelto con `.resolve()` para que el ID del subgraph no quede vacío al invocar el analyzer con ruta relativa (`.`)
85
+ - Consola: `VERSION` en `menu/branding.py` desincronizado con `pyproject.toml` — ahora ambos se actualizan juntos en el bump
86
+
87
+ ## [1.0.7] - 2026-05-25
88
+
89
+ ### Fixed
90
+ - `parse_settings_modules`: detección de módulos con sintaxis `include("modulo")` sin `:` inicial (común en proyectos Android Groovy DSL que adoptan convenciones tipo Kotlin). Antes devolvía 0 módulos silenciosamente. `includeBuild` ya no se cuenta como módulo del proyecto.
91
+
92
+ ## [1.0.6] - 2026-05-24
93
+
94
+ ### Added
95
+ - Soporte para `analyzer.yml` en la raíz del proyecto analizado — configura defaults de CLI sin pasar flags manualmente
96
+
97
+ ## [1.0.5] - 2026-05-24
98
+
99
+ ### Fixed
100
+ - Colones en nombres de archivo en Windows (`external_callers`) — módulos como `core:analytics:tracker` generaban `[Errno 22]`
101
+
102
+ ## [1.0.4] - 2026-05-24
103
+
104
+ ### Fixed
105
+ - UnicodeEncodeError en Windows: todos los CLIs ahora fuerzan UTF-8 en stdout/stderr al arrancar (fix para terminales con encoding cp1252)
106
+
107
+ ## [1.0.3] - 2026-05-24
108
+
109
+ ### Fixed
110
+ - Menú: al cancelar la selección de módulo focal ya no procede el análisis — vuelve correctamente al menú principal
111
+ - Menú: selector de módulo focal muestra los nombres directamente en la lista (sin paso intermedio)
112
+
113
+ ## [1.0.2] - 2026-05-24
114
+
115
+ ### Added
116
+ - Menú interactivo: formato DOT y ASCII disponibles en el selector de formato
117
+ - Menú interactivo: prompt de zoom (`--focus`) en análisis de dependencias internas
118
+ - Menú interactivo: ASCII se muestra inline en el panel del terminal
119
+ - Menú interactivo: offer post-análisis para renderizar DOT a PNG/SVG con Graphviz local
120
+
121
+ ## [1.0.1] - 2026-05-24
122
+
123
+ ### Added
124
+ - Graphviz DOT format (`--format dot` / `gradle-dot`) con colores por tipo de módulo
125
+ - ASCII format (`--format ascii` / `gradle-ascii`) para visualización en terminal
126
+ - `--focus <module[,module]>`: zoom al subgrafo de un módulo en todos los formatos
127
+ - Entry points independientes `gradle-dot` y `gradle-ascii`
128
+ - Parsing paralelo con `ThreadPoolExecutor` en los 3 analizadores (gradle-analyzer, gradle-externals, gradle-impact)
129
+
130
+ ### Fixed
131
+ - Build files con nombre custom (ej. `chat.gradle.kts`) ahora se detectan correctamente — fix para proyectos como MEGA Android que no usan `build.gradle.kts` estándar
132
+
133
+ ## [1.0.0] - 2026-05-24
134
+
135
+ ### Added
136
+ - `gradle-impact`: nuevo analizador de impacto de cambios — dado un módulo, muestra qué otros módulos se ven afectados (BFS sobre grafo invertido, niveles directos y transitivos)
137
+ - `--quiet` en todos los CLIs para suprimir output de progreso (ideal para CI)
138
+ - `--json` en todos los CLIs para salida estructurada a stdout
139
+ - `--fail-on-cycle` en `gradle-sanity`: `exit 1` si se detecta algún ciclo (CI gate)
140
+ - `--fail-on-score-below N` en `gradle-sanity`: `exit 1` si el score cae por debajo de N
141
+ - Sección de módulos huérfanos en reporte de sanidad (Ca=0 y Ce=0, sin penalización)
142
+ - Parsing de `settings.gradle.kts` / `settings.gradle` como fuente de verdad para módulos
143
+ - HTML export con Mermaid embebido (renderizado inline via CDN)
144
+ - `examples/github-actions-dependency-health.yml`: ejemplo de integración GitHub Actions
145
+ - `scripts/bump_version.py`: script de versionado semántico
146
+ - `CHANGELOG.md` con formato Keep a Changelog
147
+
148
+ ### Fixed
149
+ - Falsos positivos en detección de versiones hardcodeadas: las versiones dentro de comentarios (`// "lib:x:1.2.3"`) ya no se reportan
150
+
151
+ ## [0.1.0] - 2026-05-24
152
+
153
+ ### Added
154
+ - `gradle-analyzer`: análisis de dependencias internas con salida PlantUML, Mermaid y texto
155
+ - `gradle-externals`: detección de callers externos a un módulo
156
+ - `gradle-sanity`: métricas Ca/Ce/I, detección de ciclos, violaciones SDP y score 0–100
157
+ - `gradle-analyzer-menu`: menú interactivo con Rich + questionary, historial y export
158
+ - Export a HTML, Markdown, ZIP y PDF (opcional via weasyprint)
159
+ - Configuración personalizable via `analyzer_config.json`
160
+ - Detección automática de ciclos con DFS y coloreo de nodos
161
+ - Soporte para Groovy DSL y Kotlin DSL (`build.gradle` y `build.gradle.kts`)
162
+ - Scopes soportados: `implementation`, `api`, `kapt`, `compileOnly`, `testImplementation` y más
@@ -0,0 +1,153 @@
1
+ # Contribuyendo a Android Gradle Analyzer
2
+
3
+ ¡Gracias por tu interés en contribuir! 🎉
4
+
5
+ ## 🚀 Cómo Contribuir
6
+
7
+ ### Reportar Bugs
8
+
9
+ 1. Verifica que el bug no haya sido reportado antes
10
+ 2. Abre un [issue](https://github.com/pfranccino/android-gradle-analyzer/issues/new)
11
+ 3. Incluye:
12
+ - Descripción clara del problema
13
+ - Pasos para reproducirlo
14
+ - Comportamiento esperado vs actual
15
+ - Versión de Python
16
+ - Sistema operativo
17
+ - Ejemplo de archivo gradle (si es relevante)
18
+
19
+ ### Sugerir Mejoras
20
+
21
+ 1. Abre un issue con el tag `enhancement`
22
+ 2. Describe la funcionalidad que te gustaría ver
23
+ 3. Explica por qué sería útil
24
+ 4. Proporciona ejemplos de uso si es posible
25
+
26
+ ### Enviar Pull Requests
27
+
28
+ 1. **Fork** el repositorio
29
+ 2. **Crea una rama** desde `main`:
30
+ ```bash
31
+ git checkout -b feature/mi-nueva-funcionalidad
32
+ ```
33
+ 3. **Haz tus cambios**:
34
+ - Sigue el estilo de código existente
35
+ - Agrega comentarios donde sea necesario
36
+ - Actualiza documentación si es relevante
37
+ 4. **Prueba tus cambios**:
38
+ ```bash
39
+ python3 gradle_analyzer.py /ruta/test
40
+ python3 external_callers.py /ruta/test modulo
41
+ ```
42
+ 5. **Commit** con mensajes descriptivos:
43
+ ```bash
44
+ git commit -m "Agrega soporte para formato Groovy DSL"
45
+ ```
46
+ 6. **Push** a tu fork:
47
+ ```bash
48
+ git push origin feature/mi-nueva-funcionalidad
49
+ ```
50
+ 7. **Abre un Pull Request** con:
51
+ - Descripción clara de los cambios
52
+ - Referencias a issues relacionados
53
+ - Screenshots si aplica
54
+
55
+ ## 📝 Guía de Estilo
56
+
57
+ ### Python
58
+
59
+ - Usa **4 espacios** para indentación
60
+ - Nombres de funciones y variables en **snake_case**
61
+ - Nombres de clases en **PascalCase**
62
+ - Docstrings para funciones públicas
63
+ - Líneas máximo 100 caracteres
64
+
65
+ **Ejemplo:**
66
+
67
+ ```python
68
+ def analyze_dependencies(self):
69
+ """
70
+ Analiza las dependencias desde los archivos gradle
71
+
72
+ Returns:
73
+ self para encadenamiento
74
+ """
75
+ for module in self.modules:
76
+ self._parse_gradle_file(module)
77
+ return self
78
+ ```
79
+
80
+ ### Commits
81
+
82
+ Usa mensajes claros y en presente:
83
+
84
+ ✅ **Bueno:**
85
+
86
+ - `Agrega soporte para Gradle Kotlin DSL`
87
+ - `Corrige detección de dependencias anidadas`
88
+ - `Mejora documentación de instalación`
89
+
90
+ ❌ **Malo:**
91
+
92
+ - `Fix`
93
+ - `Changes`
94
+ - `Updated stuff`
95
+
96
+ ## 🧪 Testing
97
+
98
+ Antes de enviar un PR, prueba con:
99
+
100
+ 1. **Proyecto simple** (pocos módulos)
101
+ 2. **Proyecto complejo** (muchos módulos anidados)
102
+ 3. **Diferentes formatos** de gradle:
103
+ - `build.gradle` (Groovy)
104
+ - `build.gradle.kts` (Kotlin DSL)
105
+
106
+ ## 🏷️ Proceso de release
107
+
108
+ 1. Actualiza el `CHANGELOG.md` bajo `[Unreleased]` con los cambios del release
109
+ 2. Ejecuta el script de bump:
110
+ ```bash
111
+ python scripts/bump_version.py 0.2.0
112
+ ```
113
+ 3. Revisa el diff generado en `pyproject.toml` y `menu/branding.py`
114
+ 4. Commitea y haz push a `main` — el workflow se encarga del resto:
115
+ ```bash
116
+ git add pyproject.toml menu/branding.py CHANGELOG.md
117
+ git commit -m "chore: bump version to v0.2.0"
118
+ git push
119
+ ```
120
+ El CI crea el tag `v0.2.0`, publica el GitHub Release y sube el paquete a PyPI
121
+ (vía Trusted Publisher / OIDC) automáticamente. **No crees el tag a mano:** si el
122
+ tag ya existe, el workflow lo detecta y se salta el release y el publish.
123
+
124
+ ## 💡 Ideas para Contribuir
125
+
126
+ ### Features Buscadas
127
+
128
+ - [ ] Análisis de performance de build
129
+ - [ ] Sugerencias de optimización automáticas
130
+ - [ ] Comparación de grafos entre ramas
131
+
132
+ ### Mejoras de Documentación
133
+
134
+ - [ ] Tutorial en video
135
+ - [ ] Más ejemplos de uso
136
+ - [ ] Traducción a otros idiomas
137
+ - [ ] Guía de arquitectura modular
138
+ - [ ] Comparación con otras herramientas
139
+
140
+ ## 🤔 ¿Preguntas?
141
+
142
+ Si tienes dudas sobre cómo contribuir, abre un issue con el tag `question` o únete a las discusiones.
143
+
144
+ ## 📜 Código de Conducta
145
+
146
+ - Se respetuoso y constructivo
147
+ - Acepta críticas constructivas
148
+ - Enfócate en lo mejor para el proyecto
149
+ - Sé paciente con otros colaboradores
150
+
151
+ ---
152
+
153
+ ¡Gracias por hacer este proyecto mejor! 🙏