Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add schema syncing across Neon DB branches #413

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 26 additions & 0 deletions .github/workflows/cleanup-neon-preview.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
name: Cleanup Neon Preview Database
on:
pull_request:
types: [closed]

env:
NEON_PROJECT_ID: ${{ secrets.NEON_PROJECT_ID }}

jobs:
delete-preview:
if: github.event.pull_request.merged == true
runs-on: ubuntu-latest
steps:
- name: Get Branch Name
id: branch-name
run: |
# Convert branch name to Neon-compatible format and add preview/ prefix
BRANCH_NAME="preview/$(echo "${{ github.event.pull_request.head.ref }}" | tr '/' '-')"
echo "branch_name=$BRANCH_NAME" >> $GITHUB_OUTPUT

- name: Delete Neon Branch
uses: neondatabase/delete-branch-action@v3
with:
project_id: ${{ env.NEON_PROJECT_ID }}
branch: ${{ steps.branch-name.outputs.branch_name }}
api_key: ${{ secrets.NEON_API_KEY }}
112 changes: 112 additions & 0 deletions .github/workflows/migrate-neon-preview.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
name: Deploy Preview

on:
pull_request:
paths-ignore:
- 'package.json'
- 'package-lock.json'
- 'yarn.lock'
branches:
- '**'
- '!dependabot/**'

env:
NEON_DATABASE_USERNAME: ${{ secrets.NEON_DATABASE_USERNAME }}
NEON_API_KEY: ${{ secrets.NEON_API_KEY }}
NEON_PROJECT_ID: ${{ secrets.NEON_PROJECT_ID }}

jobs:
run-migrations:
permissions: write-all
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'

- name: Install pnpm
run: npm install -g pnpm

- name: Install Dependencies
run: pnpm install

- name: Get branch name
id: branch-name
uses: tj-actions/branch-names@v8

- name: Get Neon Branch Details
id: neon-branch
run: |
BRANCH_NAME="preview/${{ steps.branch-name.outputs.current_branch }}"

# Get branch details from Neon API
RESPONSE=$(curl -s \
-H "Authorization: Bearer ${{ env.NEON_API_KEY }}" \
-H "Accept: application/json" \
"https://console.neon.tech/api/v2/projects/${{ env.NEON_PROJECT_ID }}/branches")

# Extract the branch details using jq
BRANCH_INFO=$(echo $RESPONSE | jq -r --arg BRANCH_NAME "$BRANCH_NAME" '.branches[] | select(.name == $BRANCH_NAME)')
BRANCH_ID=$(echo $BRANCH_INFO | jq -r '.id')

if [ -z "$BRANCH_ID" ]; then
echo "Branch not found"
exit 1
fi

# Get connection details
ENDPOINTS_RESPONSE=$(curl -s \
-H "Authorization: Bearer ${{ env.NEON_API_KEY }}" \
-H "Accept: application/json" \
"https://console.neon.tech/api/v2/projects/${{ env.NEON_PROJECT_ID }}/branches/$BRANCH_ID/endpoints")

HOST=$(echo $ENDPOINTS_RESPONSE | jq -r '.endpoints[0].host')

# Get password for the branch
PASSWORD_RESPONSE=$(curl -s \
-H "Authorization: Bearer ${{ env.NEON_API_KEY }}" \
-H "Accept: application/json" \
"https://console.neon.tech/api/v2/projects/${{ env.NEON_PROJECT_ID }}/branches/$BRANCH_ID/roles/neondb_owner/reveal_password")

PASSWORD=$(echo $PASSWORD_RESPONSE | jq -r '.password')

# Construct database URLs using retrieved password
DB_URL="postgres://${{ env.NEON_DATABASE_USERNAME }}:${PASSWORD}@${HOST}/neondb"
POOLER_URL="postgres://${{ env.NEON_DATABASE_USERNAME }}:${PASSWORD}@${HOST}/neondb"

# Set outputs
echo "branch_id=$BRANCH_ID" >> $GITHUB_OUTPUT
echo "db_url=$DB_URL" >> $GITHUB_OUTPUT
echo "db_url_with_pooler=$POOLER_URL" >> $GITHUB_OUTPUT

- name: Run Migrations
id: migrations
run: |
touch .env
echo "DATABASE_URL=${{ steps.neon-branch.outputs.db_url_with_pooler }}?pgbouncer=true&sslmode=require" >> .env
echo "DATABASE_URL_UNPOOLED=${{ steps.neon-branch.outputs.db_url }}?sslmode=require" >> .env

# Add error handling and verification
npx prisma format --schema=packages/database/prisma/schema.prisma
npx prisma validate --schema=packages/database/prisma/schema.prisma
npx prisma generate --schema=packages/database/prisma/schema.prisma

