Flyway Database Migrations #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
| # ============================================================================= | |
| # FLYWAY DATABASE MIGRATIONS WORKFLOW | |
| # ============================================================================= | |
| # Ejecuta migraciones en AMBAS bases de datos: | |
| # - PostgreSQL Metadata (schema: metadata) → db/migration/ | |
| # - TimescaleDB (schema: iot) → db/migration-timescaledb/ | |
| # | |
| # Flujo: | |
| # 1. PR merged a develop → Migrate DEV databases → Deploy DEV | |
| # 2. PR merged a main → Migrate PROD databases → Deploy PROD | |
| # | |
| # REGLAS DE ORO: | |
| # 1. NUNCA modificar una migración ya aplicada | |
| # 2. NUNCA eliminar una migración ya aplicada | |
| # 3. Una migración = un cambio atómico | |
| # 4. Las migraciones son INMUTABLES | |
| # ============================================================================= | |
| name: Flyway Database Migrations | |
| on: | |
| push: | |
| branches: | |
| - main | |
| - develop | |
| paths: | |
| - 'src/main/resources/db/migration/**' | |
| - 'src/main/resources/db/migration-timescaledb/**' | |
| workflow_dispatch: | |
| inputs: | |
| environment: | |
| description: 'Environment to migrate' | |
| required: true | |
| type: choice | |
| options: | |
| - dev | |
| - prod | |
| action: | |
| description: 'Flyway action to perform' | |
| required: true | |
| type: choice | |
| options: | |
| - info | |
| - validate | |
| - migrate | |
| - repair | |
| dry_run: | |
| description: 'Dry run (solo mostrar qué haría)' | |
| required: false | |
| type: boolean | |
| default: true | |
| env: | |
| FLYWAY_VERSION: '10.20.1' | |
| jobs: | |
| # =================================================================== | |
| # JOB 1: Detectar cambios y determinar entorno | |
| # =================================================================== | |
| detect-changes: | |
| name: Detect Migration Changes | |
| runs-on: ubuntu-latest | |
| outputs: | |
| has_metadata_migrations: ${{ steps.check.outputs.has_metadata_migrations }} | |
| has_timescaledb_migrations: ${{ steps.check.outputs.has_timescaledb_migrations }} | |
| target_env: ${{ steps.env.outputs.target_env }} | |
| steps: | |
| - uses: actions/checkout@v4 | |
| with: | |
| fetch-depth: 2 | |
| - name: Check for migration changes | |
| id: check | |
| run: | | |
| if git diff --name-only HEAD~1 HEAD | grep -q 'db/migration/'; then | |
| echo "has_metadata_migrations=true" >> $GITHUB_OUTPUT | |
| echo "✅ Detected metadata migration changes" | |
| else | |
| echo "has_metadata_migrations=false" >> $GITHUB_OUTPUT | |
| fi | |
| if git diff --name-only HEAD~1 HEAD | grep -q 'db/migration-timescaledb/'; then | |
| echo "has_timescaledb_migrations=true" >> $GITHUB_OUTPUT | |
| echo "✅ Detected TimescaleDB migration changes" | |
| else | |
| echo "has_timescaledb_migrations=false" >> $GITHUB_OUTPUT | |
| fi | |
| - name: Determine target environment | |
| id: env | |
| run: | | |
| if [[ "${{ github.event_name }}" == "workflow_dispatch" ]]; then | |
| echo "target_env=${{ github.event.inputs.environment }}" >> $GITHUB_OUTPUT | |
| elif [[ "${{ github.ref }}" == "refs/heads/main" ]]; then | |
| echo "target_env=prod" >> $GITHUB_OUTPUT | |
| else | |
| echo "target_env=dev" >> $GITHUB_OUTPUT | |
| fi | |
| # =================================================================== | |
| # JOB 2: Validar migraciones | |
| # =================================================================== | |
| validate: | |
| name: Validate Migrations | |
| needs: detect-changes | |
| if: > | |
| needs.detect-changes.outputs.has_metadata_migrations == 'true' || | |
| needs.detect-changes.outputs.has_timescaledb_migrations == 'true' || | |
| github.event_name == 'workflow_dispatch' | |
| runs-on: ubuntu-latest | |
| environment: ${{ needs.detect-changes.outputs.target_env }} | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Setup Flyway | |
| run: | | |
| wget -qO- https://download.red-gate.com/maven/release/com/redgate/flyway/flyway-commandline/${FLYWAY_VERSION}/flyway-commandline-${FLYWAY_VERSION}-linux-x64.tar.gz | tar -xz | |
| sudo ln -s $(pwd)/flyway-${FLYWAY_VERSION}/flyway /usr/local/bin/flyway | |
| flyway --version | |
| - name: Validate Metadata PostgreSQL | |
| if: > | |
| needs.detect-changes.outputs.has_metadata_migrations == 'true' || | |
| github.event_name == 'workflow_dispatch' | |
| env: | |
| DB_URL: ${{ secrets.FLYWAY_DB_URL }} | |
| DB_USER: ${{ secrets.FLYWAY_DB_USER }} | |
| DB_PASSWORD: ${{ secrets.FLYWAY_DB_PASSWORD }} | |
| run: | | |
| echo "📊 Metadata PostgreSQL - Current state:" | |
| flyway -url="${DB_URL}" \ | |
| -user="${DB_USER}" \ | |
| -password="${DB_PASSWORD}" \ | |
| -schemas=metadata \ | |
| -locations="filesystem:src/main/resources/db/migration" \ | |
| -baselineOnMigrate=true \ | |
| -baselineVersion=1 \ | |
| info | |
| echo "🔍 Validating metadata migrations..." | |
| flyway -url="${DB_URL}" \ | |
| -user="${DB_USER}" \ | |
| -password="${DB_PASSWORD}" \ | |
| -schemas=metadata \ | |
| -locations="filesystem:src/main/resources/db/migration" \ | |
| -baselineOnMigrate=true \ | |
| -baselineVersion=1 \ | |
| -ignoreMigrationPatterns='*:pending' \ | |
| validate | |
| echo "✅ Metadata validation passed!" | |
| - name: Validate TimescaleDB | |
| if: > | |
| needs.detect-changes.outputs.has_timescaledb_migrations == 'true' || | |
| github.event_name == 'workflow_dispatch' | |
| env: | |
| DB_URL: ${{ secrets.FLYWAY_TIMESCALEDB_URL }} | |
| DB_USER: ${{ secrets.FLYWAY_TIMESCALEDB_USER }} | |
| DB_PASSWORD: ${{ secrets.FLYWAY_TIMESCALEDB_PASSWORD }} | |
| run: | | |
| if [[ -z "${DB_URL}" ]]; then | |
| echo "⚠️ FLYWAY_TIMESCALEDB_URL secret not configured - skipping TimescaleDB validation" | |
| exit 0 | |
| fi | |
| echo "📊 TimescaleDB - Current state:" | |
| flyway -url="${DB_URL}" \ | |
| -user="${DB_USER}" \ | |
| -password="${DB_PASSWORD}" \ | |
| -schemas=iot \ | |
| -locations="filesystem:src/main/resources/db/migration-timescaledb" \ | |
| -baselineOnMigrate=true \ | |
| -baselineVersion=31 \ | |
| info | |
| echo "🔍 Validating TimescaleDB migrations..." | |
| flyway -url="${DB_URL}" \ | |
| -user="${DB_USER}" \ | |
| -password="${DB_PASSWORD}" \ | |
| -schemas=iot \ | |
| -locations="filesystem:src/main/resources/db/migration-timescaledb" \ | |
| -baselineOnMigrate=true \ | |
| -baselineVersion=31 \ | |
| -ignoreMigrationPatterns='*:pending' \ | |
| validate | |
| echo "✅ TimescaleDB validation passed!" | |
| # =================================================================== | |
| # JOB 3: Ejecutar migraciones | |
| # =================================================================== | |
| migrate: | |
| name: Run Migrations | |
| needs: [detect-changes, validate] | |
| runs-on: ubuntu-latest | |
| environment: | |
| name: ${{ needs.detect-changes.outputs.target_env }} | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Setup Flyway | |
| run: | | |
| wget -qO- https://download.red-gate.com/maven/release/com/redgate/flyway/flyway-commandline/${FLYWAY_VERSION}/flyway-commandline-${FLYWAY_VERSION}-linux-x64.tar.gz | tar -xz | |
| sudo ln -s $(pwd)/flyway-${FLYWAY_VERSION}/flyway /usr/local/bin/flyway | |
| - name: Determine action | |
| id: action | |
| run: | | |
| if [[ "${{ github.event_name }}" == "workflow_dispatch" ]]; then | |
| echo "action=${{ github.event.inputs.action }}" >> $GITHUB_OUTPUT | |
| echo "dry_run=${{ github.event.inputs.dry_run }}" >> $GITHUB_OUTPUT | |
| else | |
| echo "action=migrate" >> $GITHUB_OUTPUT | |
| echo "dry_run=false" >> $GITHUB_OUTPUT | |
| fi | |
| - name: Run Flyway on Metadata PostgreSQL | |
| if: > | |
| needs.detect-changes.outputs.has_metadata_migrations == 'true' || | |
| github.event_name == 'workflow_dispatch' | |
| env: | |
| DB_URL: ${{ secrets.FLYWAY_DB_URL }} | |
| DB_USER: ${{ secrets.FLYWAY_DB_USER }} | |
| DB_PASSWORD: ${{ secrets.FLYWAY_DB_PASSWORD }} | |
| run: | | |
| ACTION="${{ steps.action.outputs.action }}" | |
| DRY_RUN="${{ steps.action.outputs.dry_run }}" | |
| ENV="${{ needs.detect-changes.outputs.target_env }}" | |
| echo "🚀 Running Flyway ${ACTION} on Metadata PostgreSQL (${ENV})" | |
| if [[ "${DRY_RUN}" == "true" ]]; then | |
| echo "⚠️ DRY RUN MODE" | |
| flyway -url="${DB_URL}" \ | |
| -user="${DB_USER}" \ | |
| -password="${DB_PASSWORD}" \ | |
| -schemas=metadata \ | |
| -locations="filesystem:src/main/resources/db/migration" \ | |
| -baselineOnMigrate=true \ | |
| -baselineVersion=1 \ | |
| -dryRunOutput=flyway-metadata-dryrun.sql \ | |
| ${ACTION} | |
| if [[ -f flyway-metadata-dryrun.sql ]]; then | |
| cat flyway-metadata-dryrun.sql | |
| fi | |
| else | |
| flyway -url="${DB_URL}" \ | |
| -user="${DB_USER}" \ | |
| -password="${DB_PASSWORD}" \ | |
| -schemas=metadata \ | |
| -locations="filesystem:src/main/resources/db/migration" \ | |
| -baselineOnMigrate=true \ | |
| -baselineVersion=1 \ | |
| -cleanDisabled=true \ | |
| ${ACTION} | |
| fi | |
| - name: Run Flyway on TimescaleDB | |
| if: > | |
| needs.detect-changes.outputs.has_timescaledb_migrations == 'true' || | |
| github.event_name == 'workflow_dispatch' | |
| env: | |
| DB_URL: ${{ secrets.FLYWAY_TIMESCALEDB_URL }} | |
| DB_USER: ${{ secrets.FLYWAY_TIMESCALEDB_USER }} | |
| DB_PASSWORD: ${{ secrets.FLYWAY_TIMESCALEDB_PASSWORD }} | |
| run: | | |
| if [[ -z "${DB_URL}" ]]; then | |
| echo "⚠️ FLYWAY_TIMESCALEDB_URL secret not configured - skipping TimescaleDB migrations" | |
| exit 0 | |
| fi | |
| ACTION="${{ steps.action.outputs.action }}" | |
| DRY_RUN="${{ steps.action.outputs.dry_run }}" | |
| ENV="${{ needs.detect-changes.outputs.target_env }}" | |
| echo "🚀 Running Flyway ${ACTION} on TimescaleDB (${ENV})" | |
| if [[ "${DRY_RUN}" == "true" ]]; then | |
| echo "⚠️ DRY RUN MODE" | |
| flyway -url="${DB_URL}" \ | |
| -user="${DB_USER}" \ | |
| -password="${DB_PASSWORD}" \ | |
| -schemas=iot \ | |
| -locations="filesystem:src/main/resources/db/migration-timescaledb" \ | |
| -baselineOnMigrate=true \ | |
| -baselineVersion=31 \ | |
| -dryRunOutput=flyway-timescaledb-dryrun.sql \ | |
| ${ACTION} | |
| if [[ -f flyway-timescaledb-dryrun.sql ]]; then | |
| cat flyway-timescaledb-dryrun.sql | |
| fi | |
| else | |
| flyway -url="${DB_URL}" \ | |
| -user="${DB_USER}" \ | |
| -password="${DB_PASSWORD}" \ | |
| -schemas=iot \ | |
| -locations="filesystem:src/main/resources/db/migration-timescaledb" \ | |
| -baselineOnMigrate=true \ | |
| -baselineVersion=31 \ | |
| -cleanDisabled=true \ | |
| ${ACTION} | |
| fi | |
| - name: Show final state | |
| env: | |
| DB_URL: ${{ secrets.FLYWAY_DB_URL }} | |
| DB_USER: ${{ secrets.FLYWAY_DB_USER }} | |
| DB_PASSWORD: ${{ secrets.FLYWAY_DB_PASSWORD }} | |
| TSDB_URL: ${{ secrets.FLYWAY_TIMESCALEDB_URL }} | |
| TSDB_USER: ${{ secrets.FLYWAY_TIMESCALEDB_USER }} | |
| TSDB_PASSWORD: ${{ secrets.FLYWAY_TIMESCALEDB_PASSWORD }} | |
| run: | | |
| echo "📊 Final state - Metadata PostgreSQL:" | |
| flyway -url="${DB_URL}" \ | |
| -user="${DB_USER}" \ | |
| -password="${DB_PASSWORD}" \ | |
| -schemas=metadata \ | |
| -locations="filesystem:src/main/resources/db/migration" \ | |
| info | |
| if [[ -n "${TSDB_URL}" ]]; then | |
| echo "" | |
| echo "📊 Final state - TimescaleDB:" | |
| flyway -url="${TSDB_URL}" \ | |
| -user="${TSDB_USER}" \ | |
| -password="${TSDB_PASSWORD}" \ | |
| -schemas=iot \ | |
| -locations="filesystem:src/main/resources/db/migration-timescaledb" \ | |
| info | |
| fi | |
| # =================================================================== | |
| # JOB 4: Notificar resultado | |
| # =================================================================== | |
| notify: | |
| name: Notify Result | |
| needs: [detect-changes, migrate] | |
| if: always() | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Create summary | |
| run: | | |
| echo "## Flyway Migration Summary" >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| echo "**Environment:** ${{ needs.detect-changes.outputs.target_env }}" >> $GITHUB_STEP_SUMMARY | |
| echo "**Branch:** ${{ github.ref_name }}" >> $GITHUB_STEP_SUMMARY | |
| echo "**Status:** ${{ needs.migrate.result }}" >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| if [[ "${{ needs.migrate.result }}" == "success" ]]; then | |
| echo "✅ Migrations completed successfully!" >> $GITHUB_STEP_SUMMARY | |
| else | |
| echo "❌ Migrations failed! Check the logs for details." >> $GITHUB_STEP_SUMMARY | |
| fi |