git-ez 1.0.0__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.
- git_ez-1.0.0/.github/workflows/tests.yml +25 -0
- git_ez-1.0.0/.gitignore +19 -0
- git_ez-1.0.0/LICENSE +21 -0
- git_ez-1.0.0/PKG-INFO +188 -0
- git_ez-1.0.0/README.md +139 -0
- git_ez-1.0.0/gitez_demo.png +0 -0
- git_ez-1.0.0/pyproject.toml +43 -0
- git_ez-1.0.0/src/git_ez/__init__.py +4 -0
- git_ez-1.0.0/src/git_ez/__main__.py +4 -0
- git_ez-1.0.0/src/git_ez/main.py +125 -0
- git_ez-1.0.0/tests/test_main.py +54 -0
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
name: tests
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
pull_request:
|
|
7
|
+
|
|
8
|
+
jobs:
|
|
9
|
+
test:
|
|
10
|
+
runs-on: ubuntu-latest
|
|
11
|
+
strategy:
|
|
12
|
+
fail-fast: false
|
|
13
|
+
matrix:
|
|
14
|
+
python-version: ["3.10", "3.12", "3.13"]
|
|
15
|
+
steps:
|
|
16
|
+
- uses: actions/checkout@v4
|
|
17
|
+
- uses: actions/setup-python@v5
|
|
18
|
+
with:
|
|
19
|
+
python-version: ${{ matrix.python-version }}
|
|
20
|
+
- name: Instalar
|
|
21
|
+
run: pip install -e .
|
|
22
|
+
- name: Tests
|
|
23
|
+
run: python -m unittest discover -s tests -v
|
|
24
|
+
- name: Verificar comando
|
|
25
|
+
run: git-ez --help
|
git_ez-1.0.0/.gitignore
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# Entorno virtual
|
|
2
|
+
venv/
|
|
3
|
+
.venv/
|
|
4
|
+
env/
|
|
5
|
+
|
|
6
|
+
# Caché de Python y compilados
|
|
7
|
+
__pycache__/
|
|
8
|
+
*.pyc
|
|
9
|
+
*.pyo
|
|
10
|
+
|
|
11
|
+
# Archivos de empaquetado/build
|
|
12
|
+
build/
|
|
13
|
+
dist/
|
|
14
|
+
*.egg-info/
|
|
15
|
+
.eggs/
|
|
16
|
+
|
|
17
|
+
# IDEs y configuración del editor
|
|
18
|
+
.vscode/
|
|
19
|
+
.idea/
|
git_ez-1.0.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Andrés Morales C
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
git_ez-1.0.0/PKG-INFO
ADDED
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: git-ez
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Summary: Generador interactivo y local de mensajes de commit bajo Conventional Commits, en español.
|
|
5
|
+
Project-URL: Homepage, https://github.com/AAMoralesC/GitEz
|
|
6
|
+
Project-URL: Repository, https://github.com/AAMoralesC/GitEz
|
|
7
|
+
Project-URL: Issues, https://github.com/AAMoralesC/GitEz/issues
|
|
8
|
+
Author-email: Andrés Morales <andresdremc@gmail.com>
|
|
9
|
+
License: MIT License
|
|
10
|
+
|
|
11
|
+
Copyright (c) 2026 Andrés Morales C
|
|
12
|
+
|
|
13
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
14
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
15
|
+
in the Software without restriction, including without limitation the rights
|
|
16
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
17
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
18
|
+
furnished to do so, subject to the following conditions:
|
|
19
|
+
|
|
20
|
+
The above copyright notice and this permission notice shall be included in all
|
|
21
|
+
copies or substantial portions of the Software.
|
|
22
|
+
|
|
23
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
24
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
25
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
26
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
27
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
28
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
29
|
+
SOFTWARE.
|
|
30
|
+
License-File: LICENSE
|
|
31
|
+
Keywords: cli,commit,conventional-commits,git,typer
|
|
32
|
+
Classifier: Development Status :: 5 - Production/Stable
|
|
33
|
+
Classifier: Environment :: Console
|
|
34
|
+
Classifier: Intended Audience :: Developers
|
|
35
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
36
|
+
Classifier: Natural Language :: Spanish
|
|
37
|
+
Classifier: Operating System :: OS Independent
|
|
38
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
39
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
40
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
41
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
42
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
43
|
+
Classifier: Topic :: Software Development :: Version Control :: Git
|
|
44
|
+
Requires-Python: >=3.10
|
|
45
|
+
Requires-Dist: questionary>=2.0.0
|
|
46
|
+
Requires-Dist: rich>=13.0.0
|
|
47
|
+
Requires-Dist: typer>=0.9.0
|
|
48
|
+
Description-Content-Type: text/markdown
|
|
49
|
+
|
|
50
|
+
# GitEz 🚀
|
|
51
|
+
|
|
52
|
+
[](https://github.com/AAMoralesC/GitEz/actions/workflows/tests.yml)
|
|
53
|
+
[](./LICENSE)
|
|
54
|
+
[](https://www.python.org/)
|
|
55
|
+
|
|
56
|
+
> **Generador interactivo, ultrarrápido y local de mensajes de commit — en español.**
|
|
57
|
+
|
|
58
|
+
`GitEz` analiza los archivos que agregaste a *staging* (`git add .`) y te sugiere mensajes
|
|
59
|
+
estandarizados bajo **Conventional Commits**, redactados en español. Se ejecuta en milisegundos,
|
|
60
|
+
funciona 100% offline y no requiere API keys.
|
|
61
|
+
|
|
62
|
+
---
|
|
63
|
+
|
|
64
|
+
## 📸 Demo
|
|
65
|
+
|
|
66
|
+

|
|
67
|
+
|
|
68
|
+
---
|
|
69
|
+
|
|
70
|
+
## ⚡ Características
|
|
71
|
+
|
|
72
|
+
- 🚀 **Ultra rápido:** sin llamadas a servicios externos ni LLMs de pago.
|
|
73
|
+
- 🔒 **100% privado:** todo el análisis ocurre en tu máquina.
|
|
74
|
+
- 🇪🇸 **Mensajes en español:** nativo, no traducciones automáticas.
|
|
75
|
+
- 🎯 **Sugerencias por contexto:** infiere el tipo de commit (`feat`, `fix`, `docs`, `test`, `chore`) según extensiones, documentación, tests y configuración.
|
|
76
|
+
- 🏷️ **Integración con tickets:** detecta el ID en el nombre de la rama (`feature/PROJ-42-login` → `PROJ-42`).
|
|
77
|
+
- ⌨️ **TUI fluida:** flechas y `Enter`, o escribe tu propio mensaje.
|
|
78
|
+
|
|
79
|
+
---
|
|
80
|
+
|
|
81
|
+
## 🧠 Sin IA, a propósito
|
|
82
|
+
|
|
83
|
+
GitEz no usa LLMs y es una decisión de diseño, no una limitación:
|
|
84
|
+
|
|
85
|
+
- **Privacidad:** tu código y tus nombres de archivo no salen de tu máquina.
|
|
86
|
+
- **Velocidad:** responde al instante, sin red ni latencia de modelo.
|
|
87
|
+
- **Determinismo:** el mismo staging produce la misma sugerencia, siempre.
|
|
88
|
+
|
|
89
|
+
Para un commit message el contexto útil son las rutas y extensiones de archivo, así que un LLM
|
|
90
|
+
añade costo, latencia y una dependencia externa sin mejorar el resultado.
|
|
91
|
+
|
|
92
|
+
---
|
|
93
|
+
|
|
94
|
+
## 📦 Instalación
|
|
95
|
+
|
|
96
|
+
```bash
|
|
97
|
+
pipx install git-ez
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
Si el paquete aún no aparece en PyPI, instala directo desde el repositorio:
|
|
101
|
+
|
|
102
|
+
```bash
|
|
103
|
+
pipx install git+https://github.com/AAMoralesC/GitEz.git
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
Con `pip` convencional:
|
|
107
|
+
|
|
108
|
+
```bash
|
|
109
|
+
pip install git+https://github.com/AAMoralesC/GitEz.git
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
### Desarrollo local
|
|
113
|
+
|
|
114
|
+
```bash
|
|
115
|
+
git clone https://github.com/AAMoralesC/GitEz.git
|
|
116
|
+
cd GitEz
|
|
117
|
+
pip install -e .
|
|
118
|
+
python -m unittest discover -s tests
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
### Actualización
|
|
122
|
+
|
|
123
|
+
```bash
|
|
124
|
+
pipx upgrade git-ez
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
---
|
|
128
|
+
|
|
129
|
+
## 💻 Uso
|
|
130
|
+
|
|
131
|
+
1. Agrega tus cambios al staging como siempre:
|
|
132
|
+
|
|
133
|
+
```bash
|
|
134
|
+
git add .
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
2. Ejecuta GitEz:
|
|
138
|
+
|
|
139
|
+
```bash
|
|
140
|
+
git ez
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
También funciona como `git-ez`, como `git ez commit`, o como `python -m git_ez`.
|
|
144
|
+
|
|
145
|
+
3. Navega con `↑`/`↓`, elige una sugerencia o escribe tu propio mensaje, y presiona `Enter`.
|
|
146
|
+
El commit se ejecuta solo.
|
|
147
|
+
|
|
148
|
+
Ejemplo de mensajes generados:
|
|
149
|
+
|
|
150
|
+
| Tipo | Ejemplo |
|
|
151
|
+
|------|---------|
|
|
152
|
+
| `feat` | `feat: actualizar main.py, utils.py` |
|
|
153
|
+
| `fix` | `fix: resolver problema en auth.py` |
|
|
154
|
+
| `docs` | `docs: actualizar documentación (README.md)` |
|
|
155
|
+
| `test` | `test: agregar o actualizar pruebas unitarias` |
|
|
156
|
+
| `chore` | `chore: actualizar dependencias y configuración del proyecto` |
|
|
157
|
+
|
|
158
|
+
Con ticket en la rama (`feature/PROJ-42-login`):
|
|
159
|
+
|
|
160
|
+
```
|
|
161
|
+
feat: [PROJ-42] actualizar main.py, utils.py
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
**Requisitos:** Python 3.10+ y Git en el `PATH`. Probado en 3.10, 3.12 y 3.13.
|
|
165
|
+
|
|
166
|
+
---
|
|
167
|
+
|
|
168
|
+
## 🛠️ Stack
|
|
169
|
+
|
|
170
|
+
Python · [Typer](https://typer.tiangolo.com/) · [Questionary](https://questionary.readthedocs.io/) · [Rich](https://rich.readthedocs.io/)
|
|
171
|
+
|
|
172
|
+
---
|
|
173
|
+
|
|
174
|
+
## 🤝 Contribuciones
|
|
175
|
+
|
|
176
|
+
Issues y PRs son bienvenidos. Para cambios grandes, abre primero un issue.
|
|
177
|
+
|
|
178
|
+
```bash
|
|
179
|
+
git checkout -b feature/mi-aporte
|
|
180
|
+
# ...cambios y tests...
|
|
181
|
+
git ez # 😉
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
---
|
|
185
|
+
|
|
186
|
+
## 📄 Licencia
|
|
187
|
+
|
|
188
|
+
MIT — ver [LICENSE](./LICENSE).
|
git_ez-1.0.0/README.md
ADDED
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
# GitEz 🚀
|
|
2
|
+
|
|
3
|
+
[](https://github.com/AAMoralesC/GitEz/actions/workflows/tests.yml)
|
|
4
|
+
[](./LICENSE)
|
|
5
|
+
[](https://www.python.org/)
|
|
6
|
+
|
|
7
|
+
> **Generador interactivo, ultrarrápido y local de mensajes de commit — en español.**
|
|
8
|
+
|
|
9
|
+
`GitEz` analiza los archivos que agregaste a *staging* (`git add .`) y te sugiere mensajes
|
|
10
|
+
estandarizados bajo **Conventional Commits**, redactados en español. Se ejecuta en milisegundos,
|
|
11
|
+
funciona 100% offline y no requiere API keys.
|
|
12
|
+
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
## 📸 Demo
|
|
16
|
+
|
|
17
|
+

|
|
18
|
+
|
|
19
|
+
---
|
|
20
|
+
|
|
21
|
+
## ⚡ Características
|
|
22
|
+
|
|
23
|
+
- 🚀 **Ultra rápido:** sin llamadas a servicios externos ni LLMs de pago.
|
|
24
|
+
- 🔒 **100% privado:** todo el análisis ocurre en tu máquina.
|
|
25
|
+
- 🇪🇸 **Mensajes en español:** nativo, no traducciones automáticas.
|
|
26
|
+
- 🎯 **Sugerencias por contexto:** infiere el tipo de commit (`feat`, `fix`, `docs`, `test`, `chore`) según extensiones, documentación, tests y configuración.
|
|
27
|
+
- 🏷️ **Integración con tickets:** detecta el ID en el nombre de la rama (`feature/PROJ-42-login` → `PROJ-42`).
|
|
28
|
+
- ⌨️ **TUI fluida:** flechas y `Enter`, o escribe tu propio mensaje.
|
|
29
|
+
|
|
30
|
+
---
|
|
31
|
+
|
|
32
|
+
## 🧠 Sin IA, a propósito
|
|
33
|
+
|
|
34
|
+
GitEz no usa LLMs y es una decisión de diseño, no una limitación:
|
|
35
|
+
|
|
36
|
+
- **Privacidad:** tu código y tus nombres de archivo no salen de tu máquina.
|
|
37
|
+
- **Velocidad:** responde al instante, sin red ni latencia de modelo.
|
|
38
|
+
- **Determinismo:** el mismo staging produce la misma sugerencia, siempre.
|
|
39
|
+
|
|
40
|
+
Para un commit message el contexto útil son las rutas y extensiones de archivo, así que un LLM
|
|
41
|
+
añade costo, latencia y una dependencia externa sin mejorar el resultado.
|
|
42
|
+
|
|
43
|
+
---
|
|
44
|
+
|
|
45
|
+
## 📦 Instalación
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
pipx install git-ez
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
Si el paquete aún no aparece en PyPI, instala directo desde el repositorio:
|
|
52
|
+
|
|
53
|
+
```bash
|
|
54
|
+
pipx install git+https://github.com/AAMoralesC/GitEz.git
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
Con `pip` convencional:
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
pip install git+https://github.com/AAMoralesC/GitEz.git
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
### Desarrollo local
|
|
64
|
+
|
|
65
|
+
```bash
|
|
66
|
+
git clone https://github.com/AAMoralesC/GitEz.git
|
|
67
|
+
cd GitEz
|
|
68
|
+
pip install -e .
|
|
69
|
+
python -m unittest discover -s tests
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
### Actualización
|
|
73
|
+
|
|
74
|
+
```bash
|
|
75
|
+
pipx upgrade git-ez
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
---
|
|
79
|
+
|
|
80
|
+
## 💻 Uso
|
|
81
|
+
|
|
82
|
+
1. Agrega tus cambios al staging como siempre:
|
|
83
|
+
|
|
84
|
+
```bash
|
|
85
|
+
git add .
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
2. Ejecuta GitEz:
|
|
89
|
+
|
|
90
|
+
```bash
|
|
91
|
+
git ez
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
También funciona como `git-ez`, como `git ez commit`, o como `python -m git_ez`.
|
|
95
|
+
|
|
96
|
+
3. Navega con `↑`/`↓`, elige una sugerencia o escribe tu propio mensaje, y presiona `Enter`.
|
|
97
|
+
El commit se ejecuta solo.
|
|
98
|
+
|
|
99
|
+
Ejemplo de mensajes generados:
|
|
100
|
+
|
|
101
|
+
| Tipo | Ejemplo |
|
|
102
|
+
|------|---------|
|
|
103
|
+
| `feat` | `feat: actualizar main.py, utils.py` |
|
|
104
|
+
| `fix` | `fix: resolver problema en auth.py` |
|
|
105
|
+
| `docs` | `docs: actualizar documentación (README.md)` |
|
|
106
|
+
| `test` | `test: agregar o actualizar pruebas unitarias` |
|
|
107
|
+
| `chore` | `chore: actualizar dependencias y configuración del proyecto` |
|
|
108
|
+
|
|
109
|
+
Con ticket en la rama (`feature/PROJ-42-login`):
|
|
110
|
+
|
|
111
|
+
```
|
|
112
|
+
feat: [PROJ-42] actualizar main.py, utils.py
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
**Requisitos:** Python 3.10+ y Git en el `PATH`. Probado en 3.10, 3.12 y 3.13.
|
|
116
|
+
|
|
117
|
+
---
|
|
118
|
+
|
|
119
|
+
## 🛠️ Stack
|
|
120
|
+
|
|
121
|
+
Python · [Typer](https://typer.tiangolo.com/) · [Questionary](https://questionary.readthedocs.io/) · [Rich](https://rich.readthedocs.io/)
|
|
122
|
+
|
|
123
|
+
---
|
|
124
|
+
|
|
125
|
+
## 🤝 Contribuciones
|
|
126
|
+
|
|
127
|
+
Issues y PRs son bienvenidos. Para cambios grandes, abre primero un issue.
|
|
128
|
+
|
|
129
|
+
```bash
|
|
130
|
+
git checkout -b feature/mi-aporte
|
|
131
|
+
# ...cambios y tests...
|
|
132
|
+
git ez # 😉
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
---
|
|
136
|
+
|
|
137
|
+
## 📄 Licencia
|
|
138
|
+
|
|
139
|
+
MIT — ver [LICENSE](./LICENSE).
|
|
Binary file
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["hatchling"]
|
|
3
|
+
build-backend = "hatchling.build"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "git-ez"
|
|
7
|
+
version = "1.0.0"
|
|
8
|
+
description = "Generador interactivo y local de mensajes de commit bajo Conventional Commits, en español."
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
license = { file = "LICENSE" }
|
|
11
|
+
requires-python = ">=3.10"
|
|
12
|
+
authors = [{ name = "Andrés Morales", email = "andresdremc@gmail.com" }]
|
|
13
|
+
keywords = ["git", "commit", "conventional-commits", "cli", "typer"]
|
|
14
|
+
classifiers = [
|
|
15
|
+
"Development Status :: 5 - Production/Stable",
|
|
16
|
+
"Environment :: Console",
|
|
17
|
+
"Intended Audience :: Developers",
|
|
18
|
+
"License :: OSI Approved :: MIT License",
|
|
19
|
+
"Natural Language :: Spanish",
|
|
20
|
+
"Operating System :: OS Independent",
|
|
21
|
+
"Programming Language :: Python :: 3 :: Only",
|
|
22
|
+
"Programming Language :: Python :: 3.10",
|
|
23
|
+
"Programming Language :: Python :: 3.11",
|
|
24
|
+
"Programming Language :: Python :: 3.12",
|
|
25
|
+
"Programming Language :: Python :: 3.13",
|
|
26
|
+
"Topic :: Software Development :: Version Control :: Git",
|
|
27
|
+
]
|
|
28
|
+
dependencies = [
|
|
29
|
+
"typer>=0.9.0",
|
|
30
|
+
"questionary>=2.0.0",
|
|
31
|
+
"rich>=13.0.0",
|
|
32
|
+
]
|
|
33
|
+
|
|
34
|
+
[project.urls]
|
|
35
|
+
Homepage = "https://github.com/AAMoralesC/GitEz"
|
|
36
|
+
Repository = "https://github.com/AAMoralesC/GitEz"
|
|
37
|
+
Issues = "https://github.com/AAMoralesC/GitEz/issues"
|
|
38
|
+
|
|
39
|
+
[project.scripts]
|
|
40
|
+
git-ez = "git_ez.main:app"
|
|
41
|
+
|
|
42
|
+
[tool.hatch.build.targets.wheel]
|
|
43
|
+
packages = ["src/git_ez"]
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
import subprocess
|
|
2
|
+
import re
|
|
3
|
+
import sys
|
|
4
|
+
import typer
|
|
5
|
+
import questionary
|
|
6
|
+
from rich import print
|
|
7
|
+
|
|
8
|
+
app = typer.Typer(help="CLI para generar commits inteligentes y rápidos.")
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
@app.callback(invoke_without_command=True)
|
|
12
|
+
def main(ctx: typer.Context):
|
|
13
|
+
"""Ejecuta `commit` cuando no se indica subcomando (`git ez`)."""
|
|
14
|
+
if ctx.invoked_subcommand is None:
|
|
15
|
+
commit()
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def get_git_branch() -> str:
|
|
19
|
+
"""Obtiene el nombre de la rama Git actual."""
|
|
20
|
+
try:
|
|
21
|
+
result = subprocess.run(
|
|
22
|
+
["git", "rev-parse", "--abbrev-ref", "HEAD"],
|
|
23
|
+
capture_output=True, text=True, check=True
|
|
24
|
+
)
|
|
25
|
+
return result.stdout.strip()
|
|
26
|
+
except subprocess.CalledProcessError:
|
|
27
|
+
return ""
|
|
28
|
+
|
|
29
|
+
def get_staged_files() -> list[str]:
|
|
30
|
+
"""Obtiene la lista de archivos actualmente en staging."""
|
|
31
|
+
try:
|
|
32
|
+
result = subprocess.run(
|
|
33
|
+
["git", "diff", "--cached", "--name-only"],
|
|
34
|
+
capture_output=True, text=True, check=True
|
|
35
|
+
)
|
|
36
|
+
files = [f for f in result.stdout.strip().split("\n") if f]
|
|
37
|
+
return files
|
|
38
|
+
except subprocess.CalledProcessError:
|
|
39
|
+
return []
|
|
40
|
+
|
|
41
|
+
def extract_ticket_from_branch(branch_name: str) -> str:
|
|
42
|
+
"""Extrae un ID de ticket (ej. ABC-123 o MELI-88) del nombre de la rama."""
|
|
43
|
+
match = re.search(r"([A-Za-z]+-\d+)", branch_name)
|
|
44
|
+
return match.group(1).upper() if match else ""
|
|
45
|
+
|
|
46
|
+
def generate_suggestions(files: list[str], ticket: str) -> list[str]:
|
|
47
|
+
"""Genera opciones de mensajes basados en los archivos en staging."""
|
|
48
|
+
if not files:
|
|
49
|
+
return []
|
|
50
|
+
|
|
51
|
+
ticket_prefix = f"[{ticket}] " if ticket else ""
|
|
52
|
+
|
|
53
|
+
# Analizamos extensiones de archivos para inferir el 'scope'
|
|
54
|
+
has_docs = any(f.endswith(".md") or "docs/" in f for f in files)
|
|
55
|
+
has_tests = any("test" in f or "spec" in f for f in files)
|
|
56
|
+
has_config = any(f in ["package.json", "requirements.txt", "Dockerfile", "pyproject.toml"] for f in files)
|
|
57
|
+
|
|
58
|
+
suggestions = []
|
|
59
|
+
|
|
60
|
+
if has_docs and len(files) == 1:
|
|
61
|
+
suggestions.append(f"docs: {ticket_prefix}actualizar documentación ({files[0]})")
|
|
62
|
+
|
|
63
|
+
if has_tests:
|
|
64
|
+
suggestions.append(f"test: {ticket_prefix}agregar o actualizar pruebas unitarias")
|
|
65
|
+
|
|
66
|
+
if has_config:
|
|
67
|
+
suggestions.append(f"chore: {ticket_prefix}actualizar dependencias y configuración del proyecto")
|
|
68
|
+
|
|
69
|
+
# Opción genérica basada en la cantidad de archivos
|
|
70
|
+
file_summary = ", ".join([f.split("/")[-1] for f in files[:2]])
|
|
71
|
+
if len(files) > 2:
|
|
72
|
+
file_summary += f" y {len(files) - 2} más"
|
|
73
|
+
|
|
74
|
+
suggestions.append(f"feat: {ticket_prefix}actualizar {file_summary}")
|
|
75
|
+
suggestions.append(f"fix: {ticket_prefix}resolver problema en {file_summary}")
|
|
76
|
+
|
|
77
|
+
return suggestions
|
|
78
|
+
|
|
79
|
+
@app.command()
|
|
80
|
+
def commit():
|
|
81
|
+
"""Analiza staging y abre el menú para hacer commit."""
|
|
82
|
+
staged_files = get_staged_files()
|
|
83
|
+
|
|
84
|
+
if not staged_files:
|
|
85
|
+
print("[bold yellow]⚠️ No hay archivos en staging.[/bold yellow] Usa [bold cyan]git add[/bold cyan] primero.")
|
|
86
|
+
raise typer.Exit()
|
|
87
|
+
|
|
88
|
+
branch = get_git_branch()
|
|
89
|
+
ticket = extract_ticket_from_branch(branch)
|
|
90
|
+
suggestions = generate_suggestions(staged_files, ticket)
|
|
91
|
+
|
|
92
|
+
# Opción para escribir algo personalizado
|
|
93
|
+
CUSTOM_OPTION = "✏️ Escribir un mensaje personalizado..."
|
|
94
|
+
choices = suggestions + [CUSTOM_OPTION]
|
|
95
|
+
|
|
96
|
+
print(f"[bold green]✓[/bold green] {len(staged_files)} archivo(s) detectado(s) en staging.")
|
|
97
|
+
if ticket:
|
|
98
|
+
print(f"[bold blue]ℹ[/bold blue] Ticket detectado en la rama: [bold]{ticket}[/bold]\n")
|
|
99
|
+
|
|
100
|
+
# Interfaz interactiva en la terminal
|
|
101
|
+
selected_message = questionary.select(
|
|
102
|
+
"Selecciona el mensaje para tu commit:",
|
|
103
|
+
choices=choices
|
|
104
|
+
).ask()
|
|
105
|
+
|
|
106
|
+
if not selected_message:
|
|
107
|
+
print("[yellow]Operación cancelada.[/yellow]")
|
|
108
|
+
raise typer.Exit(code=1)
|
|
109
|
+
|
|
110
|
+
if selected_message == CUSTOM_OPTION:
|
|
111
|
+
selected_message = questionary.text("Escribe tu mensaje de commit:").ask()
|
|
112
|
+
if not selected_message:
|
|
113
|
+
print("[red]El mensaje no puede estar vacío.[/red]")
|
|
114
|
+
raise typer.Exit(code=1)
|
|
115
|
+
|
|
116
|
+
# Ejecutar el commit
|
|
117
|
+
try:
|
|
118
|
+
subprocess.run(["git", "commit", "-m", selected_message], check=True)
|
|
119
|
+
print(f"\n[bold green]🚀 Commit realizado con éxito:[/bold green] \"{selected_message}\"")
|
|
120
|
+
except subprocess.CalledProcessError:
|
|
121
|
+
print("\n[bold red]❌ Error al ejecutar git commit.[/bold red]")
|
|
122
|
+
raise typer.Exit(code=1)
|
|
123
|
+
|
|
124
|
+
if __name__ == "__main__":
|
|
125
|
+
app()
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import unittest
|
|
2
|
+
import sys
|
|
3
|
+
import os
|
|
4
|
+
|
|
5
|
+
# Add src/ to PYTHONPATH so we can import git_ez
|
|
6
|
+
sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), "..", "src")))
|
|
7
|
+
|
|
8
|
+
from git_ez.main import generate_suggestions, extract_ticket_from_branch
|
|
9
|
+
|
|
10
|
+
class TestGitEzSuggestions(unittest.TestCase):
|
|
11
|
+
def test_only_docs_no_ticket(self):
|
|
12
|
+
files = ["README.md"]
|
|
13
|
+
suggestions = generate_suggestions(files, "")
|
|
14
|
+
self.assertIn("docs: actualizar documentación (README.md)", suggestions)
|
|
15
|
+
self.assertIn("feat: actualizar README.md", suggestions)
|
|
16
|
+
self.assertIn("fix: resolver problema en README.md", suggestions)
|
|
17
|
+
|
|
18
|
+
def test_only_docs_with_ticket(self):
|
|
19
|
+
files = ["README.md"]
|
|
20
|
+
suggestions = generate_suggestions(files, "ABC-123")
|
|
21
|
+
self.assertIn("docs: [ABC-123] actualizar documentación (README.md)", suggestions)
|
|
22
|
+
|
|
23
|
+
def test_has_tests(self):
|
|
24
|
+
files = ["src/git_ez/main.py", "tests/test_main.py"]
|
|
25
|
+
suggestions = generate_suggestions(files, "ABC-123")
|
|
26
|
+
self.assertIn("test: [ABC-123] agregar o actualizar pruebas unitarias", suggestions)
|
|
27
|
+
|
|
28
|
+
def test_has_config(self):
|
|
29
|
+
files = ["pyproject.toml"]
|
|
30
|
+
suggestions = generate_suggestions(files, "")
|
|
31
|
+
self.assertIn("chore: actualizar dependencias y configuración del proyecto", suggestions)
|
|
32
|
+
|
|
33
|
+
def test_multiple_generic_files(self):
|
|
34
|
+
files = ["src/a.py", "src/b.py", "src/c.py", "src/d.py"]
|
|
35
|
+
suggestions = generate_suggestions(files, "XYZ-99")
|
|
36
|
+
expected_feat = "feat: [XYZ-99] actualizar a.py, b.py y 2 más"
|
|
37
|
+
self.assertIn(expected_feat, suggestions)
|
|
38
|
+
|
|
39
|
+
def test_no_files_returns_empty(self):
|
|
40
|
+
self.assertEqual(generate_suggestions([], "ABC-123"), [])
|
|
41
|
+
|
|
42
|
+
class TestTicketExtraction(unittest.TestCase):
|
|
43
|
+
def test_ticket_found(self):
|
|
44
|
+
self.assertEqual(extract_ticket_from_branch("feature/PROJ-42-login"), "PROJ-42")
|
|
45
|
+
|
|
46
|
+
def test_ticket_is_uppercased(self):
|
|
47
|
+
self.assertEqual(extract_ticket_from_branch("fix/abc-7"), "ABC-7")
|
|
48
|
+
|
|
49
|
+
def test_branch_without_ticket(self):
|
|
50
|
+
self.assertEqual(extract_ticket_from_branch("main"), "")
|
|
51
|
+
self.assertEqual(extract_ticket_from_branch(""), "")
|
|
52
|
+
|
|
53
|
+
if __name__ == "__main__":
|
|
54
|
+
unittest.main()
|