Skip to content

Commit df8e8c1

Browse files
Copilotcommjoen
andcommitted
Fix HTML formatting in Thymeleaf preview generation and add documentation
Co-authored-by: commjoen <[email protected]>
1 parent 2a6ff7f commit df8e8c1

File tree

3 files changed

+203
-15
lines changed

3 files changed

+203
-15
lines changed

.github/scripts/README.md

Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
# Thymeleaf Preview Generation for GitHub Pages
2+
3+
## Overview
4+
5+
This directory contains scripts to generate static HTML previews from Thymeleaf templates for GitHub Pages PR previews.
6+
7+
## Files
8+
9+
- `generate_thymeleaf_previews.py` - Main script that converts Thymeleaf templates to static HTML
10+
- `test_thymeleaf_generation.sh` - Test script to verify the generation works
11+
- `update_pr_index.py` - Updates the main GitHub Pages index with PR information
12+
- `remove_pr_from_index.py` - Removes PR information when PR is closed
13+
14+
## How It Works
15+
16+
### Thymeleaf Template Processing
17+
18+
The `generate_thymeleaf_previews.py` script:
19+
20+
1. **Parses Thymeleaf Templates**: Reads template files from `src/main/resources/templates/`
21+
2. **Processes Thymeleaf Syntax**: Converts Thymeleaf expressions to static HTML:
22+
- `th:text="${variable}"` → Static text with mock data
23+
- `th:each="item : ${items}"` → Generated HTML loops
24+
- `th:if="${condition}"` → Conditional content
25+
- `th:href="@{/path}"` → Static links
26+
3. **Adds Mock Data**: Provides realistic preview data for:
27+
- Challenge information
28+
- User stats and configuration
29+
- Session data
30+
- Canary token information
31+
4. **Generates Navigation**: Adds navigation between preview pages
32+
5. **Outputs Static HTML**: Saves processed templates as standalone HTML files
33+
34+
### Generated Pages
35+
36+
- **welcome.html** - Home page with challenge table (from `welcome.html` template)
37+
- **about.html** - About page with project information (from `about.html` template)
38+
- **stats.html** - Stats and configuration page (from `stats.html` template)
39+
- **challenge-example.html** - Sample challenge page (from `challenge.html` template)
40+
41+
### Integration with GitHub Actions
42+
43+
The script is integrated into the GitHub Pages preview workflow:
44+
45+
1. PR is opened/updated with template changes
46+
2. GitHub Actions runs the build process
47+
3. Script generates static HTML from updated templates
48+
4. Static files are deployed to GitHub Pages
49+
5. PR comment includes links to preview pages
50+
51+
## Usage
52+
53+
### Manual Testing
54+
55+
```bash
56+
# Generate previews for PR number 123
57+
python3 .github/scripts/generate_thymeleaf_previews.py 123
58+
59+
# Run tests
60+
./.github/scripts/test_thymeleaf_generation.sh
61+
```
62+
63+
### GitHub Actions Integration
64+
65+
The script runs automatically in the `github-pages-preview.yml` workflow when:
66+
- PRs are opened, synchronized, or reopened
67+
- Changes are made to templates or related files
68+
69+
## Mock Data
70+
71+
The script includes realistic mock data:
72+
73+
- **Challenges**: 10 sample challenges with different difficulties and environments
74+
- **Stats**: Session counters, canary callbacks, configuration settings
75+
- **User State**: Some challenges marked as completed for preview purposes
76+
- **Configuration**: Hints, reasons, CTF mode, and other feature flags
77+
78+
## Benefits
79+
80+
- **Better PR Reviews**: Reviewers can see rendered HTML instead of raw templates
81+
- **Visual Validation**: Changes to styling and layout are immediately visible
82+
- **No Build Required**: Works without running the full Spring Boot application
83+
- **Fast Preview**: Generates quickly using mock data
84+
- **Mobile Friendly**: Responsive design works on all devices
85+
86+
## Limitations
87+
88+
- Uses mock data instead of real application data
89+
- Some dynamic features (JavaScript interactions) may not work fully
90+
- Template fragments and includes are simplified
91+
- Complex Thymeleaf expressions may need manual handling
92+
93+
## Adding New Templates
94+
95+
To add preview support for new templates:
96+
97+
1. Add the template processing logic to `ThymeleafToStaticConverter.generate_*_page()` method
98+
2. Add mock data for any new variables in `self.mock_data`
99+
3. Add navigation links in `generate_navigation_html()`
100+
4. Update the main preview page to link to the new page
101+
5. Test with the test script
102+
103+
## Troubleshooting
104+
105+
- **Missing templates**: Script will generate fallback content
106+
- **Parse errors**: Check Thymeleaf syntax in templates
107+
- **Empty output**: Verify mock data contains expected variables
108+
- **Broken layout**: Check CSS/JS file paths in static assets

