Skip to content

feat: add Vercel deployment option (Vercel Services)#1095

Open
AmanVarshney01 wants to merge 15 commits into
mainfrom
aman/vercel-services-deploy
Open

feat: add Vercel deployment option (Vercel Services)#1095
AmanVarshney01 wants to merge 15 commits into
mainfrom
aman/vercel-services-deploy

Conversation

@AmanVarshney01

@AmanVarshney01 AmanVarshney01 commented Jul 1, 2026

Copy link
Copy Markdown
Owner

Summary

Adds vercel as a web and server deploy target using Vercel Services — monorepo web + server deployed as services in one Vercel project with a generated vercel.json, same-origin /api routing, and one-command env sync.

What's generated

  • vercel.json — web/server services with framework presets, monorepo install commands, and combined-mode rewrites (/api/(.*) → server with a path transform stripping the /api prefix, /(.*) → web)
  • scripts/sync-vercel-env.ts — syncs local .env files to Vercel per environment (vercel env add per key, --force --yes --non-interactive); in combined web+server mode it overrides *_SERVER_URL to /api and skips CORS_ORIGIN/BETTER_AUTH_URL/NODE_ENV (derived at runtime from VERCEL_* instead)
  • Root scriptsdev:vercel, env:vercel:preview|production, deploy:vercel[:prod], deploy:vercel:check (vercel deploy --dry)
  • Template changes — oRPC web clients (react/solid/svelte/astro/nuxt) normalize same-origin /api paths to absolute URLs (RPCLink rejects relative URLs); server env derives CORS_ORIGIN/BETTER_AUTH_URL from the deployment origin (VERCEL_ENV-aware, preview uses VERCEL_URL); hono/elysia export the app for Vercel functions with guarded local listen; Astro swaps to @astrojs/vercel
  • CLI/web/docs — prompts, schemas, compatibility validation (vercel server deploy requires bun/node + separate backend), stack-builder rules, docs pages, plugin skill

Docs + smoke harness

  • docs/guides/vercel — Vercel Services model, vercel.json anatomy, the link → env sync → deploy flow, the three-layer env model, troubleshooting
  • docs/guides/docker — compose stack anatomy, image builds, env flow, moving to a real host (closes the docs-parity gap: every deploy target now has a guide, and generated READMEs link to the matching one)
  • scripts/vercel-smoke.sh (bun smoke:vercel) — offline smoke harness mirroring docker-smoke.sh: scaffolds 6 combos, validates vercel.json + the env-sync script, builds web services with the exact generated buildCommand, boots server entrypoints and probes them, and verifies guarded entrypoints don't bind under VERCEL=1. 6/6 passing.

Review + fixes included

