Developer deploying a web app from a laptop to the cloud

How to Set Up CI/CD for a Vibe-Coded Project on GitHub Actions

CI/CD for a vibe-coded project is the difference between shipping a demo URL and running a product you can iterate on without fear. AI builders and Cursor sessions move fast — but without lint, test, build, and deploy gates on every push, you redeploy the same TypeScript error five times and wonder why Vercel hates you. This tutorial walks through a production-ready GitHub Actions pipeline for Next.js and React repos born from vibe coding: sample YAML, secrets management, Vercel deploy hooks, and the failure patterns AI codegen leaves behind. Updated November 2026.

GitHub Actions workflow running automated tests and deployment for an AI-built Next.js application
Every green check on main is proof the AI diff did not break lint, types, tests, or the production build.

Before you wire CI, confirm the app actually builds locally — start with deploy a vibe-coded app to Vercel if you have not shipped yet. Pair this guide with write tests for AI-generated code and how to review AI-generated code so your pipeline catches what prompt-and-pray misses.

Table of contents

  1. Why CI/CD matters for vibe-coded repos
  2. Pipeline overview: lint, test, build, deploy
  3. Sample GitHub Actions workflow (Next.js + Vercel)
  4. Secrets management for AI-built apps
  5. Branch strategy and preview deploys
  6. Common CI failures in AI-generated Next.js code
  7. Hardening checklist before you trust the bot
  8. FAQ

Why CI/CD matters for vibe-coded repos

Vibe coding optimizes for speed: describe a feature, accept a diff, preview in the browser. That loop breaks down at team scale and at production scale because AI output is probabilistic. The same prompt on Tuesday produces different import paths than on Monday. Without automated gates, you merge broken code because the preview looked fine in dev mode with hot reload masking a failed production build.

Continuous integration (CI) runs on every push or pull request: lint, typecheck, unit tests, and a production build. Continuous deployment (CD) promotes passing builds to staging or production — often via Vercel, Netlify, or Cloudflare Pages connected to GitHub. For solo builders, CI is your unpaid senior engineer who refuses to merge red builds.

Without CI/CDWith CI/CD
Deploy broken builds; debug in productionCatch failures before users see them
Forget to run tests after AI refactorsTests run automatically on every diff
Secrets leak in committed .env filesSecrets live in GitHub/Vercel only; CI uses masked vars
No audit trail of what shipped whenGit history + workflow logs = reproducible deploys
Preview URLs only from local npm run devPR previews on real infra with prod-like env

If you exported from Lovable, Bolt, or Replit, you likely already have a GitHub repo. Adding Actions takes 30–60 minutes once and saves hours per week. Align with the vibe coding starter stack 2026 (Cursor + Supabase + Vercel) so your pipeline matches your hosting choice.

Pipeline overview: lint, test, build, deploy

A sensible default for Next.js App Router projects:

  1. Lint — ESLint catches unused vars, hook violations, and accessibility issues AI ignores
  2. Typecheck — tsc --noEmit fails on implicit any and broken imports stricter than dev server
  3. Unit tests — Vitest or Jest on critical paths (auth helpers, API route handlers, utils)
  4. Build — next build mirrors Vercel; catches SSR and env errors local dev hides
  5. Deploy — Vercel Git integration (simplest) or GitHub Action calling Vercel CLI with tokens

You can split jobs for speed: lint + typecheck in parallel with test; build depends on both. Cache node_modules via actions/cache keyed on lockfile hash. Pin Node version (node-version: '20') — AI often assumes latest Node APIs.

StageTypical commandWhat AI code breaks
Lintnpm run lintUnused imports, missing deps in useEffect
Typechecknpx tsc --noEmitAny types, wrong prop shapes, bad generics
Testnpm testUntested edge cases, mocked env gaps
Buildnpm run buildMissing env at build time, dynamic import errors
DeployVercel / vercel --prodWrong root directory, missing secrets

Sample GitHub Actions workflow (Next.js + Vercel)

Create .github/workflows/ci.yml in your repo root. This example runs on push to main and on all pull requests. Deploy step uses Vercel CLI; alternatively, skip deploy in Actions and let Vercel’s GitHub app deploy when CI passes (recommended for beginners).

name: CI

on:
  push:
    branches: [main]
  pull_request:
    branches: [main]

concurrency:
  group: ${{ github.workflow }}-${{ github.ref }}
  cancel-in-progress: true

