Skip to content

Commit 4055ab0

Browse files
authored
Merge pull request #16 from CodeBoxxTechSchool/ml/deploy-digitalocean-github-actions
Deploy to CodeBoxx Web Claude droplet via GitHub Actions
2 parents 52b90a6 + f372696 commit 4055ab0

2 files changed

Lines changed: 78 additions & 0 deletions

File tree

.github/workflows/deploy.yml

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
name: CodeBoxx Web Claude - Deploy to DigitalOcean
2+
3+
on:
4+
push:
5+
branches: [main]
6+
workflow_dispatch:
7+
8+
jobs:
9+
deploy:
10+
runs-on: ubuntu-latest
11+
steps:
12+
- name: Checkout
13+
uses: actions/checkout@v4
14+
15+
- name: Set up Node
16+
uses: actions/setup-node@v4
17+
with:
18+
node-version: 20
19+
cache: npm
20+
21+
- name: Install dependencies
22+
run: npm ci
23+
24+
- name: Build
25+
run: npm run build
26+
env:
27+
VITE_SANITY_PROJECT_ID: ${{ secrets.VITE_SANITY_PROJECT_ID }}
28+
VITE_SANITY_DATASET: ${{ secrets.VITE_SANITY_DATASET }}
29+
VITE_SANITY_API_VERSION: ${{ secrets.VITE_SANITY_API_VERSION }}
30+
VITE_SANITY_TOKEN: ${{ secrets.VITE_SANITY_TOKEN }}
31+
32+
- name: Add deploy SSH key
33+
uses: webfactory/ssh-agent@v0.9.0
34+
with:
35+
ssh-private-key: ${{ secrets.DROPLET_SSH_KEY }}
36+
37+
- name: Deploy dist/ to Droplet
38+
run: |
39+
mkdir -p ~/.ssh
40+
ssh-keyscan -H "${{ secrets.DROPLET_HOST }}" >> ~/.ssh/known_hosts
41+
rsync -avz --delete dist/ "${{ secrets.DROPLET_USER }}@${{ secrets.DROPLET_HOST }}:${{ secrets.DROPLET_TARGET_PATH }}"

readme.md

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,43 @@ Brand components with no Bootstrap equivalent (`Logo`, `Avatar`) are tiny hand-w
124124
components in `src/components/`, not a vendored bundle. Everything else — buttons, badges,
125125
forms, the Codi/Enroll drawers — is react-bootstrap, restyled via the SCSS above.
126126

127+
## Deployment
128+
129+
`.github/workflows/deploy.yml` builds the site and rsyncs `dist/` to the
130+
"CodeBoxx Web Claude" DigitalOcean Droplet over SSH on every push to `main` (or
131+
manually via "Run workflow"). It does not provision anything — the Droplet, nginx,
132+
the `deploy` user, and the target directory are already set up (see below).
133+
134+
Required repo secrets (Settings → Secrets and variables → Actions):
135+
136+
| Secret | Value |
137+
| ------------------------- | --------------------------------------------------------------------- |
138+
| `VITE_SANITY_PROJECT_ID` | Same as `.env`'s `VITE_SANITY_PROJECT_ID` |
139+
| `VITE_SANITY_DATASET` | Same as `.env`'s `VITE_SANITY_DATASET` |
140+
| `VITE_SANITY_API_VERSION` | Same as `.env`'s `VITE_SANITY_API_VERSION` |
141+
| `VITE_SANITY_TOKEN` | Same as `.env`'s `VITE_SANITY_TOKEN` (blank is fine if unset there) |
142+
| `DROPLET_HOST` | `159.223.145.47` |
143+
| `DROPLET_USER` | `deploy` — a dedicated, non-root, key-only user with no sudo |
144+
| `DROPLET_SSH_KEY` | Private half of the `deploy` user's dedicated deploy key (no passphrase) |
145+
| `DROPLET_TARGET_PATH` | `/var/www/codeboxx` |
146+
147+
On the Droplet (already done for "CodeBoxx Web Claude"):
148+
149+
- nginx installed and enabled, serving `/var/www/codeboxx` with
150+
`try_files $uri /index.html;` in its `location /` block — required because this
151+
is a client-side-routed SPA (`react-router` `BrowserRouter`); without the
152+
fallback, deep links like `/blog/some-post` 404 on a hard refresh.
153+
- A `deploy` system user owns `/var/www/codeboxx`, has no sudo access, and accepts
154+
SSH only via the dedicated deploy key (password auth disabled). Its
155+
`authorized_keys` holds only that key's public half.
156+
- No domain/TLS yet — nginx answers on port 80 for any `Host` (catch-all
157+
`server_name _;`). Point a domain's A record at the Droplet and run `certbot
158+
--nginx` later to add HTTPS; update `server_name` accordingly at that point.
159+
160+
The deploy step runs `rsync --delete`, so `DROPLET_TARGET_PATH` should stay
161+
dedicated to this site — anything else living in that directory gets removed to
162+
match `dist/`.
163+
127164
## Structure
128165

129166
```

0 commit comments

Comments
 (0)