Skip to content

Flyway Database Migrations #26

Flyway Database Migrations

Flyway Database Migrations #26

# =============================================================================
# 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