Skip to content

Commit 96306e1

Browse files
committed
Add comprehensive deployment guide for static site setup
1 parent 88eeb6f commit 96306e1

1 file changed

Lines changed: 252 additions & 0 deletions

File tree

deploy.md

Lines changed: 252 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,252 @@
1+
# Deployment Guide
2+
3+
This repository is served as a static site from a Docker container on a cloud server (e.g., GCP), accessible at `dev.cppdigest.org/boost/`. GitHub Pages remains active solely to redirect legacy visitors to the new URL.
4+
5+
## Overview
6+
7+
```
8+
Push to main
9+
├── deploy.yml → SSH into server → git pull /opt/boost → nginx container serves updated files
10+
└── pages.yml → (only if index.html or 404.html changed) → GitHub Pages serves redirect-only artifact
11+
```
12+
13+
The site content lives in a git clone at `/opt/boost` on the server. An nginx Docker container bind-mounts that directory read-only, so a `git pull` is all that is needed to deploy content changes — no Docker image rebuild, no container restart.
14+
15+
---
16+
17+
## Repository Files
18+
19+
| File | Purpose |
20+
|---|---|
21+
| `Dockerfile` | Builds the nginx:alpine container image with a custom nginx config |
22+
| `nginx.conf` | Container-internal nginx config — serves files from the bind-mounted directory |
23+
| `docker-compose.yml` | Defines the `boost-site` container, port mapping, and bind mount |
24+
| `.github/workflows/deploy.yml` | CD workflow — SSHs into the server and runs `git pull` on every push to `main` |
25+
| `.github/workflows/pages.yml` | GitHub Pages workflow — deploys redirect-only artifact when `index.html` or `404.html` change |
26+
| `index.html` | Root page — contains a hostname-conditional redirect for GitHub Pages visitors |
27+
| `404.html` | GitHub Pages 404 handler — redirects deep-linked legacy URLs to their GCP equivalents |
28+
29+
---
30+
31+
## Part 1 — GitHub Repository Configuration
32+
33+
### 1.1 Add Repository Secrets
34+
35+
Go to **Settings → Secrets and variables → Actions → New repository secret** and add:
36+
37+
| Secret name | Value |
38+
|---|---|
39+
| `SERVER_HOST` | IP address or hostname of your cloud server |
40+
| `SERVER_USER` | SSH username on the server (e.g. `ubuntu`) |
41+
| `SERVER_SSH_KEY` | Full contents of the private SSH key (the matching public key must be in `~/.ssh/authorized_keys` on the server) |
42+
43+
### 1.2 Switch GitHub Pages Source to GitHub Actions
44+
45+
Go to **Settings → Pages → Build and deployment → Source** and change from **"Deploy from a branch"** to **"GitHub Actions"**.
46+
47+
This is required for `pages.yml` to deploy. Without it the workflow will fail with a permissions error.
48+
49+
> After this change, GitHub Pages will only update when the `pages.yml` workflow runs — which only happens when `index.html` or `404.html` are modified. Routine content pushes (e.g. updating the `develop/` directory) will not trigger a Pages redeploy.
50+
51+
---
52+
53+
## Part 2 — Server Setup (one-time)
54+
55+
SSH into your server and run the following commands once to prepare the environment.
56+
57+
### 2.1 Install Docker and Docker Compose
58+
59+
If not already installed:
60+
61+
```bash
62+
curl -fsSL https://get.docker.com | sh
63+
sudo usermod -aG docker $USER
64+
newgrp docker
65+
```
66+
67+
Verify:
68+
69+
```bash
70+
docker --version
71+
docker compose version
72+
```
73+
74+
### 2.2 Clone the Repository
75+
76+
```bash
77+
sudo git clone https://github.com/CppDigest/cppdigest.github.io.git /opt/boost
78+
```
79+
80+
### 2.3 Set Ownership and Permissions
81+
82+
Transfer ownership to your SSH user so that `git pull` works without `sudo`, and set world-readable permissions so the nginx container user (UID 101 inside `nginx:alpine`) can read the files via the bind mount:
83+
84+
```bash
85+
sudo chown -R $USER:$USER /opt/boost
86+
find /opt/boost -type d -exec chmod 755 {} \;
87+
find /opt/boost -type f -exec chmod 644 {} \;
88+
```
89+
90+
**Why this works:** The `nginx:alpine` worker process runs as the `nginx` user (UID 101). The bind mount is owned by your SSH user, so nginx reads the files as "other". Standard `755` (directories) and `644` (files) permissions grant world-read access. The deploy workflow's `git pull` runs as your SSH user (the owner), so no `sudo` is needed there either.
91+
92+
### 2.4 Build and Start the Container
93+
94+
```bash
95+
cd /opt/boost
96+
docker compose up -d --build
97+
```
98+
99+
### 2.5 Verify the Container is Serving
100+
101+
```bash
102+
curl http://localhost:9102/
103+
```
104+
105+
You should receive the HTML content of `index.html`. If you see an nginx error page, check `docker logs boost-site`.
106+
107+
---
108+
109+
## Part 3 — Host nginx Configuration (one-time)
110+
111+
The server runs a host-level nginx instance that routes subdirectory requests to individual Docker containers. Add a `location` block for `/boost/` inside the existing `server` block for `dev.cppdigest.org`.
112+
113+
```nginx
114+
location /boost/ {
115+
proxy_pass http://127.0.0.1:9102/;
116+
proxy_set_header Host $host;
117+
proxy_set_header X-Real-IP $remote_addr;
118+
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
119+
}
120+
```
121+
122+
The trailing slash on `proxy_pass` causes nginx to strip the `/boost/` prefix before forwarding requests to the container. All internal HTML links in this repo use relative paths, so no URL rewriting is needed.
123+
124+
Test and reload:
125+
126+
```bash
127+
sudo nginx -t && sudo systemctl reload nginx
128+
```
129+
130+
Verify end-to-end from your local machine:
131+
132+
```bash
133+
curl https://dev.cppdigest.org/boost/
134+
```
135+
136+
---
137+
138+
## Part 4 — Continuous Deployment
139+
140+
After the one-time setup above, all future deployments are automatic.
141+
142+
### How deploy.yml works
143+
144+
On every push to `main` (or manual dispatch), the workflow SSHs into the server and runs:
145+
146+
```bash
147+
cd /opt/boost
148+
git fetch origin main
149+
CHANGED=$(git diff HEAD origin/main --name-only)
150+
git pull origin main
151+
if echo "$CHANGED" | grep -qE "^(Dockerfile|nginx\.conf|docker-compose\.yml)$"; then
152+
docker compose up -d --build
153+
fi
154+
```
155+
156+
- **Content changes** (HTML files, JSON data): `git pull` updates files on disk; the container's bind mount reflects changes immediately. No restart needed.
157+
- **Infrastructure changes** (`Dockerfile`, `nginx.conf`, `docker-compose.yml`): the container is rebuilt and restarted automatically.
158+
159+
### How pages.yml works
160+
161+
Triggers only when `index.html` or `404.html` are modified. It copies those two files into a temporary `_pages/` directory and uploads that as the GitHub Pages artifact. The full site content (hundreds of HTML files and large JSON data) is never pushed to GitHub Pages.
162+
163+
---
164+
165+
## Part 5 — GitHub Pages Redirect Behaviour
166+
167+
### index.html redirect
168+
169+
A small script near the top of `<head>` in `index.html` fires only when the page is served from `cppdigest.github.io`:
170+
171+
```html
172+
<script>
173+
if (window.location.hostname === 'cppdigest.github.io') {
174+
window.location.replace('https://dev.cppdigest.org/boost/');
175+
}
176+
</script>
177+
```
178+
179+
When served from `dev.cppdigest.org`, the hostname check fails silently and the full page content displays normally. The same file is used for both deployments.
180+
181+
### 404.html deep-link forwarding
182+
183+
GitHub Pages serves `404.html` for any path not present in the Pages artifact (which only contains `index.html` and `404.html`). This handles bookmarked deep links such as `cppdigest.github.io/v1.90/asio.html` by reconstructing the equivalent GCP URL:
184+
185+
```javascript
186+
window.location.replace(
187+
'https://dev.cppdigest.org/boost' + window.location.pathname
188+
);
189+
```
190+
191+
For example:
192+
- `cppdigest.github.io/v1.90/``dev.cppdigest.org/boost/v1.90/`
193+
- `cppdigest.github.io/v1.90/libraries/asio.html``dev.cppdigest.org/boost/v1.90/libraries/asio.html`
194+
195+
---
196+
197+
## Part 6 — Validation Checklist
198+
199+
After completing all steps above, run through this checklist:
200+
201+
1. Push a content-only change to `main` (e.g. edit a file in `develop/`)
202+
- `deploy.yml` should trigger and complete in ~10s
203+
- `pages.yml` should **not** trigger (confirm in the Actions tab)
204+
2. Browse to `https://dev.cppdigest.org/boost/` and confirm the change is live
205+
3. Push a change to `index.html`
206+
- Both `deploy.yml` and `pages.yml` should trigger
207+
4. Visit `https://cppdigest.github.io/` and confirm it redirects to `https://dev.cppdigest.org/boost/`
208+
5. Visit `https://cppdigest.github.io/v1.90/` and confirm it redirects to `https://dev.cppdigest.org/boost/v1.90/`
209+
6. Test manual dispatch: go to **Actions → Deploy to GCP → Run workflow**
210+
211+
---
212+
213+
## Troubleshooting
214+
215+
**Container is not serving files**
216+
217+
```bash
218+
docker logs boost-site
219+
docker compose ps
220+
```
221+
222+
Check that `/opt/boost` is world-readable:
223+
```bash
224+
ls -la /opt/boost
225+
```
226+
227+
**deploy.yml fails with "Permission denied" on git pull**
228+
229+
Ensure ownership was transferred in step 2.3:
230+
```bash
231+
ls -la /opt/
232+
sudo chown -R $USER:$USER /opt/boost
233+
```
234+
235+
**pages.yml fails with "Resource not accessible by integration"**
236+
237+
The GitHub Pages source has not been switched to GitHub Actions. See step 1.2.
238+
239+
**nginx returns 502 Bad Gateway for /boost/**
240+
241+
The Docker container is not running or is not bound to port 9102:
242+
```bash
243+
docker compose ps
244+
curl http://localhost:9102/
245+
```
246+
247+
**Host nginx config change not taking effect**
248+
249+
```bash
250+
sudo nginx -t # check for syntax errors
251+
sudo systemctl reload nginx
252+
```

0 commit comments

Comments
 (0)