Skip to content
Merged
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
59 changes: 59 additions & 0 deletions .github/workflows/db-migration.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
name: Database Migration Check

on:
pull_request:
paths:
- 'prisma/schema.prisma'
- 'prisma/migrations/**'

permissions:
contents: read
checks: write

jobs:
validate:
name: Validate Migrations
runs-on: ubuntu-latest

services:
postgres:
image: postgres:16-alpine
env:
POSTGRES_USER: test_user
POSTGRES_PASSWORD: test_password
POSTGRES_DB: test_db
options: >-
--health-cmd pg_isready
--health-interval 10s
--health-timeout 5s
--health-retries 5
ports:
- 5432:5432

steps:
- name: Checkout code
uses: actions/checkout@v4

- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: 22.x
cache: npm

- name: Install dependencies
run: npm ci

- name: Check migration status
run: npx prisma migrate status
env:
DATABASE_URL: postgresql://test_user:test_password@localhost:5432/test_db

- name: Deploy migrations
run: npx prisma migrate deploy
env:
DATABASE_URL: postgresql://test_user:test_password@localhost:5432/test_db

- name: Generate Prisma client
run: npx prisma generate
env:
DATABASE_URL: postgresql://test_user:test_password@localhost:5432/test_db
132 changes: 132 additions & 0 deletions docs/MIGRATION.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
# Database Migration Workflow

Sentinel uses [Prisma Migrate](https://www.prisma.io/docs/orm/prisma-migrate) for schema management. This document describes the migration workflow for local development and production deployments.

## Prerequisites

- Node.js 20+ and dependencies installed (`npm install`)
- PostgreSQL instance running (local, Docker, or remote)
- `DATABASE_URL` environment variable set

## Migration Scripts

All migration commands are available via npm scripts or directly through shell scripts in `scripts/`.

### Quick Reference

| Command | Purpose |
|---|---|
| `npm run prisma:migrate:deploy` | Apply all pending migrations |
| `npm run prisma:migrate:status` | Show which migrations have been applied |
| `npm run db:migrate:new` | Create a migration file without applying it |
| `npm run db:migrate:dev [name]` | Full dev cycle: generate client + apply + create migration |
| `npm run db:status` | Check migration status |
| `npm run db:rollback:guide` | Print rollback instructions |
| `npm run prisma:studio` | Open Prisma Studio to inspect data |
| `npm run prisma:migrate:reset` | Reset database (drops all data) |

## Development Workflow

### 1. Make schema changes

Edit `prisma/schema.prisma` to add/modify models, fields, or relations.

### 2. Run the migration

```bash
# Apply pending migrations and create a new migration for your changes
npm run db:migrate:dev add_user_roles
```

This runs:
1. `prisma generate` — regenerates the Prisma client
2. `prisma migrate deploy` — applies any pending migrations from other developers
3. `prisma migrate dev --name add_user_roles` — creates and applies the new migration

### 3. Generate Prisma client (if needed)

```bash
npm run prisma:generate
```

### 4. Verify migration status

```bash
npm run prisma:migrate:status
```

## Production Deployment

### Applying migrations

```bash
# Set the production DATABASE_URL
export DATABASE_URL="postgresql://user:pass@prod-host:5432/sentinel"

# Apply migrations
npm run prisma:migrate:deploy
```

### Via Docker

Migrations run automatically during Docker startup via the entrypoint. To run manually:

```bash
npm run docker:db:migrate
```

## Rollback Strategy

Prisma Migrate does not support automatic rollback. Follow these steps:

### Option 1 — Reverse migration (recommended)

1. Revert the schema changes in `prisma/schema.prisma`
2. Create a reverse migration:
```bash
npm run db:migrate:new
# Name it: rollback_<original_migration_name>
```
3. Review and edit the generated migration file
4. Deploy:
```bash
npm run prisma:migrate:deploy
```

### Option 2 — Reset (development only)

```bash
npm run prisma:migrate:reset
```

This drops all data and re-applies all migrations.

### Option 3 — Mark a failed migration as rolled back

If a migration was partially applied:

```bash
npx prisma migrate resolve --rolled-back <migration_name>
```

Then fix the issue and create a new migration.

## CI/CD Integration

The CI pipeline (`ci.yml`) runs `prisma:generate` as part of the build step. For deployments, add the following step after build:

```yaml
- name: Run database migrations
run: npx prisma migrate deploy
env:
DATABASE_URL: ${{ secrets.DATABASE_URL }}
```

## Best Practices

1. **One change per migration** — Keep migrations focused on a single schema change
2. **Review migration SQL** — Always review the generated SQL before deploying
3. **Never edit applied migrations** — Create new migrations instead
4. **Commit migration files** — All migration files in `prisma/migrations/` must be committed
5. **Back up production DB** — Before deploying migrations to production
6. **Test locally first** — Always run migrations against a local copy of the database
9 changes: 9 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,15 @@
"test:ci": "npm run test -- --ci --coverage",
"prisma:generate": "prisma generate",
"prisma:migrate": "prisma migrate dev",
"prisma:migrate:deploy": "prisma migrate deploy",
"prisma:migrate:status": "prisma migrate status",
"prisma:migrate:reset": "prisma migrate reset",
"prisma:studio": "prisma studio",
"db:migrate": "bash scripts/migrate.sh deploy",
"db:migrate:dev": "bash scripts/migrate-dev.sh",
"db:migrate:new": "bash scripts/migrate.sh create-only",
"db:status": "bash scripts/migrate.sh status",
"db:rollback:guide": "bash scripts/rollback.sh",
"docker:up": "docker-compose up -d",
"docker:down": "docker-compose down",
"docker:logs": "docker-compose logs -f",
Expand Down
36 changes: 36 additions & 0 deletions scripts/migrate-dev.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
#!/usr/bin/env bash
set -euo pipefail

# ──────────────────────────────────────────────────────────────────────────────
# Sentinel – Development Migration Workflow
#
# Runs the full dev cycle: generate client → apply pending → create new
# migration if schema changed → seed (if applicable).
#
# Usage:
# ./scripts/migrate-dev.sh [name]
#
# name Optional. If provided, creates a named migration for schema changes.
# If omitted, only applies pending migrations and re-generates client.
# ──────────────────────────────────────────────────────────────────────────────

BIN="npx prisma"
SCHEMA="prisma/schema.prisma"

echo "✦ Step 1 — Generate Prisma client"
$BIN generate --schema="$SCHEMA"

echo ""
echo "✦ Step 2 — Apply pending migrations"
$BIN migrate deploy --schema="$SCHEMA"

NAME="${1:-}"
if [ -n "$NAME" ]; then
echo ""
echo "✦ Step 3 — Create migration: $NAME"
$BIN migrate dev --schema="$SCHEMA" --name "$NAME"
echo "✓ Migration '$NAME' created and applied"
fi

echo ""
echo "✓ Development migration cycle complete"
86 changes: 86 additions & 0 deletions scripts/migrate.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
#!/usr/bin/env bash
set -euo pipefail

# ──────────────────────────────────────────────────────────────────────────────
# Sentinel – Database Migration Workflow
#
# Usage:
# ./scripts/migrate.sh [command]
#
# Commands:
# generate Generate a new migration from schema changes
# deploy Apply pending migrations to the database
# status Show migration status
# reset Reset the database (drops all data)
# studio Open Prisma Studio to inspect data
# create-only Create a migration file without applying it
#
# Environment:
# DATABASE_URL Required. PostgreSQL connection string.
#
# Examples:
# ./scripts/migrate.sh generate
# ./scripts/migrate.sh deploy
# DATABASE_URL="postgresql://user:pass@localhost:5432/sentinel" ./scripts/migrate.sh deploy
# ──────────────────────────────────────────────────────────────────────────────

BIN="npx prisma"
SCHEMA="prisma/schema.prisma"

show_help() {
sed -n '3,20p' "$0"
exit 0
}

if [ $# -eq 0 ]; then
show_help
fi

CMD="${1:-}"

case "$CMD" in
generate)
echo "✦ Generating Prisma client..."
$BIN generate --schema="$SCHEMA"
echo "✓ Prisma client generated"
;;

deploy)
echo "✦ Deploying pending migrations..."
$BIN migrate deploy --schema="$SCHEMA"
echo "✓ Migrations deployed"
;;

