chore(db): regenerate prod database types #26
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # Deploy to production — runs on push to main | |
| # Migrations require manual approval via the "production" environment | |
| name: Deploy Prod | |
| on: | |
| push: | |
| branches: [main] | |
| workflow_dispatch: | |
| inputs: | |
| force_migrations: | |
| description: 'Force-push migrations (skip preflight diff check)' | |
| type: boolean | |
| default: true | |
| # Never cancel in-flight migrations | |
| concurrency: | |
| group: deploy-prod | |
| cancel-in-progress: false | |
| jobs: | |
| # ─── Preflight: detect if migrations changed ────────────────── | |
| preflight: | |
| name: Preflight | |
| runs-on: ubuntu-latest | |
| outputs: | |
| has_new_migrations: ${{ steps.check.outputs.has_new_migrations }} | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - uses: supabase/setup-cli@v1 | |
| with: | |
| version: latest | |
| - name: Link to prod project | |
| run: supabase link --project-ref "${{ secrets.SUPABASE_PROD_PROJECT_REF }}" | |
| working-directory: packages/db | |
| env: | |
| SUPABASE_ACCESS_TOKEN: ${{ secrets.SUPABASE_ACCESS_TOKEN }} | |
| - name: Check for pending migrations | |
| id: check | |
| working-directory: packages/db | |
| env: | |
| SUPABASE_ACCESS_TOKEN: ${{ secrets.SUPABASE_ACCESS_TOKEN }} | |
| SUPABASE_DB_PASSWORD: ${{ secrets.SUPABASE_PROD_DB_PASSWORD }} | |
| run: | | |
| if [ "${{ github.event_name }}" = "workflow_dispatch" ] && [ "${{ inputs.force_migrations }}" = "true" ]; then | |
| echo "has_new_migrations=true" >> "$GITHUB_OUTPUT" | |
| echo "### Forced migrations via workflow_dispatch 🔄" >> "$GITHUB_STEP_SUMMARY" | |
| exit 0 | |
| fi | |
| OUTPUT=$(supabase db diff --linked 2>&1) | |
| # Detect actual drift by checking for SQL statements (contain semicolons) | |
| if echo "$OUTPUT" | grep -q ';'; then | |
| echo "has_new_migrations=true" >> "$GITHUB_OUTPUT" | |
| echo "### New migrations detected 🔄" >> "$GITHUB_STEP_SUMMARY" | |
| else | |
| echo "has_new_migrations=false" >> "$GITHUB_OUTPUT" | |
| echo "### No new migrations ✅" >> "$GITHUB_STEP_SUMMARY" | |
| fi | |
| # ─── Migrate: only if new migrations, requires manual approval ─ | |
| migrate: | |
| name: Push Migrations | |
| needs: [preflight] | |
| if: needs.preflight.outputs.has_new_migrations == 'true' | |
| runs-on: ubuntu-latest | |
| # Requires manual approval from reviewers configured on the "production" environment | |
| environment: production | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - uses: supabase/setup-cli@v1 | |
| with: | |
| version: latest | |
| - name: Link to prod project | |
| run: supabase link --project-ref "${{ secrets.SUPABASE_PROD_PROJECT_REF }}" | |
| working-directory: packages/db | |
| env: | |
| SUPABASE_ACCESS_TOKEN: ${{ secrets.SUPABASE_ACCESS_TOKEN }} | |
| - name: Lint migrations | |
| run: supabase db lint --linked | |
| working-directory: packages/db | |
| env: | |
| SUPABASE_ACCESS_TOKEN: ${{ secrets.SUPABASE_ACCESS_TOKEN }} | |
| - name: Push migrations | |
| run: supabase db push | |
| working-directory: packages/db | |
| env: | |
| SUPABASE_ACCESS_TOKEN: ${{ secrets.SUPABASE_ACCESS_TOKEN }} | |
| SUPABASE_DB_PASSWORD: ${{ secrets.SUPABASE_PROD_DB_PASSWORD }} | |
| - name: Verify no schema drift after push | |
| run: | | |
| OUTPUT=$(supabase db diff --linked 2>&1) | |
| # Detect remaining drift by checking for SQL statements (contain semicolons) | |
| if echo "$OUTPUT" | grep -q ';'; then | |
| echo "::error::Schema drift remains after push — production may have manual changes" | |
| echo '```sql' >> "$GITHUB_STEP_SUMMARY" | |
| echo "$OUTPUT" >> "$GITHUB_STEP_SUMMARY" | |
| echo '```' >> "$GITHUB_STEP_SUMMARY" | |
| exit 1 | |
| fi | |
| echo "### Schema drift check — Clean ✅" >> "$GITHUB_STEP_SUMMARY" | |
| working-directory: packages/db | |
| env: | |
| SUPABASE_ACCESS_TOKEN: ${{ secrets.SUPABASE_ACCESS_TOKEN }} | |
| SUPABASE_DB_PASSWORD: ${{ secrets.SUPABASE_PROD_DB_PASSWORD }} | |
| - name: Summary | |
| if: always() | |
| run: echo "### Prod Migrate ${{ job.status == 'success' && '✅' || '❌' }}" >> "$GITHUB_STEP_SUMMARY" | |
| # ─── Generate prod types ────────────────────────────────────── | |
| generate-types: | |
| name: Generate Prod Types | |
| needs: [preflight, migrate] | |
| # Run after migrate succeeds, or if migrate was skipped (no new migrations) | |
| if: always() && (needs.migrate.result == 'success' || needs.migrate.result == 'skipped') | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v4 | |
| with: | |
| ref: main | |
| token: ${{ secrets.TYPES_PUSH_TOKEN }} | |
| - uses: supabase/setup-cli@v1 | |
| with: | |
| version: latest | |
| - name: Link to prod project | |
| run: supabase link --project-ref "${{ secrets.SUPABASE_PROD_PROJECT_REF }}" | |
| working-directory: packages/db | |
| env: | |
| SUPABASE_ACCESS_TOKEN: ${{ secrets.SUPABASE_ACCESS_TOKEN }} | |
| - name: Generate TypeScript types | |
| run: supabase gen types typescript --linked > types/database.ts | |
| working-directory: packages/db | |
| env: | |
| SUPABASE_ACCESS_TOKEN: ${{ secrets.SUPABASE_ACCESS_TOKEN }} | |
| SUPABASE_DB_PASSWORD: ${{ secrets.SUPABASE_PROD_DB_PASSWORD }} | |
| - name: Push types if changed | |
| run: | | |
| git config user.name "github-actions[bot]" | |
| git config user.email "41898282+github-actions[bot]@users.noreply.github.com" | |
| git add packages/db/types/database.ts | |
| if git diff --staged --quiet; then | |
| echo "No type changes detected" | |
| echo "TYPES_CHANGED=false" >> "$GITHUB_ENV" | |
| else | |
| git commit -m "chore(db): regenerate prod database types" | |
| git push | |
| echo "TYPES_CHANGED=true" >> "$GITHUB_ENV" | |
| fi | |
| - name: Summary | |
| run: | | |
| if [ "$TYPES_CHANGED" = "true" ]; then | |
| echo "### Types — pushed to main ✅" >> "$GITHUB_STEP_SUMMARY" | |
| else | |
| echo "### Types — No changes ✅" >> "$GITHUB_STEP_SUMMARY" | |
| fi | |
| # ─── Smoke test: always runs (even if migration was skipped) ── | |
| smoke-test: | |
| name: Smoke Test | |
| needs: [preflight, migrate] | |
| # Run regardless of whether migrate ran or was skipped | |
| if: always() && (needs.migrate.result == 'success' || needs.migrate.result == 'skipped') | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Health check | |
| run: | | |
| PROD_URL="${{ vars.PRODUCTION_URL }}" | |
| if [ -z "$PROD_URL" ]; then | |
| echo "::notice::PRODUCTION_URL variable not set — skipping smoke test" | |
| echo "### Smoke Test — Skipped (no PRODUCTION_URL) ⏭️" >> "$GITHUB_STEP_SUMMARY" | |
| exit 0 | |
| fi | |
| # Retry up to 3 times with 10s wait (Vercel deploy may still be propagating) | |
| for i in 1 2 3; do | |
| STATUS=$(curl -sf -o /dev/null -w '%{http_code}' "${PROD_URL}/api/health" || true) | |
| if [ "$STATUS" = "200" ]; then | |
| echo "### Smoke Test — Healthy ✅" >> "$GITHUB_STEP_SUMMARY" | |
| exit 0 | |
| fi | |
| echo "Attempt $i: got status $STATUS, retrying in 10s..." | |
| sleep 10 | |
| done | |
| echo "::error::Health check failed after 3 attempts (last status: $STATUS)" | |
| echo "### Smoke Test — Failed ❌ (status: $STATUS)" >> "$GITHUB_STEP_SUMMARY" | |
| exit 1 | |
| # ─── Summary ────────────────────────────────────────────────── | |
| summary: | |
| name: Summary | |
| needs: [preflight, migrate, generate-types, smoke-test] | |
| if: always() | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Deployment summary | |
| run: | | |
| cat >> "$GITHUB_STEP_SUMMARY" <<EOF | |
| ## Production Deploy Summary | |
| | Job | Status | | |
| |-----|--------| | |
| | Preflight | ${{ needs.preflight.result }} | | |
| | Migrations | ${{ needs.migrate.result }} | | |
| | Generate Types | ${{ needs.generate-types.result }} | | |
| | Smoke Test | ${{ needs.smoke-test.result }} | | |
| **New migrations:** ${{ needs.preflight.outputs.has_new_migrations }} | |
| EOF |