-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathversion.py
More file actions
154 lines (131 loc) · 4.96 KB
/
Copy pathversion.py
File metadata and controls
154 lines (131 loc) · 4.96 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
#!/usr/bin/env python3
"""
AI-Powered Semantic Versioning for FOCAL
Analyzes git changes and uses OpenAI to determine the next version.
"""
import subprocess
import re
import json
from pathlib import Path
from openai import OpenAI
def get_openai_key():
"""Get OpenAI API key from workflow/.openai_key file."""
key_file = Path(__file__).parent / 'workflow' / '.openai_key'
if not key_file.exists():
raise FileNotFoundError("OpenAI API key not found. Run install.sh first.")
return key_file.read_text().strip()
def get_current_version():
"""Get current version from git tags or default to 1.0.0."""
try:
# Get latest tag
result = subprocess.run(
['git', 'describe', '--tags', '--abbrev=0'],
capture_output=True, text=True, check=True
)
tag = result.stdout.strip()
# Extract version from tag (handles v1.0.0 or 1.0.0)
match = re.search(r'(\d+\.\d+\.\d+)', tag)
return match.group(1) if match else "1.0.0"
except subprocess.CalledProcessError:
return "1.0.0"
def get_changes_since_last_tag():
"""Get all commits since the last tag."""
try:
# Try to get commits since last tag
result = subprocess.run(
['git', 'log', '--oneline', '--no-merges', '--since="1 week ago"'],
capture_output=True, text=True, check=True
)
if not result.stdout.strip():
# If no recent commits, get last few commits
result = subprocess.run(
['git', 'log', '--oneline', '--no-merges', '-10'],
capture_output=True, text=True, check=True
)
return result.stdout.strip()
except subprocess.CalledProcessError:
return "No git history available"
def get_file_changes():
"""Get summary of changed files."""
try:
result = subprocess.run(
['git', 'diff', '--name-status', 'HEAD~5..HEAD'],
capture_output=True, text=True, check=True
)
return result.stdout.strip()
except subprocess.CalledProcessError:
return "No file changes detected"
def ask_openai_for_version(current_version, changes, file_changes):
"""Use OpenAI to analyze changes and suggest next version."""
client = OpenAI(api_key=get_openai_key())
prompt = f"""
You are a semantic versioning expert. Analyze these git changes and determine the next version number.
Current version: {current_version}
Recent commits:
{changes}
File changes:
{file_changes}
Semantic Versioning Rules:
- MAJOR (X.y.z): Breaking changes, incompatible API changes
- MINOR (x.Y.z): New features, backwards compatible
- PATCH (x.y.Z): Bug fixes, backwards compatible
Respond with ONLY a JSON object:
{{
"next_version": "X.Y.Z",
"bump_type": "major|minor|patch",
"reasoning": "Brief explanation of why this version bump"
}}
"""
try:
response = client.chat.completions.create(
model="gpt-4",
messages=[{"role": "user", "content": prompt}],
max_tokens=200,
temperature=0.1
)
result_text = response.choices[0].message.content.strip()
# Extract JSON from response
json_match = re.search(r'\{[^}]+\}', result_text, re.DOTALL)
if json_match:
return json.loads(json_match.group())
else:
raise ValueError("No valid JSON found in OpenAI response")
except Exception as e:
print(f"OpenAI API error: {e}")
# Fallback to simple patch increment
parts = current_version.split('.')
parts[2] = str(int(parts[2]) + 1)
return {
"next_version": '.'.join(parts),
"bump_type": "patch",
"reasoning": f"Fallback patch increment due to API error: {e}"
}
def increment_version(current_version, bump_type):
"""Manually increment version based on bump type."""
major, minor, patch = map(int, current_version.split('.'))
if bump_type == "major":
return f"{major + 1}.0.0"
elif bump_type == "minor":
return f"{major}.{minor + 1}.0"
else: # patch
return f"{major}.{minor}.{patch + 1}"
def get_next_version():
"""Main function to get the next version using AI analysis."""
current_version = get_current_version()
changes = get_changes_since_last_tag()
file_changes = get_file_changes()
print(f"Current version: {current_version}")
print(f"Analyzing {len(changes.splitlines())} recent commits...")
# Get AI recommendation
ai_result = ask_openai_for_version(current_version, changes, file_changes)
print(f"AI Analysis:")
print(f" Recommended: {ai_result['next_version']} ({ai_result['bump_type']} bump)")
print(f" Reasoning: {ai_result['reasoning']}")
return ai_result
if __name__ == "__main__":
try:
result = get_next_version()
print(f"\nNext version: {result['next_version']}")
except Exception as e:
print(f"Error: {e}")
exit(1)