Skip to content

Feat/orgs

Feat/orgs #43

Workflow file for this run

name: Deploy Vercel + Neon (Web + API)
on:
push:
branches: [main]
paths:
- "packages/app/**"
- "packages/api/**"
- "packages/common/**"
- ".github/workflows/deploy.yml"
pull_request:
types: [opened, synchronize, reopened, closed]
paths:
- "packages/app/**"
- "packages/api/**"
- "packages/common/**"
- ".github/workflows/deploy.yml"
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
# Determine if this is a production or preview deployment
setup:
name: Setup
outputs:
is_production: ${{ steps.deployment_type.outputs.is_production }}
is_preview: ${{ steps.deployment_type.outputs.is_preview }}
branch: ${{ steps.branch_name.outputs.current_branch }}
neon_branch: ${{ steps.neon_branch.outputs.neon_branch }}
runs-on: ubuntu-latest
steps:
- name: Get branch name
id: branch_name
uses: tj-actions/branch-names@v8
- name: Determine deployment type
id: deployment_type
run: |
if [[ "${{ github.event_name }}" == "push" && "${{ github.ref }}" == "refs/heads/main" ]]; then
echo "is_production=true" >> $GITHUB_OUTPUT
echo "is_preview=false" >> $GITHUB_OUTPUT
else
echo "is_production=false" >> $GITHUB_OUTPUT
echo "is_preview=true" >> $GITHUB_OUTPUT
fi
- name: Generate Neon branch name
id: neon_branch
run: |
if [[ "${{ steps.deployment_type.outputs.is_production }}" == "true" ]]; then
echo "neon_branch=main" >> $GITHUB_OUTPUT
else
# Use branch name + commit hash for unique, descriptive branch names
COMMIT_HASH="${{ github.sha }}"
SHORT_HASH="${COMMIT_HASH:0:8}"
BRANCH_NAME="${{ steps.branch_name.outputs.current_branch }}"
# Clean branch name (remove special characters that might cause issues)
CLEAN_BRANCH_NAME=$(echo "$BRANCH_NAME" | sed 's/[^a-zA-Z0-9-]/-/g' | sed 's/--*/-/g' | sed 's/^-\|-$//g')
echo "neon_branch=preview/${CLEAN_BRANCH_NAME}/${SHORT_HASH}" >> $GITHUB_OUTPUT
fi
# Create/Delete Neon branch for preview deployments
neon_branch_management:
name: Neon Branch Management
needs: setup
outputs:
db_url: ${{ steps.set_outputs.outputs.db_url }}
db_url_with_pooler: ${{ steps.set_outputs.outputs.db_url_with_pooler }}
has_urls: ${{ steps.set_outputs.outputs.has_urls }}
runs-on: ubuntu-latest
steps:
- name: Create Neon Branch
id: create_neon_branch
if: needs.setup.outputs.is_preview && github.event.action != 'closed'
uses: neondatabase/create-branch-action@v5
with:
project_id: ${{ vars.NEON_PROJECT_ID }}
branch_name: ${{ needs.setup.outputs.neon_branch }}
api_key: ${{ secrets.NEON_API_KEY }}
- name: Install jq
if: needs.setup.outputs.is_preview && github.event.action != 'closed'
run: sudo apt-get update && sudo apt-get install -y jq
- name: Wait for Neon Branch to be Ready
if: needs.setup.outputs.is_preview && github.event.action != 'closed'
run: |
echo "Waiting for Neon branch to be ready..."
BRANCH_ID="${{ steps.create_neon_branch.outputs.branch_id }}"
echo "Branch ID: $BRANCH_ID"
# Wait up to 5 minutes for the branch to be ready
for i in {1..30}; do
echo "Checking branch status (attempt $i/30)..."
# Check branch status using curl to Neon API
STATUS=$(curl -s -H "Authorization: Bearer ${{ secrets.NEON_API_KEY }}" \
"https://console.neon.tech/api/v2/projects/${{ vars.NEON_PROJECT_ID }}/branches/$BRANCH_ID" \
| jq -r '.branch.current_state' 2>/dev/null || echo "unknown")
echo "Current status: $STATUS"
if [[ "$STATUS" == "ready" ]]; then
echo "Branch is ready!"
break
elif [[ "$STATUS" == "init" || "$STATUS" == "unknown" ]]; then
echo "Branch still initializing, waiting 10 seconds..."
sleep 10
else
echo "Unexpected status: $STATUS"
break
fi
done
# Final check
FINAL_STATUS=$(curl -s -H "Authorization: Bearer ${{ secrets.NEON_API_KEY }}" \
"https://console.neon.tech/api/v2/projects/${{ vars.NEON_PROJECT_ID }}/branches/$BRANCH_ID" \
| jq -r '.branch.current_state' 2>/dev/null || echo "unknown")
if [[ "$FINAL_STATUS" != "ready" ]]; then
echo "Warning: Branch may not be fully ready (status: $FINAL_STATUS)"
fi
- name: Debug info
run: |
echo "Debug information:"
echo "is_preview: ${{ needs.setup.outputs.is_preview }}"
echo "github.event.action: ${{ github.event.action }}"
echo "github.event_name: ${{ github.event_name }}"
echo "github.sha: ${{ github.sha }}"
echo "neon_branch: ${{ needs.setup.outputs.neon_branch }}"
- name: Set outputs
id: set_outputs
run: |
if [[ "${{ needs.setup.outputs.is_preview }}" == "true" && "${{ github.event.action }}" != "closed" ]]; then
echo "Setting Neon branch URLs..."
echo "db_url=${{ steps.create_neon_branch.outputs.db_url }}" >> $GITHUB_OUTPUT
echo "db_url_with_pooler=${{ steps.create_neon_branch.outputs.db_url_with_pooler }}" >> $GITHUB_OUTPUT
echo "has_urls=true" >> $GITHUB_OUTPUT
echo "Neon branch URLs set successfully"
else
echo "Not setting Neon branch URLs (not a preview deployment or PR closed)"
echo "db_url=" >> $GITHUB_OUTPUT
echo "db_url_with_pooler=" >> $GITHUB_OUTPUT
echo "has_urls=false" >> $GITHUB_OUTPUT
fi
- name: Delete Neon Branch
if: needs.setup.outputs.is_preview && github.event.action == 'closed'
uses: neondatabase/delete-branch-action@v3
with:
project_id: ${{ vars.NEON_PROJECT_ID }}
branch: ${{ needs.setup.outputs.neon_branch }}
api_key: ${{ secrets.NEON_API_KEY }}
continue-on-error: true
# Run database migrations
migrate:
name: Run Database Migrations
needs: [setup, neon_branch_management]
if: needs.setup.outputs.is_preview && needs.neon_branch_management.outputs.has_urls == 'true'
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: "22"
cache: "npm"
- name: Install dependencies
run: npm ci
- name: Get DATABASE_URL
uses: ./.github/actions/get-database-url
with:
is_production: "false"
neon_branch: "${{ needs.setup.outputs.neon_branch }}"
neon_project_id: "${{ vars.NEON_PROJECT_ID }}"
neon_api_key: "${{ secrets.NEON_API_KEY }}"
vercel_token: "${{ secrets.VERCEL_TOKEN }}"
vercel_team: "${{ vars.VERCEL_TEAM }}"
vercel_api_project: "${{ vars.VERCEL_API_PROJECT }}"
- name: Run Migrations
run: npm run migrate
env:
DATABASE_URL: "${{ env.DATABASE_URL }}"
working-directory: packages/common
# Deploy API project
deploy_api:
name: Deploy API
needs: [setup, neon_branch_management]
if: always() && (needs.setup.outputs.is_production || (needs.setup.outputs.is_preview && needs.neon_branch_management.outputs.has_urls == 'true'))
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: "22"
cache: "npm"
- name: Install dependencies
run: npm ci
- name: Get DATABASE_URL
uses: ./.github/actions/get-database-url
with:
is_production: "${{ needs.setup.outputs.is_production }}"
neon_branch: "${{ needs.setup.outputs.neon_branch }}"
neon_project_id: "${{ vars.NEON_PROJECT_ID }}"
neon_api_key: "${{ secrets.NEON_API_KEY }}"
vercel_token: "${{ secrets.VERCEL_TOKEN }}"
vercel_team: "${{ vars.VERCEL_TEAM }}"
vercel_api_project: "${{ vars.VERCEL_API_PROJECT }}"
- name: Install Vercel CLI
run: npm install --global vercel@latest
- name: Pull Vercel Environment Information (API)
run: |
cd packages/api
vercel pull --yes --environment=${{ needs.setup.outputs.is_production == 'true' && 'production' || 'preview' }} --token=${{ secrets.VERCEL_TOKEN }}
env:
VERCEL_ORG_ID: ${{ vars.VERCEL_TEAM }}
VERCEL_PROJECT_ID: ${{ vars.VERCEL_API_PROJECT }}
- name: Build API for Vercel
run: |
cd packages/api
vercel build --token=${{ secrets.VERCEL_TOKEN }}
env:
VERCEL_ORG_ID: ${{ vars.VERCEL_TEAM }}
VERCEL_PROJECT_ID: ${{ vars.VERCEL_API_PROJECT }}
DATABASE_URL: ${{ env.DATABASE_URL }}
NODE_ENV: production
- name: Deploy API to Vercel (Prebuilt)
run: |
vercel deploy --prebuilt --token=${{ secrets.VERCEL_TOKEN }} ${{ needs.setup.outputs.is_production == 'true' && '--prod' || '' }}
env:
VERCEL_ORG_ID: ${{ vars.VERCEL_TEAM }}
VERCEL_PROJECT_ID: ${{ vars.VERCEL_API_PROJECT }}
DATABASE_URL: ${{ env.DATABASE_URL }}
# Deploy Web project
deploy_web:
name: Deploy Web
needs: [setup, deploy_api]
if: always() && needs.deploy_api.result == 'success'
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: "22"
cache: "npm"
- name: Install dependencies
run: npm ci
- name: Get DATABASE_URL
uses: ./.github/actions/get-database-url
with:
is_production: "${{ needs.setup.outputs.is_production }}"
neon_branch: "${{ needs.setup.outputs.neon_branch }}"
neon_project_id: "${{ vars.NEON_PROJECT_ID }}"
neon_api_key: "${{ secrets.NEON_API_KEY }}"
vercel_token: "${{ secrets.VERCEL_TOKEN }}"
vercel_team: "${{ vars.VERCEL_TEAM }}"
vercel_api_project: "${{ vars.VERCEL_API_PROJECT }}"
- name: Install Vercel CLI
run: npm install --global vercel@latest
- name: Pull Vercel Environment Information (Web)
run: |
cd packages/app
vercel pull --yes --environment=${{ needs.setup.outputs.is_production == 'true' && 'production' || 'preview' }} --token=${{ secrets.VERCEL_TOKEN }}
env:
VERCEL_ORG_ID: ${{ vars.VERCEL_TEAM }}
VERCEL_PROJECT_ID: ${{ vars.VERCEL_WEB_PROJECT }}
- name: Build Web for Vercel
run: |
cd packages/app
vercel build --token=${{ secrets.VERCEL_TOKEN }}
env:
VERCEL_ORG_ID: ${{ vars.VERCEL_TEAM }}
VERCEL_PROJECT_ID: ${{ vars.VERCEL_WEB_PROJECT }}
DATABASE_URL: ${{ env.DATABASE_URL }}
RESEND_API_KEY: ${{ secrets.RESEND_API_KEY }}
NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY: ${{ needs.setup.outputs.is_production == 'true' && secrets.NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY_PROD || secrets.NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY_PREVIEW }}
CLERK_SECRET_KEY: ${{ needs.setup.outputs.is_production == 'true' && secrets.CLERK_SECRET_KEY_PROD || secrets.CLERK_SECRET_KEY_PREVIEW }}
NODE_ENV: production
- name: Deploy Web to Vercel (Prebuilt)
run: |
vercel deploy --prebuilt --token=${{ secrets.VERCEL_TOKEN }} ${{ needs.setup.outputs.is_production == 'true' && '--prod' || '' }}
env:
VERCEL_ORG_ID: ${{ vars.VERCEL_TEAM }}
VERCEL_PROJECT_ID: ${{ vars.VERCEL_WEB_PROJECT }}
DATABASE_URL: ${{ env.DATABASE_URL }}
# Post deployment summary
deployment_summary:
name: Deployment Summary
needs: [setup, deploy_api, deploy_web]
if: always() && (needs.deploy_api.result == 'success' || needs.deploy_web.result == 'success')
runs-on: ubuntu-latest
steps:
- name: Deployment Summary
run: |
echo "## 🚀 Deployment Summary" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "**Deployment Type:** ${{ needs.setup.outputs.is_production == 'true' && 'Production' || 'Preview' }}" >> $GITHUB_STEP_SUMMARY
echo "**Branch:** ${{ needs.setup.outputs.branch }}" >> $GITHUB_STEP_SUMMARY
echo "**Neon Branch:** ${{ needs.setup.outputs.neon_branch }}" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "### Deployment Status:" >> $GITHUB_STEP_SUMMARY
echo "- **API:** ${{ needs.deploy_api.result == 'success' && '✅ Success' || '❌ Failed' }}" >> $GITHUB_STEP_SUMMARY
echo "- **Web:** ${{ needs.deploy_web.result == 'success' && '✅ Success' || '❌ Failed' }}" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
if [[ "${{ needs.setup.outputs.is_preview }}" == "true" ]]; then
echo "### Preview URLs:" >> $GITHUB_STEP_SUMMARY
echo "- Check the Vercel dashboard for preview URLs" >> $GITHUB_STEP_SUMMARY
fi