-
-
Notifications
You must be signed in to change notification settings - Fork 533
501 lines (426 loc) · 18.8 KB
/
pr-preview.yml
File metadata and controls
501 lines (426 loc) · 18.8 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
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 23
uses: actions/setup-java@v5
with:
java-version: "23"
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@v7
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 23 for PR build
uses: actions/setup-java@v5
with:
java-version: "23"
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 23 for main
uses: actions/setup-java@v5
with:
java-version: "23"
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@v4
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@v7
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
# });