.github/scripts/generate_thymeleaf_previews.py

Lines changed: 36 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -112,23 +112,44 @@ def process_thymeleaf_syntax(self, content, template_name):
112112

113113
def replace_th_text(self, content):
114114
"""Replace th:text attributes with mock values."""
115-
replacements = {
116-
r'th:text="\$\{totalScore\}"': f'>{self.mock_data["totalScore"]}<',
117-
r'th:text="\$\{sessioncounter\}"': f'>{self.mock_data["sessioncounter"]}<',
118-
r'th:text="\$\{canaryCounter\}"': f'>{self.mock_data["canaryCounter"]}<',
119-
r'th:text="\$\{lastCanaryToken\}"': f'>{self.mock_data["lastCanaryToken"]}<',
120-
r'th:text="\$\{hintsEnabled\}"': f'>{self.mock_data["hintsEnabled"]}<',
121-
r'th:text="\$\{reasonEnabled\}"': f'>{self.mock_data["reasonEnabled"]}<',
122-
r'th:text="\$\{ctfModeEnabled\}"': f'>{self.mock_data["ctfModeEnabled"]}<',
123-
r'th:text="\$\{spoilingEnabled\}"': f'>{self.mock_data["spoilingEnabled"]}<',
124-
r'th:text="\$\{swaggerUIEnabled\}"': f'>{self.mock_data["swaggerUIEnabled"]}<',
125-
r'th:text="\$\{springdocenabled\}"': f'>{self.mock_data["springdocenabled"]}<',
126-
r'th:text="\$\{swaggerURI\}"': f'>{self.mock_data["swaggerURI"]}<',
127-
r'th:text="\$\{#strings\.replace\(environment,\'_\',\' _\'\)\}"': f'>{self.mock_data["environment"]}<',
128-
r'th:text="\'Total score: \'\+\$\{totalScore\}"': f'>Total score: {self.mock_data["totalScore"]}<',
115+
# Handle th:text patterns with proper content replacement
116+
patterns = [
117+
(r'<span[^>]*th:text="\$\{totalScore\}"[^>]*>[^<]*</span>', f'<span>{self.mock_data["totalScore"]}</span>'),
118+
(r'<span[^>]*th:text="\$\{sessioncounter\}"[^>]*>[^<]*</span>', f'<span>{self.mock_data["sessioncounter"]}</span>'),
119+
(r'<span[^>]*th:text="\$\{canaryCounter\}"[^>]*>[^<]*</span>', f'<span>{self.mock_data["canaryCounter"]}</span>'),
120+
(r'<span[^>]*th:text="\$\{lastCanaryToken\}"[^>]*>[^<]*</span>', f'<span>{self.mock_data["lastCanaryToken"]}</span>'),
121+
(r'<span[^>]*th:text="\$\{hintsEnabled\}"[^>]*>[^<]*</span>', f'<span>{self.mock_data["hintsEnabled"]}</span>'),
122+
(r'<span[^>]*th:text="\$\{reasonEnabled\}"[^>]*>[^<]*</span>', f'<span>{self.mock_data["reasonEnabled"]}</span>'),
123+
(r'<span[^>]*th:text="\$\{ctfModeEnabled\}"[^>]*>[^<]*</span>', f'<span>{self.mock_data["ctfModeEnabled"]}</span>'),
124+
(r'<span[^>]*th:text="\$\{spoilingEnabled\}"[^>]*>[^<]*</span>', f'<span>{self.mock_data["spoilingEnabled"]}</span>'),
125+
(r'<span[^>]*th:text="\$\{swaggerUIEnabled\}"[^>]*>[^<]*</span>', f'<span>{self.mock_data["swaggerUIEnabled"]}</span>'),
126+
(r'<span[^>]*th:text="\$\{springdocenabled\}"[^>]*>[^<]*</span>', f'<span>{self.mock_data["springdocenabled"]}</span>'),
127+
(r'<span[^>]*th:text="\$\{swaggerURI\}"[^>]*>[^<]*</span>', f'<span>{self.mock_data["swaggerURI"]}</span>'),
128+
(r'<span[^>]*th:text="\$\{#strings\.replace\(environment,\'_\',\' _\'\)\}"[^>]*>[^<]*</span>', f'<span>{self.mock_data["environment"]}</span>'),
129+
(r'<p[^>]*th:text="\'Total score: \'\+\$\{totalScore\}"[^>]*>[^<]*</p>', f'<p>Total score: {self.mock_data["totalScore"]}</p>'),
130+
]
131+
132+
for pattern, replacement in patterns:
133+
content = re.sub(pattern, replacement, content)
134+
135+
# Also handle simple th:text attributes without full element matching
136+
simple_replacements = {
137+
r'th:text="\$\{totalScore\}"': '',
138+
r'th:text="\$\{sessioncounter\}"': '',
139+
r'th:text="\$\{canaryCounter\}"': '',
140+
r'th:text="\$\{lastCanaryToken\}"': '',
141+
r'th:text="\$\{hintsEnabled\}"': '',
142+
r'th:text="\$\{reasonEnabled\}"': '',
143+
r'th:text="\$\{ctfModeEnabled\}"': '',
144+
r'th:text="\$\{spoilingEnabled\}"': '',
145+
r'th:text="\$\{swaggerUIEnabled\}"': '',
146+
r'th:text="\$\{springdocenabled\}"': '',
147+
r'th:text="\$\{swaggerURI\}"': '',
148+
r'th:text="\$\{#strings\.replace\(environment,\'_\',\' _\'\)\}"': '',
149+
r'th:text="\'Total score: \'\+\$\{totalScore\}"': '',
129150
}
130151

131-
for pattern, replacement in replacements.items():
152+
for pattern, replacement in simple_replacements.items():
132153
content = re.sub(pattern, replacement, content)
133154

134155
return content
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
#!/bin/bash
2+
# Simple test script to verify the Thymeleaf preview generation works
3+
4+
set -e
5+
6+
echo "🧪 Testing Thymeleaf Preview Generation..."
7+
8+
# Clean up any existing test files
9+
rm -rf test-static-site
10+
11+
# Test the script
12+
python3 .github/scripts/generate_thymeleaf_previews.py 999
13+
14+
# Check that files were generated
15+
if [ ! -d "static-site/pr-999/pages" ]; then
16+
echo "❌ Error: Pages directory not created"
17+
exit 1
18+
fi
19+
20+
# Check that all expected files exist
21+
expected_files=("welcome.html" "about.html" "stats.html" "challenge-example.html")
22+
for file in "${expected_files[@]}"; do
23+
if [ ! -f "static-site/pr-999/pages/$file" ]; then
24+
echo "❌ Error: $file was not generated"
25+
exit 1
26+
fi
27+
echo "✅ Generated $file"
28+
done
29+
30+
# Check that HTML contains expected content
31+
if ! grep -q "OWASP WrongSecrets" "static-site/pr-999/pages/welcome.html"; then
32+
echo "❌ Error: welcome.html missing expected content"
33+
exit 1
34+
fi
35+
36+
if ! grep -q "About WrongSecrets" "static-site/pr-999/pages/about.html"; then
37+
echo "❌ Error: about.html missing expected content"
38+
exit 1
39+
fi
40+
41+
if ! grep -q "Current Stats" "static-site/pr-999/pages/stats.html"; then
42+
echo "❌ Error: stats.html missing expected content"
43+
exit 1
44+
fi
45+
46+
# Check file sizes (should not be empty)
47+
for file in "${expected_files[@]}"; do
48+
size=$(stat -c%s "static-site/pr-999/pages/$file")
49+
if [ "$size" -lt 1000 ]; then
50+
echo "❌ Error: $file is too small ($size bytes)"
51+
exit 1
52+
fi
53+
echo "$file has good size ($size bytes)"
54+
done
55+
56+
# Clean up
57+
rm -rf static-site
58+
59+
echo "🎉 All tests passed! Thymeleaf preview generation is working correctly."

0 commit comments

Comments
 (0)