Initial commit

This commit is contained in:
Zhongwei Li
2025-11-29 18:29:12 +08:00
commit 8d8103de19
10 changed files with 1264 additions and 0 deletions

View File

@@ -0,0 +1,11 @@
{
"name": "deployment",
"description": "Cloudflare Workers and Pages deployment with integrated debugging and troubleshooting chains",
"version": "1.0.0",
"author": {
"name": "Grey Haven Studio"
},
"skills": [
"./skills/deployment-cloudflare"
]
}

3
README.md Normal file
View File

@@ -0,0 +1,3 @@
# deployment
Cloudflare Workers and Pages deployment with integrated debugging and troubleshooting chains

69
plugin.lock.json Normal file
View File

@@ -0,0 +1,69 @@
{
"$schema": "internal://schemas/plugin.lock.v1.json",
"pluginId": "gh:greyhaven-ai/claude-code-config:grey-haven-plugins/deployment",
"normalized": {
"repo": null,
"ref": "refs/tags/v20251128.0",
"commit": "d968ce3992c04fbe1017e52826b17ef053439784",
"treeHash": "acdc75a68c6f0ff0bec92578b029945776afbe354a669d6c82c981c1af96eeee",
"generatedAt": "2025-11-28T10:17:04.293548Z",
"toolVersion": "publish_plugins.py@0.2.0"
},
"origin": {
"remote": "git@github.com:zhongweili/42plugin-data.git",
"branch": "master",
"commit": "aa1497ed0949fd50e99e70d6324a29c5b34f9390",
"repoRoot": "/Users/zhongweili/projects/openmind/42plugin-data"
},
"manifest": {
"name": "deployment",
"description": "Cloudflare Workers and Pages deployment with integrated debugging and troubleshooting chains",
"version": "1.0.0"
},
"content": {
"files": [
{
"path": "README.md",
"sha256": "375fffa8ad704c8a21a397ac730d70af51d3f72ad5f361029101821fed787b61"
},
{
"path": ".claude-plugin/plugin.json",
"sha256": "0f8fc3e95b33b5efb1455b86e819090a23f181e4a6fac61df753dbca001c508c"
},
{
"path": "skills/deployment-cloudflare/SKILL.md",
"sha256": "3bea75b9d6331ad7d605ade33981bd991e36671530ef5f813effced5b33e6a14"
},
{
"path": "skills/deployment-cloudflare/checklists/deployment-checklist.md",
"sha256": "5f7ea4265cedc70764c7559a06db526a3c58245b86bc84ad957365cc2c272e10"
},
{
"path": "skills/deployment-cloudflare/examples/INDEX.md",
"sha256": "c18993e7fa3e2d96fe6d0249c6096e82bb465e8fe3345a14fce361364cd9cf8f"
},
{
"path": "skills/deployment-cloudflare/scripts/deploy.py",
"sha256": "b33b4972ff81999eeb7a63d56141bbc1ee528d95de6d713ca49bd1ce85446dfe"
},
{
"path": "skills/deployment-cloudflare/scripts/rollback.py",
"sha256": "b98e96e02422580dd636b28cbda1349a2b61f0933fe0add2a6c00b484862e7ee"
},
{
"path": "skills/deployment-cloudflare/scripts/migrate.py",
"sha256": "34c466de7d9225ef97020e1b27b73e29e3078b34037c0c100c3119782ceb7286"
},
{
"path": "skills/deployment-cloudflare/reference/INDEX.md",
"sha256": "6102aff62f2b938f6da2b67bcb5696ea306573979d3692bdd7175f17675f01e6"
}
],
"dirSha256": "acdc75a68c6f0ff0bec92578b029945776afbe354a669d6c82c981c1af96eeee"
},
"security": {
"scannedAt": null,
"scannerVersion": null,
"flags": []
}
}

View File

