commitmessagegenerator 2.2.0__tar.gz → 2.5.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.
- {commitmessagegenerator-2.2.0 → commitmessagegenerator-2.5.1}/PKG-INFO +25 -8
- {commitmessagegenerator-2.2.0 → commitmessagegenerator-2.5.1}/README.md +120 -103
- {commitmessagegenerator-2.2.0 → commitmessagegenerator-2.5.1}/commitmessagegenerator/cli.py +52 -28
- commitmessagegenerator-2.5.1/commitmessagegenerator/configure.py +196 -0
- {commitmessagegenerator-2.2.0 → commitmessagegenerator-2.5.1}/commitmessagegenerator/generator.py +2 -3
- {commitmessagegenerator-2.2.0 → commitmessagegenerator-2.5.1}/commitmessagegenerator.egg-info/PKG-INFO +25 -8
- {commitmessagegenerator-2.2.0 → commitmessagegenerator-2.5.1}/pyproject.toml +21 -21
- commitmessagegenerator-2.2.0/commitmessagegenerator/configure.py +0 -103
- {commitmessagegenerator-2.2.0 → commitmessagegenerator-2.5.1}/LICENSE +0 -0
- {commitmessagegenerator-2.2.0 → commitmessagegenerator-2.5.1}/commitmessagegenerator/__init__.py +0 -0
- {commitmessagegenerator-2.2.0 → commitmessagegenerator-2.5.1}/commitmessagegenerator.egg-info/SOURCES.txt +0 -0
- {commitmessagegenerator-2.2.0 → commitmessagegenerator-2.5.1}/commitmessagegenerator.egg-info/dependency_links.txt +0 -0
- {commitmessagegenerator-2.2.0 → commitmessagegenerator-2.5.1}/commitmessagegenerator.egg-info/entry_points.txt +0 -0
- {commitmessagegenerator-2.2.0 → commitmessagegenerator-2.5.1}/commitmessagegenerator.egg-info/requires.txt +0 -0
- {commitmessagegenerator-2.2.0 → commitmessagegenerator-2.5.1}/commitmessagegenerator.egg-info/top_level.txt +0 -0
- {commitmessagegenerator-2.2.0 → commitmessagegenerator-2.5.1}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: commitmessagegenerator
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.5.1
|
|
4
4
|
Summary: Generate commit messages with AI (Google Gemini) automatically using `git diff`.
|
|
5
5
|
Author-email: Gabriel Terceiro <gcarolinoterceiro@gmail.com>
|
|
6
6
|
License: MIT
|
|
@@ -35,6 +35,14 @@ pip install commitmessagegenerator
|
|
|
35
35
|
commitgen -cf
|
|
36
36
|
```
|
|
37
37
|
|
|
38
|
+
You can explicitly choose where configuration is written:
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
commitgen -cf --config-scope auto # default behavior
|
|
42
|
+
commitgen -cf --config-scope local # always write .env in current directory
|
|
43
|
+
commitgen -cf --config-scope global # always write ~/.commitgen/.env
|
|
44
|
+
```
|
|
45
|
+
|
|
38
46
|
This opens an interactive configuration menu where you can:
|
|
39
47
|
|
|
40
48
|
1. Set or update your Gemini API key
|
|
@@ -53,10 +61,18 @@ Create a `.env` file in the directory where you will run commitgen (usually the
|
|
|
53
61
|
|
|
54
62
|
```
|
|
55
63
|
GEMINI_API_KEY=your-gemini-api-key
|
|
56
|
-
AI_MODEL=gemini-2.
|
|
64
|
+
AI_MODEL=gemini-2.5-flash
|
|
57
65
|
AUTO_ADD_ALL=true
|
|
58
66
|
```
|
|
59
67
|
|
|
68
|
+
Config discovery order:
|
|
69
|
+
|
|
70
|
+
1. `.env` in current directory
|
|
71
|
+
2. `.env` in parent directories (useful when running from subfolders)
|
|
72
|
+
3. Global config in `~/.commitgen/.env` (created automatically by `commitgen -cf --config-scope auto` when no local `.env` exists)
|
|
73
|
+
|
|
74
|
+
Environment variables already set in your shell (e.g. `GEMINI_API_KEY`) are also respected.
|
|
75
|
+
|
|
60
76
|
## 🚀 Usage
|
|
61
77
|
|
|
62
78
|
With the terminal, inside any Git repository with pending changes, run:
|
|
@@ -77,18 +93,19 @@ The command will:
|
|
|
77
93
|
- `commitgen -c` - Generate and commit with the message
|
|
78
94
|
- `commitgen -cp` - Generate, commit, and push
|
|
79
95
|
- `commitgen -cf` - Configure API key, model, and file staging behavior
|
|
96
|
+
- `commitgen -cf --config-scope [auto|local|global]` - Choose where `.env` is created/updated
|
|
80
97
|
- `commitgen -s` - Show current configuration status
|
|
81
98
|
|
|
82
99
|
### Available Models
|
|
83
100
|
|
|
84
101
|
When configuring with `-cf`, you can choose from:
|
|
85
102
|
|
|
86
|
-
1. **gemini-2.
|
|
87
|
-
2. **gemini-
|
|
88
|
-
3. **gemini-
|
|
89
|
-
4. **gemini-
|
|
90
|
-
5. **gemini-
|
|
91
|
-
6. **gemini-
|
|
103
|
+
1. **gemini-2.5-flash** (default) - Fast and efficient, best price-performance
|
|
104
|
+
2. **gemini-2.5-flash-lite** - Fastest and most economical
|
|
105
|
+
3. **gemini-2.5-pro** - Advanced reasoning and coding
|
|
106
|
+
4. **gemini-3-flash** - Frontier performance (preview)
|
|
107
|
+
5. **gemini-3.5-flash** - Latest stable model, best for coding
|
|
108
|
+
6. **gemini-3.1-pro** - Highest quality (preview)
|
|
92
109
|
|
|
93
110
|
### File Staging Behavior
|
|
94
111
|
|
|
@@ -1,103 +1,120 @@
|
|
|
1
|
-
# commitmessagegenerator
|
|
2
|
-
|
|
3
|
-
Generate objective and technical commit messages with AI (Google Gemini) automatically using your `git diff`.
|
|
4
|
-
|
|
5
|
-
## 📦 Install
|
|
6
|
-
|
|
7
|
-
```bash
|
|
8
|
-
pip install commitmessagegenerator
|
|
9
|
-
```
|
|
10
|
-
|
|
11
|
-
Or, if you're using a `venv`:
|
|
12
|
-
|
|
13
|
-
```bash
|
|
14
|
-
python -m venv venv
|
|
15
|
-
source venv/bin/activate # or .\venv\Scripts\activate in Windows
|
|
16
|
-
pip install commitmessagegenerator
|
|
17
|
-
```
|
|
18
|
-
|
|
19
|
-
## ⚙️ Configuring
|
|
20
|
-
|
|
21
|
-
```bash
|
|
22
|
-
commitgen -cf
|
|
23
|
-
```
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
```
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
-
|
|
92
|
-
-
|
|
93
|
-
-
|
|
94
|
-
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
1
|
+
# commitmessagegenerator
|
|
2
|
+
|
|
3
|
+
Generate objective and technical commit messages with AI (Google Gemini) automatically using your `git diff`.
|
|
4
|
+
|
|
5
|
+
## 📦 Install
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
pip install commitmessagegenerator
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
Or, if you're using a `venv`:
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
python -m venv venv
|
|
15
|
+
source venv/bin/activate # or .\venv\Scripts\activate in Windows
|
|
16
|
+
pip install commitmessagegenerator
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## ⚙️ Configuring
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
commitgen -cf
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
You can explicitly choose where configuration is written:
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
commitgen -cf --config-scope auto # default behavior
|
|
29
|
+
commitgen -cf --config-scope local # always write .env in current directory
|
|
30
|
+
commitgen -cf --config-scope global # always write ~/.commitgen/.env
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
This opens an interactive configuration menu where you can:
|
|
34
|
+
|
|
35
|
+
1. Set or update your Gemini API key
|
|
36
|
+
2. Change the AI model
|
|
37
|
+
3. Configure file staging behavior
|
|
38
|
+
|
|
39
|
+
Each option can be configured independently, and you can exit at any time without saving changes.
|
|
40
|
+
|
|
41
|
+
## Run this and type you API key to the terminal so the package creates the .env file and automatically adds it to the .gitignore
|
|
42
|
+
|
|
43
|
+
Or do it manually:
|
|
44
|
+
|
|
45
|
+
## IMPORTANT - BEFORE CREATING THIS FILE ADD '.venv' TO YOUR .gitignore SO YOUR API KEY ISN'T EXPOSED
|
|
46
|
+
|
|
47
|
+
Create a `.env` file in the directory where you will run commitgen (usually the root of your Git project):
|
|
48
|
+
|
|
49
|
+
```
|
|
50
|
+
GEMINI_API_KEY=your-gemini-api-key
|
|
51
|
+
AI_MODEL=gemini-2.5-flash
|
|
52
|
+
AUTO_ADD_ALL=true
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
Config discovery order:
|
|
56
|
+
|
|
57
|
+
1. `.env` in current directory
|
|
58
|
+
2. `.env` in parent directories (useful when running from subfolders)
|
|
59
|
+
3. Global config in `~/.commitgen/.env` (created automatically by `commitgen -cf --config-scope auto` when no local `.env` exists)
|
|
60
|
+
|
|
61
|
+
Environment variables already set in your shell (e.g. `GEMINI_API_KEY`) are also respected.
|
|
62
|
+
|
|
63
|
+
## 🚀 Usage
|
|
64
|
+
|
|
65
|
+
With the terminal, inside any Git repository with pending changes, run:
|
|
66
|
+
|
|
67
|
+
```bash
|
|
68
|
+
commitgen (-c/-cp)
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
The command will:
|
|
72
|
+
|
|
73
|
+
- Read the git diff;
|
|
74
|
+
- Send it to the Google Gemini API using your configured model;
|
|
75
|
+
- Return a commit message suggestion directly in your terminal.
|
|
76
|
+
|
|
77
|
+
### Available Commands
|
|
78
|
+
|
|
79
|
+
- `commitgen` - Generate commit message only
|
|
80
|
+
- `commitgen -c` - Generate and commit with the message
|
|
81
|
+
- `commitgen -cp` - Generate, commit, and push
|
|
82
|
+
- `commitgen -cf` - Configure API key, model, and file staging behavior
|
|
83
|
+
- `commitgen -cf --config-scope [auto|local|global]` - Choose where `.env` is created/updated
|
|
84
|
+
- `commitgen -s` - Show current configuration status
|
|
85
|
+
|
|
86
|
+
### Available Models
|
|
87
|
+
|
|
88
|
+
When configuring with `-cf`, you can choose from:
|
|
89
|
+
|
|
90
|
+
1. **gemini-2.5-flash** (default) - Fast and efficient, best price-performance
|
|
91
|
+
2. **gemini-2.5-flash-lite** - Fastest and most economical
|
|
92
|
+
3. **gemini-2.5-pro** - Advanced reasoning and coding
|
|
93
|
+
4. **gemini-3-flash** - Frontier performance (preview)
|
|
94
|
+
5. **gemini-3.5-flash** - Latest stable model, best for coding
|
|
95
|
+
6. **gemini-3.1-pro** - Highest quality (preview)
|
|
96
|
+
|
|
97
|
+
### File Staging Behavior
|
|
98
|
+
|
|
99
|
+
When configuring with `-cf`, you can choose how files are staged:
|
|
100
|
+
|
|
101
|
+
1. **Auto-add all files** (default) - Automatically runs `git add --all` before generating the commit message
|
|
102
|
+
2. **Staged only** - Only reads the diff from files you've already staged with `git add`
|
|
103
|
+
|
|
104
|
+
The "staged only" option gives you more control over which changes are included in the commit message.
|
|
105
|
+
|
|
106
|
+
## 🧩 Requisites
|
|
107
|
+
|
|
108
|
+
- Python 3.8 or higher
|
|
109
|
+
- Gemini API Key (Google Generative AI, free at: https://aistudio.google.com/app/apikey)
|
|
110
|
+
- Initialized Git repository
|
|
111
|
+
- Python dependencies (Automatically installed with the package):
|
|
112
|
+
- `GitPython`
|
|
113
|
+
- `google-generativeai`
|
|
114
|
+
- `python-dotenv`
|
|
115
|
+
|
|
116
|
+
## 📄 License
|
|
117
|
+
|
|
118
|
+
```
|
|
119
|
+
MIT License
|
|
120
|
+
```
|
|
@@ -1,21 +1,34 @@
|
|
|
1
1
|
import argparse
|
|
2
2
|
import subprocess
|
|
3
3
|
from .generator import gerar_mensagem_commit
|
|
4
|
-
from .configure import
|
|
4
|
+
from .configure import (
|
|
5
|
+
api_key,
|
|
6
|
+
get_configured_model,
|
|
7
|
+
get_auto_add_setting,
|
|
8
|
+
update_setting,
|
|
9
|
+
get_api_key_status,
|
|
10
|
+
load_config_env,
|
|
11
|
+
resolve_env_path,
|
|
12
|
+
)
|
|
5
13
|
import sys
|
|
6
14
|
import getpass
|
|
7
15
|
|
|
8
16
|
MODEL_MAP = {
|
|
9
|
-
"1": "gemini-2.
|
|
10
|
-
"2": "gemini-
|
|
11
|
-
"3": "gemini-
|
|
12
|
-
"4": "gemini-
|
|
13
|
-
"5": "gemini-
|
|
14
|
-
"6": "gemini-
|
|
17
|
+
"1": "gemini-2.5-flash",
|
|
18
|
+
"2": "gemini-2.5-flash-lite",
|
|
19
|
+
"3": "gemini-2.5-pro",
|
|
20
|
+
"4": "gemini-3-flash",
|
|
21
|
+
"5": "gemini-3.5-flash",
|
|
22
|
+
"6": "gemini-3.1-pro",
|
|
15
23
|
}
|
|
16
24
|
|
|
17
25
|
def configure_menu():
|
|
18
26
|
"""Interactive configuration menu"""
|
|
27
|
+
configure_menu_with_scope("auto")
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def configure_menu_with_scope(config_scope):
|
|
31
|
+
"""Interactive configuration menu with target scope."""
|
|
19
32
|
while True:
|
|
20
33
|
# Get current settings
|
|
21
34
|
api_set = get_api_key_status()
|
|
@@ -37,15 +50,15 @@ def configure_menu():
|
|
|
37
50
|
print("\nExiting configuration.\n")
|
|
38
51
|
break
|
|
39
52
|
elif choice == "1":
|
|
40
|
-
configure_api_key()
|
|
53
|
+
configure_api_key(config_scope)
|
|
41
54
|
elif choice == "2":
|
|
42
|
-
configure_model()
|
|
55
|
+
configure_model(config_scope)
|
|
43
56
|
elif choice == "3":
|
|
44
|
-
configure_staging()
|
|
57
|
+
configure_staging(config_scope)
|
|
45
58
|
else:
|
|
46
59
|
print("\nInvalid option. Please try again.")
|
|
47
60
|
|
|
48
|
-
def configure_api_key():
|
|
61
|
+
def configure_api_key(config_scope):
|
|
49
62
|
"""Configure the API key"""
|
|
50
63
|
print("\n" + "-"*40)
|
|
51
64
|
print("API KEY CONFIGURATION")
|
|
@@ -59,10 +72,10 @@ def configure_api_key():
|
|
|
59
72
|
print("\nNo changes made.")
|
|
60
73
|
return
|
|
61
74
|
|
|
62
|
-
update_setting("GEMINI_API_KEY", key)
|
|
75
|
+
update_setting("GEMINI_API_KEY", key, scope=config_scope)
|
|
63
76
|
print("\n✓ API Key saved successfully!")
|
|
64
77
|
|
|
65
|
-
def configure_model():
|
|
78
|
+
def configure_model(config_scope):
|
|
66
79
|
"""Configure the AI model"""
|
|
67
80
|
current_model = get_configured_model()
|
|
68
81
|
|
|
@@ -71,12 +84,12 @@ def configure_model():
|
|
|
71
84
|
print("-"*40)
|
|
72
85
|
print(f"Current model: {current_model}\n")
|
|
73
86
|
print("Available models:")
|
|
74
|
-
print("1. gemini-2.
|
|
75
|
-
print("2. gemini-
|
|
76
|
-
print("3. gemini-
|
|
77
|
-
print("4. gemini-
|
|
78
|
-
print("5. gemini-
|
|
79
|
-
print("6. gemini-
|
|
87
|
+
print("1. gemini-2.5-flash (default, fast and efficient)")
|
|
88
|
+
print("2. gemini-2.5-flash-lite (fastest, most economical)")
|
|
89
|
+
print("3. gemini-2.5-pro (advanced reasoning and coding)")
|
|
90
|
+
print("4. gemini-3-flash (frontier performance, preview)")
|
|
91
|
+
print("5. gemini-3.5-flash (latest stable, best for coding)")
|
|
92
|
+
print("6. gemini-3.1-pro (highest quality, preview)")
|
|
80
93
|
print("\n0. Cancel")
|
|
81
94
|
|
|
82
95
|
choice = input("\nSelect model (0-6): ").strip()
|
|
@@ -87,12 +100,12 @@ def configure_model():
|
|
|
87
100
|
|
|
88
101
|
if choice in MODEL_MAP:
|
|
89
102
|
selected_model = MODEL_MAP[choice]
|
|
90
|
-
update_setting("AI_MODEL", selected_model)
|
|
103
|
+
update_setting("AI_MODEL", selected_model, scope=config_scope)
|
|
91
104
|
print(f"\n✓ Model changed to: {selected_model}")
|
|
92
105
|
else:
|
|
93
106
|
print("\nInvalid option. No changes made.")
|
|
94
107
|
|
|
95
|
-
def configure_staging():
|
|
108
|
+
def configure_staging(config_scope):
|
|
96
109
|
"""Configure file staging behavior"""
|
|
97
110
|
auto_add = get_auto_add_setting()
|
|
98
111
|
|
|
@@ -111,10 +124,10 @@ def configure_staging():
|
|
|
111
124
|
return
|
|
112
125
|
|
|
113
126
|
if choice == "1":
|
|
114
|
-
update_setting("AUTO_ADD_ALL", "true")
|
|
127
|
+
update_setting("AUTO_ADD_ALL", "true", scope=config_scope)
|
|
115
128
|
print("\n✓ Set to: Auto-add all files")
|
|
116
129
|
elif choice == "2":
|
|
117
|
-
update_setting("AUTO_ADD_ALL", "false")
|
|
130
|
+
update_setting("AUTO_ADD_ALL", "false", scope=config_scope)
|
|
118
131
|
print("\n✓ Set to: Staged only")
|
|
119
132
|
else:
|
|
120
133
|
print("\nInvalid option. No changes made.")
|
|
@@ -125,14 +138,18 @@ def main():
|
|
|
125
138
|
parser.add_argument("-cp", "--commitpush", action="store_true", help="Commits and pushes with the generated message")
|
|
126
139
|
parser.add_argument("-cf", "--configure", action="store_true", help="Configures the GEMINI_API_KEY environment variable")
|
|
127
140
|
parser.add_argument("-s", "--status", action="store_true", help="Shows current configuration status")
|
|
141
|
+
parser.add_argument(
|
|
142
|
+
"--config-scope",
|
|
143
|
+
choices=["auto", "local", "global"],
|
|
144
|
+
default="auto",
|
|
145
|
+
help="Where -cf writes configuration (.env): auto, local, or global",
|
|
146
|
+
)
|
|
128
147
|
args = parser.parse_args()
|
|
129
148
|
|
|
130
149
|
if args.status:
|
|
131
|
-
from .configure import get_configured_model, get_auto_add_setting
|
|
132
150
|
import os
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
load_dotenv()
|
|
151
|
+
|
|
152
|
+
env_path = load_config_env()
|
|
136
153
|
key = os.getenv("GEMINI_API_KEY")
|
|
137
154
|
model = get_configured_model()
|
|
138
155
|
auto_add = get_auto_add_setting()
|
|
@@ -141,6 +158,10 @@ def main():
|
|
|
141
158
|
print(f"API Key: {'✓ Set' if key else '✗ Not set'}")
|
|
142
159
|
print(f"Model: {model}")
|
|
143
160
|
print(f"Auto-add all files: {'✓ Yes' if auto_add else '✗ No (staged only)'}")
|
|
161
|
+
if env_path is not None:
|
|
162
|
+
print(f"Config file: {env_path}")
|
|
163
|
+
else:
|
|
164
|
+
print("Config file: not found (.env not discovered)")
|
|
144
165
|
return
|
|
145
166
|
|
|
146
167
|
if not args.configure:
|
|
@@ -161,7 +182,10 @@ def main():
|
|
|
161
182
|
subprocess.run(["git", "push"])
|
|
162
183
|
|
|
163
184
|
if args.configure:
|
|
164
|
-
|
|
185
|
+
configure_menu_with_scope(args.config_scope)
|
|
186
|
+
configured_path = resolve_env_path()
|
|
187
|
+
if configured_path is not None:
|
|
188
|
+
print(f"\nConfiguration file in use: {configured_path}")
|
|
165
189
|
|
|
166
190
|
if len(sys.argv) == 1:
|
|
167
191
|
print("\nRemoving staged changes (git reset)...")
|
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
import os
|
|
2
|
+
from pathlib import Path
|
|
3
|
+
from dotenv import load_dotenv
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
GLOBAL_ENV_PATH = Path.home() / ".commitgen" / ".env"
|
|
7
|
+
CONFIG_SCOPES = {"auto", "local", "global"}
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def _iter_local_env_candidates():
|
|
11
|
+
"""Yield .env paths from cwd up to filesystem root."""
|
|
12
|
+
current = Path.cwd().resolve()
|
|
13
|
+
for directory in (current, *current.parents):
|
|
14
|
+
yield directory / ".env"
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def resolve_local_env_path():
|
|
18
|
+
"""Resolve an existing local .env from cwd up to filesystem root."""
|
|
19
|
+
for candidate in _iter_local_env_candidates():
|
|
20
|
+
if candidate.exists():
|
|
21
|
+
return candidate
|
|
22
|
+
return None
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def resolve_env_path():
|
|
26
|
+
"""Resolve the best available config file path."""
|
|
27
|
+
local_path = resolve_local_env_path()
|
|
28
|
+
if local_path is not None:
|
|
29
|
+
return local_path
|
|
30
|
+
if GLOBAL_ENV_PATH.exists():
|
|
31
|
+
return GLOBAL_ENV_PATH
|
|
32
|
+
return None
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def resolve_writable_env_path(scope="auto"):
|
|
36
|
+
"""
|
|
37
|
+
Resolve where settings should be written.
|
|
38
|
+
|
|
39
|
+
scope:
|
|
40
|
+
- auto: existing local .env, then existing global .env, else new global .env
|
|
41
|
+
- local: .env in current working directory
|
|
42
|
+
- global: ~/.commitgen/.env
|
|
43
|
+
"""
|
|
44
|
+
if scope not in CONFIG_SCOPES:
|
|
45
|
+
raise ValueError(f"Invalid config scope: {scope}")
|
|
46
|
+
|
|
47
|
+
if scope == "local":
|
|
48
|
+
return Path.cwd().resolve() / ".env"
|
|
49
|
+
|
|
50
|
+
if scope == "global":
|
|
51
|
+
return GLOBAL_ENV_PATH
|
|
52
|
+
|
|
53
|
+
existing_path = resolve_env_path()
|
|
54
|
+
if existing_path is not None:
|
|
55
|
+
return existing_path
|
|
56
|
+
return GLOBAL_ENV_PATH
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def load_config_env():
|
|
60
|
+
"""
|
|
61
|
+
Load configuration from the resolved .env path (if found).
|
|
62
|
+
Returns the loaded path or None.
|
|
63
|
+
"""
|
|
64
|
+
env_path = resolve_env_path()
|
|
65
|
+
if env_path is not None:
|
|
66
|
+
load_dotenv(dotenv_path=env_path, override=False)
|
|
67
|
+
return env_path
|
|
68
|
+
load_dotenv(override=False)
|
|
69
|
+
return None
|
|
70
|
+
|
|
71
|
+
def update_setting(setting_name, value, scope="auto"):
|
|
72
|
+
"""Update a single setting in the .env file"""
|
|
73
|
+
env_path = resolve_writable_env_path(scope=scope)
|
|
74
|
+
env_path.parent.mkdir(parents=True, exist_ok=True)
|
|
75
|
+
|
|
76
|
+
if env_path.exists():
|
|
77
|
+
with open(env_path, "r", encoding="utf-8") as f:
|
|
78
|
+
lines = f.readlines()
|
|
79
|
+
|
|
80
|
+
found = False
|
|
81
|
+
new_lines = []
|
|
82
|
+
for line in lines:
|
|
83
|
+
key_name = line.split("=")[0] if "=" in line else None
|
|
84
|
+
if key_name == setting_name:
|
|
85
|
+
new_lines.append(f"{setting_name}={value}\n")
|
|
86
|
+
found = True
|
|
87
|
+
else:
|
|
88
|
+
new_lines.append(line)
|
|
89
|
+
|
|
90
|
+
if not found:
|
|
91
|
+
new_lines.append(f"{setting_name}={value}\n")
|
|
92
|
+
|
|
93
|
+
with open(env_path, "w", encoding="utf-8") as f:
|
|
94
|
+
f.writelines(new_lines)
|
|
95
|
+
else:
|
|
96
|
+
with open(env_path, "w", encoding="utf-8") as f:
|
|
97
|
+
f.write(f"{setting_name}={value}\n")
|
|
98
|
+
|
|
99
|
+
_ensure_gitignore(env_path)
|
|
100
|
+
|
|
101
|
+
def _ensure_gitignore(env_path):
|
|
102
|
+
"""Ensure .env is in .gitignore for local (non-global) config files."""
|
|
103
|
+
if env_path == GLOBAL_ENV_PATH:
|
|
104
|
+
return
|
|
105
|
+
|
|
106
|
+
gitignore_path = env_path.parent / ".gitignore"
|
|
107
|
+
if gitignore_path.exists():
|
|
108
|
+
with open(gitignore_path, "r+", encoding="utf-8") as outfile:
|
|
109
|
+
lines = outfile.readlines()
|
|
110
|
+
env_in_gitignore = next((line for line in lines if line.strip() == ".env"), None)
|
|
111
|
+
if not env_in_gitignore:
|
|
112
|
+
outfile.write("\n.env")
|
|
113
|
+
|
|
114
|
+
def api_key(key, model="gemini-2.5-flash", auto_add_all=True, scope="auto"):
|
|
115
|
+
"""Set all configuration at once (legacy function)"""
|
|
116
|
+
config = {
|
|
117
|
+
"GEMINI_API_KEY": key,
|
|
118
|
+
"AI_MODEL": model,
|
|
119
|
+
"AUTO_ADD_ALL": str(auto_add_all).lower()
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
env_path = resolve_writable_env_path(scope=scope)
|
|
123
|
+
env_path.parent.mkdir(parents=True, exist_ok=True)
|
|
124
|
+
|
|
125
|
+
if env_path.exists():
|
|
126
|
+
with open(env_path, "r", encoding="utf-8") as f:
|
|
127
|
+
lines = f.readlines()
|
|
128
|
+
|
|
129
|
+
existing_keys = set()
|
|
130
|
+
new_lines = []
|
|
131
|
+
for line in lines:
|
|
132
|
+
key_name = line.split("=")[0] if "=" in line else None
|
|
133
|
+
if key_name in config:
|
|
134
|
+
new_lines.append(f"{key_name}={config[key_name]}\n")
|
|
135
|
+
existing_keys.add(key_name)
|
|
136
|
+
else:
|
|
137
|
+
new_lines.append(line)
|
|
138
|
+
|
|
139
|
+
for key_name, value in config.items():
|
|
140
|
+
if key_name not in existing_keys:
|
|
141
|
+
new_lines.append(f"{key_name}={value}\n")
|
|
142
|
+
|
|
143
|
+
with open(env_path, "w", encoding="utf-8") as f:
|
|
144
|
+
f.writelines(new_lines)
|
|
145
|
+
else:
|
|
146
|
+
with open(env_path, "w", encoding="utf-8") as f:
|
|
147
|
+
for key_name, value in config.items():
|
|
148
|
+
f.write(f"{key_name}={value}\n")
|
|
149
|
+
|
|
150
|
+
_ensure_gitignore(env_path)
|
|
151
|
+
|
|
152
|
+
def get_api_key_status():
|
|
153
|
+
"""Check if API key is set"""
|
|
154
|
+
env_key = os.getenv("GEMINI_API_KEY")
|
|
155
|
+
if env_key:
|
|
156
|
+
return True
|
|
157
|
+
|
|
158
|
+
env_path = resolve_env_path()
|
|
159
|
+
if env_path is not None:
|
|
160
|
+
with open(env_path, "r", encoding="utf-8") as f:
|
|
161
|
+
lines = f.readlines()
|
|
162
|
+
api_line = next((line for line in lines if line.startswith("GEMINI_API_KEY=")), None)
|
|
163
|
+
if api_line:
|
|
164
|
+
value = api_line.split("=", 1)[1].strip()
|
|
165
|
+
return bool(value)
|
|
166
|
+
return False
|
|
167
|
+
|
|
168
|
+
def get_configured_model():
|
|
169
|
+
"""Get the currently configured AI model from .env file"""
|
|
170
|
+
env_model = os.getenv("AI_MODEL")
|
|
171
|
+
if env_model:
|
|
172
|
+
return env_model
|
|
173
|
+
|
|
174
|
+
env_path = resolve_env_path()
|
|
175
|
+
if env_path is not None:
|
|
176
|
+
with open(env_path, "r", encoding="utf-8") as outfile:
|
|
177
|
+
lines = outfile.readlines()
|
|
178
|
+
model_line = next((line for line in lines if line.startswith("AI_MODEL=")), None)
|
|
179
|
+
if model_line:
|
|
180
|
+
return model_line.split("=", 1)[1].strip()
|
|
181
|
+
return "gemini-2.5-flash" # Default fallback
|
|
182
|
+
|
|
183
|
+
def get_auto_add_setting():
|
|
184
|
+
"""Get the auto-add all files setting from .env file"""
|
|
185
|
+
env_auto_add = os.getenv("AUTO_ADD_ALL")
|
|
186
|
+
if env_auto_add is not None:
|
|
187
|
+
return env_auto_add.strip().lower() == "true"
|
|
188
|
+
|
|
189
|
+
env_path = resolve_env_path()
|
|
190
|
+
if env_path is not None:
|
|
191
|
+
with open(env_path, "r", encoding="utf-8") as outfile:
|
|
192
|
+
lines = outfile.readlines()
|
|
193
|
+
auto_add_line = next((line for line in lines if line.startswith("AUTO_ADD_ALL=")), None)
|
|
194
|
+
if auto_add_line:
|
|
195
|
+
return auto_add_line.split("=", 1)[1].strip().lower() == "true"
|
|
196
|
+
return True # Default: auto-add all files (current behavior)
|
{commitmessagegenerator-2.2.0 → commitmessagegenerator-2.5.1}/commitmessagegenerator/generator.py
RENAMED
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
import os
|
|
2
|
-
from dotenv import load_dotenv
|
|
3
2
|
from google import genai
|
|
4
3
|
from git import Repo
|
|
5
|
-
from .configure import get_configured_model, get_auto_add_setting
|
|
4
|
+
from .configure import get_configured_model, get_auto_add_setting, load_config_env
|
|
6
5
|
|
|
7
6
|
def gerar_mensagem_commit():
|
|
8
|
-
|
|
7
|
+
load_config_env()
|
|
9
8
|
key = os.getenv("GEMINI_API_KEY")
|
|
10
9
|
if not key:
|
|
11
10
|
raise RuntimeError("The GEMINI_API_KEY environment variable is not set.")
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: commitmessagegenerator
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.5.1
|
|
4
4
|
Summary: Generate commit messages with AI (Google Gemini) automatically using `git diff`.
|
|
5
5
|
Author-email: Gabriel Terceiro <gcarolinoterceiro@gmail.com>
|
|
6
6
|
License: MIT
|
|
@@ -35,6 +35,14 @@ pip install commitmessagegenerator
|
|
|
35
35
|
commitgen -cf
|
|
36
36
|
```
|
|
37
37
|
|
|
38
|
+
You can explicitly choose where configuration is written:
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
commitgen -cf --config-scope auto # default behavior
|
|
42
|
+
commitgen -cf --config-scope local # always write .env in current directory
|
|
43
|
+
commitgen -cf --config-scope global # always write ~/.commitgen/.env
|
|
44
|
+
```
|
|
45
|
+
|
|
38
46
|
This opens an interactive configuration menu where you can:
|
|
39
47
|
|
|
40
48
|
1. Set or update your Gemini API key
|
|
@@ -53,10 +61,18 @@ Create a `.env` file in the directory where you will run commitgen (usually the
|
|
|
53
61
|
|
|
54
62
|
```
|
|
55
63
|
GEMINI_API_KEY=your-gemini-api-key
|
|
56
|
-
AI_MODEL=gemini-2.
|
|
64
|
+
AI_MODEL=gemini-2.5-flash
|
|
57
65
|
AUTO_ADD_ALL=true
|
|
58
66
|
```
|
|
59
67
|
|
|
68
|
+
Config discovery order:
|
|
69
|
+
|
|
70
|
+
1. `.env` in current directory
|
|
71
|
+
2. `.env` in parent directories (useful when running from subfolders)
|
|
72
|
+
3. Global config in `~/.commitgen/.env` (created automatically by `commitgen -cf --config-scope auto` when no local `.env` exists)
|
|
73
|
+
|
|
74
|
+
Environment variables already set in your shell (e.g. `GEMINI_API_KEY`) are also respected.
|
|
75
|
+
|
|
60
76
|
## 🚀 Usage
|
|
61
77
|
|
|
62
78
|
With the terminal, inside any Git repository with pending changes, run:
|
|
@@ -77,18 +93,19 @@ The command will:
|
|
|
77
93
|
- `commitgen -c` - Generate and commit with the message
|
|
78
94
|
- `commitgen -cp` - Generate, commit, and push
|
|
79
95
|
- `commitgen -cf` - Configure API key, model, and file staging behavior
|
|
96
|
+
- `commitgen -cf --config-scope [auto|local|global]` - Choose where `.env` is created/updated
|
|
80
97
|
- `commitgen -s` - Show current configuration status
|
|
81
98
|
|
|
82
99
|
### Available Models
|
|
83
100
|
|
|
84
101
|
When configuring with `-cf`, you can choose from:
|
|
85
102
|
|
|
86
|
-
1. **gemini-2.
|
|
87
|
-
2. **gemini-
|
|
88
|
-
3. **gemini-
|
|
89
|
-
4. **gemini-
|
|
90
|
-
5. **gemini-
|
|
91
|
-
6. **gemini-
|
|
103
|
+
1. **gemini-2.5-flash** (default) - Fast and efficient, best price-performance
|
|
104
|
+
2. **gemini-2.5-flash-lite** - Fastest and most economical
|
|
105
|
+
3. **gemini-2.5-pro** - Advanced reasoning and coding
|
|
106
|
+
4. **gemini-3-flash** - Frontier performance (preview)
|
|
107
|
+
5. **gemini-3.5-flash** - Latest stable model, best for coding
|
|
108
|
+
6. **gemini-3.1-pro** - Highest quality (preview)
|
|
92
109
|
|
|
93
110
|
### File Staging Behavior
|
|
94
111
|
|
|
@@ -1,21 +1,21 @@
|
|
|
1
|
-
[build-system]
|
|
2
|
-
requires = ["setuptools>=61.0"]
|
|
3
|
-
build-backend = "setuptools.build_meta"
|
|
4
|
-
|
|
5
|
-
[project]
|
|
6
|
-
name = "commitmessagegenerator"
|
|
7
|
-
version = "2.
|
|
8
|
-
description = "Generate commit messages with AI (Google Gemini) automatically using `git diff`."
|
|
9
|
-
readme = "README.md"
|
|
10
|
-
license = {text = "MIT"}
|
|
11
|
-
authors = [
|
|
12
|
-
{name = "Gabriel Terceiro", email = "gcarolinoterceiro@gmail.com"}
|
|
13
|
-
]
|
|
14
|
-
dependencies = [
|
|
15
|
-
"google-genai==1.20.0",
|
|
16
|
-
"GitPython==3.1.44",
|
|
17
|
-
"python-dotenv==1.1.0"
|
|
18
|
-
]
|
|
19
|
-
|
|
20
|
-
[project.scripts]
|
|
21
|
-
commitgen = "commitmessagegenerator.cli:main"
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=61.0"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "commitmessagegenerator"
|
|
7
|
+
version = "2.5.1"
|
|
8
|
+
description = "Generate commit messages with AI (Google Gemini) automatically using `git diff`."
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
license = {text = "MIT"}
|
|
11
|
+
authors = [
|
|
12
|
+
{name = "Gabriel Terceiro", email = "gcarolinoterceiro@gmail.com"}
|
|
13
|
+
]
|
|
14
|
+
dependencies = [
|
|
15
|
+
"google-genai==1.20.0",
|
|
16
|
+
"GitPython==3.1.44",
|
|
17
|
+
"python-dotenv==1.1.0"
|
|
18
|
+
]
|
|
19
|
+
|
|
20
|
+
[project.scripts]
|
|
21
|
+
commitgen = "commitmessagegenerator.cli:main"
|
|
@@ -1,103 +0,0 @@
|
|
|
1
|
-
import os
|
|
2
|
-
|
|
3
|
-
def update_setting(setting_name, value):
|
|
4
|
-
"""Update a single setting in the .env file"""
|
|
5
|
-
if os.path.exists(".env"):
|
|
6
|
-
with open(".env", "r") as f:
|
|
7
|
-
lines = f.readlines()
|
|
8
|
-
|
|
9
|
-
found = False
|
|
10
|
-
new_lines = []
|
|
11
|
-
for line in lines:
|
|
12
|
-
key_name = line.split("=")[0] if "=" in line else None
|
|
13
|
-
if key_name == setting_name:
|
|
14
|
-
new_lines.append(f"{setting_name}={value}\n")
|
|
15
|
-
found = True
|
|
16
|
-
else:
|
|
17
|
-
new_lines.append(line)
|
|
18
|
-
|
|
19
|
-
if not found:
|
|
20
|
-
new_lines.append(f"{setting_name}={value}\n")
|
|
21
|
-
|
|
22
|
-
with open(".env", "w") as f:
|
|
23
|
-
f.writelines(new_lines)
|
|
24
|
-
else:
|
|
25
|
-
with open(".env", "w") as f:
|
|
26
|
-
f.write(f"{setting_name}={value}\n")
|
|
27
|
-
|
|
28
|
-
_ensure_gitignore()
|
|
29
|
-
|
|
30
|
-
def _ensure_gitignore():
|
|
31
|
-
"""Ensure .env is in .gitignore"""
|
|
32
|
-
if os.path.exists(".gitignore"):
|
|
33
|
-
with open(".gitignore", "r+") as outfile:
|
|
34
|
-
lines = outfile.readlines()
|
|
35
|
-
env_in_gitignore = next((line for line in lines if line.strip() == ".env"), None)
|
|
36
|
-
if not env_in_gitignore:
|
|
37
|
-
outfile.write("\n.env")
|
|
38
|
-
|
|
39
|
-
def api_key(key, model="gemini-2.0-flash", auto_add_all=True):
|
|
40
|
-
"""Set all configuration at once (legacy function)"""
|
|
41
|
-
config = {
|
|
42
|
-
"GEMINI_API_KEY": key,
|
|
43
|
-
"AI_MODEL": model,
|
|
44
|
-
"AUTO_ADD_ALL": str(auto_add_all).lower()
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
if os.path.exists(".env"):
|
|
48
|
-
with open(".env", "r") as f:
|
|
49
|
-
lines = f.readlines()
|
|
50
|
-
|
|
51
|
-
existing_keys = set()
|
|
52
|
-
new_lines = []
|
|
53
|
-
for line in lines:
|
|
54
|
-
key_name = line.split("=")[0] if "=" in line else None
|
|
55
|
-
if key_name in config:
|
|
56
|
-
new_lines.append(f"{key_name}={config[key_name]}\n")
|
|
57
|
-
existing_keys.add(key_name)
|
|
58
|
-
else:
|
|
59
|
-
new_lines.append(line)
|
|
60
|
-
|
|
61
|
-
for key_name, value in config.items():
|
|
62
|
-
if key_name not in existing_keys:
|
|
63
|
-
new_lines.append(f"{key_name}={value}\n")
|
|
64
|
-
|
|
65
|
-
with open(".env", "w") as f:
|
|
66
|
-
f.writelines(new_lines)
|
|
67
|
-
else:
|
|
68
|
-
with open(".env", "w") as f:
|
|
69
|
-
for key_name, value in config.items():
|
|
70
|
-
f.write(f"{key_name}={value}\n")
|
|
71
|
-
|
|
72
|
-
_ensure_gitignore()
|
|
73
|
-
|
|
74
|
-
def get_api_key_status():
|
|
75
|
-
"""Check if API key is set"""
|
|
76
|
-
if os.path.exists(".env"):
|
|
77
|
-
with open(".env", "r") as f:
|
|
78
|
-
lines = f.readlines()
|
|
79
|
-
api_line = next((line for line in lines if line.startswith("GEMINI_API_KEY=")), None)
|
|
80
|
-
if api_line:
|
|
81
|
-
value = api_line.split("=", 1)[1].strip()
|
|
82
|
-
return bool(value)
|
|
83
|
-
return False
|
|
84
|
-
|
|
85
|
-
def get_configured_model():
|
|
86
|
-
"""Get the currently configured AI model from .env file"""
|
|
87
|
-
if os.path.exists(".env"):
|
|
88
|
-
with open(".env", "r") as outfile:
|
|
89
|
-
lines = outfile.readlines()
|
|
90
|
-
model_line = next((line for line in lines if line.startswith("AI_MODEL=")), None)
|
|
91
|
-
if model_line:
|
|
92
|
-
return model_line.split("=", 1)[1].strip()
|
|
93
|
-
return "gemini-2.0-flash" # Default fallback
|
|
94
|
-
|
|
95
|
-
def get_auto_add_setting():
|
|
96
|
-
"""Get the auto-add all files setting from .env file"""
|
|
97
|
-
if os.path.exists(".env"):
|
|
98
|
-
with open(".env", "r") as outfile:
|
|
99
|
-
lines = outfile.readlines()
|
|
100
|
-
auto_add_line = next((line for line in lines if line.startswith("AUTO_ADD_ALL=")), None)
|
|
101
|
-
if auto_add_line:
|
|
102
|
-
return auto_add_line.split("=", 1)[1].strip().lower() == "true"
|
|
103
|
-
return True # Default: auto-add all files (current behavior)
|
|
File without changes
|
{commitmessagegenerator-2.2.0 → commitmessagegenerator-2.5.1}/commitmessagegenerator/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|