jobs:
  quality:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - uses: actions/setup-node@v4
        with:
          node-version: '20'
          cache: 'npm'

      - run: npm ci

      - name: Lint
        run: npm run lint

      - name: Typecheck
        run: npx tsc --noEmit

      - name: Unit tests
        run: npm test -- --run
        env:
          CI: true

      - name: Production build
        run: npm run build
        env:
          NEXT_PUBLIC_SITE_URL: https://example.com
          # Add build-time public env vars here or use dummy values for CI

  deploy-preview:
    needs: quality
    if: github.event_name == 'pull_request'
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: '20'
          cache: 'npm'
      - run: npm ci
      - name: Deploy Preview to Vercel
        run: npx vercel deploy --token=${{ secrets.VERCEL_TOKEN }}
        env:
          VERCEL_ORG_ID: ${{ secrets.VERCEL_ORG_ID }}
          VERCEL_PROJECT_ID: ${{ secrets.VERCEL_PROJECT_ID }}

  deploy-production:
    needs: quality
    if: github.ref == 'refs/heads/main' && github.event_name == 'push'
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: '20'
          cache: 'npm'
      - run: npm ci
      - name: Deploy Production to Vercel
        run: npx vercel deploy --prod --token=${{ secrets.VERCEL_TOKEN }}
        env:
          VERCEL_ORG_ID: ${{ secrets.VERCEL_ORG_ID }}
          VERCEL_PROJECT_ID: ${{ secrets.VERCEL_PROJECT_ID }}

Simpler path: Delete the deploy jobs and rely on Vercel’s native GitHub integration. Keep the quality job only — Vercel will not deploy if you add “Required status checks” on the quality job in branch protection. That pattern separates “does it build?” (Actions) from “ship it” (Vercel).

Add a package.json scripts block if AI omitted them:

"scripts": {
  "dev": "next dev",
  "build": "next build",
  "start": "next start",
  "lint": "next lint",
  "test": "vitest"
}

For monorepos (Turborepo), set working-directory on steps or use turbo run lint test build. AI loves nested apps/web without documenting root — match Vercel’s Root Directory setting.

Terminal showing successful GitHub Actions CI pipeline with lint test and build steps passing
Green CI on a PR is your signal to review logic — not skip review because “the bot wrote it.”

Secrets management for AI-built apps

AI agents routinely suggest committing .env files or hardcoding Stripe keys in source. CI/CD forces secrets into the right places.

Where secrets live

  • GitHub Actions secrets — Repository Settings → Secrets and variables → Actions. Use for VERCEL_TOKEN, deploy keys, optional E2E credentials
  • Vercel environment variables — Project Settings → Environment Variables. Production, Preview, and Development scopes. Server secrets never get NEXT_PUBLIC_ prefix
  • Local only — .env.local gitignored; commit .env.example with placeholder names only

Required secrets for Vercel CLI deploy from Actions

Secret nameWhere to get itNotes
VERCEL_TOKENVercel → Account Settings → TokensScope to deploy only; rotate if leaked
VERCEL_ORG_ID.vercel/project.json after vercel linkNot sensitive but required for CLI
VERCEL_PROJECT_IDSame fileMaps repo to Vercel project

For Supabase, Clerk, or Stripe: store API keys in Vercel env vars, not GitHub Actions, unless tests need them. In CI build step, use dummy NEXT_PUBLIC_* values so next build does not fail on missing URLs — document real values only in Vercel.

Preventing leaks in CI logs

GitHub masks secrets in logs when referenced as ${{ secrets.NAME }}. Never echo secrets. Avoid printing full env in debug steps. If AI added console.log(process.env) anywhere, remove it before first CI run — that is a common accidental exposure vector.

Run gitleaks or GitHub secret scanning on the repo. AI-generated commits sometimes paste keys in comments or test fixtures. Review diffs with the same rigor as production code — see how to review AI-generated code.

Branch strategy and preview deploys

Recommended flow for vibe-coded solo projects scaling to collaborators:

  1. main — production; protected; requires passing CI
  2. Feature branches — feat/add-billing, one AI session per branch when possible
  3. Pull requests — even solo: PR triggers CI + Vercel Preview URL for sharing
  4. Hotfix — short-lived branch from main, merge after CI green

