Skip to content

Commit 0722609

Browse files
authored
Add GitHub workflow to compare dependencies with customizable package… (#451)
* Add GitHub workflow to compare dependencies with customizable package and version - Add workflow_dispatch workflow that accepts package and version inputs - Run Maven versions:compare-dependencies command - Parse output to identify downgrades and upgrades - Display downgrades first (most important) - Show notable upgrades (Spring, Hibernate, Jackson, etc.) - Generate markdown summary with collapsible full upgrade list - Upload summary as artifact and display in job summary * Add explicit permissions to workflow for security - Add permissions block to restrict GITHUB_TOKEN access - Set contents: read for repository access - Set issues: write for PR comment functionality - Follows security best practices for GitHub Actions * test with push branch * test with push branch * Update workflow to use default inputs on push and fix step summary size limit - Add push trigger to run workflow automatically on branch pushes - Use default inputs (Spring Boot 3.5.9) when triggered by push - Create truncated step summary to stay under GitHub's 1024k limit - Keep full summary in artifact for complete details - Step summary now shows downgrades and first 50 notable upgrades only * Add push trigger to workflow - Add push trigger for feature branch to automatically run workflow - Uses default inputs (Spring Boot 3.5.9) when triggered by push - Workflow is now PR-ready * ready for review * Categorize upgrades into Major and Minor, ignore patch upgrades - Replace 'notable upgrades' with Major (x.0.0) and Minor (0.x.0) categories - Ignore patch-level upgrades (0.0.x) to reduce noise - Summary now shows: Downgrades → Major Upgrades → Minor Upgrades - Both full summary and step summary use the new categorization * Fix default value syntax in workflow expressions - Add quotes around default values in logical OR expressions - Ensures proper syntax for GitHub Actions expressions - Defaults work correctly when triggered by push event * Remove patch-level upgrade exclusion note * Add 'No Major Upgrades Found' message when no major upgrades exist * Add 'No Minor Upgrades Found' message when no minor upgrades exist * ready for review
1 parent 7d233d7 commit 0722609

File tree

1 file changed

+238
-0
lines changed

1 file changed

+238
-0
lines changed
Lines changed: 238 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,238 @@
1+
name: Compare Dependencies
2+
3+
on:
4+
workflow_dispatch:
5+
inputs:
6+
package:
7+
description: 'Maven package (e.g., org.springframework.boot:spring-boot-dependencies)'
8+
required: true
9+
default: 'org.springframework.boot:spring-boot-dependencies'
10+
type: string
11+
version:
12+
description: 'Version to compare against (e.g., 3.5.9)'
13+
required: true
14+
default: '3.5.9'
15+
type: string
16+
17+
jobs:
18+
compare:
19+
permissions:
20+
contents: read
21+
issues: write
22+
runs-on: ubuntu-latest
23+
steps:
24+
- name: Checkout code
25+
uses: actions/checkout@v4
26+
27+
- name: Set up JDK 21
28+
uses: actions/setup-java@v4
29+
with:
30+
java-version: '21'
31+
distribution: 'zulu'
32+
33+
- name: Cache Maven dependencies
34+
uses: actions/cache@v4
35+
with:
36+
path: ~/.m2
37+
key: ${{ runner.os }}-m2-${{ hashFiles('**/pom.xml') }}
38+
restore-keys: ${{ runner.os }}-m2
39+
40+
- name: Run Maven versions:compare-dependencies
41+
id: compare
42+
run: |
43+
mvn versions:compare-dependencies -DremotePom=${{ inputs.package }}:${{ inputs.version }} > compare-output.txt 2>&1 || true
44+
cat compare-output.txt
45+
46+
- name: Parse and generate summary
47+
id: summary
48+
env:
49+
PACKAGE: ${{ inputs.package }}
50+
VERSION: ${{ inputs.version }}
51+
run: |
52+
python3 << 'EOF'
53+
import re
54+
import os
55+
56+
downgrades = set()
57+
major_upgrades = []
58+
minor_upgrades = []
59+
60+
def get_version_type(old_ver, new_ver):
61+
"""
62+
Compare two version strings.
63+
Returns: 'downgrade', 'major', 'minor', 'patch', or 'same'
64+
"""
65+
old_parts = re.findall(r'\d+', old_ver)
66+
new_parts = re.findall(r'\d+', new_ver)
67+
68+
if not old_parts or not new_parts:
69+
return 'same'
70+
71+
# Compare major version (first number)
72+
old_major = int(old_parts[0])
73+
new_major = int(new_parts[0])
74+
75+
if new_major < old_major:
76+
return 'downgrade'
77+
elif new_major > old_major:
78+
return 'major'
79+
80+
# Major versions are equal, check minor version
81+
if len(old_parts) >= 2 and len(new_parts) >= 2:
82+
old_minor = int(old_parts[1])
83+
new_minor = int(new_parts[1])
84+
85+
if new_minor < old_minor:
86+
return 'downgrade'
87+
elif new_minor > old_minor:
88+
return 'minor'
89+
90+
# Major and minor are equal - this is a patch upgrade (we ignore these)
91+
return 'patch'
92+
93+
with open('compare-output.txt', 'r') as f:
94+
for line in f:
95+
if '[INFO] ' in line and ' -> ' in line:
96+
match = re.search(r'\[INFO\]\s+([^\s].*?)\s+\.+\s+([0-9.]+[^\s]*)\s+->\s+([0-9.]+[^\s]*)', line)
97+
if match:
98+
dep = match.group(1).strip()
99+
old_ver = match.group(2).strip()
100+
new_ver = match.group(3).strip()
101+
102+
version_type = get_version_type(old_ver, new_ver)
103+
entry = f'{dep}: {old_ver} -> {new_ver}'
104+
105+
if version_type == 'downgrade':
106+
downgrades.add(entry)
107+
elif version_type == 'major':
108+
major_upgrades.append(entry)
109+
elif version_type == 'minor':
110+
minor_upgrades.append(entry)
111+
# Ignore patch upgrades
112+
113+
# Generate summary
114+
package = os.environ.get('PACKAGE', 'unknown')
115+
version = os.environ.get('VERSION', 'unknown')
116+
117+
summary_lines = []
118+
summary_lines.append("# Dependency Comparison Summary")
119+
summary_lines.append(f"\n**Comparing against:** `{package}:{version}`\n")
120+
121+
if downgrades:
122+
summary_lines.append("## ⚠️ DOWNGRADES (Most Important)")
123+
summary_lines.append("")
124+
for d in sorted(downgrades):
125+
summary_lines.append(f"- `{d}`")
126+
summary_lines.append(f"\n**Total downgrades:** {len(downgrades)}\n")
127+
else:
128+
summary_lines.append("## ✅ No Downgrades Found\n")
129+
130+
if major_upgrades:
131+
summary_lines.append("## 🔴 Major Upgrades (x.0.0)")
132+
summary_lines.append("")
133+
for u in sorted(set(major_upgrades)):
134+
summary_lines.append(f"- `{u}`")
135+
summary_lines.append(f"\n**Total major upgrades:** {len(set(major_upgrades))}\n")
136+
else:
137+
summary_lines.append("## ✅ No Major Upgrades Found\n")
138+
139+
if minor_upgrades:
140+
summary_lines.append("## 🟡 Minor Upgrades (0.x.0)")
141+
summary_lines.append("")
142+
for u in sorted(set(minor_upgrades)):
143+
summary_lines.append(f"- `{u}`")
144+
summary_lines.append(f"\n**Total minor upgrades:** {len(set(minor_upgrades))}\n")
145+
else:
146+
summary_lines.append("## ✅ No Minor Upgrades Found\n")
147+
148+
summary = "\n".join(summary_lines)
149+
150+
# Write full summary to file
151+
with open('summary.md', 'w') as f:
152+
f.write(summary)
153+
154+
# Create truncated summary for step summary (GitHub limit: 1024k)
155+
# Include downgrades and limited major/minor upgrades
156+
step_summary_lines = []
157+
step_summary_lines.append("# Dependency Comparison Summary")
158+
step_summary_lines.append(f"\n**Comparing against:** `{package}:{version}`\n")
159+
160+
if downgrades:
161+
step_summary_lines.append("## ⚠️ DOWNGRADES (Most Important)")
162+
step_summary_lines.append("")
163+
for d in sorted(downgrades):
164+
step_summary_lines.append(f"- `{d}`")
165+
step_summary_lines.append(f"\n**Total downgrades:** {len(downgrades)}\n")
166+
else:
167+
step_summary_lines.append("## ✅ No Downgrades Found\n")
168+
169+
if major_upgrades:
170+
unique_major = sorted(set(major_upgrades))
171+
step_summary_lines.append("## 🔴 Major Upgrades (x.0.0)")
172+
step_summary_lines.append("")
173+
# Show first 50 major upgrades
174+
for u in unique_major[:50]:
175+
step_summary_lines.append(f"- `{u}`")
176+
if len(unique_major) > 50:
177+
step_summary_lines.append(f"\n... and {len(unique_major) - 50} more major upgrades")
178+
step_summary_lines.append(f"\n**Total major upgrades:** {len(unique_major)}\n")
179+
else:
180+
step_summary_lines.append("## ✅ No Major Upgrades Found\n")
181+
182+
if minor_upgrades:
183+
unique_minor = sorted(set(minor_upgrades))
184+
step_summary_lines.append("## 🟡 Minor Upgrades (0.x.0)")
185+
step_summary_lines.append("")
186+
# Show first 50 minor upgrades
187+
for u in unique_minor[:50]:
188+
step_summary_lines.append(f"- `{u}`")
189+
if len(unique_minor) > 50:
190+
step_summary_lines.append(f"\n... and {len(unique_minor) - 50} more minor upgrades")
191+
step_summary_lines.append(f"\n**Total minor upgrades:** {len(unique_minor)}\n")
192+
else:
193+
step_summary_lines.append("## ✅ No Minor Upgrades Found\n")
194+
195+
step_summary_lines.append("---\n")
196+
step_summary_lines.append("📦 **Full details available in the workflow artifact:** `dependency-comparison-summary`")
197+
198+
step_summary = "\n".join(step_summary_lines)
199+
200+
# Write truncated summary for step summary
201+
with open('step-summary.md', 'w') as f:
202+
f.write(step_summary)
203+
204+
# Also print to console
205+
print("\n" + "="*80)
206+
print(summary)
207+
print("="*80)
208+
EOF
209+
210+
- name: Display summary
211+
run: |
212+
cat summary.md
213+
214+
- name: Add job summary
215+
run: |
216+
cat step-summary.md >> $GITHUB_STEP_SUMMARY
217+
218+
- name: Comment on workflow (if PR)
219+
if: github.event_name == 'pull_request'
220+
uses: actions/github-script@v7
221+
with:
222+
script: |
223+
const fs = require('fs');
224+
const summary = fs.readFileSync('summary.md', 'utf8');
225+
github.rest.issues.createComment({
226+
issue_number: context.issue.number,
227+
owner: context.repo.owner,
228+
repo: context.repo.repo,
229+
body: summary
230+
});
231+
232+
- name: Upload summary as artifact
233+
uses: actions/upload-artifact@v4
234+
with:
235+
name: dependency-comparison-summary
236+
path: summary.md
237+
retention-days: 30
238+

0 commit comments

Comments
 (0)