Skip to content

feat: implement comprehensive CI/CD workflow suite with automation #2

feat: implement comprehensive CI/CD workflow suite with automation

feat: implement comprehensive CI/CD workflow suite with automation #2

Workflow file for this run

name: Pull Request Validation
on:
pull_request:
types: [opened, synchronize, reopened, ready_for_review]
pull_request_review:
types: [submitted]
jobs:
validate-changes:
if: github.event.pull_request.draft == false
runs-on: ubuntu-latest
permissions:
contents: read
pull-requests: write
checks: write
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Set up JDK 21
uses: actions/setup-java@v4
with:
java-version: '21'
distribution: 'temurin'
- name: Setup Gradle
uses: gradle/actions/setup-gradle@417ae3ccd767c252f5661f1ace9f835f9654f2b5
- name: Make gradlew executable
run: chmod +x ./gradlew
- name: Analyze changed files
id: changes
run: |
# Get list of changed files
git diff --name-only origin/master...HEAD > changed-files.txt
echo "Changed files:"
cat changed-files.txt
# Categorize changes
JAVA_CHANGES=$(grep -c "\.java$" changed-files.txt || echo "0")
TEST_CHANGES=$(grep -c "src/test/" changed-files.txt || echo "0")
MAIN_CHANGES=$(grep -c "src/main/" changed-files.txt || echo "0")
DOC_CHANGES=$(grep -c -E "\.(md|txt|html)$" changed-files.txt || echo "0")
BUILD_CHANGES=$(grep -c -E "(build\.gradle|\.yml|\.yaml)$" changed-files.txt || echo "0")
echo "java_changes=$JAVA_CHANGES" >> $GITHUB_OUTPUT
echo "test_changes=$TEST_CHANGES" >> $GITHUB_OUTPUT
echo "main_changes=$MAIN_CHANGES" >> $GITHUB_OUTPUT
echo "doc_changes=$DOC_CHANGES" >> $GITHUB_OUTPUT
echo "build_changes=$BUILD_CHANGES" >> $GITHUB_OUTPUT
- name: Validate test coverage for new code
if: steps.changes.outputs.main_changes > 0
run: |
echo "## Test Coverage Analysis" > coverage-analysis.md
echo "" >> coverage-analysis.md
# Run tests and generate coverage
./gradlew test jacocoTestReport
# Check if new main code has corresponding tests
echo "### New/Modified Files Analysis:" >> coverage-analysis.md
grep "src/main/" changed-files.txt | while read main_file; do
# Convert main file path to test file path
test_file=$(echo "$main_file" | sed 's|src/main/|src/test/|' | sed 's|\.java$|Test.java|')
if [ -f "$test_file" ]; then
echo "✅ $main_file → $test_file (test exists)" >> coverage-analysis.md
else
echo "⚠️ $main_file → $test_file (test missing)" >> coverage-analysis.md
fi
done
echo "" >> coverage-analysis.md
echo "Please ensure all new functionality has appropriate test coverage." >> coverage-analysis.md
- name: Check code quality
run: |
echo "## Code Quality Analysis" > quality-analysis.md
echo "" >> quality-analysis.md
# Run static analysis
./gradlew checkstyleMain checkstyleTest || true
./gradlew pmdMain pmdTest || true
# Count quality issues
CHECKSTYLE_ISSUES=0
PMD_ISSUES=0
if [ -f "build/reports/checkstyle/main.xml" ]; then
CHECKSTYLE_ISSUES=$(grep -c "<error" build/reports/checkstyle/main.xml || echo "0")
fi
if [ -f "build/reports/pmd/main.xml" ]; then
PMD_ISSUES=$(grep -c "<violation" build/reports/pmd/main.xml || echo "0")
fi
echo "- **Checkstyle issues**: $CHECKSTYLE_ISSUES" >> quality-analysis.md
echo "- **PMD issues**: $PMD_ISSUES" >> quality-analysis.md
if [ $CHECKSTYLE_ISSUES -gt 0 ] || [ $PMD_ISSUES -gt 0 ]; then
echo "" >> quality-analysis.md
echo "⚠️ Please address code quality issues before merging." >> quality-analysis.md
else
echo "" >> quality-analysis.md
echo "✅ No code quality issues found." >> quality-analysis.md
fi
- name: Performance impact analysis
if: steps.changes.outputs.java_changes > 0
run: |
echo "## Performance Impact Analysis" > performance-analysis.md
echo "" >> performance-analysis.md
# Look for potential performance-impacting changes
echo "### Potential Performance Impact:" >> performance-analysis.md
# Check for synchronization changes
if grep -r "synchronized\|volatile\|AtomicReference\|locks\." $(cat changed-files.txt | grep "\.java$") 2>/dev/null; then
echo "⚠️ Synchronization primitives detected - please verify performance impact" >> performance-analysis.md
fi
# Check for thread creation
if grep -r "new Thread\|newFixedThreadPool\|newCachedThreadPool" $(cat changed-files.txt | grep "\.java$") 2>/dev/null; then
echo "⚠️ Thread creation detected - consider using existing thread pools" >> performance-analysis.md
fi
# Check for virtual thread usage
if grep -r "Thread.ofVirtual\|newVirtualThreadPerTaskExecutor" $(cat changed-files.txt | grep "\.java$") 2>/dev/null; then
echo "✅ Virtual threads usage detected - good for I/O bound tasks" >> performance-analysis.md
fi
echo "" >> performance-analysis.md
echo "Consider running performance benchmarks if significant changes were made." >> performance-analysis.md
- name: Security analysis
run: |
echo "## Security Analysis" > security-analysis.md
echo "" >> security-analysis.md
# Check for potential security issues
echo "### Security Considerations:" >> security-analysis.md
# Check for hardcoded credentials or secrets
if grep -r -i "password\|secret\|token\|key" $(cat changed-files.txt | grep "\.java$") 2>/dev/null | grep -v "// " | grep -v "/\*"; then
echo "⚠️ Potential hardcoded credentials detected - please verify" >> security-analysis.md
fi
# Check for unsafe concurrency patterns
if grep -r "Thread.stop\|Thread.suspend\|Thread.resume" $(cat changed-files.txt | grep "\.java$") 2>/dev/null; then
echo "⚠️ Unsafe thread operations detected - these methods are deprecated" >> security-analysis.md
fi
# Check for proper exception handling
if grep -r "catch.*Exception.*{[^}]*}" $(cat changed-files.txt | grep "\.java$") 2>/dev/null; then
echo "⚠️ Empty catch blocks detected - ensure proper error handling" >> security-analysis.md
fi
echo "" >> security-analysis.md
echo "Security analysis completed." >> security-analysis.md
- name: Compile comprehensive PR analysis
run: |
echo "# 🔍 Pull Request Analysis Report" > pr-analysis.md
echo "" >> pr-analysis.md
echo "**PR**: #${{ github.event.pull_request.number }}" >> pr-analysis.md
echo "**Author**: @${{ github.event.pull_request.user.login }}" >> pr-analysis.md
echo "**Branch**: ${{ github.event.pull_request.head.ref }}" >> pr-analysis.md
echo "" >> pr-analysis.md
echo "## 📊 Change Summary" >> pr-analysis.md
echo "- **Java files changed**: ${{ steps.changes.outputs.java_changes }}" >> pr-analysis.md
echo "- **Main code changes**: ${{ steps.changes.outputs.main_changes }}" >> pr-analysis.md
echo "- **Test changes**: ${{ steps.changes.outputs.test_changes }}" >> pr-analysis.md
echo "- **Documentation changes**: ${{ steps.changes.outputs.doc_changes }}" >> pr-analysis.md
echo "- **Build changes**: ${{ steps.changes.outputs.build_changes }}" >> pr-analysis.md
echo "" >> pr-analysis.md
# Append other analyses
[ -f coverage-analysis.md ] && cat coverage-analysis.md >> pr-analysis.md && echo "" >> pr-analysis.md
[ -f quality-analysis.md ] && cat quality-analysis.md >> pr-analysis.md && echo "" >> pr-analysis.md
[ -f performance-analysis.md ] && cat performance-analysis.md >> pr-analysis.md && echo "" >> pr-analysis.md
[ -f security-analysis.md ] && cat security-analysis.md >> pr-analysis.md && echo "" >> pr-analysis.md
echo "---" >> pr-analysis.md
echo "*Analysis generated on $(date)*" >> pr-analysis.md
- name: Comment PR analysis
uses: actions/github-script@v7
with:
script: |
const fs = require('fs');
let analysisContent = '';
try {
analysisContent = fs.readFileSync('pr-analysis.md', 'utf8');
} catch (error) {
analysisContent = 'Analysis report could not be generated.';
}
// Check if we already commented on this PR
const comments = await github.rest.issues.listComments({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.issue.number
});
const botComment = comments.data.find(comment =>
comment.user.type === 'Bot' &&
comment.body.includes('Pull Request Analysis Report')
);
if (botComment) {
// Update existing comment
await github.rest.issues.updateComment({
owner: context.repo.owner,
repo: context.repo.repo,
comment_id: botComment.id,
body: analysisContent
});
} else {
// Create new comment
await github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.issue.number,
body: analysisContent
});
}
- name: Upload analysis artifacts
uses: actions/upload-artifact@v4
with:
name: pr-analysis-${{ github.event.pull_request.number }}
path: |
pr-analysis.md
coverage-analysis.md
quality-analysis.md
performance-analysis.md
security-analysis.md
changed-files.txt
build/reports/
check-breaking-changes:
if: github.event.pull_request.draft == false
runs-on: ubuntu-latest
permissions:
contents: read
pull-requests: write
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Set up JDK 21
uses: actions/setup-java@v4
with:
java-version: '21'
distribution: 'temurin'
- name: Check for breaking changes
run: |
echo "## 🚨 Breaking Changes Analysis" > breaking-changes.md
echo "" >> breaking-changes.md
# Get changed Java files
git diff --name-only origin/master...HEAD | grep "\.java$" > changed-java-files.txt || touch changed-java-files.txt
if [ -s changed-java-files.txt ]; then
echo "### Checking for potential breaking changes:" >> breaking-changes.md
# Check for removed public methods/classes
for file in $(cat changed-java-files.txt); do
if [ -f "$file" ]; then
echo "Analyzing $file..." >> breaking-changes.md
# Check for method signature changes
git show origin/master:"$file" 2>/dev/null | grep -E "public.*\(" > old-methods.tmp || touch old-methods.tmp
grep -E "public.*\(" "$file" > new-methods.tmp || touch new-methods.tmp
# Compare methods
if ! diff -q old-methods.tmp new-methods.tmp >/dev/null 2>&1; then
echo "⚠️ Public method signatures changed in $file" >> breaking-changes.md
fi
rm -f old-methods.tmp new-methods.tmp
fi
done
echo "" >> breaking-changes.md
echo "**Note**: This is an automated check. Manual review is recommended for API changes." >> breaking-changes.md
else
echo "No Java files changed in this PR." >> breaking-changes.md
fi
- name: Comment breaking changes analysis
if: always()
uses: actions/github-script@v7
with:
script: |
const fs = require('fs');
let breakingChanges = '';
try {
breakingChanges = fs.readFileSync('breaking-changes.md', 'utf8');
} catch (error) {
breakingChanges = 'Breaking changes analysis could not be completed.';
}
// Only comment if there are potential breaking changes
if (breakingChanges.includes('⚠️')) {
await github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.issue.number,
body: breakingChanges
});
}