Skip to content

Commit 91bd68d

Browse files
authored
Merge pull request #2129 from OWASP/copilot/fix-2128
Fix GitHub Pages preview for multiple PRs - enable parallel deployments with shared index
2 parents ad7f3d9 + 6125bdc commit 91bd68d

File tree

5 files changed

+300
-102
lines changed

5 files changed

+300
-102
lines changed
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
#!/usr/bin/env python3
2+
import re
3+
import sys
4+
import os
5+
from datetime import datetime
6+
7+
8+
def main():
9+
pr_number = os.environ.get("PR_NUMBER", "unknown")
10+
11+
try:
12+
with open("/tmp/cleaned-site/index.html", "r") as f:
13+
content = f.read()
14+
15+
# Remove the PR card for this specific PR number
16+
card_pattern = (
17+
f'<div class="pr-card"[^>]*data-pr="{pr_number}"[^>]*>.*?</div>\s*</div>'
18+
)
19+
updated_content = re.sub(card_pattern, "", content, flags=re.DOTALL)
20+
21+
# Check if there are any remaining PR cards
22+
remaining_cards = re.findall(
23+
r'<div class="pr-card"[^>]*data-pr="[^"]*"[^>]*>', updated_content
24+
)
25+
26+
if not remaining_cards:
27+
# No remaining PRs, add a "no previews" message
28+
no_previews_html = """ <div class="no-previews" style="text-align: center; color: #666; font-style: italic; grid-column: 1 / -1;">
29+
No active PR previews available
30+
</div>"""
31+
32+
# Replace any existing content in the grid
33+
grid_pattern = r'(<div class="pr-grid">)(.*?)(</div>\s*</div>\s*<div style="text-align: center)'
34+
updated_content = re.sub(
35+
grid_pattern,
36+
r"\1\n" + no_previews_html + r"\n \3",
37+
updated_content,
38+
flags=re.DOTALL,
39+
)
40+
41+
# Update the last updated timestamp
42+
timestamp_pattern = r"Last updated: [^<•]*"
43+
new_timestamp = f'Last updated: {datetime.now().strftime("%Y-%m-%d %H:%M UTC")}'
44+
updated_content = re.sub(timestamp_pattern, new_timestamp, updated_content)
45+
46+
with open("/tmp/cleaned-site/index.html", "w") as f:
47+
f.write(updated_content)
48+
49+
print(f"Successfully removed PR #{pr_number} from index")
50+
51+
except Exception as e:
52+
print(f"Error updating index: {e}")
53+
# Don't fail the cleanup if index update fails
54+
55+
56+
if __name__ == "__main__":
57+
main()

.github/scripts/update_pr_index.py