@@ -0,0 +1,317 @@
---
name: grey-haven-deployment-cloudflare
description: Deploy TanStack Start applications to Cloudflare Workers/Pages with GitHub Actions, Doppler, Wrangler, database migrations, and rollback procedures. Use when deploying Grey Haven applications.
---
# Grey Haven Cloudflare Deployment
Deploy **TanStack Start** applications to Cloudflare Workers using GitHub Actions, Doppler for secrets, and Wrangler CLI.
## Deployment Architecture
### TanStack Start on Cloudflare Workers
- **SSR**: Server-side rendering with TanStack Start server functions
- **Edge Runtime**: Global deployment on Cloudflare's edge network
- **Database**: PostgreSQL (PlanetScale) with connection pooling
- **Cache**: Cloudflare KV for sessions, R2 for file uploads
- **Secrets**: Managed via Doppler, injected in GitHub Actions
### Core Infrastructure
- **Workers**: Edge compute (TanStack Start)
- **KV Storage**: Session management
- **R2 Storage**: File uploads and assets
- **D1 Database**: Edge data (optional)
- **Queues**: Background jobs (optional)
## Wrangler Configuration
### Basic `wrangler.toml`
```toml
name = "grey-haven-app"
main = "dist/server/index.js"
compatibility_date = "2025-01-15"
node_compat = true
[vars]
ENVIRONMENT = "production"
DATABASE_POOL_MIN = "2"
DATABASE_POOL_MAX = "10"
# KV namespace for session storage
[[kv_namespaces]]
binding = "SESSIONS"
id = "your-kv-namespace-id"
preview_id = "your-preview-kv-namespace-id"
# R2 bucket for file uploads
[[r2_buckets]]
binding = "UPLOADS"
bucket_name = "grey-haven-uploads"
preview_bucket_name = "grey-haven-uploads-preview"
# Routes
routes = [
{ pattern = "app.greyhaven.studio", zone_name = "greyhaven.studio" }
]
```
### Environment-Specific Configs
- **Development**: `wrangler.toml` with `ENVIRONMENT = "development"`
- **Staging**: `wrangler.staging.toml` with staging routes
- **Production**: `wrangler.production.toml` with production routes
## Doppler Integration
### Required GitHub Secrets
- `DOPPLER_TOKEN`: Doppler service token for CI/CD
- `CLOUDFLARE_API_TOKEN`: Wrangler deployment token
### Required Doppler Secrets (Production)
```bash
# Application
BETTER_AUTH_SECRET=<random-secret>
BETTER_AUTH_URL=https://app.greyhaven.studio
JWT_SECRET_KEY=<random-secret>
# Database (PlanetScale)
DATABASE_URL=postgresql://user:pass@host/db
DATABASE_URL_ADMIN=postgresql://admin:pass@host/db
# Redis (Upstash)
REDIS_URL=redis://user:pass@host:port
# Email (Resend)
RESEND_API_KEY=re_...
# OAuth Providers
GOOGLE_CLIENT_ID=...
GOOGLE_CLIENT_SECRET=...
GITHUB_CLIENT_ID=...
GITHUB_CLIENT_SECRET=...
# Cloudflare
CLOUDFLARE_ACCOUNT_ID=...
CLOUDFLARE_API_TOKEN=...
# Monitoring (optional)
SENTRY_DSN=https://...@sentry.io/...
AXIOM_TOKEN=xaat-...
```
## GitHub Actions Deployment
### Production Deployment Flow
```yaml
# .github/workflows/deploy-production.yml
- Checkout code
- Setup Node.js 22 with cache
- Install dependencies (npm ci)
- Install Doppler CLI
- Run tests (doppler run --config test)
- Build (doppler run --config production)
- Run database migrations
- Deploy to Cloudflare Workers
- Inject secrets from Doppler
- Run smoke tests
- Rollback on failure
```
### Key Deployment Commands
```bash
# Build with Doppler secrets
doppler run --config production -- npm run build
# Run migrations before deployment
doppler run --config production -- npm run db:migrate
# Deploy to Cloudflare
npx wrangler deploy --config wrangler.production.toml
# Inject secrets into Workers
doppler secrets download --config production --format json > secrets.json
cat secrets.json | jq -r 'to_entries | .[] | "\(.key)=\(.value)"' | while read -r line; do
key=$(echo "$line" | cut -d= -f1)
value=$(echo "$line" | cut -d= -f2-)
echo "$value" | npx wrangler secret put "$key"
done
rm secrets.json
```
## Database Migrations
### Drizzle Migrations (TanStack Start)
```typescript
// scripts/migrate.ts
import { drizzle } from "drizzle-orm/node-postgres";
import { migrate } from "drizzle-orm/node-postgres/migrator";
import { Pool } from "pg";
const pool = new Pool({
connectionString: process.env.DATABASE_URL_ADMIN,
});
const db = drizzle(pool);
async function main() {
console.log("Running migrations...");
await migrate(db, { migrationsFolder: "./drizzle/migrations" });
console.log("Migrations complete!");
await pool.end();
}
main().catch((err) => {
console.error("Migration failed:", err);
process.exit(1);
});
```
**package.json scripts**:
```json
{
"scripts": {
"db:migrate": "tsx scripts/migrate.ts",
"db:migrate:production": "doppler run --config production -- tsx scripts/migrate.ts"
}
}
```
## Rollback Procedures
### Wrangler Rollback
```bash
# List recent deployments
npx wrangler deployments list --config wrangler.production.toml
# Rollback to previous deployment
npx wrangler rollback --config wrangler.production.toml
# Rollback to specific deployment ID
npx wrangler rollback --deployment-id abc123 --config wrangler.production.toml
```
### Database Rollback
```bash
# Drizzle - rollback last migration
doppler run --config production -- drizzle-kit migrate:rollback
# Alembic - rollback one migration
doppler run --config production -- alembic downgrade -1
```
### Emergency Rollback Playbook
1. **Identify issue**: Check Cloudflare Workers logs, Sentry
2. **Rollback Workers**: `npx wrangler rollback`
3. **Rollback database** (if needed): `drizzle-kit migrate:rollback`
4. **Verify rollback**: Run smoke tests
5. **Notify team**: Update Linear issue
6. **Root cause analysis**: Create postmortem
## Cloudflare Resources Setup
### KV Namespace (Session Storage)
```bash
# Create KV namespace
npx wrangler kv:namespace create "SESSIONS" --config wrangler.production.toml
npx wrangler kv:namespace create "SESSIONS" --preview --config wrangler.production.toml
# List KV namespaces
npx wrangler kv:namespace list
```
### R2 Bucket (File Uploads)
```bash
# Create R2 bucket
npx wrangler r2 bucket create grey-haven-uploads
npx wrangler r2 bucket create grey-haven-uploads-preview
# List R2 buckets
npx wrangler r2 bucket list
```
## Monitoring
### Wrangler Tail (Real-time Logs)
```bash
# Stream production logs
npx wrangler tail --config wrangler.production.toml
# Filter by status code
npx wrangler tail --status error --config wrangler.production.toml
```
### Sentry Integration (Error Tracking)
```typescript
import * as Sentry from "@sentry/browser";
Sentry.init({
dsn: process.env.SENTRY_DSN,
environment: process.env.ENVIRONMENT,
tracesSampleRate: 1.0,
});
```
## Local Development
### Wrangler Dev (Local Workers)
```bash
# Run Workers locally with Doppler
doppler run --config dev -- npx wrangler dev
# Run with remote mode (uses production KV/R2)
doppler run --config dev -- npx wrangler dev --remote
```
## Supporting Documentation
All supporting files are under 500 lines per Anthropic best practices:
- **[examples/](examples/)** - Complete deployment examples
- [github-actions-workflow.md](examples/github-actions-workflow.md) - Full CI/CD workflows
- [wrangler-config.md](examples/wrangler-config.md) - Complete wrangler.toml examples
- [doppler-secrets.md](examples/doppler-secrets.md) - Secret management patterns
- [migrations.md](examples/migrations.md) - Database migration examples
- [INDEX.md](examples/INDEX.md) - Examples navigation
- **[reference/](reference/)** - Deployment references
- [rollback-procedures.md](reference/rollback-procedures.md) - Rollback strategies
- [monitoring.md](reference/monitoring.md) - Monitoring and alerting
- [troubleshooting.md](reference/troubleshooting.md) - Common issues and fixes
- [INDEX.md](reference/INDEX.md) - Reference navigation
- **[templates/](templates/)** - Copy-paste ready templates
- [wrangler.toml](templates/wrangler.toml) - Basic wrangler config
- [deploy-production.yml](templates/deploy-production.yml) - GitHub Actions workflow
- **[checklists/](checklists/)** - Deployment checklists
- [deployment-checklist.md](checklists/deployment-checklist.md) - Pre-deployment validation
## When to Apply This Skill
Use this skill when:
- Deploying TanStack Start to Cloudflare Workers
- Setting up CI/CD with GitHub Actions
- Configuring Doppler multi-environment secrets
- Running database migrations in production
- Rolling back failed deployments
- Setting up KV namespaces or R2 buckets
- Troubleshooting deployment failures
- Configuring monitoring and alerting
## Template Reference
These patterns are from Grey Haven's production templates:
- **cvi-template**: TanStack Start + Cloudflare Workers
- **cvi-backend-template**: FastAPI + Python Workers
## Critical Reminders
1. **Doppler for ALL secrets**: Never commit secrets to git
2. **Migrations BEFORE deployment**: Run `db:migrate` before `wrangler deploy`
3. **Smoke tests AFTER deployment**: Validate production after deploy
4. **Automated rollback**: GitHub Actions rolls back on failure
5. **Connection pooling**: Match wrangler.toml pool settings with database
6. **Environment-specific configs**: Separate wrangler files per environment
7. **KV/R2 bindings**: Configure in wrangler.toml, create with CLI
8. **Custom domains**: Use Cloudflare Proxy for DDoS protection
9. **Monitoring**: Set up Sentry + Axiom + Wrangler tail
10. **Emergency playbook**: Know how to rollback both Workers and database