Enable branch protection on main: require status checks from quality job, require PR (optional for solo but builds habit), disallow force push. AI agents that auto-commit to main bypass your safety net — configure Cursor or Claude Code to work on branches.

Preview deploys let you test auth callbacks, webhooks, and Supabase RLS with staging credentials. Set Preview env vars in Vercel to a staging Supabase project so vibe coding experiments do not corrupt production rows.

Common CI failures in AI-generated Next.js code

These show up repeatedly in vibe-coded repos. Fix locally, push, watch CI go green.

CI errorRoot cause (AI pattern)Fix
Module not found: Can't resolve 'X'Import added without npm installInstall package; commit lockfile; run npm ci locally
TypeScript errors only in CILocal skips tsc; strict mode in CIRun tsc --noEmit before push; fix types, not @ts-ignore
next build — env undefinedNEXT_PUBLIC_* used at build time, missing in Actions envAdd dummy vars to workflow build step + real vars in Vercel
ESLint react-hooks/exhaustive-depsAI omitted dependency array entriesFix effect deps; do not disable rule globally
Tests fail — fetch / networkTests hit real APIs without mocksMock Supabase/Stripe; use MSW for route handlers
Edge runtime + Node APIexport const runtime = 'edge' with fs or bcryptSwitch route to Node runtime or swap library
Out of memory on buildGiant images imported staticallyUse next/image remote patterns; compress assets
ERESOLVE peer dependencyAI pinned React 19 with lib expecting 18Align versions in package.json; one major line
Playwright/E2E timeoutCI has no server runningStart app in background step or use Vercel preview URL

Flaky tests: AI-generated tests often use Date.now() or random IDs without seeding. Prefer deterministic fixtures. See write tests for AI-generated code for Vitest patterns on utils and API routes without slowing vibe coding.

False green locally: Developers run next dev but never next build. Add a pre-push hook or habit: npm run build && npm test before opening PR. CI should not be the first time you compile for production.

Lockfile drift: AI runs npm install package without committing package-lock.json. CI uses npm ci which fails on mismatch. Always commit lockfiles after dependency changes.

Hardening checklist before you trust the bot

  • .github/workflows/ci.yml committed; runs on PR and push to main
  • Branch protection requires CI pass before merge
  • .env.example documents all env var names; no secrets in git history
  • Vercel env vars set for Production and Preview scopes
  • npm run lint, tsc --noEmit, npm test, npm run build all pass locally
  • At least smoke tests on auth and one API route — per testing guide
  • Code review checklist applied to AI diffs — escaping, auth, error handling
  • Optional: Dependabot or Renovate for dependency updates AI forgets

Once CI is green, deployment becomes boring — the goal. Your next iteration is prompt chains for multi-file refactors with confidence that regressions surface in minutes, not after a user emails you.

FAQ

Do I need GitHub Actions if Vercel already builds my app?

Vercel builds on push, but it may not run your test suite or custom lint rules unless configured. A dedicated CI job runs tests and typecheck explicitly; you can block merges until CI passes even if Vercel would have deployed. Many teams use Actions for quality gates and Vercel for hosting only.

How do I run CI on a private repo for free?

GitHub Actions includes free minutes for private repos (limits vary by plan). Public repos get generous free usage. Lint + test + build on a small Next.js app typically consumes a few minutes per run. Cache dependencies to cut time and cost.

Should I deploy to production from GitHub Actions or Vercel Git integration?

For most vibe coders, Vercel Git integration is simpler: connect repo, set env vars, auto-deploy on merge to main. Use Actions deploy jobs only if you need custom steps (migrations, Slack notifications, multi-target deploys). Never duplicate deploy paths without meaning to ship twice.

CI fails on Supabase env vars I only set in Vercel — why?

The GitHub Actions build step runs on GitHub servers, not Vercel. It does not see Vercel env vars unless you duplicate them as GitHub secrets or use placeholder public vars for build-only. Server secrets are injected at runtime on Vercel, not during GitHub’s next build — structure code so server keys are not required at compile time.

Soft CTA: Open your vibe-coded repo tonight. Add the sample workflow, fix the first red build, and protect main. Tomorrow’s AI prompt ships through a pipeline that catches mistakes before your users do. If you have not deployed yet, start with deploy to Vercel, then come back and wire CI — ship first, automate second, but do not skip automate.

Leave a Reply

Your email address will not be published. Required fields are marked *