|
| 1 | +# ============================================================================= |
| 2 | +# Flyway Dev Templates — Schema Model → Migration Script Generation |
| 3 | +# ============================================================================= |
| 4 | +# Generate versioned migration SQL from schema-model changes using |
| 5 | +# `flyway diff model` and `flyway diff generate`. |
| 6 | +# |
| 7 | +# Include alongside flyway.yml in any pipeline: |
| 8 | +# include: |
| 9 | +# - local: '.gitlab/ci/flyway.yml' |
| 10 | +# - local: '.gitlab/ci/dev.yml' |
| 11 | +# |
| 12 | +# Workflow: |
| 13 | +# 1. generate stage — `flyway diff model` syncs SchemaModel with the Dev |
| 14 | +# database, then `flyway diff generate` compares SchemaModel against |
| 15 | +# existing Migrations to produce new V*__*.sql scripts. Runs automatically. |
| 16 | +# 2. review stage — Manual gate. Reviewer inspects the generated SQL |
| 17 | +# (available as job artifacts), then clicks ▶ Play to commit the |
| 18 | +# scripts back to the branch. |
| 19 | +# 3. deploy stage — Reviewer clicks ▶ Play to run flyway migrate |
| 20 | +# (uses .flyway_migrate from flyway.yml). |
| 21 | +# |
| 22 | +# CI/CD Variables required (in addition to flyway.yml variables): |
| 23 | +# TARGET_DATABASE_JDBC — JDBC URL of the Dev database (diff source) |
| 24 | +# TARGET_DATABASE_USER — Dev database user |
| 25 | +# TARGET_DATABASE_PASSWORD — Dev database password |
| 26 | +# SHADOW_DATABASE_JDBC — JDBC URL of a shadow/build database (empty DB |
| 27 | +# Flyway uses to rebuild from migrations) |
| 28 | +# |
| 29 | +# Optional CI/CD Variables: |
| 30 | +# SHADOW_DATABASE_USER — Shadow DB user (defaults to TARGET_DATABASE_USER) |
| 31 | +# SHADOW_DATABASE_PASSWORD — Shadow DB password (defaults to TARGET_DATABASE_PASSWORD) |
| 32 | +# GENERATE_DESCRIPTION — Description for generated migration (default: AutoGenerated) |
| 33 | +# GENERATE_TYPES — Migration types to generate (default: versioned,undo) |
| 34 | +# GIT_PUSH_TOKEN — Token with write_repository scope (if CI_JOB_TOKEN can't push) |
| 35 | +# GITLAB_EXTERNAL_URL — Browser-reachable GitLab URL for MR links |
| 36 | +# (default: CI_SERVER_URL; set if behind port mapping, |
| 37 | +# e.g. http://localhost:8080) |
| 38 | +# MR_TARGET_BRANCH — Branch the MR targets (default: main) |
| 39 | +# |
| 40 | +# Consumer repo structure: |
| 41 | +# my-flyway-project/ |
| 42 | +# ├── flyway.toml # Flyway project config |
| 43 | +# ├── schema-model/ # Schema definitions (devs edit these) |
| 44 | +# ├── migrations/ # Versioned SQL (generated + committed by CI) |
| 45 | +# └── .gitlab-ci.yml |
| 46 | +# ============================================================================= |
| 47 | + |
| 48 | +# --------------------------------------------------------------------------- |
| 49 | +# Base: shared image + variables for flyway diff jobs |
| 50 | +# --------------------------------------------------------------------------- |
| 51 | +.flyway_dev_base: |
| 52 | + image: |
| 53 | + name: redgate/flyway:latest |
| 54 | + entrypoint: [""] |
| 55 | + variables: |
| 56 | + SOURCE_DATABASE_JDBC: "${TARGET_DATABASE_JDBC}" |
| 57 | + SOURCE_DATABASE_USER: "${TARGET_DATABASE_USER}" |
| 58 | + SOURCE_DATABASE_PASSWORD: "${TARGET_DATABASE_PASSWORD}" |
| 59 | + SHADOW_DATABASE_USER: "${TARGET_DATABASE_USER}" |
| 60 | + SHADOW_DATABASE_PASSWORD: "${TARGET_DATABASE_PASSWORD}" |
| 61 | + GENERATE_DESCRIPTION: "AutoGenerated" |
| 62 | + GENERATE_TYPES: "versioned,undo" |
| 63 | + WORKING_DIRECTORY: "." |
| 64 | + before_script: |
| 65 | + - "echo ==========================================" |
| 66 | + - "echo Flyway — Migration Script Generation" |
| 67 | + - "echo ==========================================" |
| 68 | + - "echo Source Dev DB: $SOURCE_DATABASE_JDBC" |
| 69 | + - "echo Shadow DB: $SHADOW_DATABASE_JDBC" |
| 70 | + - "echo ==========================================" |
| 71 | + |
| 72 | +# --------------------------------------------------------------------------- |
| 73 | +# Generate migration scripts from schema-model changes |
| 74 | +# --------------------------------------------------------------------------- |
| 75 | +# Two-step workflow using `flyway diff`: |
| 76 | +# 1. flyway diff model — syncs SchemaModel folder with the Dev database |
| 77 | +# 2. flyway diff generate — compares SchemaModel to Migrations (via a |
| 78 | +# shadow/build DB) and generates new V*__*.sql + U*__*.sql scripts |
| 79 | +# |
| 80 | +# Generated scripts are saved as artifacts for review. |
| 81 | +# --------------------------------------------------------------------------- |
| 82 | +.flyway_generate_migrations: |
| 83 | + extends: .flyway_dev_base |
| 84 | + script: |
| 85 | + - "echo '--- Step 1: Sync SchemaModel with Dev database ---'" |
| 86 | + - "flyway diff model -workingDirectory=${WORKING_DIRECTORY} -diff.source=dev -diff.target=schemaModel -environments.dev.url=${SOURCE_DATABASE_JDBC} -environments.dev.user=${SOURCE_DATABASE_USER} -environments.dev.password=${SOURCE_DATABASE_PASSWORD} -email=${FLYWAY_EMAIL} -token=${FLYWAY_TOKEN}" |
| 87 | + - "echo '--- Step 2: Generate migration scripts ---'" |
| 88 | + - "flyway diff generate -workingDirectory=${WORKING_DIRECTORY} -diff.source=schemaModel -diff.target=migrations -generate.types=${GENERATE_TYPES} -generate.description=${GENERATE_DESCRIPTION} -diff.buildEnvironment=shadow -environments.shadow.url=${SHADOW_DATABASE_JDBC} -environments.shadow.user=${SHADOW_DATABASE_USER} -environments.shadow.password=${SHADOW_DATABASE_PASSWORD} -email=${FLYWAY_EMAIL} -token=${FLYWAY_TOKEN}" |
| 89 | + - "echo '=== Generated migration files ==='" |
| 90 | + - "ls -la migrations/" |
| 91 | + artifacts: |
| 92 | + paths: |
| 93 | + - migrations/ |
| 94 | + expire_in: 1 week |
| 95 | + allow_failure: false |
| 96 | + |
| 97 | +# --------------------------------------------------------------------------- |
| 98 | +# Commit generated migrations to the dev branch and open MR to main |
| 99 | +# --------------------------------------------------------------------------- |
| 100 | +# This is a MANUAL job. Click ▶ Play to: |
| 101 | +# 1. Commit the generated scripts to the current (dev) branch |
| 102 | +# 2. Open a merge request from dev → main |
| 103 | +# |
| 104 | +# The MR diff shows exactly what migration SQL will be added. You can |
| 105 | +# edit files inline in the MR view before merging. Merging into main |
| 106 | +# triggers the deploy stages. |
| 107 | +# |
| 108 | +# Authentication: Set GIT_PUSH_TOKEN (PAT with api scope) as a CI/CD |
| 109 | +# variable. See README → Git Push Authentication. |
| 110 | +# --------------------------------------------------------------------------- |
| 111 | +.flyway_commit_migrations: |
| 112 | + image: alpine:latest |
| 113 | + variables: |
| 114 | + MIGRATIONS_PATH: "./migrations" |
| 115 | + GIT_COMMIT_MESSAGE: "chore: add auto-generated migration scripts" |
| 116 | + MR_TITLE: "Flyway: auto-generated migration scripts" |
| 117 | + MR_TARGET_BRANCH: "main" |
| 118 | + before_script: |
| 119 | + - "apk add --no-cache git curl jq >/dev/null 2>&1" |
| 120 | + script: |
| 121 | + - "git config user.email gitlab-ci@${CI_SERVER_HOST}" |
| 122 | + - "git config user.name 'GitLab CI'" |
| 123 | + - "git add ${MIGRATIONS_PATH}/" |
| 124 | + - "if git diff --cached --quiet; then echo 'No new migration scripts — nothing to do.'; exit 0; fi" |
| 125 | + - "git diff --cached --stat" |
| 126 | + - "git commit -m \"${GIT_COMMIT_MESSAGE}\"" |
| 127 | + - "if [ -n \"${GIT_PUSH_TOKEN}\" ]; then CURRENT_URL=$(git remote get-url origin); PUSH_URL=$(echo \"${CURRENT_URL}\" | sed \"s|gitlab-ci-token:[^@]*|gitlab-ci-token:${GIT_PUSH_TOKEN}|\"); git remote set-url origin \"${PUSH_URL}\"; fi" |
| 128 | + - "git push origin HEAD:${CI_COMMIT_REF_NAME}" |
| 129 | + - "echo '--- Creating merge request ---'" |
| 130 | + - "API_TOKEN=${GIT_PUSH_TOKEN:-${CI_JOB_TOKEN}}" |
| 131 | + - "GITLAB_API_URL=$(git remote get-url origin | sed 's|/[^/]*/[^/]*\\.git$||')/api/v4" |
| 132 | + - "EXISTING_MR=$(curl -sf --header \"PRIVATE-TOKEN: ${API_TOKEN}\" \"${GITLAB_API_URL}/projects/${CI_PROJECT_ID}/merge_requests?source_branch=${CI_COMMIT_REF_NAME}&target_branch=${MR_TARGET_BRANCH}&state=opened\" | jq '.[0].iid // empty')" |
| 133 | + - "if [ -n \"${EXISTING_MR}\" ]; then echo \"Merge request already exists (MR !${EXISTING_MR})\"; MR_IID=${EXISTING_MR}; else MR_RESPONSE=$(curl -sf --header \"PRIVATE-TOKEN: ${API_TOKEN}\" --header \"Content-Type: application/json\" --data \"{\\\"source_branch\\\": \\\"${CI_COMMIT_REF_NAME}\\\", \\\"target_branch\\\": \\\"${MR_TARGET_BRANCH}\\\", \\\"title\\\": \\\"${MR_TITLE}\\\", \\\"remove_source_branch\\\": false}\" \"${GITLAB_API_URL}/projects/${CI_PROJECT_ID}/merge_requests\"); MR_IID=$(echo \"${MR_RESPONSE}\" | jq -r .iid); fi" |
| 134 | + - "BROWSE_URL=${GITLAB_EXTERNAL_URL:-${CI_SERVER_URL}}/${CI_PROJECT_PATH}/-/merge_requests/${MR_IID}" |
| 135 | + - "echo '========================================'" |
| 136 | + - "echo \"Review and merge: ${BROWSE_URL}\"" |
| 137 | + - "echo '========================================'" |
| 138 | + environment: |
| 139 | + name: "review/$CI_COMMIT_REF_NAME" |
| 140 | + url: "http://localhost:8080/$CI_PROJECT_PATH/-/merge_requests" |
| 141 | + action: prepare |
| 142 | + when: manual |
| 143 | + allow_failure: false |
0 commit comments