name: Database Migration on: push: paths: - 'prisma/schema.prisma' - 'prisma/migrations/**' branches: [main, develop] pull_request: paths: - 'prisma/schema.prisma' - 'prisma/migrations/**' workflow_dispatch: inputs: run_deploy: description: 'Run deploy migration against staging DB' type: boolean default: false env: NODE_VERSION: '22' jobs: # ═══════════════════════════════════════════════════════════════ # 1. Validate schema + migration consistency # ═══════════════════════════════════════════════════════════════ validate: name: Validate Schema runs-on: ubuntu-latest timeout-minutes: 5 steps: - uses: actions/checkout@v4 - uses: actions/setup-node@v4 with: node-version: ${{ env.NODE_VERSION }} cache: 'npm' - run: npm ci - run: npx prisma generate env: DATABASE_URL: ${{ secrets.MIGRATION_DATABASE_URL || 'mysql://placeholder:placeholder@localhost:3306/placeholder' }} - name: Validate no schema drift run: | echo "Checking prisma schema is formatted..." npx prisma format if ! git diff --exit-code prisma/schema.prisma; then echo "::error::prisma/schema.prisma has unformatted changes. Run 'npx prisma format' locally." exit 1 fi - name: Validate prisma generate output run: | if [ ! -f node_modules/.prisma/client/index.js ]; then echo "::error::Prisma Client not generated" exit 1 fi # ═══════════════════════════════════════════════════════════════ # 2. Dry-run migration (check what would be applied) # ═══════════════════════════════════════════════════════════════ dry-run: name: Dry-Run Migration runs-on: ubuntu-latest timeout-minutes: 10 needs: validate services: mysql: image: mysql:8.0 env: MYSQL_ROOT_PASSWORD: testpass MYSQL_DATABASE: zhixi_test ports: - 3306:3306 options: >- --health-cmd "mysqladmin ping -h localhost" --health-interval 10s --health-timeout 5s --health-retries 5 steps: - uses: actions/checkout@v4 - uses: actions/setup-node@v4 with: node-version: ${{ env.NODE_VERSION }} cache: 'npm' - run: npm ci - run: npx prisma generate env: DATABASE_URL: mysql://root:testpass@127.0.0.1:3306/zhixi_test - name: Push schema to temp DB run: npx prisma db push --skip-generate --accept-data-loss env: DATABASE_URL: mysql://root:testpass@127.0.0.1:3306/zhixi_test - name: Apply migrations run: npx prisma migrate deploy env: DATABASE_URL: mysql://root:testpass@127.0.0.1:3306/zhixi_test - name: Verify migration completed run: npx prisma migrate status env: DATABASE_URL: mysql://root:testpass@127.0.0.1:3306/zhixi_test # ═══════════════════════════════════════════════════════════════ # 3. Deploy migration (staging/production) # ═══════════════════════════════════════════════════════════════ deploy-migration: name: Deploy Migration runs-on: ubuntu-latest timeout-minutes: 10 needs: dry-run if: github.event_name == 'workflow_dispatch' && inputs.run_deploy environment: staging steps: - uses: actions/checkout@v4 - name: Deploy migration via SSH uses: appleboy/ssh-action@v1 with: host: ${{ secrets.DEPLOY_HOST }} username: ${{ secrets.DEPLOY_USER }} key: ${{ secrets.DEPLOY_SSH_KEY }} script: | set -e cd /opt/zhixi/api-server echo "=== Migration status before ===" docker compose exec -T api npx prisma migrate status echo "=== Applying migrations ===" docker compose exec -T api npx prisma migrate deploy echo "=== Migration status after ===" docker compose exec -T api npx prisma migrate status