Skip to content

Commit b078746

Browse files
authored
Merge 3325b0c into c1d64e7
2 parents c1d64e7 + 3325b0c commit b078746

File tree

1 file changed

+285
-0
lines changed

1 file changed

+285
-0
lines changed
Lines changed: 285 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,285 @@
1+
name: Update Spring Boot Versions
2+
3+
on:
4+
schedule:
5+
# Run every Monday at 9:00 AM UTC
6+
- cron: '0 9 * * 1'
7+
workflow_dispatch: # Allow manual triggering
8+
9+
permissions:
10+
contents: write
11+
pull-requests: write
12+
13+
jobs:
14+
update-spring-boot-versions:
15+
runs-on: ubuntu-latest
16+
steps:
17+
- name: Checkout repository
18+
uses: actions/checkout@v5
19+
with:
20+
token: ${{ secrets.GITHUB_TOKEN }}
21+
22+
- name: Set up Python
23+
uses: actions/setup-python@v6
24+
with:
25+
python-version: '3.11'
26+
27+
- name: Install dependencies
28+
run: |
29+
pip install requests packaging
30+
31+
- name: Update Spring Boot versions
32+
run: |
33+
cat << 'EOF' > update_versions.py
34+
import json
35+
import re
36+
import requests
37+
from packaging import version
38+
import yaml
39+
import sys
40+
from pathlib import Path
41+
42+
def get_spring_boot_versions():
43+
"""Fetch all Spring Boot versions from Maven Central"""
44+
url = "https://search.maven.org/solrsearch/select"
45+
params = {
46+
"q": "g:org.springframework.boot AND a:spring-boot",
47+
"rows": 200,
48+
"wt": "json"
49+
}
50+
51+
try:
52+
response = requests.get(url, params=params, timeout=30)
53+
response.raise_for_status()
54+
data = response.json()
55+
56+
versions = []
57+
for doc in data['response']['docs']:
58+
v = doc['v']
59+
# Only include release versions (no SNAPSHOT, RC, M versions for 2.x and 3.x)
60+
if not any(suffix in v for suffix in ['SNAPSHOT', 'RC', 'BUILD']):
61+
versions.append(v)
62+
63+
return sorted(versions, key=version.parse)
64+
except Exception as e:
65+
print(f"Error fetching versions: {e}")
66+
return []
67+
68+
def parse_current_versions(workflow_file):
69+
"""Parse current Spring Boot versions from workflow file"""
70+
content = Path(workflow_file).read_text()
71+
72+
# Find the springboot-version matrix line
73+
pattern = r'springboot-version:\s*\[\s*([^\]]+)\s*\]'
74+
match = re.search(pattern, content)
75+
76+
if not match:
77+
return []
78+
79+
# Extract versions from the match
80+
versions_str = match.group(1)
81+
versions = []
82+
for v in versions_str.split(','):
83+
v = v.strip().strip("'\"")
84+
if v:
85+
versions.append(v)
86+
87+
return versions
88+
89+
def get_latest_patch(all_versions, minor_version):
90+
"""Get the latest patch version for a given minor version"""
91+
target_minor = '.'.join(minor_version.split('.')[:2])
92+
patches = [v for v in all_versions if v.startswith(target_minor + '.')]
93+
return max(patches, key=version.parse) if patches else minor_version
94+
95+
def update_version_matrix(current_versions, all_versions, major_version):
96+
"""Update version matrix based on available versions"""
97+
if not current_versions or not all_versions:
98+
return current_versions, False
99+
100+
# Filter versions for this major version
101+
major_versions = [v for v in all_versions if v.startswith(f"{major_version}.")]
102+
if not major_versions:
103+
return current_versions, False
104+
105+
updated_versions = []
106+
changes_made = False
107+
108+
# Always keep the minimum supported version (first version)
109+
min_version = current_versions[0]
110+
updated_versions.append(min_version)
111+
112+
# Update patch versions for existing minor versions
113+
for curr_version in current_versions[1:]: # Skip min version
114+
if any(suffix in curr_version for suffix in ['M', 'RC', 'SNAPSHOT']):
115+
# Keep milestone/RC versions as-is for pre-release majors
116+
updated_versions.append(curr_version)
117+
continue
118+
119+
latest_patch = get_latest_patch(major_versions, curr_version)
120+
if latest_patch != curr_version:
121+
print(f"Updating {curr_version} -> {latest_patch}")
122+
changes_made = True
123+
updated_versions.append(latest_patch)
124+
125+
# Check for new minor versions
126+
current_minors = set()
127+
for v in current_versions:
128+
if not any(suffix in v for suffix in ['M', 'RC', 'SNAPSHOT']):
129+
current_minors.add('.'.join(v.split('.')[:2]))
130+
131+
available_minors = set()
132+
for v in major_versions:
133+
if not any(suffix in v for suffix in ['M', 'RC', 'SNAPSHOT']):
134+
available_minors.add('.'.join(v.split('.')[:2]))
135+
136+
new_minors = available_minors - current_minors
137+
if new_minors:
138+
# Add latest patch of new minor versions
139+
for new_minor in sorted(new_minors, key=version.parse):
140+
latest_patch = get_latest_patch(major_versions, new_minor + '.0')
141+
updated_versions.append(latest_patch)
142+
print(f"Adding new minor version: {latest_patch}")
143+
changes_made = True
144+
145+
# Remove second oldest minor (but keep absolute minimum)
146+
if len(updated_versions) > 7: # If we have more than 7 versions
147+
# Sort by version, keep min version and remove second oldest
148+
sorted_versions = sorted(updated_versions, key=version.parse)
149+
min_version = sorted_versions[0]
150+
other_versions = sorted_versions[1:]
151+
152+
# Keep all but the oldest of the "other" versions
153+
if len(other_versions) > 6:
154+
updated_versions = [min_version] + other_versions[1:]
155+
print(f"Removed second oldest version: {other_versions[0]}")
156+
changes_made = True
157+
158+
# Sort final versions
159+
min_version = updated_versions[0]
160+
other_versions = sorted([v for v in updated_versions if v != min_version], key=version.parse)
161+
final_versions = [min_version] + other_versions
162+
163+
return final_versions, changes_made
164+
165+
def update_workflow_file(workflow_file, new_versions):
166+
"""Update the workflow file with new versions"""
167+
content = Path(workflow_file).read_text()
168+
169+
# Format new versions for YAML
170+
versions_str = ", ".join([f"'{v}'" for v in new_versions])
171+
new_matrix_line = f" springboot-version: [ {versions_str} ]"
172+
173+
# Replace the matrix line
174+
pattern = r'(\s*)springboot-version:\s*\[\s*[^\]]+\s*\]'
175+
replacement = new_matrix_line
176+
177+
updated_content = re.sub(pattern, replacement, content)
178+
179+
if updated_content != content:
180+
Path(workflow_file).write_text(updated_content)
181+
return True
182+
return False
183+
184+
def main():
185+
print("Fetching Spring Boot versions...")
186+
all_versions = get_spring_boot_versions()
187+
188+
if not all_versions:
189+
print("No versions found, exiting")
190+
sys.exit(1)
191+
192+
print(f"Found {len(all_versions)} versions")
193+
194+
workflows = [
195+
(".github/workflows/spring-boot-2-matrix.yml", "2"),
196+
(".github/workflows/spring-boot-3-matrix.yml", "3"),
197+
(".github/workflows/spring-boot-4-matrix.yml", "4")
198+
]
199+
200+
changes_made = False
201+
change_summary = []
202+
203+
for workflow_file, major_version in workflows:
204+
if not Path(workflow_file).exists():
205+
continue
206+
207+
print(f"\nProcessing {workflow_file} (Spring Boot {major_version}.x)")
208+
209+
current_versions = parse_current_versions(workflow_file)
210+
if not current_versions:
211+
continue
212+
213+
print(f"Current versions: {current_versions}")
214+
215+
new_versions, file_changed = update_version_matrix(current_versions, all_versions, major_version)
216+
217+
if file_changed:
218+
print(f"New versions: {new_versions}")
219+
if update_workflow_file(workflow_file, new_versions):
220+
changes_made = True
221+
change_summary.append(f"Spring Boot {major_version}.x: {' -> '.join([str(current_versions), str(new_versions)])}")
222+
else:
223+
print("No changes needed")
224+
225+
if changes_made:
226+
print(f"\nChanges made to workflows:")
227+
for change in change_summary:
228+
print(f" - {change}")
229+
230+
# Write summary for later use
231+
with open('version_changes.txt', 'w') as f:
232+
f.write('\n'.join(change_summary))
233+
else:
234+
print("\nNo version updates needed")
235+
236+
sys.exit(0 if changes_made else 1)
237+
238+
if __name__ == "__main__":
239+
main()
240+
EOF
241+
242+
python update_versions.py
243+
244+
- name: Check for changes
245+
id: changes
246+
run: |
247+
if git diff --quiet; then
248+
echo "has_changes=false" >> $GITHUB_OUTPUT
249+
else
250+
echo "has_changes=true" >> $GITHUB_OUTPUT
251+
fi
252+
253+
- name: Create Pull Request
254+
if: steps.changes.outputs.has_changes == 'true'
255+
uses: peter-evans/create-pull-request@v7
256+
with:
257+
token: ${{ secrets.GITHUB_TOKEN }}
258+
commit-message: "chore: Update Spring Boot version matrices"
259+
title: "Automated Spring Boot Version Update"
260+
body: |
261+
## Automated Spring Boot Version Update
262+
263+
This PR updates the Spring Boot version matrices in our test workflows based on the latest available versions.
264+
265+
### Changes Made:
266+
$(cat version_changes.txt 2>/dev/null || echo "See diff for changes")
267+
268+
### Update Strategy:
269+
- **Patch updates**: Updated to latest patch version of existing minor versions
270+
- **New minor versions**: Added new minor versions and removed second oldest (keeping minimum supported)
271+
- **Minimum version preserved**: Always keeps the minimum supported version for compatibility testing
272+
273+
This ensures our CI tests stay current with Spring Boot releases while maintaining coverage of older versions that users may still be using.
274+
branch: automated-spring-boot-version-update
275+
delete-branch: true
276+
draft: false
277+
278+
- name: Summary
279+
run: |
280+
if [ "${{ steps.changes.outputs.has_changes }}" = "true" ]; then
281+
echo "✅ Spring Boot version updates found and PR created"
282+
cat version_changes.txt 2>/dev/null || true
283+
else
284+
echo "ℹ️ No Spring Boot version updates needed"
285+
fi

0 commit comments

Comments
 (0)