Skip to content

Upgrade Spring Boot from 3.5.x to 4.0.3 #202

Upgrade Spring Boot from 3.5.x to 4.0.3

Upgrade Spring Boot from 3.5.x to 4.0.3 #202

Workflow file for this run

name: PR Preview and Visual Diff
on:
pull_request:
types: [opened, synchronize, reopened, closed]
paths:
- 'src/main/resources/templates/**'
- 'src/main/resources/static/**'
- 'src/main/resources/explanations/**'
- 'src/main/java/**'
permissions:
contents: read
packages: write
pull-requests: write
jobs:
build-preview:
runs-on: ubuntu-latest
if: github.event.action != 'closed'
outputs:
image-tag: ${{ steps.meta.outputs.tags }}
image-digest: ${{ steps.build.outputs.digest }}
steps:
- name: Checkout code
uses: actions/checkout@v5
- name: Set up JDK 25
uses: actions/setup-java@v5
with:
java-version: "25"
distribution: "temurin"
cache: "maven"
- name: Extract version from pom.xml
id: extract-version
run: |
echo "Extracting version from pom.xml..."
chmod +x ./mvnw
VERSION=$(./mvnw help:evaluate -Dexpression=project.version -q -DforceStdout)
DOCKER_VERSION=${VERSION%-SNAPSHOT}
echo "version=$VERSION" >> $GITHUB_OUTPUT
echo "docker_version=$DOCKER_VERSION" >> $GITHUB_OUTPUT
echo "Detected version: $VERSION"
echo "Docker version: $DOCKER_VERSION"
- name: Build application
run: ./mvnw --no-transfer-progress clean package -DskipTests
- name: Verify JAR file was created
run: |
echo "Checking target directory..."
ls -la target/
echo "Looking for JAR files..."
find target/ -name "*.jar" -type f
echo "Verifying specific JAR exists..."
JAR_FILE="target/wrongsecrets-${{ steps.extract-version.outputs.version }}.jar"
if [ -f "$JAR_FILE" ]; then
echo "✅ JAR file found: $JAR_FILE"
ls -la "$JAR_FILE"
else
echo "❌ Expected JAR file not found: $JAR_FILE"
echo "Available JAR files:"
find target/ -name "*.jar" -type f || echo "No JAR files found"
exit 1
fi
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Log in to GitHub Container Registry
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Extract metadata
id: meta
uses: docker/metadata-action@v5
with:
images: ghcr.io/${{ github.repository }}/wrongsecrets-pr
tags: |
type=ref,event=pr,suffix=-{{sha}}
type=ref,event=pr
- name: Build and push Docker image
id: build
uses: docker/build-push-action@v6
with:
context: .
push: true
platforms: linux/amd64,linux/arm64
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
build-args: |
argBasedVersion=${{ steps.extract-version.outputs.docker_version }}
cache-from: type=gha
cache-to: type=gha,mode=max
- name: Verify Docker image was built
run: |
echo "Verifying Docker image was built successfully..."
docker images | grep wrongsecrets-pr || echo "No wrongsecrets-pr images found"
- name: Save Docker image as artifact
run: |
echo "Saving Docker image as tar artifact..."
IMAGE_TAG=$(echo "${{ steps.meta.outputs.tags }}" | head -n1)
echo "Using image tag: $IMAGE_TAG"
# Check if image is available locally first
if docker images --format "table {{.Repository}}:{{.Tag}}" | grep -q "$IMAGE_TAG"; then
echo "Image found locally, using local image"
else
echo "Image not found locally, attempting to pull..."
if docker pull "$IMAGE_TAG"; then
echo "Successfully pulled image"
else
echo "Failed to pull image, but this might be expected immediately after push"
echo "Waiting 10 seconds and trying again..."
sleep 10
docker pull "$IMAGE_TAG" || echo "Still failed to pull, continuing with local build"
fi
fi
# Save the image as tar
docker save "$IMAGE_TAG" -o wrongsecrets-preview.tar
echo "Docker image saved to wrongsecrets-preview.tar"
ls -lh wrongsecrets-preview.tar
- name: Upload Docker image artifact
uses: actions/upload-artifact@v4
with:
name: wrongsecrets-preview-pr-${{ github.event.number }}
path: wrongsecrets-preview.tar
retention-days: 30
# Comment out Render deployment for now
# - name: Deploy to Render (Preview)
# env:
# RENDER_API_KEY: ${{ secrets.RENDER_API_KEY }}
# run: |
# # Create a temporary Render service for this PR
# curl -X POST "https://api.render.com/v1/services" \
# -H "Authorization: Bearer $RENDER_API_KEY" \
# -H "Content-Type: application/json" \
# -d '{
# "type": "web_service",
# "name": "wrongsecrets-pr-${{ github.event.number }}",
# "runtime": "docker",
# "dockerImage": {
# "url": "${{ steps.meta.outputs.tags }}"
# },
# "plan": "free",
# "envVars": [
# {"key": "PORT", "value": "8080"}
# ]
# }'
- name: Comment PR with build info
uses: actions/github-script@v8
with:
script: |
const prNumber = context.issue.number;
const imageTag = `${{ steps.meta.outputs.tags }}`.split('\n')[0];
const runId = context.runId;
const comment = `🔨 **Preview Build Complete!**
Your changes have been built and pushed to GitHub Container Registry.
**🐳 Docker Image:** \`${imageTag}\`
**📦 Download & Test Locally:**
1. [📁 Download Docker Image Artifact](https://github.com/${{ github.repository }}/actions/runs/${runId}) (look for \`wrongsecrets-preview-pr-${prNumber}\`)
2. Load and run the image:
\`\`\`bash
# Download the artifact, extract it, then:
docker load < wrongsecrets-preview.tar
docker run -p 8080:8080 wrongsecrets-preview
\`\`\`
**🚀 Alternative - Pull from Registry:**
\`\`\`bash
docker pull ${imageTag}
docker run -p 8080:8080 ${imageTag}
\`\`\`
Then visit: http://localhost:8080
**📝 Changes in this PR:**`;
// Get the list of changed files
const { data: files } = await github.rest.pulls.listFiles({
owner: context.repo.owner,
repo: context.repo.repo,
pull_number: prNumber
});
const relevantFiles = files.filter(file =>
file.filename.includes('templates/') ||
file.filename.includes('static/') ||
file.filename.includes('explanations/') ||
file.filename.includes('src/main/java/')
);
let filesList = '';
if (relevantFiles.length > 0) {
filesList = relevantFiles.slice(0, 10).map(file => `- \`${file.filename}\``).join('\n ');
if (relevantFiles.length > 10) {
filesList += `\n - ... and ${relevantFiles.length - 10} more files`;
}
} else {
filesList = '- No relevant files changed';
}
const finalComment = comment + '\n ' + filesList + `
Visual diff screenshots will be available shortly...
---
<sub>Preview built by GitHub Actions</sub>`;
github.rest.issues.createComment({
issue_number: prNumber,
owner: context.repo.owner,
repo: context.repo.repo,
body: finalComment
});
visual-diff:
runs-on: ubuntu-latest
needs: build-preview
if: github.event.action != 'closed'
steps:
- name: Checkout PR code
uses: actions/checkout@v5
with:
path: pr-code
- name: Checkout main branch
uses: actions/checkout@v5
with:
ref: master
path: main-code
- name: Set up JDK 25 for PR build
uses: actions/setup-java@v5
with:
java-version: "25"
distribution: "temurin"
cache: "maven"
- name: Extract PR version
id: extract-pr-version
working-directory: pr-code
run: |
echo "Extracting PR version from pom.xml..."
chmod +x ./mvnw
VERSION=$(./mvnw help:evaluate -Dexpression=project.version -q -DforceStdout)
DOCKER_VERSION=${VERSION%-SNAPSHOT}
echo "version=$VERSION" >> $GITHUB_OUTPUT
echo "docker_version=$DOCKER_VERSION" >> $GITHUB_OUTPUT
echo "PR version: $VERSION"
echo "PR Docker version: $DOCKER_VERSION"
- name: Build PR version
working-directory: pr-code
run: |
echo "Building PR version..."
./mvnw --no-transfer-progress clean package -DskipTests
echo "PR JAR built successfully"
docker build --build-arg argBasedVersion="${{ steps.extract-pr-version.outputs.docker_version }}" -t wrongsecrets-pr .
echo "PR Docker image built successfully"
- name: Set up JDK 25 for main
uses: actions/setup-java@v5
with:
java-version: "25"
distribution: "temurin"
cache: "maven"
- name: Extract main version
id: extract-main-version
working-directory: main-code
run: |
echo "Extracting main version from pom.xml..."
chmod +x ./mvnw
VERSION=$(./mvnw help:evaluate -Dexpression=project.version -q -DforceStdout)
DOCKER_VERSION=${VERSION%-SNAPSHOT}
echo "version=$VERSION" >> $GITHUB_OUTPUT
echo "docker_version=$DOCKER_VERSION" >> $GITHUB_OUTPUT
echo "Main version: $VERSION"
echo "Main Docker version: $DOCKER_VERSION"
- name: Build main version
working-directory: main-code
run: |
echo "Building main version..."
./mvnw --no-transfer-progress clean package -DskipTests
echo "Main JAR built successfully"
docker build --build-arg argBasedVersion="${{ steps.extract-main-version.outputs.docker_version }}" -t wrongsecrets-main .
echo "Main Docker image built successfully"
# Alternative approach: Pull the PR image from registry
# - name: Log in to GitHub Container Registry
# uses: docker/login-action@v3
# with:
# registry: ghcr.io
# username: ${{ github.actor }}
# password: ${{ secrets.GITHUB_TOKEN }}
# - name: Pull PR image
# run: |
# docker pull ${{ needs.build-preview.outputs.image-tag }}
# docker tag ${{ needs.build-preview.outputs.image-tag }} wrongsecrets-pr
- name: Start both versions
run: |
docker run -d -p 8080:8080 --name pr-version wrongsecrets-pr
docker run -d -p 8081:8080 --name main-version wrongsecrets-main
# Wait for services to start
echo "Waiting for services to start..."
for i in {1..30}; do
if curl -s http://localhost:8080 >/dev/null && curl -s http://localhost:8081 >/dev/null; then
echo "Both services are ready!"
break
fi
echo "Attempt $i/30: Services not ready yet..."
sleep 2
done
- name: Setup Node.js
uses: actions/setup-node@v6
with:
node-version: '24'
- name: Install Playwright
run: |
npm install playwright@latest
npx playwright install --with-deps chromium
- name: Take screenshots
run: |
mkdir -p screenshots
# Verify services are still running
echo "Verifying services are still running..."
docker ps --filter "name=pr-version" --format "table {{.Names}}\t{{.Status}}"
docker ps --filter "name=main-version" --format "table {{.Names}}\t{{.Status}}"
# Test connectivity one more time
echo "Testing connectivity..."
curl -s -o /dev/null -w "%{http_code}" http://localhost:8080 || echo "PR version not responding"
curl -s -o /dev/null -w "%{http_code}" http://localhost:8081 || echo "Main version not responding"
node -e "
const { chromium } = require('playwright');
(async () => {
const browser = await chromium.launch({ headless: true });
const page = await browser.newPage();
await page.setViewportSize({ width: 1280, height: 1024 });
try {
// PR version screenshots
console.log('Taking PR screenshots...');
await page.goto('http://localhost:8080', { waitUntil: 'networkidle', timeout: 30000 });
await page.screenshot({ path: 'screenshots/pr-home.png', fullPage: true });
await page.goto('http://localhost:8080/about', { waitUntil: 'networkidle', timeout: 30000 });
await page.screenshot({ path: 'screenshots/pr-about.png', fullPage: true });
// Try to get a challenge page
await page.goto('http://localhost:8080/challenge/1', { waitUntil: 'networkidle', timeout: 30000 });
await page.screenshot({ path: 'screenshots/pr-challenge.png', fullPage: true });
// Main version screenshots
console.log('Taking main branch screenshots...');
await page.goto('http://localhost:8081', { waitUntil: 'networkidle', timeout: 30000 });
await page.screenshot({ path: 'screenshots/main-home.png', fullPage: true });
await page.goto('http://localhost:8081/about', { waitUntil: 'networkidle', timeout: 30000 });
await page.screenshot({ path: 'screenshots/main-about.png', fullPage: true });
await page.goto('http://localhost:8081/challenge/1', { waitUntil: 'networkidle', timeout: 30000 });
await page.screenshot({ path: 'screenshots/main-challenge.png', fullPage: true });
} catch (error) {
console.error('Screenshot error:', error);
process.exit(1);
} finally {
await browser.close();
}
})();
"
- name: Upload screenshots
uses: actions/upload-artifact@v4
with:
name: visual-diff-pr-${{ github.event.number }}
path: screenshots/
retention-days: 30
- name: Comment with visual diff
uses: actions/github-script@v8
with:
script: |
const comment = `📸 **Visual Diff Ready!**
Screenshots comparing your changes with the main branch are available:
[📁 Download Visual Diff Artifacts](https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }})
**🖼️ Included screenshots:**
- \`pr-home.png\` vs \`main-home.png\` - Welcome page comparison
- \`pr-about.png\` vs \`main-about.png\` - About page comparison
- \`pr-challenge.png\` vs \`main-challenge.png\` - Challenge page comparison
**🔍 How to review:**
1. Download the artifact zip file
2. Extract and compare the \`pr-*\` and \`main-*\` images side by side
3. Look for visual differences in layout, styling, and content
**💡 Tip:** Use an image comparison tool or open both images in separate browser tabs to spot differences easily.
---
<sub>Visual diff generated by GitHub Actions • PR #${{ github.event.number }}</sub>`;
github.rest.issues.createComment({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
body: comment
});
#only need to enable if you want to clean up resources on PR close:
# cleanup-preview:
# runs-on: ubuntu-latest
# if: github.event.action == 'closed'
# steps:
# - name: Log in to GitHub Container Registry
# uses: docker/login-action@v3
# with:
# registry: ghcr.io
# username: ${{ github.actor }}
# password: ${{ secrets.GITHUB_TOKEN }}
# - name: Delete PR container images
# run: |
# # Delete container images for this PR
# PR_NUMBER=${{ github.event.number }}
# REPO_LOWER=$(echo "${{ github.repository }}" | tr '[:upper:]' '[:lower:]')
# # Get all tags for this PR and delete them
# for tag in $(curl -s -H "Authorization: Bearer ${{ secrets.GITHUB_TOKEN }}" \
# "https://api.github.com/orgs/${{ github.repository_owner }}/packages/container/${REPO_LOWER}%2Fwrongsecrets-pr/versions" | \
# jq -r --arg pr "$PR_NUMBER" '.[] | select(.metadata.container.tags[]? | contains($pr)) | .id'); do
# echo "Deleting container version: $tag"
# curl -X DELETE \
# -H "Authorization: Bearer ${{ secrets.GITHUB_TOKEN }}" \
# "https://api.github.com/orgs/${{ github.repository_owner }}/packages/container/${REPO_LOWER}%2Fwrongsecrets-pr/versions/$tag"
# done
# Comment out Render cleanup for now
# - name: Cleanup Render service
# env:
# RENDER_API_KEY: ${{ secrets.RENDER_API_KEY }}
# run: |
# # Delete the temporary Render service
# SERVICE_ID=$(curl -H "Authorization: Bearer $RENDER_API_KEY" \
# "https://api.render.com/v1/services" | \
# jq -r ".[] | select(.name==\"wrongsecrets-pr-${{ github.event.number }}\") | .id")
#
# if [ "$SERVICE_ID" != "null" ]; then
# curl -X DELETE "https://api.render.com/v1/services/$SERVICE_ID" \
# -H "Authorization: Bearer $RENDER_API_KEY"
# fi
# - name: Comment PR closure
# uses: actions/github-script@v7
# with:
# script: |
# const comment = `🧹 **Preview Cleanup Complete**
# PR preview resources have been cleaned up:
# - ✅ Container images deleted from GitHub Container Registry
# - ✅ Artifacts will expire automatically in 30 days
# Thanks for contributing to WrongSecrets! 🎉
# ---
# <sub>Cleanup completed by GitHub Actions</sub>`;
# github.rest.issues.createComment({
# issue_number: context.issue.number,
# owner: context.repo.owner,
# repo: context.repo.repo,
# body: comment
# });