View File

@@ -0,0 +1,100 @@
# Deployment Checklist
**Use before deploying to production.**
## Pre-Deployment
### Doppler Configuration
- [ ] All required secrets set in Doppler production environment
- [ ] DOPPLER_TOKEN added to GitHub repository secrets
- [ ] CLOUDFLARE_API_TOKEN added to GitHub repository secrets
- [ ] Test Doppler access: `doppler secrets --config production`
### Wrangler Configuration
- [ ] wrangler.production.toml configured with correct routes
- [ ] KV namespaces created and IDs added to wrangler.toml
- [ ] R2 buckets created and names added to wrangler.toml
- [ ] Custom domain DNS configured in Cloudflare
- [ ] Connection pool settings match database capacity
### Database
- [ ] Migrations tested locally
- [ ] Migration reversible (has downgrade)
- [ ] Seed data prepared for production (if needed)
- [ ] DATABASE_URL_ADMIN has elevated permissions for migrations
- [ ] Connection pooling configured (PlanetScale)
### Application
- [ ] All tests passing locally
- [ ] All tests passing in CI
- [ ] Build completes without errors
- [ ] No TypeScript errors
- [ ] No console.log statements in production code
- [ ] Environment variables validated
### GitHub Actions
- [ ] Workflow file present (.github/workflows/deploy-production.yml)
- [ ] Workflow triggers configured (push to main)
- [ ] Node.js version matches local (22)
- [ ] Doppler CLI action configured
- [ ] Smoke tests configured
## During Deployment
### Deployment Steps
- [ ] Merge PR to main branch
- [ ] GitHub Actions workflow triggered
- [ ] Tests pass in CI
- [ ] Build completes successfully
- [ ] Database migrations run successfully
- [ ] Workers deployment completes
- [ ] Secrets injected into Workers
- [ ] Smoke tests pass
### Monitoring
- [ ] Watch deployment logs in GitHub Actions
- [ ] Monitor Cloudflare Workers dashboard
- [ ] Check Wrangler tail for errors
- [ ] Verify no Sentry errors
- [ ] Verify application loads at production URL
## Post-Deployment
### Validation
- [ ] Production URL loads successfully
- [ ] Authentication works (login/logout)
- [ ] Database queries work (tenant isolation)
- [ ] File uploads work (R2 storage)
- [ ] Session management works (KV storage)
- [ ] API endpoints respond correctly
### Smoke Tests
- [ ] Critical user flows tested
- [ ] Multi-tenant isolation verified
- [ ] Performance acceptable (< 500ms response time)
- [ ] No console errors in browser
- [ ] Mobile responsive (if applicable)
### Rollback Readiness
- [ ] Know how to rollback Workers: `npx wrangler rollback`
- [ ] Know how to rollback database: `drizzle-kit migrate:rollback`
- [ ] Emergency contacts notified
- [ ] Linear issue updated with deployment status
## Rollback Triggers
Rollback immediately if:
- [ ] Smoke tests fail
- [ ] Critical user flow broken
- [ ] 500 errors in production
- [ ] Database connection failures
- [ ] Authentication broken
- [ ] Multi-tenant isolation breach
## Post-Deployment Documentation
- [ ] Update Linear issue with deployment notes
- [ ] Document any manual steps taken
- [ ] Update team on deployment status
- [ ] Schedule postmortem if issues occurred
- [ ] Update runbook with any new learnings

