Skip to content

Commit ad7f3d9

Browse files
authored
Merge pull request #2127 from OWASP/copilot/fix-2126
Add GitHub Pages static preview system for PR reviews
2 parents 92698bb + 35f391f commit ad7f3d9

File tree

2 files changed

+366
-3
lines changed

2 files changed

+366
-3
lines changed
Lines changed: 337 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,337 @@
1+
name: GitHub Pages PR Preview
2+
3+
on:
4+
pull_request:
5+
types: [opened, synchronize, reopened, closed]
6+
paths:
7+
- 'src/main/resources/templates/**'
8+
- 'src/main/resources/static/**'
9+
- 'src/main/resources/explanations/**'
10+
- 'src/main/java/**'
11+
12+
permissions:
13+
contents: read
14+
pages: write
15+
id-token: write
16+
pull-requests: write
17+
18+
# Allow only one concurrent deployment per PR, skipping runs queued between the run in-progress and latest queued.
19+
concurrency:
20+
group: "pages-pr-${{ github.event.number }}"
21+
cancel-in-progress: false
22+
23+
jobs:
24+
generate-static-preview:
25+
runs-on: ubuntu-latest
26+
if: github.event.action != 'closed'
27+
environment:
28+
name: github-pages
29+
url: ${{ steps.deployment.outputs.page_url }}pr-${{ github.event.number }}/
30+
outputs:
31+
preview-url: ${{ steps.deployment.outputs.page_url }}pr-${{ github.event.number }}/
32+
steps:
33+
- name: Checkout
34+
uses: actions/checkout@v4
35+
36+
- name: Set up JDK 23
37+
uses: actions/setup-java@v4
38+
with:
39+
java-version: "23"
40+
distribution: "oracle"
41+
cache: "maven"
42+
43+
- name: Build application (JAR only)
44+
run: |
45+
echo "Building WrongSecrets application..."
46+
./mvnw --no-transfer-progress clean package -DskipTests
47+
48+
# Verify JAR was created
49+
find target -name "*.jar" -type f | head -5
50+
51+
- name: Create static preview content
52+
run: |
53+
echo "Creating static preview for PR #${{ github.event.number }}..."
54+
55+
# Create the preview directory structure
56+
mkdir -p static-site/pr-${{ github.event.number }}
57+
58+
# Copy static assets (CSS, JS, images, etc.)
59+
echo "Copying static assets..."
60+
cp -r src/main/resources/static/* static-site/pr-${{ github.event.number }}/ 2>/dev/null || true
61+
62+
# Create a simple landing page for this PR preview
63+
cat > static-site/pr-${{ github.event.number }}/index.html << 'EOF'
64+
<!DOCTYPE html>
65+
<html lang="en">
66+
<head>
67+
<meta charset="UTF-8">
68+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
69+
<title>WrongSecrets PR Preview</title>
70+
<link href="css/bootstrap.min.css" rel="stylesheet" />
71+
<link href="css/custom.css" rel="stylesheet" />
72+
<link rel="icon" type="image/png" href="favicon.png">
73+
</head>
74+
<body>
75+
<div class="container-fluid mt-3">
76+
<div class="row">
77+
<div class="col-md-12">
78+
<div class="display-5 mb-3">🔐 OWASP WrongSecrets</div>
79+
<div class="alert alert-info" role="alert">
80+
<h5 class="alert-heading">📋 PR Preview #${{ github.event.number }}</h5>
81+
<p><strong>Static Preview Notice:</strong> This is a static preview of the UI changes in this pull request.</p>
82+
<p><strong>Commit:</strong> <code>${{ github.sha }}</code></p>
83+
<p><strong>For full functionality:</strong> Please use the Docker preview from the <a href="${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}" target="_blank">GitHub Actions build</a>.</p>
84+
</div>
85+
86+
<div class="card">
87+
<div class="card-header">
88+
<h5>📁 Available Preview Content</h5>
89+
</div>
90+
<div class="card-body">
91+
<p>This static preview includes:</p>
92+
<ul>
93+
<li>✅ All CSS stylesheets and themes</li>
94+
<li>✅ JavaScript files and interactions</li>
95+
<li>✅ Images, icons, and static assets</li>
96+
<li>⚠️ Template structure (limited dynamic content)</li>
97+
</ul>
98+
99+
<h6 class="mt-3">📋 Key Files Changed in This PR:</h6>
100+
<div id="changed-files">
101+
<em>Loading changed files...</em>
102+
</div>
103+
104+
<div class="mt-3">
105+
<a href="${{ github.server_url }}/${{ github.repository }}/pull/${{ github.event.number }}" class="btn btn-primary" target="_blank">
106+
View Full PR Details
107+
</a>
108+
<a href="${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}" class="btn btn-secondary" target="_blank">
109+
Docker Preview (Full Functionality)
110+
</a>
111+
</div>
112+
</div>
113+
</div>
114+
</div>
115+
</div>
116+
</div>
117+
118+
<script>
119+
// Try to fetch and display changed files information
120+
(async function() {
121+
try {
122+
const response = await fetch('${{ github.api_url }}/repos/${{ github.repository }}/pulls/${{ github.event.number }}/files');
123+
const files = await response.json();
124+
125+
const relevantFiles = files.filter(file =>
126+
file.filename.includes('templates/') ||
127+
file.filename.includes('static/') ||
128+
file.filename.includes('explanations/') ||
129+
file.filename.includes('src/main/java/')
130+
);
131+
132+
const changedFilesDiv = document.getElementById('changed-files');
133+
if (relevantFiles.length > 0) {
134+
changedFilesDiv.innerHTML = '<ul>' +
135+
relevantFiles.slice(0, 10).map(file =>
136+
`<li><code>${file.filename}</code> <span class="badge bg-${file.status === 'added' ? 'success' : file.status === 'removed' ? 'danger' : 'warning'}">${file.status}</span></li>`
137+
).join('') +
138+
(relevantFiles.length > 10 ? `<li><em>... and ${relevantFiles.length - 10} more files</em></li>` : '') +
139+
'</ul>';
140+
} else {
141+
changedFilesDiv.innerHTML = '<em>No relevant UI/template files changed in this PR.</em>';
142+
}
143+
} catch (error) {
144+
console.log('Could not load changed files information:', error);
145+
document.getElementById('changed-files').innerHTML = '<em>Could not load changed files information.</em>';
146+
}
147+
})();
148+
</script>
149+
</body>
150+
</html>
151+
EOF
152+
153+
- name: Create preview directory index
154+
run: |
155+
# Create or update the main index page that lists all PR previews
156+
mkdir -p static-site
157+
158+
cat > static-site/index.html << 'EOF'
159+
<!DOCTYPE html>
160+
<html lang="en">
161+
<head>
162+
<meta charset="UTF-8">
163+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
164+
<title>OWASP WrongSecrets - PR Previews</title>
165+
<style>
166+
body {
167+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
168+
margin: 40px;
169+
background-color: #f8f9fa;
170+
}
171+
.container { max-width: 1200px; margin: 0 auto; }
172+
.header { text-align: center; margin-bottom: 40px; }
173+
.pr-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); gap: 20px; }
174+
.pr-card {
175+
background: white;
176+
padding: 20px;
177+
border-radius: 8px;
178+
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
179+
border-left: 4px solid #0366d6;
180+
}
181+
.pr-link { text-decoration: none; color: #0366d6; font-weight: bold; font-size: 1.2em; }
182+
.pr-link:hover { text-decoration: underline; }
183+
.meta { color: #666; font-size: 0.9em; margin-top: 10px; }
184+
.badge {
185+
background: #e1f5fe;
186+
color: #01579b;
187+
padding: 2px 8px;
188+
border-radius: 12px;
189+
font-size: 0.8em;
190+
}
191+
</style>
192+
</head>
193+
<body>
194+
<div class="container">
195+
<div class="header">
196+
<h1>🔐 OWASP WrongSecrets</h1>
197+
<h2>Pull Request Previews</h2>
198+
<p>Static previews of pull requests for UI and template changes</p>
199+
</div>
200+
201+
<div id="pr-list">
202+
<div class="pr-grid">
203+
<div class="pr-card">
204+
<a href="pr-${{ github.event.number }}/" class="pr-link">PR #${{ github.event.number }} Preview</a>
205+
<div class="meta">
206+
<span class="badge">Latest</span><br>
207+
Static preview of pull request #${{ github.event.number }}<br>
208+
Updated: <script>document.write(new Date().toLocaleString())</script>
209+
</div>
210+
</div>
211+
</div>
212+
</div>
213+
214+
<div style="text-align: center; margin-top: 40px; color: #666;">
215+
<p>
216+
<a href="${{ github.server_url }}/${{ github.repository }}" target="_blank">View Repository</a> |
217+
<a href="${{ github.server_url }}/${{ github.repository }}/pulls" target="_blank">All Pull Requests</a>
218+
</p>
219+
<small>Generated by GitHub Actions</small>
220+
</div>
221+
</div>
222+
</body>
223+
</html>
224+
EOF
225+
226+
- name: Setup Pages
227+
uses: actions/configure-pages@v5
228+
229+
- name: Upload artifact
230+
uses: actions/upload-pages-artifact@v3
231+
with:
232+
path: ./static-site
233+
234+
- name: Deploy to GitHub Pages
235+
id: deployment
236+
uses: actions/deploy-pages@v4
237+
238+
- name: Comment PR with preview link
239+
uses: actions/github-script@v7
240+
with:
241+
script: |
242+
const prNumber = context.issue.number;
243+
const previewUrl = `${{ steps.deployment.outputs.page_url }}pr-${prNumber}/`;
244+
245+
const comment = `🌐 **GitHub Pages Preview Ready!**
246+
247+
Your static preview is now available at:
248+
**🔗 [Preview PR #${prNumber}](${previewUrl})**
249+
250+
📄 **What's included:**
251+
- ✅ All CSS, JavaScript, and static assets
252+
- ✅ Current styling and layout preview
253+
- ✅ Images, icons, and UI components
254+
- ⚠️ Limited dynamic functionality (static preview only)
255+
256+
**For full functionality testing:** Use the [Docker preview](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}) instead.
257+
258+
**🔄 Auto-updates:** This preview will be updated automatically when you push new commits to this PR.
259+
260+
---
261+
<sub>Static preview generated by GitHub Actions</sub>`;
262+
263+
github.rest.issues.createComment({
264+
issue_number: prNumber,
265+
owner: context.repo.owner,
266+
repo: context.repo.repo,
267+
body: comment
268+
});
269+
270+
cleanup-preview:
271+
runs-on: ubuntu-latest
272+
if: github.event.action == 'closed'
273+
steps:
274+
- name: Checkout repository
275+
uses: actions/checkout@v4
276+
with:
277+
fetch-depth: 0
278+
279+
- name: Check for gh-pages branch and clean up
280+
run: |
281+
PR_NUMBER=${{ github.event.number }}
282+
echo "Cleaning up preview for PR #${PR_NUMBER}..."
283+
284+
# Check if gh-pages branch exists
285+
if git ls-remote --exit-code --heads origin gh-pages >/dev/null 2>&1; then
286+
echo "gh-pages branch exists, proceeding with cleanup..."
287+
288+
# Checkout gh-pages branch
289+
git checkout gh-pages || {
290+
echo "Failed to checkout gh-pages branch, it may not exist yet"
291+
exit 0
292+
}
293+
294+
PR_DIR="pr-${PR_NUMBER}"
295+
296+
if [ -d "$PR_DIR" ]; then
297+
echo "Removing preview directory: $PR_DIR"
298+
rm -rf "$PR_DIR"
299+
300+
# Configure git
301+
git config user.name github-actions[bot]
302+
git config user.email 41898282+github-actions[bot]@users.noreply.github.com
303+
304+
# Commit and push the removal
305+
git add .
306+
if git commit -m "Clean up preview for closed PR #${PR_NUMBER}"; then
307+
git push origin gh-pages
308+
echo "✅ Successfully cleaned up preview for PR #${PR_NUMBER}"
309+
else
310+
echo "Nothing to commit for cleanup"
311+
fi
312+
else
313+
echo "Preview directory $PR_DIR not found, nothing to clean up"
314+
fi
315+
else
316+
echo "gh-pages branch doesn't exist yet, nothing to clean up"
317+
fi
318+
319+
- name: Comment PR cleanup completion
320+
uses: actions/github-script@v7
321+
with:
322+
script: |
323+
const comment = `🧹 **Preview Cleanup Complete**
324+
325+
The static preview for this PR has been removed from GitHub Pages.
326+
327+
Thanks for contributing to WrongSecrets! 🎉
328+
329+
---
330+
<sub>Cleanup completed by GitHub Actions</sub>`;
331+
332+
github.rest.issues.createComment({
333+
issue_number: context.issue.number,
334+
owner: context.repo.owner,
335+
repo: context.repo.repo,
336+
body: comment
337+
});

docs/PR_PREVIEW_SETUP.md

Lines changed: 29 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,26 +4,52 @@ This document explains how to set up preview deployments for pull requests in th
44

55
## Available Preview Methods
66

7-
### 1. Full Preview Deployment (Recommended)
7+
### 1. GitHub Pages Static Preview (NEW)
8+
- **File**: `.github/workflows/github-pages-preview.yml`
9+
- **What it does**: Creates static HTML previews on GitHub Pages for each PR
10+
- **Requirements**: GitHub Pages enabled for the repository
11+
- **Benefits**: Fast, lightweight previews of UI changes, automatic cleanup, no external dependencies
12+
- **URL Format**: `https://owasp.github.io/wrongsecrets/pr-{number}/`
13+
14+
### 2. Full Preview Deployment (Recommended for testing)
815
- **File**: `.github/workflows/pr-preview.yml`
916
- **What it does**: Deploys each PR to a temporary environment
1017
- **Requirements**: Render.com account and API key
1118
- **Benefits**: Full functional testing, shareable links
1219

13-
### 2. Build-Only Preview
20+
### 3. Build-Only Preview
1421
- **File**: `.github/workflows/build-preview.yml`
1522
- **What it does**: Builds Docker image, provides local testing instructions
1623
- **Requirements**: None (uses GitHub Actions only)
1724
- **Benefits**: No external dependencies, quick setup
1825

19-
### 3. Visual Diff
26+
### 4. Visual Diff
2027
- **File**: `.github/workflows/visual-diff.yml`
2128
- **What it does**: Takes screenshots comparing PR vs main branch
2229
- **Requirements**: None (uses GitHub Actions only)
2330
- **Benefits**: Visual comparison of UI changes
2431

2532
## Setup Instructions
2633

34+
### For GitHub Pages Static Preview
35+
36+
1. **Enable GitHub Pages** in repository settings:
37+
- Go to repository Settings > Pages
38+
- Source: GitHub Actions
39+
- No additional configuration needed
40+
41+
2. **The workflow is automatic**: Once the workflow file is present, it will:
42+
- Trigger on PRs affecting templates, static files, or Java code
43+
- Generate static preview with all CSS, JS, and assets
44+
- Deploy to GitHub Pages with PR-specific URL
45+
- Clean up automatically when PR is closed
46+
47+
3. **What gets previewed**:
48+
- All static assets (CSS, JavaScript, images)
49+
- Basic HTML structure and styling
50+
- Theme toggle and UI components
51+
- Links to full Docker preview for functionality testing
52+
2753
### For Full Preview Deployment
2854

2955
1. **Create Render.com account** (free tier available)

0 commit comments

Comments
 (0)