status)
echo "✦ Migration status:"
$BIN migrate status --schema="$SCHEMA"
;;

reset)
echo "⚠️ WARNING: This will drop all data in the database!"
read -rp "Are you sure? (yes/no): " CONFIRM
if [ "$CONFIRM" = "yes" ]; then
$BIN migrate reset --schema="$SCHEMA" --force
echo "✓ Database reset complete"
else
echo "Aborted."
fi
;;

studio)
echo "✦ Opening Prisma Studio..."
$BIN studio --schema="$SCHEMA"
;;

create-only)
echo "✦ Creating migration file (not applied)..."
read -rp "Migration name: " NAME
$BIN migrate dev --schema="$SCHEMA" --name "$NAME" --create-only
echo "✓ Migration file created — review and edit before deploying"
;;

*)
echo "Unknown command: $CMD"
show_help
;;
esac
49 changes: 49 additions & 0 deletions scripts/rollback.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
#!/usr/bin/env bash
set -euo pipefail

# ──────────────────────────────────────────────────────────────────────────────
# Sentinel – Migration Rollback
#
# Rolls back the last N migrations. Prisma does not support named rollbacks
# natively; this script wraps `prisma migrate resolve` to handle the process.
#
# Usage:
# ./scripts/rollback.sh # Show rollback guide
# ./scripts/rollback.sh --last 1 # Roll back the last migration
#
# Note: Prisma uses "rollback" via `migrate diff` + `migrate resolve`.
# A safer approach is to create a "down" migration manually and apply it.
# ──────────────────────────────────────────────────────────────────────────────

BIN="npx prisma"
SCHEMA="prisma/schema.prisma"

echo "═══════════════════════════════════════════════════════════════"
echo " Sentinel – Migration Rollback Guide"
echo "═══════════════════════════════════════════════════════════════"
echo ""
echo "Prisma does not support automatic rollback. To roll back safely:"
echo ""
echo " Option 1 — Create a reverse migration (recommended):"
echo " 1. Revert the schema changes in schema.prisma"
echo " 2. Run: ./scripts/migrate.sh create-only"
echo " 3. Name it: rollback_<previous_migration_name>"
echo " 4. Deploy: ./scripts/migrate.sh deploy"
echo ""
echo " Option 2 — Reset and re-migrate (dev only):"
echo " 1. Run: ./scripts/migrate.sh reset"
echo " 2. Run: ./scripts/migrate.sh deploy"
echo ""
echo " Option 3 — Resolve a failed migration:"
echo " 1. Mark migration as rolled back:"
echo " $BIN migrate resolve --rolled-back <migration_name>"
echo " 2. Apply the fix: ./scripts/migrate.sh deploy"
echo ""

if [ "${1:-}" = "--last" ]; then
COUNT="${2:-1}"
echo "To roll back the last $COUNT migration(s):"
echo " 1. Revert schema.changes in prisma/schema.prisma"
echo " 2. Run: $BIN migrate dev --name rollback_step"
echo " 3. Verify: ./scripts/migrate.sh status"
fi
Loading