Skip to content

Commit 6278a27

Browse files
committed
Docker Compose: Create a GitHub action that will publish one Docker Compose for each syllabus with the correct default URL and other settings to GitHub Pages
1 parent 72c85b9 commit 6278a27

File tree

3 files changed

+270
-2
lines changed

3 files changed

+270
-2
lines changed
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
name: Publish Syllabi Docker Compose Files
2+
3+
on:
4+
push:
5+
branches:
6+
- brev-reorg
7+
8+
permissions:
9+
contents: read
10+
pages: write
11+
id-token: write
12+
13+
# Allow only one concurrent deployment
14+
concurrency:
15+
group: "pages"
16+
cancel-in-progress: false
17+
18+
jobs:
19+
process-and-deploy:
20+
runs-on: ubuntu-latest
21+
environment:
22+
name: github-pages
23+
url: ${{ steps.deployment.outputs.page_url }}
24+
25+
steps:
26+
- name: Checkout repository
27+
uses: actions/checkout@v4
28+
29+
- name: Set up Python
30+
uses: actions/setup-python@v5
31+
with:
32+
python-version: '3.11'
33+
34+
- name: Process syllabi and create modified docker-compose files
35+
run: python3 brev/process-syllabi-docker-compose.py
36+
37+
- name: Setup Pages
38+
uses: actions/configure-pages@v5
39+
40+
- name: Upload artifact
41+
uses: actions/upload-pages-artifact@v3
42+
with:
43+
path: '_site'
44+
45+
- name: Deploy to GitHub Pages
46+
id: deployment
47+
uses: actions/deploy-pages@v4
Lines changed: 219 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,219 @@
1+
#!/usr/bin/env python3
2+
"""
3+
Process syllabi files and create modified docker-compose files for GitHub Pages.
4+
5+
For each syllabi file in tutorials/*/notebooks/syllabi/*.ipynb, this script:
6+
1. Copies the corresponding tutorials/*/brev/docker-compose.yml file
7+
2. Modifies it by setting the default-jupyter-url to the syllabi file path
8+
3. Outputs the modified file to _site/{tutorial}/{syllabi-name}/docker-compose.yml
9+
4. Creates an index.html listing all processed files
10+
"""
11+
12+
import re
13+
from pathlib import Path
14+
from collections import defaultdict
15+
16+
17+
def modify_docker_compose(content: str, jupyter_url: str) -> str:
18+
"""
19+
Modify docker-compose content by setting the default-jupyter-url anchor.
20+
21+
Args:
22+
content: Original docker-compose.yml content
23+
jupyter_url: Path to set as the default Jupyter URL
24+
25+
Returns:
26+
Modified docker-compose.yml content
27+
"""
28+
modified = re.sub(
29+
r'(default-jupyter-url:\s*&default-jupyter-url)\s*$',
30+
f'\\1 {jupyter_url}',
31+
content,
32+
flags=re.MULTILINE
33+
)
34+
return modified
35+
36+
37+
def generate_index_html(processed_files: list) -> str:
38+
"""
39+
Generate an HTML index page listing all processed docker-compose files.
40+
41+
Args:
42+
processed_files: List of dicts with 'tutorial', 'syllabi', and 'path' keys
43+
44+
Returns:
45+
HTML content as a string
46+
"""
47+
html = """<!DOCTYPE html>
48+
<html lang="en">
49+
<head>
50+
<meta charset="UTF-8">
51+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
52+
<title>Syllabi Docker Compose Files</title>
53+
<style>
54+
body {
55+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif;
56+
max-width: 1200px;
57+
margin: 0 auto;
58+
padding: 2rem;
59+
background-color: #f5f5f5;
60+
}
61+
h1 {
62+
color: #333;
63+
border-bottom: 3px solid #76B900;
64+
padding-bottom: 0.5rem;
65+
}
66+
h2 {
67+
color: #555;
68+
margin-top: 2rem;
69+
}
70+
.file-list {
71+
background: white;
72+
border-radius: 8px;
73+
padding: 1.5rem;
74+
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
75+
}
76+
.file-item {
77+
padding: 1rem;
78+
margin: 0.5rem 0;
79+
background: #f9f9f9;
80+
border-radius: 4px;
81+
border-left: 4px solid #76B900;
82+
}
83+
.file-item a {
84+
color: #0066cc;
85+
text-decoration: none;
86+
font-weight: 500;
87+
}
88+
.file-item a:hover {
89+
text-decoration: underline;
90+
}
91+
.tutorial-name {
92+
color: #888;
93+
font-size: 0.9em;
94+
margin-top: 0.25rem;
95+
}
96+
code {
97+
background: #eee;
98+
padding: 0.2rem 0.4rem;
99+
border-radius: 3px;
100+
font-size: 0.9em;
101+
}
102+
</style>
103+
</head>
104+
<body>
105+
<h1>🚀 Syllabi Docker Compose Files</h1>
106+
<p>Modified Docker Compose files with pre-configured syllabi for each tutorial.</p>
107+
"""
108+
109+
# Group by tutorial
110+
by_tutorial = defaultdict(list)
111+
for item in processed_files:
112+
by_tutorial[item['tutorial']].append(item)
113+
114+
for tutorial in sorted(by_tutorial.keys()):
115+
tutorial_title = tutorial.replace('-', ' ').title()
116+
html += f"\n<h2>{tutorial_title}</h2>\n<div class='file-list'>\n"
117+
118+
for item in sorted(by_tutorial[tutorial], key=lambda x: x['syllabi']):
119+
syllabi_display = item['syllabi'].replace('_', ' ').replace('__', ' - ')
120+
html += f""" <div class='file-item'>
121+
<a href="{item['path']}" download>{syllabi_display}</a>
122+
<div class='tutorial-name'><code>{item['path']}</code></div>
123+
</div>
124+
"""
125+
html += "</div>\n"
126+
127+
html += """
128+
<hr style="margin-top: 3rem; border: none; border-top: 1px solid #ddd;">
129+
<p style="color: #888; font-size: 0.9em;">
130+
Generated from the <a href="https://github.com/nvidia/accelerated-computing-hub">Accelerated Computing Hub</a> repository.
131+
</p>
132+
</body>
133+
</html>
134+
"""
135+
return html
136+
137+
138+
def main():
139+
"""Main processing function."""
140+
# Create output directory
141+
output_dir = Path('_site')
142+
output_dir.mkdir(exist_ok=True)
143+
144+
# Find all tutorials directories
145+
tutorials_base = Path('tutorials')
146+
147+
if not tutorials_base.exists():
148+
print(f"❌ Error: {tutorials_base} directory not found")
149+
return 1
150+
151+
processed_files = []
152+
153+
for tutorial_dir in tutorials_base.iterdir():
154+
if not tutorial_dir.is_dir():
155+
continue
156+
157+
# Check for syllabi directory
158+
syllabi_dir = tutorial_dir / 'notebooks' / 'syllabi'
159+
if not syllabi_dir.exists():
160+
continue
161+
162+
# Check for docker-compose.yml
163+
docker_compose_src = tutorial_dir / 'brev' / 'docker-compose.yml'
164+
if not docker_compose_src.exists():
165+
print(f"⚠️ Warning: No docker-compose.yml found for {tutorial_dir.name}")
166+
continue
167+
168+
tutorial_name = tutorial_dir.name
169+
print(f"\n📦 Processing tutorial: {tutorial_name}")
170+
171+
# Process each syllabi file
172+
for syllabi_file in syllabi_dir.glob('*.ipynb'):
173+
# Read the docker-compose file as text (to preserve YAML anchors)
174+
with open(docker_compose_src, 'r') as f:
175+
compose_content = f.read()
176+
177+
# Calculate the relative path from working-dir to syllabi file
178+
# The working_dir in docker-compose is /accelerated-computing-hub/tutorials/{tutorial}/notebooks
179+
# The syllabi file is at tutorials/{tutorial}/notebooks/syllabi/{file}.ipynb
180+
# So the relative path is: syllabi/{file}.ipynb
181+
# JupyterLab requires the "lab/tree/" prefix to open notebooks
182+
syllabi_relative_path = f"lab/tree/syllabi/{syllabi_file.name}"
183+
184+
print(f" ✓ Processing: {syllabi_file.name}")
185+
print(f" Jupyter URL: {syllabi_relative_path}")
186+
187+
# Modify the default-jupyter-url anchor
188+
modified_content = modify_docker_compose(compose_content, syllabi_relative_path)
189+
190+
# Create output directory structure
191+
syllabi_name = syllabi_file.stem # filename without extension
192+
output_subdir = output_dir / tutorial_name / syllabi_name
193+
output_subdir.mkdir(parents=True, exist_ok=True)
194+
195+
# Write modified docker-compose file
196+
output_file = output_subdir / 'docker-compose.yml'
197+
with open(output_file, 'w') as f:
198+
f.write(modified_content)
199+
200+
processed_files.append({
201+
'tutorial': tutorial_name,
202+
'syllabi': syllabi_name,
203+
'path': f"{tutorial_name}/{syllabi_name}/docker-compose.yml"
204+
})
205+
206+
# Create an index.html file
207+
html_content = generate_index_html(processed_files)
208+
with open(output_dir / 'index.html', 'w') as f:
209+
f.write(html_content)
210+
211+
print(f"\n✅ Successfully processed {len(processed_files)} syllabi files")
212+
print(f"📁 Output directory: {output_dir.absolute()}")
213+
214+
return 0
215+
216+
217+
if __name__ == '__main__':
218+
import sys
219+
sys.exit(main())

