@@ -31,295 +31,7 @@ jobs:
3131
3232 - name : Update Spring Boot versions
3333 id : update_versions
34- run : |
35- cat << 'EOF' > update_versions.py
36- import json
37- import os
38- import re
39- import requests
40- from packaging import version
41- import sys
42- from pathlib import Path
43-
44- def get_spring_boot_versions():
45- """Fetch all Spring Boot versions from Maven Central with retry logic"""
46-
47- max_retries = 3
48- timeout = 60
49-
50- for attempt in range(max_retries):
51- try:
52- print(f"Fetching versions (attempt {attempt + 1}/{max_retries})...")
53-
54- # Try the Maven Central REST API first
55- rest_url = "https://repo1.maven.org/maven2/org/springframework/boot/spring-boot/maven-metadata.xml"
56- response = requests.get(rest_url, timeout=timeout)
57-
58- if response.status_code == 200:
59- print("Using Maven metadata XML approach...")
60- # Parse XML to extract versions
61- import xml.etree.ElementTree as ET
62- root = ET.fromstring(response.text)
63- versions = []
64- versioning = root.find('versioning')
65- if versioning is not None:
66- versions_element = versioning.find('versions')
67- if versions_element is not None:
68- for version_elem in versions_element.findall('version'):
69- v = version_elem.text
70- if v and not any(suffix in v for suffix in ['SNAPSHOT', 'RC', 'BUILD', 'RELEASE']):
71- # Only include versions that start with a digit and use standard format
72- if v and v[0].isdigit() and v.count('.') >= 2:
73- versions.append(v)
74-
75- if versions:
76- print(f"Found {len(versions)} versions via XML")
77- print(f"Sample versions: {versions[-10:] if len(versions) > 10 else versions}")
78- # Filter out any versions that still can't be parsed
79- valid_versions = []
80- for v in versions:
81- try:
82- version.parse(v)
83- valid_versions.append(v)
84- except Exception as e:
85- print(f"Skipping invalid version format: {v}")
86- print(f"Filtered to {len(valid_versions)} valid versions")
87- return sorted(valid_versions, key=version.parse)
88-
89- # Fallback to search API
90- print("Trying search API fallback...")
91- search_url = "https://search.maven.org/solrsearch/select"
92- params = {
93- "q": "g:\"org.springframework.boot\" AND a:\"spring-boot\"",
94- "core": "gav",
95- "rows": 1000,
96- "wt": "json"
97- }
98-
99- response = requests.get(search_url, params=params, timeout=timeout)
100- response.raise_for_status()
101- data = response.json()
102-
103- if 'response' not in data or 'docs' not in data['response']:
104- raise Exception(f"Unexpected API response structure")
105-
106- docs = data['response']['docs']
107- print(f"Found {len(docs)} documents in search response")
108-
109- if docs and len(docs) > 0:
110- print(f"Sample doc structure: {list(docs[0].keys())}")
111-
112- versions = []
113- for doc in docs:
114- version_field = doc.get('v') or doc.get('version')
115- if (version_field and
116- not any(suffix in version_field for suffix in ['SNAPSHOT', 'RC', 'BUILD', 'RELEASE']) and
117- version_field[0].isdigit() and version_field.count('.') >= 2):
118- versions.append(version_field)
119-
120- if versions:
121- # Filter out any versions that still can't be parsed
122- valid_versions = []
123- for v in versions:
124- try:
125- version.parse(v)
126- valid_versions.append(v)
127- except Exception as e:
128- print(f"Skipping invalid version format: {v}")
129- print(f"Successfully fetched {len(valid_versions)} valid versions via search API")
130- return sorted(valid_versions, key=version.parse)
131-
132- except Exception as e:
133- print(f"Attempt {attempt + 1} failed: {e}")
134- if attempt < max_retries - 1:
135- print("Retrying...")
136- continue
137-
138- print("All attempts failed")
139- return []
140-
141- def parse_current_versions(json_file):
142- """Parse current Spring Boot versions from JSON data file"""
143- if not Path(json_file).exists():
144- return []
145-
146- try:
147- with open(json_file, 'r') as f:
148- data = json.load(f)
149- return data.get('versions', [])
150- except Exception as e:
151- print(f"Error reading {json_file}: {e}")
152- return []
153-
154- def get_latest_patch(all_versions, minor_version):
155- """Get the latest patch version for a given minor version"""
156- target_minor = '.'.join(minor_version.split('.')[:2])
157- patches = [v for v in all_versions if v.startswith(target_minor + '.')]
158- return max(patches, key=version.parse) if patches else minor_version
159-
160- def update_version_matrix(current_versions, all_versions, major_version):
161- """Update version matrix based on available versions"""
162- if not current_versions or not all_versions:
163- return current_versions, False
164-
165- # Filter versions for this major version
166- major_versions = [v for v in all_versions if v.startswith(f"{major_version}.")]
167- if not major_versions:
168- return current_versions, False
169-
170- updated_versions = []
171- changes_made = False
172-
173- # Always keep the minimum supported version (first version)
174- min_version = current_versions[0]
175- updated_versions.append(min_version)
176-
177- # Update patch versions for existing minor versions
178- for curr_version in current_versions[1:]: # Skip min version
179- if any(suffix in curr_version for suffix in ['M', 'RC', 'SNAPSHOT']):
180- # Keep milestone/RC versions as-is for pre-release majors
181- updated_versions.append(curr_version)
182- continue
183-
184- latest_patch = get_latest_patch(major_versions, curr_version)
185- if latest_patch != curr_version:
186- print(f"Updating {curr_version} -> {latest_patch}")
187- changes_made = True
188- updated_versions.append(latest_patch)
189-
190- # Check for new minor versions
191- current_minors = set()
192- for v in current_versions:
193- if not any(suffix in v for suffix in ['M', 'RC', 'SNAPSHOT']):
194- current_minors.add('.'.join(v.split('.')[:2]))
195-
196- available_minors = set()
197- for v in major_versions:
198- if not any(suffix in v for suffix in ['M', 'RC', 'SNAPSHOT']):
199- available_minors.add('.'.join(v.split('.')[:2]))
200-
201- new_minors = available_minors - current_minors
202- if new_minors:
203- # Add latest patch of new minor versions
204- for new_minor in sorted(new_minors, key=version.parse):
205- latest_patch = get_latest_patch(major_versions, new_minor + '.0')
206- updated_versions.append(latest_patch)
207- print(f"Adding new minor version: {latest_patch}")
208- changes_made = True
209-
210- # Remove second oldest minor (but keep absolute minimum)
211- if len(updated_versions) > 7: # If we have more than 7 versions
212- # Sort by version, keep min version and remove second oldest
213- sorted_versions = sorted(updated_versions, key=version.parse)
214- min_version = sorted_versions[0]
215- other_versions = sorted_versions[1:]
216-
217- # Keep all but the oldest of the "other" versions
218- if len(other_versions) > 6:
219- updated_versions = [min_version] + other_versions[1:]
220- print(f"Removed second oldest version: {other_versions[0]}")
221- changes_made = True
222-
223- # Sort final versions and remove duplicates
224- min_version = updated_versions[0]
225- other_versions = sorted([v for v in updated_versions if v != min_version], key=version.parse)
226- final_versions = [min_version] + other_versions
227-
228- # Remove duplicates while preserving order
229- seen = set()
230- deduplicated_versions = []
231- for v in final_versions:
232- if v not in seen:
233- seen.add(v)
234- deduplicated_versions.append(v)
235-
236- if len(deduplicated_versions) != len(final_versions):
237- print(f"Removed {len(final_versions) - len(deduplicated_versions)} duplicate versions")
238-
239- return deduplicated_versions, changes_made
240-
241- def update_json_file(json_file, new_versions):
242- """Update the JSON data file with new versions"""
243- try:
244- # Write new versions to JSON file with consistent formatting
245- data = {"versions": new_versions}
246- with open(json_file, 'w') as f:
247- json.dump(data, f, indent=2, separators=(',', ': '))
248- f.write('\n') # Add trailing newline
249- return True
250- except Exception as e:
251- print(f"Error writing to {json_file}: {e}")
252- return False
253-
254- def main():
255- print("Fetching Spring Boot versions...")
256- all_versions = get_spring_boot_versions()
257-
258- if not all_versions:
259- print("No versions found, exiting")
260- sys.exit(1)
261-
262- print(f"Found {len(all_versions)} versions")
263-
264- data_files = [
265- (".github/data/spring-boot-2-versions.json", "2"),
266- (".github/data/spring-boot-3-versions.json", "3"),
267- (".github/data/spring-boot-4-versions.json", "4")
268- ]
269-
270- changes_made = False
271- change_summary = []
272-
273- for json_file, major_version in data_files:
274- if not Path(json_file).exists():
275- continue
276-
277- print(f"\nProcessing {json_file} (Spring Boot {major_version}.x)")
278-
279- current_versions = parse_current_versions(json_file)
280- if not current_versions:
281- continue
282-
283- print(f"Current versions: {current_versions}")
284-
285- new_versions, file_changed = update_version_matrix(current_versions, all_versions, major_version)
286-
287- if file_changed:
288- print(f"New versions: {new_versions}")
289- if update_json_file(json_file, new_versions):
290- changes_made = True
291- change_summary.append(f"Spring Boot {major_version}.x: {' -> '.join([str(current_versions), str(new_versions)])}")
292- else:
293- print("No changes needed")
294-
295- if changes_made:
296- print(f"\nChanges made to Spring Boot version files:")
297- for change in change_summary:
298- print(f" - {change}")
299-
300- # Write summary for GitHub output
301- with open('version_changes.txt', 'w') as f:
302- f.write('\n'.join(change_summary))
303-
304- # Set GitHub output for use in PR description
305- with open(os.environ.get('GITHUB_OUTPUT', '/dev/null'), 'a') as f:
306- f.write(f"changes_summary<<EOF\n")
307- f.write('\n'.join(change_summary))
308- f.write(f"\nEOF\n")
309- else:
310- print("\nNo version updates needed")
311-
312- sys.exit(0 if changes_made else 1)
313-
314- if __name__ == "__main__":
315- main()
316- EOF
317-
318- python update_versions.py
319-
320- - name : Clean up temporary files
321- run : |
322- rm -f update_versions.py version_changes.txt
34+ run : python scripts/update-spring-boot-versions.py
32335
32436 - name : Check for changes
32537 id : changes
0 commit comments