-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathcommit-msg
More file actions
156 lines (123 loc) · 4.44 KB
/
commit-msg
File metadata and controls
156 lines (123 loc) · 4.44 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
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
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
#!/usr/bin/env python3
"""
Pre-commit hook for commit message validation.
Uses local LLM to check and fix commit message format.
"""
import subprocess
import sys
import json
import os
# Your commit message standards
COMMIT_STANDARDS = """
Required format:
- Start with JIRA ticket ID in brackets: [PROJ-123]
- Followed by brief description in present tense
- Max 72 characters for subject line
- No trailing periods
- Examples:
✓ [INFRA-456] Add retry logic to API client
✓ [SEC-789] Fix SQL injection in user query
✗ [PROJ-123] Added new feature. (past tense, period)
✗ Fixed bug (missing ticket ID)
"""
def extract_commit_message():
"""Get the commit message from git."""
try:
msg = subprocess.check_output(
['git', 'log', '-1', '--pretty=%B', 'HEAD'],
stderr=subprocess.DEVNULL
).decode('utf-8').strip()
return msg
except:
# If no commits yet, read from COMMIT_EDITMSG
commit_msg_file = '.git/COMMIT_EDITMSG'
if os.path.exists(commit_msg_file):
with open(commit_msg_file, 'r') as f:
return f.read().strip()
return None
def validate_with_llm(message: str) -> dict:
"""
Send commit message to local LLM for validation.
Replace this with your actual LLM integration.
"""
# TODO: Replace with your LLM API call
# Example for local Claude/GPT setup:
prompt = f"""{COMMIT_STANDARDS}
Current commit message:
{message}
Respond in JSON format:
{{
"valid": true/false,
"issues": ["list of problems"],
"suggested_fix": "corrected message if invalid"
}}
"""
# Placeholder - replace with actual LLM call
# For now, do basic regex validation
import re
ticket_pattern = r'^\[([A-Z]+-\d+)\]\s+'
match = re.match(ticket_pattern, message)
issues = []
if not match:
issues.append("Missing JIRA ticket ID in [PROJ-123] format")
if message.endswith('.'):
issues.append("Remove trailing period")
if len(message.split('\n')[0]) > 72:
issues.append("Subject line exceeds 72 characters")
# Check for past tense verbs
past_tense = ['added', 'fixed', 'updated', 'removed', 'changed']
message_lower = message.lower()
if any(word in message_lower for word in past_tense):
issues.append("Use present tense (add, fix, update, not added, fixed, updated)")
valid = len(issues) == 0
# Generate suggested fix if invalid
suggested = message
if issues:
# Basic auto-fix attempts
suggested = suggested.rstrip('.')
suggested = suggested.replace('Added', 'Add').replace('added', 'add')
suggested = suggested.replace('Fixed', 'Fix').replace('fixed', 'fix')
suggested = suggested.replace('Updated', 'Update').replace('updated', 'update')
# If missing ticket, try to extract from branch name
if not match:
branch = subprocess.check_output(
['git', 'rev-parse', '--abbrev-ref', 'HEAD']
).decode('utf-8').strip()
ticket_from_branch = re.search(r'([A-Z]+-\d+)', branch)
if ticket_from_branch:
suggested = f"[{ticket_from_branch.group(1)}] {suggested}"
return {
'valid': valid,
'issues': issues,
'suggested_fix': suggested if not valid else message
}
def main():
"""Main validation logic."""
message = extract_commit_message()
if not message:
print("No commit message found")
sys.exit(1)
# Skip validation for merge commits
if message.startswith('Merge'):
sys.exit(0)
result = validate_with_llm(message)
if result['valid']:
print("✓ Commit message is valid")
sys.exit(0)
print("\n❌ Commit message validation failed:\n")
for issue in result['issues']:
print(f" • {issue}")
print(f"\nCurrent message:\n {message}")
print(f"\nSuggested fix:\n {result['suggested_fix']}")
# Ask user if they want to apply the fix
response = input("\nApply suggested fix? (y/n): ").lower()
if response == 'y':
# Amend the commit with fixed message
subprocess.run(['git', 'commit', '--amend', '-m', result['suggested_fix']])
print("✓ Commit message updated")
sys.exit(0)
else:
print("Commit aborted. Please fix the message manually.")
sys.exit(1)
if __name__ == '__main__':
main()