View File

@@ -0,0 +1,41 @@
# Deployment Examples
Complete Cloudflare Workers deployment examples for Grey Haven applications.
## Available Examples
### [github-actions-workflow.md](github-actions-workflow.md)
Full GitHub Actions CI/CD workflows.
- Production deployment workflow
- Staging deployment workflow
- PR preview deployments
- Automated rollback on failure
### [wrangler-config.md](wrangler-config.md)
Complete wrangler.toml configuration examples.
- Environment-specific configurations
- KV namespace bindings
- R2 bucket bindings
- D1 database bindings
- Custom domain routing
### [doppler-secrets.md](doppler-secrets.md)
Secret management patterns with Doppler.
- Environment setup (dev, staging, production)
- Secret injection in CI/CD
- Local development with Doppler
- Rotating secrets safely
### [migrations.md](migrations.md)
Database migration examples.
- Drizzle migration scripts
- Alembic migration scripts
- Running migrations in CI/CD
- Migration rollback procedures
## Quick Reference
**Need GitHub Actions?** → [github-actions-workflow.md](github-actions-workflow.md)
**Need wrangler.toml?** → [wrangler-config.md](wrangler-config.md)
**Need Doppler setup?** → [doppler-secrets.md](doppler-secrets.md)
**Need migrations?** → [migrations.md](migrations.md)

View File

@@ -0,0 +1,34 @@
# Deployment Reference
Configuration and operational references for Cloudflare Workers deployment.
## Available References
### [rollback-procedures.md](rollback-procedures.md)
Rollback strategies and emergency procedures.
- Wrangler rollback commands
- Database rollback procedures
- Emergency rollback playbook
- Recovery validation
### [monitoring.md](monitoring.md)
Monitoring and alerting configuration.
- Cloudflare Workers analytics
- Wrangler tail (real-time logs)
- Sentry error tracking
- Axiom structured logging
- Performance metrics
### [troubleshooting.md](troubleshooting.md)
Common deployment issues and fixes.
- Deployment failures
- Secret management issues
- Database connection errors
- KV/R2 binding errors
- Performance problems
## Quick Reference
**Need rollback?** → [rollback-procedures.md](rollback-procedures.md)
**Need monitoring?** → [monitoring.md](monitoring.md)
**Need troubleshooting?** → [troubleshooting.md](troubleshooting.md)

