Skip to content

Commit 76d7dc8

Browse files
committed
feat(web): migrate web frontend to Vercel + switch CI web deploy to Vercel
The Next.js web bundle exceeds Cloudflare's free Worker size limit, so the frontend moves to Vercel (free Hobby for a non-commercial OSS demo) while the all-Cloudflare backend (DOs/Workflows/agent loop) stays on the worker chain. The app has zero CF runtime coupling (HTTP-only backend access), so it builds as a standard Next.js app. - next.config: Vercel-safe — omit output:standalone on Vercel (VERCEL env), dev-guarded dynamic import of the OpenNext shim (bracket-notation env access for noPropertyAccessFromIndexSignature). - apps/web/package.json: packageManager pnpm@10.33.2 so Vercel uses pnpm (npm can't parse the workspace catalog: protocol). - .vercelignore: exclude the legacy /cheatcode tree (special files break the CLI) + non-code dirs, ANCHORED to root so nested source dirs (apps/web/.../docs) aren't excluded. Vercel rootDirectory=apps/web; deploy from repo root so the whole pnpm workspace uploads. - CI: replace the dead Cloudflare deploy-web job with deploy-web-vercel (vercel deploy --prod; build env lives in the Vercel project). gitignore .vercel.
1 parent 67745b5 commit 76d7dc8

6 files changed

Lines changed: 32 additions & 23 deletions

File tree

.github/workflows/deploy-workers.yml

Lines changed: 13 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -120,31 +120,23 @@ jobs:
120120
workingDirectory: apps/preview-proxy
121121
command: deploy
122122

123-
deploy-web:
124-
# Independent of the worker chain; self-skips until the NEXT_PUBLIC vars exist
125-
# so a missing web var can never block the backend cutover.
126-
needs: deploy-agent
127-
if: ${{ vars.NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY != '' }}
123+
deploy-web-vercel:
124+
# The web app is hosted on Vercel: its Next.js bundle exceeds Cloudflare's
125+
# Worker size limit, so the frontend lives on Vercel while the all-Cloudflare
126+
# backend stays on the worker chain above. Independent of that chain. Build env
127+
# (NEXT_PUBLIC_*, CLERK_SECRET_KEY) lives in the Vercel project, so `vercel
128+
# deploy` triggers a server-side build — CI only needs the token + project ids.
129+
# Self-skips until VERCEL_PROJECT_ID is configured.
130+
if: ${{ vars.VERCEL_PROJECT_ID != '' && (github.event_name == 'workflow_dispatch' || (github.event.workflow_run.conclusion == 'success' && github.event.workflow_run.head_branch == 'main')) }}
128131
runs-on: ubuntu-latest
129132
environment: production
130133
steps:
131134
- uses: actions/checkout@v4
132135
with:
133136
ref: ${{ github.event.workflow_run.head_sha || github.sha }}
134-
- uses: pnpm/action-setup@v4
135-
with:
136-
version: 10.33.2
137-
- uses: actions/setup-node@v4
138-
with:
139-
node-version: 22
140-
cache: pnpm
141-
- run: pnpm install --frozen-lockfile
142-
# `run deploy` (not bare `deploy`) — `pnpm deploy` is a reserved built-in
143-
# that ignores the package's deploy script and errors NOTHING_TO_DEPLOY.
144-
- run: pnpm --filter @cheatcode/web run deploy
137+
- run: npm install -g vercel@latest
138+
- run: vercel deploy --prod --yes --token="$VERCEL_TOKEN"
145139
env:
146-
CHEATCODE_PROD_DEPLOY_APPROVED: "true"
147-
CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
148-
CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
149-
NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY: ${{ vars.NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY }}
150-
NEXT_PUBLIC_POLAR_PRO_MONTHLY_PRODUCT_ID: ${{ vars.NEXT_PUBLIC_POLAR_PRO_MONTHLY_PRODUCT_ID }}
140+
VERCEL_TOKEN: ${{ secrets.VERCEL_TOKEN }}
141+
VERCEL_ORG_ID: ${{ vars.VERCEL_ORG_ID }}
142+
VERCEL_PROJECT_ID: ${{ vars.VERCEL_PROJECT_ID }}

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ node_modules/
44
.wrangler/
55
.next/
66
.open-next/
7+
.vercel/
78
dist/
89
coverage/
910
.qa/

.vercelignore

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
# Legacy V1 tree (not a V2 workspace member; contains special files like gunicorn.ctl).
2+
# Leading slash = anchored to repo root, so it does NOT match nested source dirs
3+
# such as apps/web/src/components/docs.
4+
/cheatcode/
5+
/docs/
6+
/tasks/
7+
/plans/
8+
# Build artifacts (match anywhere; Vercel installs + builds fresh)
9+
node_modules
10+
.next
11+
.open-next
12+
.wrangler
13+
.turbo
14+
*.log

apps/web/.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11

22
# clerk configuration (can include secrets)
33
/.clerk/
4+
.vercel

apps/web/next.config.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ const nextConfig = {
1010
},
1111
// `standalone` is required by the OpenNext/Cloudflare build but conflicts with
1212
// Vercel's managed output, so emit it everywhere EXCEPT Vercel builds.
13-
...(process.env.VERCEL ? {} : { output: "standalone" as const }),
13+
...(process.env["VERCEL"] ? {} : { output: "standalone" as const }),
1414
} satisfies NextConfig;
1515

1616
const withNextIntl = createNextIntlPlugin("./src/lib/intl/request.ts");
@@ -20,7 +20,7 @@ export default withNextIntl(nextConfig);
2020
// Local-dev only: wire OpenNext's Cloudflare binding shim for `next dev`. Loaded
2121
// via dynamic import behind a dev guard so production builds (incl. Vercel, which
2222
// has no @opennextjs/cloudflare and no CF runtime) never resolve it.
23-
if (process.env.NODE_ENV === "development") {
23+
if (process.env["NODE_ENV"] === "development") {
2424
void import("@opennextjs/cloudflare")
2525
.then((m) => m.initOpenNextCloudflareForDev())
2626
.catch(() => undefined);

apps/web/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
"private": true,
44
"version": "0.0.0",
55
"type": "module",
6+
"packageManager": "pnpm@10.33.2",
67
"scripts": {
78
"build": "next build --webpack && opennextjs-cloudflare build --skipNextBuild",
89
"cf-typegen": "wrangler types --env-interface CloudflareEnv cloudflare-env.d.ts",

0 commit comments

Comments
 (0)