The fix: commit resolves findings from an adversarial multi-agent review: Astro adapter (@astrojs/node@astrojs/vercel), preview env-sync hanging on the interactive Git-branch prompt (--non-interactive), preview SSR resolving the production origin, Elysia local dev never starting (Bun does not auto-serve Elysia's default export — verified empirically), and order-sensitive env-sync arg parsing.

Live verification

Two stacks deployed to production and verified end-to-end:

self backend (Next.js) separate backend (TanStack Router + Hono + oRPC + Drizzle, combined services)
Web ✅ 200 ✅ 200, browser shows "API Status: Connected"
Server POST /api/rpc/healthCheck{"json":"OK"} GET /api/OK, POST /api/rpc/healthCheck{"json":"OK"}
Envs ✅ synced VITE_SERVER_URL=/api baked into bundle, CORS_ORIGIN skipped + runtime-derived, DATABASE_URL synced

Also verified: Astro + Vercel build emits .vercel/output via @astrojs/vercel; Elysia serves locally on bun and node and skips listen under VERCEL=1; --dry and --non-interactive flags confirmed in vercel@54 (latest).

Tests

  • bun test apps/cli/test/deployment.test.ts — 48 pass (new: combined services config, oRPC URL normalization, web-only self config, Elysia exports + node guard, Astro Vercel adapter, workers-runtime rejection, README guide links)
  • pnpm-workspace (14), web stack-builder compatibility (23), frontend/readme/api/validation (123) — all pass
  • bun smoke:vercel — 6/6 combos pass offline
  • bun run check clean; template regen deterministic; both MDX guides compile

Follow-ups (non-blocking)

  • Chain env sync into first prod deploy (ordering footgun is documented in README + guide but not enforced)
  • Warn/skip obvious local values (localhost, file:) when syncing outside combined mode
  • Service bindings for internal web→server SSR instead of the public /api round-trip

Summary by CodeRabbit

  • New Features
    • Added Vercel as an option for both web and server deployments, including generated vercel.json, Vercel env-sync, and deploy scripts.
  • Bug Fixes
    • Added Vercel compatibility checks for backend/runtime combinations and improved automatic adjustment/validation behavior.
    • Improved generated client/server URL handling and Vercel server startup behavior.
  • Documentation
    • Updated CLI docs and compatibility rules; added a dedicated Deploying to Vercel guide.
  • Tests
    • Expanded Vercel test coverage (including a new offline smoke:vercel run) and added coverage for Vercel-related workspace generation.

@vercel

vercel Bot commented Jul 1, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
create-better-t-stack-web Ready Ready Preview, Comment Jul 3, 2026 11:00pm

Request Review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 9d36baba06

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +3 to +4
{{#if (and (eq serverDeploy "vercel") (eq runtime "bun"))}}
"bunVersion": "1.x",

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Move Bun runtime config into the Vercel service

When users pick Vercel for the server with the default Bun runtime, this emits bunVersion at the top level next to services. In Services mode, Vercel's docs say top-level build/runtime fields are not valid because ownership is ambiguous and should be moved into the relevant service (https://vercel.com/docs/services#top-level-configuration-in-services-mode). This makes the default Vercel server configuration invalid or leaves the server running with the wrong runtime during vercel deploy/deploy:vercel:check.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Verified against the official schema and docs — leaving as is. The Services docs section on top-level configuration enumerates the keys that are invalid at the top level (functions, installCommand, buildCommand, devCommand, ignoreCommand, outputDirectory, framework) — bunVersion is not among them. The openapi.vercel.sh/vercel.json schema defines bunVersion only at the top level; the per-service schema (root, framework, runtime, entrypoint, …) has no bunVersion field to move it into, and the service config reference confirms this. bunVersion is documented as project-scoped ("Enables Bun for the project"), which is exactly why it isn't in the ambiguous-owner list. Also verified live: a combined web+server Services deployment with top-level bunVersion deployed and served correctly.

@coderabbitai

coderabbitai Bot commented Jul 1, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

Walkthrough

Adds Vercel support across CLI validation, web builder UI, template generation, generated templates, documentation, and smoke testing. It also updates deployment-specific URL handling, env validation, and server startup behavior for Vercel-generated stacks.

Changes

Vercel Deployment Support

Layer / File(s) Summary
CLI contracts and prompts
packages/types/src/schemas.ts, apps/cli/src/prompts/web-deploy.ts, apps/cli/src/prompts/server-deploy.ts, apps/cli/src/utils/compatibility-rules.ts, apps/cli/src/utils/config-validation.ts, apps/cli/src/helpers/core/post-installation.ts, apps/cli/README.md, apps/web/content/docs/cli/*, apps/cli/test/*
vercel is added to deploy schemas and prompt choices, validated against backend/runtime constraints, documented in CLI/user docs, and covered by deployment tests.
Web builder Vercel support
apps/web/src/lib/constant.ts, apps/web/src/app/(home)/new/_components/utils.ts, apps/web/src/components/ui/kibo-ui/code-block/index.tsx, apps/web/test/stack-builder-compatibility.test.ts
The web UI exposes Vercel deployment options, adjusts compatibility handling for workers, and updates related stack-builder tests.
Template generator wiring
packages/template-generator/src/processors/*, packages/template-generator/src/post-process/package-configs.ts, packages/template-generator/src/template-handlers/deploy.ts, packages/template-generator/src/utils/*, package.json, scripts/vercel-smoke.sh
Template generation now recognizes Vercel for dependencies, scripts, deploy handling, ignore patterns, and smoke testing.
RPC URL normalization
packages/template-generator/templates/api/orpc/web/*/*.hbs, packages/template-generator/templates/api/trpc/web/react/base/src/utils/trpc.ts.hbs, packages/template-generator/templates/frontend/react/tanstack-start/src/router.tsx.hbs, packages/template-generator/src/templates.generated.ts
Frontend ORPC and tRPC templates now normalize server URLs before building RPC links, including Vercel-aware origin resolution.
Server entrypoints for Vercel
packages/template-generator/templates/backend/server/*, packages/template-generator/src/templates.generated.ts
Elysia and Hono server templates export the app for Vercel and skip local listeners when VERCEL is set.
Vercel config and env templates
packages/template-generator/templates/deploy/vercel/*, packages/template-generator/templates/frontend/astro/*, packages/template-generator/templates/packages/env/*, packages/template-generator/templates/base/_gitignore, packages/template-generator/templates/extras/pnpm-workspace.yaml.hbs, packages/template-generator/src/templates.generated.ts
Generated Vercel config, env sync, Astro adapter wiring, env validation, ignore patterns, and workspace build approvals are updated for Vercel deployments.
Guides and option docs
apps/web/content/docs/guides/*, apps/web/content/docs/cli/*, apps/web/content/docs/project-structure.mdx, plugin/skills/scaffold-project/SKILL.md
Documentation adds Vercel deployment guidance, compatibility rules, guide metadata, and updated option references.
Vercel smoke test
package.json, scripts/vercel-smoke.sh, .gitignore
A new smoke-test entry scaffolds Vercel stacks and verifies the generated artifacts and runtime behavior.

Possibly related issues

Possibly related PRs

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main change: adding Vercel deployment support via Vercel Services.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🧹 Nitpick comments (5)
apps/cli/src/utils/compatibility-rules.ts (1)

336-357: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Consider extracting a shared helper for validateDockerServerDeploy/validateVercelServerDeploy.

Both functions are structurally identical (same backend/runtime checks, only the deploy-target string and suggested alternative differ). A small factory (e.g. createServerDeployRuntimeValidator(target, fallbackSuggestion)) would reduce duplication and keep future targets consistent.

♻️ Example refactor
-export function validateDockerServerDeploy(
-  serverDeploy: ServerDeploy | undefined,
-  backend: Backend | undefined,
-  runtime: Runtime | undefined,
-): ValidationResult {
-  if (serverDeploy !== "docker") return Result.ok(undefined);
-  if (backend === "convex" || backend === "self") {
-    return validationErr(
-      "'--server-deploy docker' requires a separate server backend (hono, express, fastify, elysia). For a fullstack 'self' backend, use '--web-deploy docker' instead.",
-    );
-  }
-  if (runtime === "workers") {
-    return validationErr(
-      "'--server-deploy docker' is not compatible with '--runtime workers'. Use '--runtime bun' or '--runtime node', or choose '--server-deploy cloudflare'.",
-    );
-  }
-  return Result.ok(undefined);
-}
-
-export function validateVercelServerDeploy(
-  serverDeploy: ServerDeploy | undefined,
-  backend: Backend | undefined,
-  runtime: Runtime | undefined,
-): ValidationResult {
-  if (serverDeploy !== "vercel") return Result.ok(undefined);
-  if (backend === "convex" || backend === "self") {
-    return validationErr(
-      "'--server-deploy vercel' requires a separate server backend (hono, express, fastify, elysia). For a fullstack 'self' backend, use '--web-deploy vercel' instead.",
-    );
-  }
-  if (runtime === "workers") {
-    return validationErr(
-      "'--server-deploy vercel' is not compatible with '--runtime workers'. Use '--runtime bun' or '--runtime node', or choose '--server-deploy cloudflare'.",
-    );
-  }
-  return Result.ok(undefined);
-}
+function validateNonWorkersServerDeploy(
+  target: "docker" | "vercel",
+  serverDeploy: ServerDeploy | undefined,
+  backend: Backend | undefined,
+  runtime: Runtime | undefined,
+): ValidationResult {
+  if (serverDeploy !== target) return Result.ok(undefined);
+  if (backend === "convex" || backend === "self") {
+    return validationErr(
+      `'--server-deploy ${target}' requires a separate server backend (hono, express, fastify, elysia). For a fullstack 'self' backend, use '--web-deploy ${target}' instead.`,
+    );
+  }
+  if (runtime === "workers") {
+    return validationErr(
+      `'--server-deploy ${target}' is not compatible with '--runtime workers'. Use '--runtime bun' or '--runtime node', or choose '--server-deploy cloudflare'.`,
+    );
+  }
+  return Result.ok(undefined);
+}
+
+export const validateDockerServerDeploy = validateNonWorkersServerDeploy.bind(null, "docker");
+export const validateVercelServerDeploy = validateNonWorkersServerDeploy.bind(null, "vercel");
apps/web/content/docs/cli/compatibility.mdx (1)

200-205: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Doc omits convex backend exclusion for --server-deploy vercel.

Per the referenced validateVercelServerDeploy implementation, --server-deploy vercel is rejected for backend === "convex" as well as backend === "self", requiring a standalone backend (hono/express/fastify/elysia). This section only documents the self exclusion; the convex restriction is not mentioned, which could mislead users trying --server-deploy vercel --backend convex.

"'--server-deploy vercel' requires a separate server backend (hono, express, fastify, elysia). For a fullstack 'self' backend, use '--web-deploy vercel' instead."

📝 Suggested doc fix
-- `--server-deploy docker` and `--server-deploy vercel` require `--runtime bun` or `--runtime node`
-- `--server-deploy` is not used for `--backend self`, because fullstack backends deploy with the web app
+- `--server-deploy docker` and `--server-deploy vercel` require `--runtime bun` or `--runtime node`
+- `--server-deploy vercel` also requires a standalone server backend and is not compatible with `--backend convex`
+- `--server-deploy` is not used for `--backend self`, because fullstack backends deploy with the web app
apps/web/src/app/(home)/new/_components/utils.ts (1)

735-751: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Consider merging duplicate docker/vercel remap blocks.

Both blocks perform the identical transition (remap to cloudflare on Workers runtime) with the same message. Could be merged into one condition to avoid duplicated logic if a future provider needs the same treatment.

♻️ Proposed consolidation
-  if (nextStack.serverDeploy === "docker" && nextStack.runtime === "workers") {
-    nextStack.serverDeploy = "cloudflare";
-    changed = true;
-    changes.push({
-      category: "serverDeploy",
-      message: "Server deploy set to 'Cloudflare' (Workers runtime deploys via Cloudflare)",
-    });
-  }
-
-  if (nextStack.serverDeploy === "vercel" && nextStack.runtime === "workers") {
-    nextStack.serverDeploy = "cloudflare";
-    changed = true;
-    changes.push({
-      category: "serverDeploy",
-      message: "Server deploy set to 'Cloudflare' (Workers runtime deploys via Cloudflare)",
-    });
-  }
+  if (
+    (nextStack.serverDeploy === "docker" || nextStack.serverDeploy === "vercel") &&
+    nextStack.runtime === "workers"
+  ) {
+    nextStack.serverDeploy = "cloudflare";
+    changed = true;
+    changes.push({
+      category: "serverDeploy",
+      message: "Server deploy set to 'Cloudflare' (Workers runtime deploys via Cloudflare)",
+    });
+  }
packages/template-generator/templates/api/orpc/web/solid/src/utils/orpc.ts.hbs (1)

16-43: 📐 Maintainability & Code Quality | 🔵 Trivial

Vercel origin resolution logic looks correct.

Normalizes trailing slash, short-circuits for absolute URLs, uses window.location.origin in-browser, and falls back to VERCEL_URL/VERCEL_PROJECT_PRODUCTION_URL (standard Vercel system env vars) on the server, with a sane localhost fallback. No functional issues spotted.

This same ~25-line helper is duplicated verbatim across Astro/Nuxt/React/Solid/Svelte templates. Given each .hbs is an isolated per-framework file (and Svelte additionally needs it gated behind {{#unless (eq backend "self")}}), extracting a shared module isn't trivial within the template-generator's current per-template model, so this is a nice-to-have rather than a blocking concern.
[recommended_refactor_reward_low]

packages/template-generator/templates/api/orpc/web/astro/src/lib/orpc.ts.hbs (1)

13-37: 📐 Maintainability & Code Quality | 🔵 Trivial

Duplicate getServerUrl implementation across templates.

This exact helper (lines 13-37) is duplicated verbatim in the React orpc template (packages/template-generator/templates/api/orpc/web/react/base/src/utils/orpc.ts.hbs), and a similar-purpose helper exists in Nuxt/Solid/Svelte. Since these are independent per-framework .hbs templates that must remain self-contained in generated output, some duplication is expected, but consider extracting the shared logic into a Handlebars partial (if the template-processor supports partials) to keep the Vercel-URL-resolution logic consistent as it evolves.

Also applies to: 39-40


ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 3115009d-0476-4641-bbf2-e1650b9035f6

📥 Commits

Reviewing files that changed from the base of the PR and between 7abd369 and 9d36bab.

📒 Files selected for processing (38)
  • apps/cli/README.md
  • apps/cli/src/prompts/server-deploy.ts
  • apps/cli/src/prompts/web-deploy.ts
  • apps/cli/src/utils/compatibility-rules.ts
  • apps/cli/src/utils/config-validation.ts
  • apps/cli/test/deployment.test.ts
  • apps/cli/test/pnpm-workspace.test.ts
  • apps/web/content/docs/cli/compatibility.mdx
  • apps/web/content/docs/cli/options.mdx
  • apps/web/content/docs/project-structure.mdx
  • apps/web/src/app/(home)/new/_components/utils.ts
  • apps/web/src/components/ui/kibo-ui/code-block/index.tsx
  • apps/web/src/lib/constant.ts
  • apps/web/test/stack-builder-compatibility.test.ts
  • packages/template-generator/src/post-process/package-configs.ts
  • packages/template-generator/src/processors/deploy-deps.ts
  • packages/template-generator/src/processors/frontend-deps.ts
  • packages/template-generator/src/processors/readme-generator.ts
  • packages/template-generator/src/template-handlers/deploy.ts
  • packages/template-generator/src/templates.generated.ts
  • packages/template-generator/src/utils/add-deps.ts
  • packages/template-generator/src/utils/generated-ignore-patterns.ts
  • packages/template-generator/templates/api/orpc/web/astro/src/lib/orpc.ts.hbs
  • packages/template-generator/templates/api/orpc/web/nuxt/app/plugins/orpc.ts.hbs
  • packages/template-generator/templates/api/orpc/web/react/base/src/utils/orpc.ts.hbs
  • packages/template-generator/templates/api/orpc/web/solid/src/utils/orpc.ts.hbs
  • packages/template-generator/templates/api/orpc/web/svelte/src/lib/orpc.ts.hbs
  • packages/template-generator/templates/backend/server/elysia/src/index.ts.hbs
  • packages/template-generator/templates/backend/server/hono/src/index.ts.hbs
  • packages/template-generator/templates/base/_gitignore
  • packages/template-generator/templates/deploy/vercel/scripts/sync-vercel-env.ts.hbs
  • packages/template-generator/templates/deploy/vercel/vercel.json.hbs
  • packages/template-generator/templates/extras/pnpm-workspace.yaml.hbs
  • packages/template-generator/templates/frontend/astro/astro.config.mjs.hbs
  • packages/template-generator/templates/packages/env/src/server.ts.hbs
  • packages/template-generator/templates/packages/env/src/web.ts.hbs
  • packages/types/src/schemas.ts
  • plugin/skills/scaffold-project/SKILL.md

Comment thread packages/template-generator/src/post-process/package-configs.ts
Comment thread packages/template-generator/src/utils/add-deps.ts
Comment thread packages/template-generator/templates/api/orpc/web/astro/src/lib/orpc.ts.hbs Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (4)
scripts/vercel-smoke.sh (4)

53-53: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Unquoted $flags intentional but worth an array for robustness.

Shellcheck flags $flags at Line 53 (SC2086) since word-splitting is required here to expand the flags string into separate CLI arguments. Functionally fine for this script's controlled hardcoded inputs, but converting TESTS entries to a flags array (or adding a targeted # shellcheck disable=SC2086) would make the intent explicit and remove the lint noise.

Source: Linters/SAST tools


76-78: 🔒 Security & Privacy | 🔵 Trivial | 💤 Low value

bash -c "$build_cmd" re-parses generated command string.

Static analysis flags interpolating a variable into bash -c as an injection pattern. Here build_cmd originates from the generated vercel.json, which is templated from hardcoded flags in the TESTS array, so there's no attacker-controlled input in this smoke-test context. Still, for defense-in-depth and to avoid setting a copy-paste-able unsafe pattern elsewhere, consider invoking via sh -c -- "$build_cmd" with explicit -- or validating the command against an allowlist before exec.

Source: Linters/SAST tools


28-29: 🔒 Security & Privacy | 🔵 Trivial | 💤 Low value

Predictable /tmp paths flagged for TOCTOU risk.

Static analysis repeatedly flags hardcoded /tmp/vercel-smoke-* and /tmp/vercel-smoke-resp.txt paths as susceptible to symlink/TOCTOU attacks on shared systems. Given this is local, single-user smoke-test tooling, the practical risk is low, but switching to mktemp-generated paths (or a per-run unique scratch subdirectory under $SCRATCH) would close the gap cheaply.

Also applies to: 52-53, 65-66, 71-73, 77-78, 89-95, 120-122

Source: Linters/SAST tools


102-113: 🩺 Stability & Availability | 🔵 Trivial | 💤 Low value

Fixed sleep 3 before asserting no-bind could be flaky.

The vercel-guard negative check waits a static 3 seconds then curls once. If a runtime is slow to initialize (e.g., first bun/tsx JIT warm-up under load), the assertion could pass even though the process would have eventually bound after the check, or intermittently fail on slower CI machines. Consider polling for a short window (similar to wait_for_server but inverted, asserting no bind across a few attempts) for a more robust check.


ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 698a93a0-cd73-4483-b1f0-aaf3adde3e05

📥 Commits

Reviewing files that changed from the base of the PR and between 9d36bab and 96500a4.

📒 Files selected for processing (8)
  • .gitignore
  • apps/cli/test/deployment.test.ts
  • apps/web/content/docs/guides/docker.mdx
  • apps/web/content/docs/guides/meta.json
  • apps/web/content/docs/guides/vercel.mdx
  • package.json
  • packages/template-generator/src/processors/readme-generator.ts
  • scripts/vercel-smoke.sh
✅ Files skipped from review due to trivial changes (2)
  • .gitignore
  • apps/web/content/docs/guides/vercel.mdx
🚧 Files skipped from review as they are similar to previous changes (2)
  • packages/template-generator/src/processors/readme-generator.ts
  • apps/cli/test/deployment.test.ts

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
packages/template-generator/src/utils/add-deps.ts (1)

239-246: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Custom dependency paths don't get the same dedup treatment.

The dependencies/devDependencies loops now dedupe between sections (Lines 220-227), but customDependencies/customDevDependencies still write directly without deleting the counterpart entry. Not currently exercised by the Vercel processors (which pass plain arrays), but could produce duplicate entries across sections if a future caller mixes custom and named deps for the same package.


ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 1d7251cd-0b45-4248-97ec-9b3b2a9cbfb4

📥 Commits

Reviewing files that changed from the base of the PR and between b2adbbc and 5e5ebc5.

📒 Files selected for processing (4)
  • apps/cli/src/helpers/core/post-installation.ts
  • apps/cli/test/deployment.test.ts
  • packages/template-generator/src/processors/deploy-deps.ts
  • packages/template-generator/src/utils/add-deps.ts
🚧 Files skipped from review as they are similar to previous changes (2)
  • packages/template-generator/src/processors/deploy-deps.ts
  • apps/cli/test/deployment.test.ts

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2


ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: afb97d38-2e14-4952-b7ea-5f2729d16b89

📥 Commits

Reviewing files that changed from the base of the PR and between 5e5ebc5 and 0faeb27.

📒 Files selected for processing (10)
  • apps/cli/test/deployment.test.ts
  • packages/template-generator/src/templates.generated.ts
  • packages/template-generator/templates/api/trpc/web/react/base/src/utils/trpc.ts.hbs
  • packages/template-generator/templates/auth/better-auth/web/astro/src/lib/auth-client.ts.hbs
  • packages/template-generator/templates/auth/better-auth/web/react/base/src/lib/auth-client.ts.hbs
  • packages/template-generator/templates/auth/better-auth/web/solid/src/lib/auth-client.ts.hbs
  • packages/template-generator/templates/auth/better-auth/web/svelte/src/lib/auth-client.ts.hbs
  • packages/template-generator/templates/deploy/vercel/scripts/sync-vercel-env.ts.hbs
  • packages/template-generator/templates/frontend/react/tanstack-start/src/router.tsx.hbs
  • scripts/vercel-smoke.sh
🚧 Files skipped from review as they are similar to previous changes (4)
  • packages/template-generator/templates/deploy/vercel/scripts/sync-vercel-env.ts.hbs
  • scripts/vercel-smoke.sh
  • apps/cli/test/deployment.test.ts
  • packages/template-generator/src/templates.generated.ts

Add 'vercel' as a web and server deploy target across the CLI prompts, schemas, compatibility rules, config validation, docs, web stack-builder, and the scaffold-project plugin skill.

Template generator:
- deploy/vercel/vercel.json.hbs: Vercel Services config (web/server services + rewrites; /api request-path transform for combined web+server deploys; bunVersion for bun runtime)
- deploy/vercel/scripts/sync-vercel-env.ts.hbs: tsx-based env sync (dotenv.parse, per-environment, skip/override keys for combined deploys)
- Root package scripts: dev:vercel, env:vercel:preview|production, deploy:vercel[:prod]; deploy devDeps @types/node, dotenv, tsx, vercel
- oRPC web clients (react/solid/svelte/astro/nuxt) normalize same-origin /api paths to absolute URLs before RPCLink
- server/web env schemas derive origins from VERCEL_* and accept same-origin server URLs
- hono/elysia entrypoints export the app for Vercel functions (hono node guarded by process.env.VERCEL)
- .vercel ignore patterns; pnpm build approvals

Known follow-ups (reviewed, not yet fixed): Astro adapter swap to @astrojs/vercel; preview env-sync interactive git-branch prompt (needs --non-interactive); SSR oRPC preferring production over preview origin; Elysia+node local-start guard.
- Astro + web-deploy vercel now ships @astrojs/vercel instead of the @astrojs/node standalone adapter, which Vercel's astro preset cannot serve (verified: build emits .vercel/output)
- env sync passes --non-interactive so 'vercel env add KEY preview' with a piped stdin value no longer hangs on the interactive Git branch prompt in normal terminals
- oRPC clients and server env prefer VERCEL_URL outside production so preview/branch SSR resolves the current deployment instead of the production origin
- Elysia Vercel deploys keep a guarded local app.listen(3000) for bun and node; Bun does not auto-serve Elysia's default export (verified empirically)
- env sync arg parsing partitions files vs flags by existence instead of position, so flags before file paths no longer drop files or leak them into vercel positionals
- add deploy:vercel:check (vercel deploy --dry) and document vercel link + env-sync-before-deploy in generated READMEs
- scripts/vercel-smoke.sh (bun smoke:vercel): scaffolds 6 Vercel combos, validates vercel.json and the env-sync script, builds each web service with the exact generated buildCommand (asserting vite/next/.vercel-output artifacts), boots server entrypoints locally and probes them, and verifies guarded entrypoints do not bind under VERCEL=1. Runs fully offline; 6/6 passing.
- docs: guides/vercel.mdx covering Vercel Services, vercel.json anatomy, the link->env-sync->deploy flow, the three-layer env model, and troubleshooting; guides/docker.mdx covering the compose stack, image builds, env flow, and moving to a real host.
- generated READMEs now link to the matching deployment guide for Vercel and Docker (parity with Cloudflare).
- workspace-deps already adds dotenv as a root dependency for every project, so the Vercel deploy devDependencies duplicated it across sections and bun warned on install; drop it from deploy-deps and make addPackageDependency reject cross-section duplicates generically (runtime dependency wins)
- post-installation next steps now include a Vercel Services section (link -> env sync -> deploy, with the guide URL), matching the Cloudflare and Docker sections
… sync runner

- better-auth clients (react/svelte/solid/astro) crashed on Vercel combined deploys with 'BetterAuthError: Invalid base URL: /api' because createAuthClient requires an absolute URL; they now use the same getServerUrl normalization as the oRPC clients, as do the tRPC client and the tanstack-start router (whose SSR fetch would also reject relative URLs)
- sync-vercel-env.ts drops the platform-specific local-bin resolution and runs the Vercel CLI through the stack's package runner (bunx/npx/pnpm exec), which resolves the local devDependency
- vercel smoke combo 1 now scaffolds trpc + better-auth to cover this class in web builds
Codex review finding (P1, verified against better-auth source): withPath() uses a baseURL that already has a path as-is and only auto-appends /api/auth to origin-only URLs. On combined Vercel deploys the normalized base is origin + /api, so auth requests went to /api/* instead of /api/auth/* and 404ed through the rewrite. All four web auth clients now append /api/auth explicitly, which is byte-identical to the previous resolved URL for origin-only bases and maps to /api/api/auth -> /api/auth on Vercel.

Verified end-to-end against an emulation of the generated vercel.json routing (static dist + /api/(.*) rewrite with path strip): browser signup succeeds, session persists, and an authenticated tRPC call returns private data.
CodeRabbit review suggestion: the same-origin URL normalization helper was inlined 11 times across the oRPC/tRPC/better-auth client templates and the tanstack-start router, so future fixes would have to be replicated by hand. It is now a partial registered in template-processor.ts ({{> getServerUrl}}, plus a getServerUrlSpaces variant derived from the same source for 2-space templates).
Found during live deployment verification: tanstack-router/solid map to the plain vite preset, which serves static output with no history fallback, so deep links like /login returned 404 in production (the Docker path already handles this via nginx try_files). The web service now carries an in-service rewrite to /index.html for those frontends; static assets still win because rewrites apply after the filesystem check.

Live-verified on four production deployments (hono/express/fastify/elysia x tanstack-router): deep links 200, /api/ OK, oRPC healthCheck OK, and a real browser signup against Neon Postgres on the hono + better-auth + tRPC stack (session persisted, authenticated tRPC returned private data).
Found by live-deploying next + self + better-auth: signup returned 403 Invalid origin for two stacked reasons.

- env sync only skipped BETTER_AUTH_URL/CORS_ORIGIN/NODE_ENV for combined web+server deploys, so self-backend apps synced their localhost values to production and better-auth rejected the real origin; the skip now also applies when webDeploy is vercel with a self backend (the app runs on Vercel and derives origins from VERCEL_* at runtime)
- deployments from non-git directories uploaded local .env files (nothing excluded them), and Next.js loads those at runtime, shadowing the runtime derivation; Vercel deploys now generate a root .vercelignore excluding env files and local sqlite artifacts

Live-verified: browser signup on next+self+better-auth+Neon now lands on the dashboard with a session and an authenticated oRPC response.
- link:vercel script so every surface (README, docs guide, post-install) references one consistent first-run command instead of three different vercel link invocations
- post-install deploy instructions now render after Clerk/Polar setup so env sync happens once real keys exist
- env sync warns when values look local-only (localhost, 127.0.0.1, file:), the recurring split-deploy footgun
- deployment guide pairs each env sync with its deploy target (preview and production are separate Vercel env stores)
…prefix

Codex review (P2): with the sync skipping BETTER_AUTH_URL in combined deploys, the server derived a bare origin, so better-auth built callback/redirect URLs at origin/api/auth/* — which the rewrite strips to /auth/* on the server service, missing the auth handler. Email/password flows never build such URLs (which is why live signup passed), but OAuth callbacks and emailed links would 404. The combined-mode fallback is now origin + /api/api/auth, the full public path to the server's auth mount; self and server-only derivations stay origin-only, which is correct for them.
…s the only cloud target

The user already chose Vercel at scaffold time, so plain names match the Cloudflare precedent (bare deploy/destroy): deploy:setup (link — a bare 'link' script would collide with the bun/npm/pnpm link builtins), env:preview, env:production, deploy, deploy:prod, deploy:check. dev:vercel keeps its name since it names the tool (vercel dev), not an action. When Vercel is mixed with Cloudflare in one repo, Alchemy owns bare deploy/destroy and the :vercel-suffixed names are generated instead; README, post-install, and docs all render whichever set applies.
When web and server deploy to different cloud platforms (Vercel + Cloudflare), the deploy scripts are named by what they deploy — deploy:web / deploy:web:prod on one side and deploy:server on the other — instead of vendor-suffixed names. Single-platform and combined deploys keep plain deploy/deploy:prod. Setup, env sync, and dry-run scripts (deploy:setup, env:preview, env:production, deploy:check) are Vercel-unique operations and keep the same names in every combination. README, post-install, and the docs guide render whichever set applies.
Mixed Vercel + Cloudflare projects keep Alchemy state under .alchemy/; without this it gets bundled into Vercel deployments.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant