Commit 03fd614
site: deploy the marketing site to Vercel instead of GitHub Pages (#273)
Moves the marketing site off GitHub Pages onto Vercel, without using
Vercel's Git integration.
## What's here
- **`scripts/deploy-site.sh`** — builds `site/` and ships the result to
Vercel as a prebuilt [Build Output API
v3](https://vercel.com/docs/build-output-api/v3) upload. `--prod` for
production, bare for a preview.
- **`.github/workflows/deploy-site.yml`** — rewritten to call the script
on pushes to `main`, same path filters as the old Pages workflow plus
the script itself.
- **`.gitignore`** — ignores `.vercel/`.
## Why prebuilt
Vercel never runs the build. The root `package.json` pulls in Electron
plus the bundled `@anthropic-ai/claude-code` platform binary — hundreds
of MB of native artifacts a static site has no use for — and needs
`--legacy-peer-deps` to install at all. We build in CI and Vercel just
serves the upload.
## Scope safety
The deploying account can also reach the user's employer team, so the
target is pinned three ways:
- every CLI call passes an explicit `--scope` (the CLI's implicit
default is whatever `vercel switch` last set, and is not trusted)
- the real org/project IDs are hardcoded and asserted against both
`.vercel/project.json` and the CI env vars
- the assertion re-runs after linking
Verified it fails closed, before any network call:
```
$ VERCEL_ORG_ID=bogus ./scripts/deploy-site.sh --prod
error: VERCEL_ORG_ID is 'bogus' but this script only deploys to 'mike-personal' (team_…). Refusing to continue.
exit code: 1
```
## URL paths are preserved
Shipped clients hardcode paths into this site —
`announcements-poller.ts` polls `/announcements.json`, and the feed
entries link at `/announcements/<slug>.html`. `config.json` stays
`{"version":3}`: no `cleanUrls`, no `trailingSlash`, no rewrites.
The script re-checks every critical path after deploying, and
**deliberately does not follow redirects** — a `cleanUrls`-style
`/foo.html` → `/foo` rewrite has to fail, not quietly resolve. JSON
responses are parsed, not just status-checked.
Live run against the production alias:
```
==> Verifying URL paths against https://ness-pi.vercel.app
ok /announcements.json 200 application/json
ok /announcements/ness-rename.html 200 text/html
ok /announcements.html 200 text/html
ok /guide.html 200 text/html
ok /releases.html 200 text/html
ok / 200 text/html
```
## Two things the original plan got wrong
1. **A bare `vercel deploy` is not a preview.** For projects with no Git
integration the CLI defaults to the *production* target and moves the
production alias. The script now always passes an explicit
`--target=preview` / `--target=production`.
2. **Per-deployment URLs are SSO-gated even for production.** Under
Vercel's default Standard Protection only the production *alias* is
public. Verification therefore targets the alias for `--prod`, and
previews report `gated by Deployment Protection` unless
`VERCEL_AUTOMATION_BYPASS_SECRET` is set.
## Not in this PR
- `src/shared/constants.ts` — `HARNESS_SITE_URL` still points at
`harness.mikelyons.org`. Domain cutover is a separate phase.
- `site/public/CNAME` — kept on purpose so the Pages deployment retains
its domain binding as a fallback during the transition. On Vercel it's
an inert `/CNAME` file. Delete once cut over.
- DNS / Vercel domain configuration.
## Before merging — secrets to add
`Settings → Secrets and variables → Actions`:
| Secret | Value |
|---|---|
| `VERCEL_TOKEN` | a token from https://vercel.com/account/tokens,
scoped to **Mike Personal** |
| `VERCEL_ORG_ID` | `team_WPsnEYquqTM2pNOKRhusfIbg` |
| `VERCEL_PROJECT_ID` | `prj_2OW4xWxb0i2khtSCDAKIoixk8khA` |
Without these the workflow fails at the deploy step.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>1 parent c9f2639 commit 03fd614
3 files changed
Lines changed: 389 additions & 24 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1 | 1 | | |
2 | 2 | | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
3 | 9 | | |
4 | 10 | | |
5 | 11 | | |
6 | 12 | | |
7 | 13 | | |
8 | 14 | | |
9 | 15 | | |
| 16 | + | |
10 | 17 | | |
11 | 18 | | |
12 | 19 | | |
13 | 20 | | |
14 | 21 | | |
15 | | - | |
16 | | - | |
17 | 22 | | |
| 23 | + | |
18 | 24 | | |
19 | | - | |
| 25 | + | |
20 | 26 | | |
21 | 27 | | |
22 | 28 | | |
23 | | - | |
| 29 | + | |
24 | 30 | | |
25 | 31 | | |
26 | 32 | | |
| |||
35 | 41 | | |
36 | 42 | | |
37 | 43 | | |
38 | | - | |
39 | | - | |
40 | | - | |
41 | | - | |
42 | | - | |
43 | | - | |
44 | | - | |
45 | | - | |
46 | | - | |
47 | | - | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
48 | 47 | | |
49 | | - | |
50 | | - | |
51 | | - | |
52 | | - | |
53 | | - | |
54 | | - | |
55 | | - | |
56 | | - | |
57 | | - | |
58 | | - | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
11 | 11 | | |
12 | 12 | | |
13 | 13 | | |
| 14 | + | |
0 commit comments