View File

@@ -0,0 +1,252 @@
#!/usr/bin/env python3
"""
Deploy Grey Haven applications to Cloudflare Workers with Doppler secrets.
This script automates the deployment workflow:
1. Runs tests with Doppler environment
2. Builds the application
3. Runs database migrations
4. Deploys to Cloudflare Workers with Wrangler
5. Injects secrets from Doppler
6. Runs smoke tests
7. Automatically rolls back on failure
Usage:
# Development deployment
python scripts/deploy.py --env dev
# Staging deployment
python scripts/deploy.py --env staging
# Production deployment (with confirmation)
python scripts/deploy.py --env production
# Skip tests (not recommended)
python scripts/deploy.py --env staging --skip-tests
# Dry run (show what would happen)
python scripts/deploy.py --env production --dry-run
Always run with --help first to see all options.
"""
import argparse
import subprocess
import sys
import json
import os
from typing import Optional
def run_command(cmd: str, description: str, dry_run: bool = False) -> bool:
"""Run a shell command and return success status."""
print(f"\n{'[DRY RUN] ' if dry_run else ''}{description}")
print(f" Command: {cmd}")
if dry_run:
return True
result = subprocess.run(cmd, shell=True, capture_output=False)
return result.returncode == 0
def confirm_production_deploy() -> bool:
"""Ask for confirmation before production deployment."""
print("\nWARNING: WARNING: You are about to deploy to PRODUCTION")
print("This will affect live users.")
response = input("Type 'deploy to production' to confirm: ")
return response.strip().lower() == "deploy to production"
def get_wrangler_config(env: str) -> str:
"""Get the appropriate wrangler config file for environment."""
if env == "production":
return "wrangler.production.toml"
elif env == "staging":
return "wrangler.staging.toml"
else:
return "wrangler.toml"
def main():
parser = argparse.ArgumentParser(
description="Deploy Grey Haven application to Cloudflare Workers",
formatter_class=argparse.RawDescriptionHelpFormatter,
epilog="""
Examples:
python scripts/deploy.py --env dev
python scripts/deploy.py --env staging --skip-migrations
python scripts/deploy.py --env production --dry-run
Environments:
dev - Development (wrangler.toml)
staging - Staging (wrangler.staging.toml)
production - Production (wrangler.production.toml)
Doppler Configuration:
Requires DOPPLER_TOKEN environment variable or doppler CLI configured.
Secrets are injected from Doppler config matching the environment.
"""
)
parser.add_argument(
"--env",
required=True,
choices=["dev", "staging", "production"],
help="Deployment environment"
)
parser.add_argument(
"--skip-tests",
action="store_true",
help="Skip running tests (not recommended)"
)
parser.add_argument(
"--skip-migrations",
action="store_true",
help="Skip database migrations"
)
parser.add_argument(
"--skip-smoke-tests",
action="store_true",
help="Skip smoke tests after deployment"
)
parser.add_argument(
"--dry-run",
action="store_true",
help="Show what would happen without executing"
)
args = parser.parse_args()
# Production confirmation
if args.env == "production" and not args.dry_run:
if not confirm_production_deploy():
print("\nERROR: Production deployment cancelled")
sys.exit(1)
env = args.env
wrangler_config = get_wrangler_config(env)
print(f"\n{'=' * 70}")
print(f" Grey Haven Deployment to {env.upper()}")
print(f"{'=' * 70}")
# Step 1: Run tests
if not args.skip_tests:
success = run_command(
f"doppler run --config test -- npm run test",
"Running tests",
args.dry_run
)
if not success:
print("\nERROR: Tests failed. Deployment aborted.")
sys.exit(1)
else:
print("\nWARNING: Skipping tests (--skip-tests)")
# Step 2: Build application
success = run_command(
f"doppler run --config {env} -- npm run build",
f"Building application for {env}",
args.dry_run
)
if not success:
print("\nERROR: Build failed. Deployment aborted.")
sys.exit(1)
# Step 3: Run database migrations
if not args.skip_migrations:
success = run_command(
f"doppler run --config {env} -- npm run db:migrate",
"Running database migrations",
args.dry_run
)
if not success:
print("\nERROR: Migrations failed. Deployment aborted.")
sys.exit(1)
else:
print("\nWARNING: Skipping migrations (--skip-migrations)")
# Step 4: Deploy to Cloudflare Workers
success = run_command(
f"npx wrangler deploy --config {wrangler_config}",
f"Deploying to Cloudflare Workers ({wrangler_config})",
args.dry_run
)
if not success:
print("\nERROR: Deployment failed.")
sys.exit(1)
# Step 5: Inject Doppler secrets to Cloudflare Workers
if not args.dry_run:
print("\n→ Injecting Doppler secrets to Cloudflare Workers")
print(" This may take a minute...")
# Download secrets from Doppler
result = subprocess.run(
f"doppler secrets download --config {env} --format json",
shell=True,
capture_output=True,
text=True
)
if result.returncode != 0:
print("\nERROR: Failed to download Doppler secrets")
sys.exit(1)
secrets = json.loads(result.stdout)
# Inject each secret to Cloudflare Workers
for key, value in secrets.items():
# Skip non-secret env vars (like NODE_ENV, ENVIRONMENT)
if key in ["NODE_ENV", "ENVIRONMENT", "CI"]:
continue
cmd = f'echo "{value}" | npx wrangler secret put {key} --config {wrangler_config}'
subprocess.run(cmd, shell=True, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
print(f" ✓ Injected {len(secrets)} secrets")
else:
print("\n[DRY RUN] → Would inject Doppler secrets to Cloudflare Workers")
# Step 6: Run smoke tests
if not args.skip_smoke_tests:
success = run_command(
f"doppler run --config {env} -- npm run test:e2e:smoke",
"Running smoke tests",
args.dry_run
)
if not success:
print("\nWARNING: Smoke tests failed. Rolling back deployment...")
if not args.dry_run:
subprocess.run(
f"npx wrangler rollback --config {wrangler_config}",
shell=True
)
print("\nERROR: Deployment rolled back due to smoke test failure")
sys.exit(1)
else:
print("\nWARNING: Skipping smoke tests (--skip-smoke-tests)")
# Success!
print(f"\n{'=' * 70}")
print(f" SUCCESS: Deployment to {env.upper()} successful!")
print(f"{'=' * 70}")
if env == "production":
print(f"\n Production URL: https://app.greyhaven.studio")
elif env == "staging":
print(f"\n Staging URL: https://staging.greyhaven.studio")
else:
print(f"\n Dev URL: https://dev.greyhaven.studio")
print("\nNext steps:")
print(" • Monitor logs: npx wrangler tail")
print(" • Check Sentry for errors")
print(" • Verify Axiom logs")
if __name__ == "__main__":
main()

View File

@@ -0,0 +1,198 @@
#!/usr/bin/env python3
"""
Run database migrations for Grey Haven applications with Doppler.
Supports both Drizzle (TypeScript) and Alembic (Python) migrations
across multiple environments.
Usage:
# Run migrations for development
python scripts/migrate.py --env dev
# Run migrations for staging
python scripts/migrate.py --env staging
# Run migrations for production (with confirmation)
python scripts/migrate.py --env production
# Rollback last migration
python scripts/migrate.py --env dev --rollback
# Rollback to specific migration
python scripts/migrate.py --env dev --rollback --to 20250115_add_users
# Dry run (show what would happen)
python scripts/migrate.py --env production --dry-run
# Use Alembic instead of Drizzle
python scripts/migrate.py --env dev --backend alembic
Always run with --help first to see all options.
"""
import argparse
import subprocess
import sys
from typing import Optional
def run_command(cmd: str, description: str, dry_run: bool = False) -> bool:
"""Run a shell command and return success status."""
print(f"\n{'[DRY RUN] ' if dry_run else ''}{description}")
print(f" Command: {cmd}")
if dry_run:
return True
result = subprocess.run(cmd, shell=True, capture_output=False)
return result.returncode == 0
def confirm_production_migration() -> bool:
"""Ask for confirmation before production migration."""
print("\nWARNING: WARNING: You are about to run migrations on PRODUCTION database")
print("This operation is IRREVERSIBLE and will affect live data.")
response = input("Type 'run production migrations' to confirm: ")
return response.strip().lower() == "run production migrations"
def main():
parser = argparse.ArgumentParser(
description="Run database migrations with Doppler environment variables",
formatter_class=argparse.RawDescriptionHelpFormatter,
epilog="""
Examples:
# Run Drizzle migrations for development
python scripts/migrate.py --env dev
# Rollback last Drizzle migration
python scripts/migrate.py --env dev --rollback
# Run Alembic migrations for staging
python scripts/migrate.py --env staging --backend alembic
# Rollback to specific Alembic migration
python scripts/migrate.py --env staging --backend alembic --rollback --to abc123
Environments:
dev - Local development database
test - CI/CD test database
staging - Staging database
production - Production database (requires confirmation)
Backends:
drizzle - Drizzle Kit (TypeScript/TanStack Start)
alembic - Alembic (Python/FastAPI)
Doppler Configuration:
Requires doppler CLI configured with appropriate access.
Uses DATABASE_URL_ADMIN from Doppler config.
"""
)
parser.add_argument(
"--env",
required=True,
choices=["dev", "test", "staging", "production"],
help="Environment to run migrations against"
)
parser.add_argument(
"--backend",
default="drizzle",
choices=["drizzle", "alembic"],
help="Migration backend to use (default: drizzle)"
)
parser.add_argument(
"--rollback",
action="store_true",
help="Rollback migrations instead of applying"
)
parser.add_argument(
"--to",
type=str,
help="Rollback to specific migration (use with --rollback)"
)
parser.add_argument(
"--dry-run",
action="store_true",
help="Show what would happen without executing"
)
args = parser.parse_args()
# Production confirmation
if args.env == "production" and not args.dry_run and not args.rollback:
if not confirm_production_migration():
print("\nERROR: Production migration cancelled")
sys.exit(1)
env = args.env
backend = args.backend
print(f"\n{'=' * 70}")
print(f" Database Migration - {env.upper()} ({backend.upper()})")
print(f"{'=' * 70}")
# Check Doppler configuration
if not args.dry_run:
result = subprocess.run(
f"doppler secrets get DATABASE_URL_ADMIN --config {env}",
shell=True,
capture_output=True
)
if result.returncode != 0:
print(f"\nERROR: Failed to get DATABASE_URL_ADMIN from Doppler config '{env}'")
print(" Make sure Doppler is configured: doppler setup")
sys.exit(1)
# Construct migration command
if backend == "drizzle":
if args.rollback:
if args.to:
cmd = f"doppler run --config {env} -- drizzle-kit migrate:rollback --to {args.to}"
else:
cmd = f"doppler run --config {env} -- drizzle-kit migrate:rollback"
description = "Rolling back Drizzle migration"
else:
cmd = f"doppler run --config {env} -- drizzle-kit push:pg"
description = "Applying Drizzle migrations"
elif backend == "alembic":
if args.rollback:
if args.to:
cmd = f"doppler run --config {env} -- alembic downgrade {args.to}"
else:
cmd = f"doppler run --config {env} -- alembic downgrade -1"
description = "Rolling back Alembic migration"
else:
cmd = f"doppler run --config {env} -- alembic upgrade head"
description = "Applying Alembic migrations"
# Run migration
success = run_command(cmd, description, args.dry_run)
if not success:
print(f"\nERROR: Migration failed for {env}")
sys.exit(1)
# Success!
print(f"\n{'=' * 70}")
if args.rollback:
print(f" SUCCESS: Rollback successful for {env.upper()}")
else:
print(f" SUCCESS: Migration successful for {env.upper()}")
print(f"{'=' * 70}")
if not args.rollback:
print("\nNext steps:")
print(" • Verify schema changes in database")
print(" • Run tests: doppler run --config test -- npm run test")
print(" • Deploy application if migrations succeeded")
else:
print("\nNext steps:")
print(" • Verify rollback was successful")
print(" • Re-deploy previous application version if needed")
if __name__ == "__main__":
main()

View File

@@ -0,0 +1,239 @@
#!/usr/bin/env python3
"""
Rollback Grey Haven Cloudflare Workers deployment to previous version.
This script handles emergency rollbacks when a deployment fails or causes
production issues. It can rollback both the Workers deployment and database
migrations.
Usage:
# Rollback to previous Workers deployment
python scripts/rollback.py --env production
# Rollback Workers and database migration
python scripts/rollback.py --env production --with-migration
# Rollback to specific deployment ID
python scripts/rollback.py --env production --deployment-id abc123
# Rollback database only
python scripts/rollback.py --env production --migration-only
# Dry run (show what would happen)
python scripts/rollback.py --env production --dry-run
Always run with --help first to see all options.
"""
import argparse
import subprocess
import sys
from typing import Optional
def run_command(cmd: str, description: str, dry_run: bool = False, capture: bool = False) -> tuple[bool, Optional[str]]:
"""Run a shell command and return success status and output."""
print(f"\n{'[DRY RUN] ' if dry_run else ''}{description}")
print(f" Command: {cmd}")
if dry_run:
return True, None
result = subprocess.run(cmd, shell=True, capture_output=capture, text=True)
return result.returncode == 0, result.stdout if capture else None
def confirm_production_rollback() -> bool:
"""Ask for confirmation before production rollback."""
print("\nWARNING: WARNING: You are about to ROLLBACK PRODUCTION deployment")
print("This will affect live users immediately.")
response = input("Type 'rollback production' to confirm: ")
return response.strip().lower() == "rollback production"
def get_wrangler_config(env: str) -> str:
"""Get the appropriate wrangler config file for environment."""
if env == "production":
return "wrangler.production.toml"
elif env == "staging":
return "wrangler.staging.toml"
else:
return "wrangler.toml"
def list_recent_deployments(wrangler_config: str, dry_run: bool = False) -> None:
"""List recent deployments for reference."""
if dry_run:
print("\n[DRY RUN] → Would list recent deployments")
return
print("\n→ Fetching recent deployments...")
subprocess.run(
f"npx wrangler deployments list --config {wrangler_config}",
shell=True
)
def main():
parser = argparse.ArgumentParser(
description="Rollback Grey Haven Cloudflare Workers deployment",
formatter_class=argparse.RawDescriptionHelpFormatter,
epilog="""
Examples:
# Emergency rollback of production Workers deployment
python scripts/rollback.py --env production
# Rollback Workers and database migration
python scripts/rollback.py --env production --with-migration
# Rollback to specific deployment
python scripts/rollback.py --env production --deployment-id abc123
# Rollback database migration only
python scripts/rollback.py --env production --migration-only --backend drizzle
Environments:
dev - Development
staging - Staging
production - Production (requires confirmation)
Emergency Rollback Procedure:
1. Identify the issue (check Sentry, Axiom, Cloudflare logs)
2. Run rollback script with appropriate flags
3. Verify rollback with smoke tests
4. Notify team and update Linear issue
5. Create postmortem for root cause analysis
"""
)
parser.add_argument(
"--env",
required=True,
choices=["dev", "staging", "production"],
help="Environment to rollback"
)
parser.add_argument(
"--deployment-id",
type=str,
help="Specific deployment ID to rollback to (optional)"
)
parser.add_argument(
"--with-migration",
action="store_true",
help="Also rollback database migration"
)
parser.add_argument(
"--migration-only",
action="store_true",
help="Rollback database migration only (not Workers)"
)
parser.add_argument(
"--backend",
default="drizzle",
choices=["drizzle", "alembic"],
help="Migration backend (default: drizzle)"
)
parser.add_argument(
"--dry-run",
action="store_true",
help="Show what would happen without executing"
)
args = parser.parse_args()
# Production confirmation
if args.env == "production" and not args.dry_run:
if not confirm_production_rollback():
print("\nERROR: Production rollback cancelled")
sys.exit(1)
env = args.env
wrangler_config = get_wrangler_config(env)
print(f"\n{'=' * 70}")
print(f" Emergency Rollback - {env.upper()}")
print(f"{'=' * 70}")
# Rollback Workers deployment (unless migration-only)
if not args.migration_only:
# List recent deployments first
if not args.deployment_id:
list_recent_deployments(wrangler_config, args.dry_run)
# Construct rollback command
if args.deployment_id:
cmd = f"npx wrangler rollback --deployment-id {args.deployment_id} --config {wrangler_config}"
description = f"Rolling back to deployment {args.deployment_id}"
else:
cmd = f"npx wrangler rollback --config {wrangler_config}"
description = "Rolling back to previous deployment"
success, _ = run_command(cmd, description, args.dry_run)
if not success:
print(f"\nERROR: Workers rollback failed for {env}")
sys.exit(1)
print(f"\nSUCCESS: Workers deployment rolled back successfully")
# Rollback database migration (if requested)
if args.with_migration or args.migration_only:
backend = args.backend
print(f"\n→ Rolling back {backend} migration for {env}")
if backend == "drizzle":
cmd = f"doppler run --config {env} -- drizzle-kit migrate:rollback"
elif backend == "alembic":
cmd = f"doppler run --config {env} -- alembic downgrade -1"
success, _ = run_command(cmd, f"Rolling back {backend} migration", args.dry_run)
if not success:
print(f"\nERROR: Database migration rollback failed for {env}")
sys.exit(1)
print(f"\nSUCCESS: Database migration rolled back successfully")
# Success!
print(f"\n{'=' * 70}")
print(f" SUCCESS: Rollback complete for {env.upper()}")
print(f"{'=' * 70}")
# Run smoke tests
print("\n→ Running smoke tests to verify rollback...")
success, _ = run_command(
f"doppler run --config {env} -- npm run test:e2e:smoke",
"Verifying rollback with smoke tests",
args.dry_run
)
if not success:
print("\nWARNING: Warning: Smoke tests failed after rollback")
print(" Manual verification required!")
else:
print("\nSUCCESS: Smoke tests passed - rollback verified")
# Post-rollback checklist
print("\n📋 Post-Rollback Checklist:")
print(" ✓ Deployment rolled back")
if args.with_migration or args.migration_only:
print(" ✓ Database migration rolled back")
print("\n WARNING: Action Items:")
print(" • Check Sentry for errors")
print(" • Verify Axiom logs")
print(" • Monitor Cloudflare Workers analytics")
print(" • Update Linear issue with rollback status")
print(" • Create postmortem for root cause analysis")
print(" • Fix the issue before re-deploying")
if env == "production":
print(f"\n Production URL: https://app.greyhaven.studio")
elif env == "staging":
print(f"\n Staging URL: https://staging.greyhaven.studio")
else:
print(f"\n Dev URL: https://dev.greyhaven.studio")
if __name__ == "__main__":
main()