Tutorial: Set Up a Deployment Pipeline
Create a complete CI/CD pipeline for your AutoDeployBase project.
Overview
We'll set up:
- GitHub Actions for CI
- Automated testing
- Database migrations
- Staging and production deployments
- Vercel + PostgreSQL
Prerequisites
- GitHub repository
- Vercel account
- PostgreSQL database (Vercel Postgres or Neon)
Step 1: Create the Project
npx autodeploybase init my-app \
--framework next \
--archetype saas \
--database postgresql \
--plugins github-actions
cd my-app
git init
git add .
git commit -m "Initial commit"
Step 2: Configure GitHub Secrets
Go to your GitHub repo → Settings → Secrets → Actions
Add these secrets:
| Secret | Description |
|---|---|
VERCEL_TOKEN | Vercel API token |
VERCEL_ORG_ID | Vercel organization ID |
VERCEL_PROJECT_ID | Vercel project ID |
DATABASE_URL | Production database URL |
DATABASE_URL_STAGING | Staging database URL |
JWT_SECRET | JWT secret key |
Get Vercel IDs
vercel link
cat .vercel/project.json
Step 3: CI Workflow
.github/workflows/ci.yml
name: CI
on:
push:
branches: [main, develop]
pull_request:
branches: [main]
env:
NODE_VERSION: '20'
jobs:
lint:
name: Lint
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: ${{ env.NODE_VERSION }}
cache: 'npm'
- run: npm ci
- run: npm run lint
typecheck:
name: Type Check
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: ${{ env.NODE_VERSION }}
cache: 'npm'
- run: npm ci
- run: npx prisma generate
- run: npm run typecheck
test:
name: Test
runs-on: ubuntu-latest
services:
postgres:
image: postgres:16
env:
POSTGRES_USER: test
POSTGRES_PASSWORD: test
POSTGRES_DB: test
ports:
- 5432:5432
options: >-
--health-cmd pg_isready
--health-interval 10s
--health-timeout 5s
--health-retries 5
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: ${{ env.NODE_VERSION }}
cache: 'npm'
- run: npm ci
- run: npx prisma generate
- name: Run migrations
run: npx prisma migrate deploy
env:
DATABASE_URL: postgresql://test:test@localhost:5432/test
- name: Run tests
run: npm test -- --coverage
env:
DATABASE_URL: postgresql://test:test@localhost:5432/test
JWT_SECRET: test-secret-for-ci
- name: Upload coverage
uses: codecov/codecov-action@v3
with:
files: ./coverage/lcov.info
build:
name: Build
runs-on: ubuntu-latest
needs: [lint, typecheck, test]
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: ${{ env.NODE_VERSION }}
cache: 'npm'
- run: npm ci
- run: npx prisma generate
- run: npm run build
Step 4: Staging Deployment
.github/workflows/deploy-staging.yml
name: Deploy Staging
on:
push:
branches: [develop]
jobs:
deploy:
name: Deploy to Staging
runs-on: ubuntu-latest
environment: staging
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm'
- run: npm ci
- name: Run migrations
run: npx prisma migrate deploy
env:
DATABASE_URL: ${{ secrets.DATABASE_URL_STAGING }}
- name: Deploy to Vercel
uses: amondnet/vercel-action@v25
with:
vercel-token: ${{ secrets.VERCEL_TOKEN }}
vercel-org-id: ${{ secrets.VERCEL_ORG_ID }}
vercel-project-id: ${{ secrets.VERCEL_PROJECT_ID }}
# No --prod flag = preview deployment
env:
VERCEL_ENV: staging
Step 5: Production Deployment
.github/workflows/deploy-production.yml
name: Deploy Production
on:
push:
branches: [main]
jobs:
deploy:
name: Deploy to Production
runs-on: ubuntu-latest
environment: production
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm'
- run: npm ci
- name: Run migrations
run: npx prisma migrate deploy
env:
DATABASE_URL: ${{ secrets.DATABASE_URL }}
- name: Deploy to Vercel
uses: amondnet/vercel-action@v25
with:
vercel-token: ${{ secrets.VERCEL_TOKEN }}
vercel-org-id: ${{ secrets.VERCEL_ORG_ID }}
vercel-project-id: ${{ secrets.VERCEL_PROJECT_ID }}
vercel-args: '--prod'
- name: Notify Slack
uses: slackapi/slack-github-action@v1
with:
channel-id: 'deployments'
slack-message: 'Production deployed: ${{ github.sha }}'
env:
SLACK_BOT_TOKEN: ${{ secrets.SLACK_BOT_TOKEN }}
Step 6: Database Migration Workflow
For manual migrations:
.github/workflows/migrate.yml
name: Database Migration
on:
workflow_dispatch:
inputs:
environment:
description: 'Environment'
required: true
type: choice
options:
- staging
- production
jobs:
migrate:
name: Run Migrations
runs-on: ubuntu-latest
environment: ${{ inputs.environment }}
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm'
- run: npm ci
- name: Run migrations
run: npx prisma migrate deploy
env:
DATABASE_URL: ${{ inputs.environment == 'production' && secrets.DATABASE_URL || secrets.DATABASE_URL_STAGING }}
- name: Verify
run: npx prisma db pull --force
env:
DATABASE_URL: ${{ inputs.environment == 'production' && secrets.DATABASE_URL || secrets.DATABASE_URL_STAGING }}
Step 7: Preview Deployments for PRs
.github/workflows/preview.yml
name: Preview Deployment
on:
pull_request:
types: [opened, synchronize]
jobs:
preview:
name: Deploy Preview
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Deploy Preview
uses: amondnet/vercel-action@v25
id: deploy
with:
vercel-token: ${{ secrets.VERCEL_TOKEN }}
vercel-org-id: ${{ secrets.VERCEL_ORG_ID }}
vercel-project-id: ${{ secrets.VERCEL_PROJECT_ID }}
- name: Comment PR
uses: actions/github-script@v7
with:
script: |
github.rest.issues.createComment({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
body: '🚀 Preview deployed: ${{ steps.deploy.outputs.preview-url }}'
})
Step 8: Vercel Configuration
vercel.json
{
"framework": "nextjs",
"buildCommand": "prisma generate && next build",
"regions": ["iad1"],
"env": {
"DATABASE_URL": "@database-url",
"JWT_SECRET": "@jwt-secret"
},
"functions": {
"api/**/*.ts": {
"maxDuration": 30
}
}
}
Workflow Diagram
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ Developer │────▶│ GitHub │────▶│ Vercel │
│ Push │ │ Actions │ │ Deploy │
└─────────────┘ └─────────────┘ └─────────────┘
│
▼
┌─────────────┐
│ Tests │
│ Lint │
│ Build │
└─────────────┘
│
┌────────────┴────────────┐
▼ ▼
┌─────────────┐ ┌─────────────┐
│ Staging │ │ Production │
│ (develop) │ │ (main) │
└─────────────┘ └─────────────┘
Monitoring
Add monitoring after deployment:
- name: Health Check
run: |
sleep 30
curl -f ${{ steps.deploy.outputs.preview-url }}/api/health || exit 1
Rollback
For quick rollback:
# List deployments
vercel ls
# Rollback to previous
vercel rollback
Or in workflow:
- name: Rollback on failure
if: failure()
run: vercel rollback --token ${{ secrets.VERCEL_TOKEN }}