tutorials/accelerated-python/brev/docker-compose.yml

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ x-config:
99
dockerfile: &dockerfile tutorials/accelerated-python/brev/dockerfile
1010
image: &image ghcr.io/nvidia/accelerated-python-tutorial:brev-reorg-latest
1111
working-dir: &working-dir /accelerated-computing-hub/tutorials/accelerated-python/notebooks
12+
default-jupyter-url: &default-jupyter-url
1213

1314
services:
1415
base:
@@ -25,8 +26,8 @@ services:
2526
- BREV_ENV_ID
2627
user: root
2728
working_dir: *working-dir
28-
command: ["/update-git-branch.bash"]
2929
restart: "no"
30+
command: ["/update-git-branch.bash"]
3031
jupyter:
3132
image: *image
3233
depends_on:
@@ -53,6 +54,7 @@ services:
5354
user: root
5455
working_dir: *working-dir
5556
restart: "unless-stopped"
57+
command: [*default-jupyter-url]
5658
nsight:
5759
image: nvcr.io/nvidia/devtools/nsight-streamer-nsys:2025.3.1
5860
depends_on:
@@ -79,5 +81,5 @@ services:
7981
- BREV_ENV_ID
8082
user: root
8183
working_dir: *working-dir
82-
entrypoint: ["/accelerated-computing-hub/brev/nsight-start.bash"]
8384
restart: "unless-stopped"
85+
entrypoint: ["/accelerated-computing-hub/brev/nsight-start.bash"]

0 commit comments

Comments
 (0)