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.
- android_gradle_analyzer-1.3.1/.github/workflows/release.yml +132 -0
- android_gradle_analyzer-1.3.1/.gitignore +63 -0
- android_gradle_analyzer-1.3.1/CHANGELOG.md +162 -0
- android_gradle_analyzer-1.3.1/CONTRIBUTING.md +153 -0
- android_gradle_analyzer-1.3.1/EXAMPLES.md +290 -0
- android_gradle_analyzer-1.3.1/LICENSE +21 -0
- android_gradle_analyzer-1.3.1/PKG-INFO +668 -0
- android_gradle_analyzer-1.3.1/README.md +618 -0
- android_gradle_analyzer-1.3.1/analyzer_config.example.json +80 -0
- android_gradle_analyzer-1.3.1/analyzer_utils.py +687 -0
- android_gradle_analyzer-1.3.1/docs/preview-menu.svg +62 -0
- android_gradle_analyzer-1.3.1/docs/preview.svg +58 -0
- android_gradle_analyzer-1.3.1/examples/github-actions-dependency-health.yml +93 -0
- android_gradle_analyzer-1.3.1/external_callers.py +317 -0
- android_gradle_analyzer-1.3.1/gradle_analyzer.py +585 -0
- android_gradle_analyzer-1.3.1/gradle_impact.py +281 -0
- android_gradle_analyzer-1.3.1/gradle_sanity.py +622 -0
- android_gradle_analyzer-1.3.1/impact/core-impact-report.txt +13 -0
- android_gradle_analyzer-1.3.1/impact/core-impact.mmd +13 -0
- android_gradle_analyzer-1.3.1/impact/core-impact.puml +20 -0
- android_gradle_analyzer-1.3.1/menu/__init__.py +447 -0
- android_gradle_analyzer-1.3.1/menu/actions.py +208 -0
- android_gradle_analyzer-1.3.1/menu/branding.py +15 -0
- android_gradle_analyzer-1.3.1/menu/exporter.py +233 -0
- android_gradle_analyzer-1.3.1/menu/prompts.py +307 -0
- android_gradle_analyzer-1.3.1/menu/state.py +82 -0
- android_gradle_analyzer-1.3.1/menu/ui.py +198 -0
- android_gradle_analyzer-1.3.1/menu.py +9 -0
- android_gradle_analyzer-1.3.1/pyproject.toml +61 -0
- android_gradle_analyzer-1.3.1/scripts/bump_version.py +60 -0
- android_gradle_analyzer-1.3.1/scripts/extract_changelog.py +45 -0
- android_gradle_analyzer-1.3.1/tests/conftest.py +1 -0
- android_gradle_analyzer-1.3.1/tests/fixtures/accessors/app/build.gradle.kts +12 -0
- android_gradle_analyzer-1.3.1/tests/fixtures/accessors/core/network_api/build.gradle.kts +6 -0
- android_gradle_analyzer-1.3.1/tests/fixtures/accessors/feature/payments-common/build.gradle.kts +6 -0
- android_gradle_analyzer-1.3.1/tests/fixtures/accessors/legacy/plain-lib/build.gradle.kts +6 -0
- android_gradle_analyzer-1.3.1/tests/fixtures/accessors/settings.gradle.kts +6 -0
- android_gradle_analyzer-1.3.1/tests/fixtures/ambiguous/payments/build.gradle +7 -0
- android_gradle_analyzer-1.3.1/tests/fixtures/ambiguous/payments/common/build.gradle +6 -0
- android_gradle_analyzer-1.3.1/tests/fixtures/ambiguous/quick-payments/build.gradle +7 -0
- android_gradle_analyzer-1.3.1/tests/fixtures/commented_version/module/build.gradle +8 -0
- android_gradle_analyzer-1.3.1/tests/fixtures/cycle/a/build.gradle +7 -0
- android_gradle_analyzer-1.3.1/tests/fixtures/cycle/b/build.gradle +7 -0
- android_gradle_analyzer-1.3.1/tests/fixtures/external/app/build.gradle +7 -0
- android_gradle_analyzer-1.3.1/tests/fixtures/external/payments/build.gradle +6 -0
- android_gradle_analyzer-1.3.1/tests/fixtures/external/payments/gateway/build.gradle +6 -0
- android_gradle_analyzer-1.3.1/tests/fixtures/external/payments-extra/build.gradle +7 -0
- android_gradle_analyzer-1.3.1/tests/fixtures/external_ambiguous/caller/build.gradle.kts +10 -0
- android_gradle_analyzer-1.3.1/tests/fixtures/external_ambiguous/caller-using-accessor/build.gradle.kts +7 -0
- android_gradle_analyzer-1.3.1/tests/fixtures/external_ambiguous/common/build.gradle.kts +3 -0
- android_gradle_analyzer-1.3.1/tests/fixtures/external_ambiguous/settings.gradle.kts +7 -0
- android_gradle_analyzer-1.3.1/tests/fixtures/external_ambiguous/target/build.gradle.kts +3 -0
- android_gradle_analyzer-1.3.1/tests/fixtures/external_ambiguous/target/common/build.gradle.kts +3 -0
- android_gradle_analyzer-1.3.1/tests/fixtures/groovy_multiline/also-ignored/build.gradle +3 -0
- android_gradle_analyzer-1.3.1/tests/fixtures/groovy_multiline/app/build.gradle +14 -0
- android_gradle_analyzer-1.3.1/tests/fixtures/groovy_multiline/core/build.gradle +3 -0
- android_gradle_analyzer-1.3.1/tests/fixtures/groovy_multiline/ignored/build.gradle +3 -0
- android_gradle_analyzer-1.3.1/tests/fixtures/groovy_multiline/shared/build.gradle +3 -0
- android_gradle_analyzer-1.3.1/tests/fixtures/kts_commented/app/build.gradle.kts +9 -0
- android_gradle_analyzer-1.3.1/tests/fixtures/kts_commented/core/build.gradle.kts +3 -0
- android_gradle_analyzer-1.3.1/tests/fixtures/kts_commented/network/build.gradle.kts +3 -0
- android_gradle_analyzer-1.3.1/tests/fixtures/kts_commented/shared/build.gradle.kts +3 -0
- android_gradle_analyzer-1.3.1/tests/fixtures/kts_multiline/app/build.gradle.kts +15 -0
- android_gradle_analyzer-1.3.1/tests/fixtures/kts_multiline/core/build.gradle.kts +3 -0
- android_gradle_analyzer-1.3.1/tests/fixtures/kts_multiline/shared/build.gradle.kts +3 -0
- android_gradle_analyzer-1.3.1/tests/fixtures/leaf_coupling/app/build.gradle +9 -0
- android_gradle_analyzer-1.3.1/tests/fixtures/leaf_coupling/cart/build.gradle +7 -0
- android_gradle_analyzer-1.3.1/tests/fixtures/leaf_coupling/checkout/build.gradle +7 -0
- android_gradle_analyzer-1.3.1/tests/fixtures/leaf_coupling/core/build.gradle +6 -0
- android_gradle_analyzer-1.3.1/tests/fixtures/leaf_coupling/database/build.gradle +7 -0
- android_gradle_analyzer-1.3.1/tests/fixtures/leaf_coupling/legacy/build.gradle +7 -0
- android_gradle_analyzer-1.3.1/tests/fixtures/leaf_coupling/model/build.gradle +7 -0
- android_gradle_analyzer-1.3.1/tests/fixtures/leaf_coupling/network/build.gradle +7 -0
- android_gradle_analyzer-1.3.1/tests/fixtures/leaf_coupling/payments/build.gradle +11 -0
- android_gradle_analyzer-1.3.1/tests/fixtures/leaf_coupling/settings.gradle.kts +12 -0
- android_gradle_analyzer-1.3.1/tests/fixtures/leaf_coupling/util/build.gradle +7 -0
- android_gradle_analyzer-1.3.1/tests/fixtures/orphan/connected/build.gradle +7 -0
- android_gradle_analyzer-1.3.1/tests/fixtures/orphan/island/build.gradle +6 -0
- android_gradle_analyzer-1.3.1/tests/fixtures/orphan/shared/build.gradle +6 -0
- android_gradle_analyzer-1.3.1/tests/fixtures/simple/app/build.gradle +7 -0
- android_gradle_analyzer-1.3.1/tests/fixtures/simple/core/build.gradle +7 -0
- android_gradle_analyzer-1.3.1/tests/fixtures/with_analyzer_yml/analyzer.yml +12 -0
- android_gradle_analyzer-1.3.1/tests/fixtures/with_settings/app/build.gradle +7 -0
- android_gradle_analyzer-1.3.1/tests/fixtures/with_settings/core/build.gradle +3 -0
- android_gradle_analyzer-1.3.1/tests/fixtures/with_settings/settings.gradle.kts +4 -0
- android_gradle_analyzer-1.3.1/tests/fixtures/with_settings_no_colon/settings.gradle +6 -0
- android_gradle_analyzer-1.3.1/tests/test_analyzer_utils.py +433 -0
- android_gradle_analyzer-1.3.1/tests/test_external_callers.py +94 -0
- android_gradle_analyzer-1.3.1/tests/test_gradle_analyzer.py +130 -0
- android_gradle_analyzer-1.3.1/tests/test_gradle_impact.py +62 -0
- android_gradle_analyzer-1.3.1/tests/test_gradle_sanity.py +101 -0
- 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! 🙏
|