Lines changed: 147 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,147 @@
1+
#!/usr/bin/env python3
2+
import re
3+
import sys
4+
import os
5+
from datetime import datetime
6+
import html
7+
8+
9+
def main():
10+
# Get environment variables
11+
pr_number = os.environ.get("PR_NUMBER", "unknown")
12+
pr_title = os.environ.get("PR_TITLE", "Unknown PR")
13+
pr_sha = os.environ.get("PR_SHA", "unknown")
14+
existing_index_file = os.environ.get("EXISTING_INDEX", "")
15+
16+
# Escape HTML characters in PR title
17+
pr_title_escaped = html.escape(pr_title.strip())
18+
19+
# HTML template for PR card
20+
pr_card_template = f""" <div class="pr-card" data-pr="{pr_number}">
21+
<a href="pr-{pr_number}/" class="pr-link">PR #{pr_number} Preview</a>
22+
<div class="meta">
23+
<span class="badge">Active</span><br>
24+
{pr_title_escaped}<br>
25+
Updated: {datetime.now().strftime("%Y-%m-%d %H:%M UTC")} | Commit: {pr_sha[:8]}
26+
</div>
27+
</div>"""
28+
29+
# Base HTML template
30+
base_html = (
31+
"""<!DOCTYPE html>
32+
<html lang="en">
33+
<head>
34+
<meta charset="UTF-8">
35+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
36+
<title>OWASP WrongSecrets - PR Previews</title>
37+
<style>
38+
body {
39+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
40+
margin: 40px;
41+
background-color: #f8f9fa;
42+
}
43+
.container { max-width: 1200px; margin: 0 auto; }
44+
.header { text-align: center; margin-bottom: 40px; }
45+
.pr-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); gap: 20px; }
46+
.pr-card {
47+
background: white;
48+
padding: 20px;
49+
border-radius: 8px;
50+
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
51+
border-left: 4px solid #0366d6;
52+
}
53+
.pr-link { text-decoration: none; color: #0366d6; font-weight: bold; font-size: 1.2em; }
54+
.pr-link:hover { text-decoration: underline; }
55+
.meta { color: #666; font-size: 0.9em; margin-top: 10px; }
56+
.badge {
57+
background: #e1f5fe;
58+
color: #01579b;
59+
padding: 2px 8px;
60+
border-radius: 12px;
61+
font-size: 0.8em;
62+
}
63+
.no-previews {
64+
text-align: center;
65+
color: #666;
66+
font-style: italic;
67+
grid-column: 1 / -1;
68+
}
69+
</style>
70+
</head>
71+
<body>
72+
<div class="container">
73+
<div class="header">
74+
<h1>🔐 OWASP WrongSecrets</h1>
75+
<h2>Pull Request Previews</h2>
76+
<p>Static previews of pull requests for UI and template changes</p>
77+
</div>
78+
79+
<div id="pr-list">
80+
<div class="pr-grid">
81+
<!-- PR_CARDS_PLACEHOLDER -->
82+
</div>
83+
</div>
84+
85+
<div style="text-align: center; margin-top: 40px; color: #666;">
86+
<p>
87+
<a href="https://github.com/OWASP/wrongsecrets" target="_blank">View Repository</a> |
88+
<a href="https://github.com/OWASP/wrongsecrets/pulls" target="_blank">All Pull Requests</a>
89+
</p>
90+
<small>Generated by GitHub Actions • Last updated: """
91+
+ datetime.now().strftime("%Y-%m-%d %H:%M UTC")
92+
+ """</small>
93+
</div>
94+
</div>
95+
</body>
96+
</html>"""
97+
)
98+
99+
try:
100+
# Try to read existing index
101+
if existing_index_file and os.path.exists(existing_index_file):
102+
with open(existing_index_file, "r") as f:
103+
existing_content = f.read()
104+
105+
# Extract existing PR cards
106+
card_pattern = r'<div class="pr-card"[^>]*>.*?</div>\s*</div>'
107+
existing_cards = re.findall(card_pattern, existing_content, re.DOTALL)
108+
109+
# Remove the current PR card if it exists
110+
filtered_cards = []
111+
for card in existing_cards:
112+
if f'data-pr="{pr_number}"' not in card:
113+
filtered_cards.append(card)
114+
115+
# Add current PR card at the beginning
116+
all_cards = [pr_card_template] + filtered_cards
117+
118+
else:
119+
# No existing index, start fresh
120+
all_cards = [pr_card_template]
121+
122+
# Generate final HTML
123+
if all_cards:
124+
cards_html = "\n".join(all_cards)
125+
else:
126+
cards_html = ' <div class="no-previews">No active PR previews</div>'
127+
128+
final_html = base_html.replace("<!-- PR_CARDS_PLACEHOLDER -->", cards_html)
129+
130+
with open("static-site/index.html", "w") as f:
131+
f.write(final_html)
132+
133+
print(f"Successfully updated index with PR #{pr_number}")
134+
135+
except Exception as e:
136+
print(f"Error updating index: {e}")
137+
# Fallback to simple index
138+
fallback_html = base_html.replace(
139+
"<!-- PR_CARDS_PLACEHOLDER -->", pr_card_template
140+
)
141+
with open("static-site/index.html", "w") as f:
142+
f.write(fallback_html)
143+
print("Created fallback index")
144+
145+
146+
if __name__ == "__main__":
147+
main()

0 commit comments

Comments
 (0)