# Capture migration output to check if any migrations were applied
MIGRATION_OUTPUT=$(npx prisma migrate deploy --schema=packages/database/prisma/schema.prisma)
if echo "$MIGRATION_OUTPUT" | grep -q "No pending migrations"; then
echo "has_migrations=false" >> $GITHUB_OUTPUT
else
echo "has_migrations=true" >> $GITHUB_OUTPUT
fi

- name: Comment Branch Info on Pull Request
if: steps.migrations.outputs.has_migrations == 'true'
uses: thollander/actions-comment-pull-request@v2
with:
message: |
[Neon branch :elephant:](https://console.neon.tech/app/projects/${{ env.NEON_PROJECT_ID }}/branches/${{ steps.neon-branch.outputs.branch_id }}) has been updated with the latest schema changes.

⚠️ Remember: Make sure you've run `pnpm migrate:dev` locally and committed the migration files before merging this PR.
93 changes: 93 additions & 0 deletions .github/workflows/migrate-neon-production.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
name: Migrate Production Database

on:
push:
branches:
- main
paths-ignore:
- 'package.json'
- 'package-lock.json'
- 'yarn.lock'

env:
NEON_DATABASE_USERNAME: ${{ secrets.NEON_DATABASE_USERNAME }}
NEON_API_KEY: ${{ secrets.NEON_API_KEY }}
NEON_PROJECT_ID: ${{ secrets.NEON_PROJECT_ID }}
NEON_BRANCH_ID: ${{ secrets.NEON_MAIN_BRANCH_ID }}

jobs:
run-migrations:
permissions:
contents: write
pull-requests: write
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'

- name: Install pnpm
run: npm install -g pnpm

- name: Install Dependencies
run: pnpm install

- name: Get Main Branch Details
id: neon-main
run: |
# Get connection details for main branch
ENDPOINTS_RESPONSE=$(curl -s \
-H "Authorization: Bearer ${{ env.NEON_API_KEY }}" \
-H "Accept: application/json" \
"https://console.neon.tech/api/v2/projects/${{ env.NEON_PROJECT_ID }}/branches/${{ env.NEON_BRANCH_ID }}/endpoints")

HOST=$(echo $ENDPOINTS_RESPONSE | jq -r '.endpoints[0].host')

# Get password for the main branch
PASSWORD_RESPONSE=$(curl -s \
-H "Authorization: Bearer ${{ env.NEON_API_KEY }}" \
-H "Accept: application/json" \
"https://console.neon.tech/api/v2/projects/${{ env.NEON_PROJECT_ID }}/branches/${{ env.NEON_BRANCH_ID }}/roles/neondb_owner/reveal_password")

PASSWORD=$(echo $PASSWORD_RESPONSE | jq -r '.password')

# Construct database URLs using retrieved password
DB_URL="postgres://${{ env.NEON_DATABASE_USERNAME }}:${PASSWORD}@${HOST}/neondb"
POOLER_URL="postgres://${{ env.NEON_DATABASE_USERNAME }}:${PASSWORD}@${HOST}/neondb"

# Fix: Correct the GITHUB_OUTPUT variable name and add the migration step
echo "db_url=$DB_URL" >> $GITHUB_OUTPUT
echo "db_url_with_pooler=$POOLER_URL" >> $GITHUB_OUTPUT

- name: Run Migrations
run: |
touch .env
echo "DATABASE_URL=${{ steps.neon-main.outputs.db_url_with_pooler }}?pgbouncer=true&sslmode=require" >> .env
echo "DATABASE_URL_UNPOOLED=${{ steps.neon-main.outputs.db_url }}?sslmode=require" >> .env

# Basic setup without logging
npx prisma format --schema=packages/database/prisma/schema.prisma > /dev/null
npx prisma validate --schema=packages/database/prisma/schema.prisma > /dev/null
npx prisma generate --schema=packages/database/prisma/schema.prisma

# Only capture migration status for decision making
MIGRATION_STATUS=$(npx prisma migrate status --schema=packages/database/prisma/schema.prisma 2>&1) || true

if echo "$MIGRATION_STATUS" | grep -q "Database schema is up to date!"; then
echo "ℹ️ Database is up to date"
elif echo "$MIGRATION_STATUS" | grep -q "have not yet been applied"; then
echo "🏗️ Applying migrations..."
if ! npx prisma migrate deploy --schema=packages/database/prisma/schema.prisma; then
echo "❌ Migration failed:"
echo "$MIGRATION_STATUS"
exit 1
fi
echo "✅ Migrations applied successfully"
else
echo "❌ Unexpected migration status:"
echo "$MIGRATION_STATUS"
exit 1
fi
Loading