Add translations with language toggle support and locale-aware challenge content #201
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: GitHub Pages PR Preview | |
| on: | |
| pull_request: | |
| paths: | |
| - 'src/main/resources/templates/**' | |
| - 'src/main/resources/static/**' | |
| - 'src/main/resources/explanations/**' | |
| - 'src/main/java/**' | |
| permissions: | |
| contents: read | |
| pages: write | |
| id-token: write | |
| pull-requests: write | |
| # Allow only one concurrent deployment per PR, but allow multiple PRs to deploy simultaneously | |
| concurrency: | |
| group: "pages-pr-${{ github.event.number }}" | |
| cancel-in-progress: true | |
| jobs: | |
| generate-static-preview: | |
| runs-on: ubuntu-latest | |
| if: github.event.action != 'closed' | |
| environment: | |
| name: github-pages | |
| url: ${{ steps.deployment.outputs.page_url }}pr-${{ github.event.number }}/ | |
| outputs: | |
| preview-url: ${{ steps.deployment.outputs.page_url }}pr-${{ github.event.number }}/ | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v5 | |
| - name: Set up JDK 25 | |
| uses: actions/setup-java@v5 | |
| with: | |
| java-version: "25" | |
| distribution: "temurin" | |
| cache: "maven" | |
| - name: Build application (JAR only) | |
| run: | | |
| echo "Building WrongSecrets application..." | |
| ./mvnw --no-transfer-progress clean package -DskipTests | |
| # Verify JAR was created | |
| find target -name "*.jar" -type f | head -5 | |
| - name: Generate Thymeleaf static previews | |
| run: | | |
| echo "Generating Thymeleaf static previews for PR #${{ github.event.number }}..." | |
| # Generate static HTML from Thymeleaf templates | |
| python3 .github/scripts/generate_thymeleaf_previews.py ${{ github.event.number }} | |
| - name: Create static preview content | |
| run: | | |
| echo "Creating static preview for PR #${{ github.event.number }}..." | |
| # Create the preview directory structure | |
| mkdir -p static-site/pr-${{ github.event.number }} | |
| # Copy static assets (CSS, JS, images, etc.) | |
| echo "Copying static assets..." | |
| cp -r src/main/resources/static/* static-site/pr-${{ github.event.number }}/ 2>/dev/null || true | |
| # Create a simple landing page for this PR preview | |
| cat > static-site/pr-${{ github.event.number }}/index.html << 'EOF' | |
| <!DOCTYPE html> | |
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>WrongSecrets PR Preview</title> | |
| <link href="css/bootstrap.min.css" rel="stylesheet" /> | |
| <link href="css/custom.css" rel="stylesheet" /> | |
| <link rel="icon" type="image/png" href="favicon.png"> | |
| </head> | |
| <body> | |
| <div class="container-fluid mt-3"> | |
| <div class="row"> | |
| <div class="col-md-12"> | |
| <div class="display-5 mb-3">🔐 OWASP WrongSecrets</div> | |
| <div class="alert alert-info" role="alert"> | |
| <h5 class="alert-heading">📋 PR Preview #${{ github.event.number }}</h5> | |
| <p><strong>Static Preview Notice:</strong> This is a static preview of the UI changes in this pull request.</p> | |
| <p><strong>Commit:</strong> <code>${{ github.sha }}</code></p> | |
| <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> | |
| </div> | |
| <div class="card"> | |
| <div class="card-header"> | |
| <h5>📁 Available Preview Content</h5> | |
| </div> | |
| <div class="card-body"> | |
| <p>This static preview includes:</p> | |
| <ul> | |
| <li>✅ All CSS stylesheets and themes</li> | |
| <li>✅ JavaScript files and interactions</li> | |
| <li>✅ Images, icons, and static assets</li> | |
| <li>✅ Generated HTML from Thymeleaf templates</li> | |
| </ul> | |
| <h6 class="mt-3">🔗 Preview Pages:</h6> | |
| <div class="row"> | |
| <div class="col-md-6"> | |
| <div class="list-group"> | |
| <a href="pages/welcome.html" class="list-group-item list-group-item-action"> | |
| 🏠 Home/Welcome Page | |
| </a> | |
| <a href="pages/about.html" class="list-group-item list-group-item-action"> | |
| ℹ️ About Page | |
| </a> | |
| </div> | |
| </div> | |
| <div class="col-md-6"> | |
| <div class="list-group"> | |
| <a href="pages/stats.html" class="list-group-item list-group-item-action"> | |
| 📊 Stats & Config Page | |
| </a> | |
| <a href="pages/challenge-57.html" class="list-group-item list-group-item-action"> | |
| 🤖 <strong>Challenge 57: LLM Security (Latest)</strong> | |
| </a> | |
| <a href="pages/challenge-example.html" class="list-group-item list-group-item-action"> | |
| 🧩 Challenge Example | |
| </a> | |
| </div> | |
| </div> | |
| </div> | |
| <h6 class="mt-3">📋 Key Files Changed in This PR:</h6> | |
| <div id="changed-files"> | |
| <em>Loading changed files...</em> | |
| </div> | |
| <div class="mt-3"> | |
| <a href="${{ github.server_url }}/${{ github.repository }}/pull/${{ github.event.number }}" class="btn btn-primary" target="_blank"> | |
| View Full PR Details | |
| </a> | |
| <a href="${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}" class="btn btn-secondary" target="_blank"> | |
| Docker Preview (Full Functionality) | |
| </a> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <script> | |
| // Try to fetch and display changed files information | |
| (async function() { | |
| try { | |
| const response = await fetch('${{ github.api_url }}/repos/${{ github.repository }}/pulls/${{ github.event.number }}/files'); | |
| const files = await response.json(); | |
| const relevantFiles = files.filter(file => | |
| file.filename.includes('templates/') || | |
| file.filename.includes('static/') || | |
| file.filename.includes('explanations/') || | |
| file.filename.includes('src/main/java/') | |
| ); | |
| const changedFilesDiv = document.getElementById('changed-files'); | |
| if (relevantFiles.length > 0) { | |
| changedFilesDiv.innerHTML = '<ul>' + | |
| relevantFiles.slice(0, 10).map(file => | |
| `<li><code>${file.filename}</code> <span class="badge bg-${file.status === 'added' ? 'success' : file.status === 'removed' ? 'danger' : 'warning'}">${file.status}</span></li>` | |
| ).join('') + | |
| (relevantFiles.length > 10 ? `<li><em>... and ${relevantFiles.length - 10} more files</em></li>` : '') + | |
| '</ul>'; | |
| } else { | |
| changedFilesDiv.innerHTML = '<em>No relevant UI/template files changed in this PR.</em>'; | |
| } | |
| } catch (error) { | |
| console.log('Could not load changed files information:', error); | |
| document.getElementById('changed-files').innerHTML = '<em>Could not load changed files information.</em>'; | |
| } | |
| })(); | |
| </script> | |
| </body> | |
| </html> | |
| EOF | |
| - name: Update main index page with current PR | |
| run: | | |
| # Download existing GitHub Pages content if it exists | |
| EXISTING_INDEX="" | |
| echo "Attempting to download existing index page..." | |
| if curl -s -f -L --max-time 30 "https://${{ github.repository_owner }}.github.io/${{ github.event.repository.name }}/index.html" > /tmp/existing_index.html 2>/dev/null; then | |
| # Verify the downloaded file has content and is HTML | |
| if [ -s /tmp/existing_index.html ] && grep -q "<html" /tmp/existing_index.html; then | |
| echo "✅ Found existing index page" | |
| EXISTING_INDEX="/tmp/existing_index.html" | |
| else | |
| echo "⚠️ Downloaded file is empty or not valid HTML, starting fresh" | |
| fi | |
| else | |
| echo "ℹ️ No existing index found or unable to download, starting fresh" | |
| fi | |
| # Run the script with environment variables | |
| export PR_NUMBER="${{ github.event.number }}" | |
| export PR_TITLE="${{ github.event.pull_request.title }}" | |
| export PR_SHA="${{ github.sha }}" | |
| export EXISTING_INDEX="$EXISTING_INDEX" | |
| python3 .github/scripts/update_pr_index.py | |
| - name: Setup Pages | |
| uses: actions/configure-pages@v5 | |
| - name: Upload artifact | |
| uses: actions/upload-pages-artifact@v4 | |
| with: | |
| path: ./static-site | |
| - name: Deploy to GitHub Pages | |
| id: deployment | |
| uses: actions/deploy-pages@v4 | |
| - name: Comment PR with preview link | |
| uses: actions/github-script@v8 | |
| with: | |
| script: | | |
| const prNumber = context.issue.number; | |
| const previewUrl = `${{ steps.deployment.outputs.page_url }}pr-${prNumber}/`; | |
| const comment = `🌐 **GitHub Pages Preview Ready!** | |
| Your static preview is now available at: | |
| **🔗 [Preview PR #${prNumber}](${previewUrl})** | |
| 📄 **What's included:** | |
| - ✅ All CSS, JavaScript, and static assets (embedded inline) | |
| - ✅ Current styling and layout preview | |
| - ✅ Images, icons, and UI components | |
| - ✅ **NEW:** Generated HTML from Thymeleaf templates | |
| - 🏠 [Home/Welcome Page](${previewUrl}pages/welcome.html) | |
| - ℹ️ [About Page](${previewUrl}pages/about.html) | |
| - 📊 [Stats & Config Page](${previewUrl}pages/stats.html) | |
| - 🤖 **[Challenge 57: LLM Security (Latest)](${previewUrl}pages/challenge-57.html)** | |
| - 🧩 [Challenge Example](${previewUrl}pages/challenge-example.html) | |
| **For full functionality testing:** Use the [Docker preview](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}) instead. | |
| **🔄 Auto-updates:** This preview will be updated automatically when you push new commits to this PR. | |
| --- | |
| <sub>Static preview with Thymeleaf generation by GitHub Actions</sub>`; | |
| github.rest.issues.createComment({ | |
| issue_number: prNumber, | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| body: comment | |
| }); | |
| cleanup-preview: | |
| runs-on: ubuntu-latest | |
| if: github.event.action == 'closed' | |
| environment: | |
| name: github-pages | |
| url: ${{ steps.deployment.outputs.page_url }} | |
| permissions: | |
| contents: read | |
| pages: write | |
| id-token: write | |
| pull-requests: write | |
| steps: | |
| - name: Checkout repository | |
| uses: actions/checkout@v5 | |
| with: | |
| fetch-depth: 0 | |
| - name: Prepare cleanup for PR preview | |
| run: | | |
| PR_NUMBER=${{ github.event.number }} | |
| echo "Preparing cleanup for PR #${PR_NUMBER}..." | |
| # Check if gh-pages branch exists | |
| if git ls-remote --exit-code --heads origin gh-pages >/dev/null 2>&1; then | |
| echo "gh-pages branch exists, checking out..." | |
| git checkout gh-pages || { | |
| echo "Failed to checkout gh-pages branch" | |
| exit 1 | |
| } | |
| else | |
| echo "gh-pages branch doesn't exist yet, nothing to clean up" | |
| exit 0 | |
| fi | |
| # Create temporary directory for the cleaned site | |
| mkdir -p /tmp/cleaned-site | |
| # Copy everything except the PR directory we want to remove | |
| PR_DIR="pr-${PR_NUMBER}" | |
| for item in *; do | |
| if [ "$item" != "$PR_DIR" ] && [ "$item" != ".git" ]; then | |
| cp -r "$item" /tmp/cleaned-site/ 2>/dev/null || true | |
| fi | |
| done | |
| # Update main index if it exists | |
| if [ -f "/tmp/cleaned-site/index.html" ]; then | |
| echo "Updating main index to remove PR #${PR_NUMBER}..." | |
| export PR_NUMBER="${PR_NUMBER}" | |
| python3 .github/scripts/remove_pr_from_index.py | |
| fi | |
| # Move cleaned content to static-site for deployment | |
| mkdir -p static-site | |
| cp -r /tmp/cleaned-site/* static-site/ 2>/dev/null || true | |
| echo "Cleanup preparation completed" | |
| - name: Setup Pages for cleanup | |
| uses: actions/configure-pages@v5 | |
| - name: Upload cleaned artifact | |
| uses: actions/upload-pages-artifact@v4 | |
| with: | |
| path: ./static-site | |
| - name: Deploy cleaned pages | |
| id: deployment | |
| uses: actions/deploy-pages@v4 | |
| - name: Comment PR cleanup completion | |
| uses: actions/github-script@v8 | |
| with: | |
| script: | | |
| const comment = `🧹 **Preview Cleanup Complete** | |
| The static preview for this PR has been removed from GitHub Pages. | |
| 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 | |
| }); |