Skip to content

Commit f546d78

Browse files
gitlab pipeline now generating scripts on dev branch, opens PR dev to main, deployment happens on main
1 parent b314116 commit f546d78

21 files changed

Lines changed: 810 additions & 28 deletions

CLI-autocapture-autoscript/automated-capture-migration-per-object.ps1 renamed to Deprecated-CLI-autocapture-autoscript/automated-capture-migration-per-object.ps1

File renamed without changes.

CLI-autocapture-autoscript/automated-capture.ps1 renamed to Deprecated-CLI-autocapture-autoscript/automated-capture.ps1

File renamed without changes.

CLI-autocapture-autoscript/snapshotDriftCheck.sh renamed to Deprecated-CLI-autocapture-autoscript/snapshotDriftCheck.sh

File renamed without changes.
Lines changed: 143 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,143 @@
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

Gitlab-Templatized/.gitlab/ci/flyway.yml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,10 @@
77
# include:
88
# - local: '.gitlab/ci/flyway.yml'
99
#
10+
# For schema-model → migration-script generation, also include:
11+
# include:
12+
# - local: '.gitlab/ci/dev.yml'
13+
#
1014
# For dynamic multi-database deployments driven by the jdbc_table_store
1115
# registry, also include:
1216
# include:

Gitlab-Templatized/.gitlab/ci/generate.yml

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,7 @@
9292
# Set TEMPLATE_PROJECT to your templates repo path so the generated child
9393
# pipeline can find flyway.yml. Leave empty if templates are in the same repo.
9494
# -------------------------------------------------------------------------
95-
# TEMPLATE_PROJECT: "your-group/flyway-ci-templates"
95+
# TEMPLATE_PROJECT: "root/templatized-with-parser"
9696
# TEMPLATE_REF: "v1.0.0"
9797

9898
# -------------------------------------------------------------------------
@@ -108,5 +108,3 @@
108108
expire_in: 2 hours
109109
rules:
110110
- when: always
111-
rules:
112-
- when: always

Gitlab-Templatized/QUICK_REFERENCE.md

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,9 @@
1515
TARGET_DATABASE_PASSWORD = your-password (Protected + Masked)
1616
```
1717

18-
3. **Choose pipeline template**
18+
3. **Choose pipeline template** from `usage-examples/`
1919
```bash
20-
cp .gitlab-ci-example-dev.yml .gitlab-ci.yml
20+
cp usage-examples/single-db-dev.gitlab-ci.yml .gitlab-ci.yml
2121
```
2222

2323
4. **Add SQL migrations**
@@ -82,13 +82,14 @@ TARGET_DATABASE_JDBC (scope: production)
8282

8383
## Pipeline Template Selection
8484

85-
| Databases | Template to Use | Command |
86-
|-----------|----------------|---------|
87-
| **1 database** | `.gitlab-ci-example-dev.yml` | `cp .gitlab-ci-example-dev.yml .gitlab-ci.yml` |
88-
| **Dev + Staging + Prod** | `.gitlab-ci-example-prod.yml` | `cp .gitlab-ci-example-prod.yml .gitlab-ci.yml` |
89-
| **2-10 databases** | `.gitlab-ci-example-multi-db.yml` | `cp .gitlab-ci-example-multi-db.yml .gitlab-ci.yml` |
90-
| **10-100 databases** | `.gitlab-ci-example-matrix.yml` | `cp .gitlab-ci-example-matrix.yml .gitlab-ci.yml` |
91-
| **100+ databases** | See SETUP_GUIDE.md | Dynamic child pipelines |
85+
| Databases | Template (in `usage-examples/`) | Command |
86+
|-----------|--------------------------------|--------|
87+
| **1 database** | `single-db-dev.gitlab-ci.yml` | `cp usage-examples/single-db-dev.gitlab-ci.yml .gitlab-ci.yml` |
88+
| **Schema-model workflow** | `schema-model.gitlab-ci.yml` | `cp usage-examples/schema-model.gitlab-ci.yml .gitlab-ci.yml` |
89+
| **Dev + Staging + Prod** | `staging-and-production.gitlab-ci.yml` | `cp usage-examples/staging-and-production.gitlab-ci.yml .gitlab-ci.yml` |
90+
| **2-10 databases** | `multi-database.gitlab-ci.yml` | `cp usage-examples/multi-database.gitlab-ci.yml .gitlab-ci.yml` |
91+
| **10-100 databases** | `matrix.gitlab-ci.yml` | `cp usage-examples/matrix.gitlab-ci.yml .gitlab-ci.yml` |
92+
| **100+ databases** | `dynamic-pipeline.gitlab-ci.yml` | `cp usage-examples/dynamic-pipeline.gitlab-ci.yml .gitlab-ci.yml` |
9293

9394
---
9495

Gitlab-Templatized/README.md

Lines changed: 62 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ Your Flyway project repo includes these templates via GitLab's cross-project `in
88

99
```
1010
.gitlab/ci/flyway.yml # Flyway job templates (.flyway_validate, .flyway_migrate, etc.)
11+
.gitlab/ci/dev.yml # Schema-model → migration generation templates (.flyway_generate_migrations, etc.)
1112
.gitlab/ci/generate.yml # Dynamic pipeline generation template (.generate_pipeline)
1213
scripts/generate_pipeline.py # Queries registry sproc → builds JDBCs → writes child pipeline YAML
1314
scripts/requirements.txt # Python dependencies (pymssql, PyYAML)
@@ -35,8 +36,8 @@ In your Flyway project's `.gitlab-ci.yml`:
3536

3637
```yaml
3738
include:
38-
- project: 'your-group/flyway-ci-templates' # path to THIS repo
39-
ref: 'v1.0.0' # pin to a tag
39+
- project: 'root/templatized-with-parser' # path to THIS repo
40+
ref: 'main' # pin to a tag or branch
4041
file: '/.gitlab/ci/flyway.yml'
4142
```
4243
@@ -97,14 +98,62 @@ migrate:custom:
9798
FLYWAY_OUT_OF_ORDER: "true"
9899
```
99100

101+
### dev.yml — Schema Model → Migration Generation
102+
103+
Generate versioned migration scripts from schema-model changes using `flyway diff model` and `flyway diff generate`. Includes a manual gate that creates a **merge request** so reviewers can inspect the generated SQL diff before merging.
104+
105+
| Template | Purpose | Notes |
106+
|----------|---------|-------|
107+
| `.flyway_generate_migrations` | `flyway diff model` + `flyway diff generate` | Auto-generates V*__*.sql from schema-model diffs |
108+
| `.flyway_commit_migrations` | Push branch + create merge request | Manual gate — opens MR for script review |
109+
110+
Additional CI/CD variables for schema-model workflows:
111+
112+
| Variable | Example | Required | Notes |
113+
|----------|---------|----------|-------|
114+
| `TARGET_DATABASE_JDBC` | `jdbc:sqlserver://host:1433;databaseName=mydb;encrypt=true` | Yes | Dev database (diff source) |
115+
| `SHADOW_DATABASE_JDBC` | `jdbc:sqlserver://host:1433;databaseName=mydb_shadow;encrypt=true` | Yes | Empty DB Flyway rebuilds from migrations |
116+
| `GIT_PUSH_TOKEN` | `glpat-xxxxxxxxxxxx` | Yes* | PAT with `api` scope — see [Git Push Authentication](#git-push-authentication) |
117+
| `GITLAB_EXTERNAL_URL` | `http://localhost:8080` | No | Browser-reachable GitLab URL for MR links (defaults to `CI_SERVER_URL`; set when behind port mapping) |
118+
| `MR_TARGET_BRANCH` | `main` | No | Branch the MR targets (default: `main`) |
119+
120+
\* Required unless CI job token permissions are enabled (Option B below).
121+
122+
See [`usage-examples/schema-model.gitlab-ci.yml`](usage-examples/schema-model.gitlab-ci.yml) for a complete example.
123+
124+
#### Git Push Authentication
125+
126+
The `.flyway_commit_migrations` job pushes a branch and creates a merge request via the GitLab API. This requires write access. Two options:
127+
128+
**Option A — Personal Access Token (recommended for self-hosted GitLab)**
129+
130+
1. Go to your GitLab instance → **Profile → Access Tokens**
131+
2. Create a token with the **`api`** scope (covers both git push and MR creation)
132+
3. Add it as a **CI/CD variable** in your project:
133+
- **Key:** `GIT_PUSH_TOKEN`
134+
- **Value:** the token
135+
- **Protected:** ✓ **Masked:** ✓
136+
137+
This is the simplest approach and works on all GitLab versions.
138+
139+
**Option B — CI Job Token permissions (GitLab 15.9+, GitLab.com)**
140+
141+
1. Go to your project → **Settings → CI/CD → Token permissions**
142+
2. Enable **"Allow CI job token to push to this project"**
143+
3. Under **Settings → Repository → Protected branches**, ensure the job token role can push
144+
145+
With this option, no `GIT_PUSH_TOKEN` variable is needed — the pipeline uses the built-in `CI_JOB_TOKEN`. However, not all GitLab versions support this, and some self-hosted instances restrict job token API access.
146+
147+
> **Which should I use?** Option A is more reliable, especially for local or self-hosted GitLab. Option B is cleaner for GitLab.com since it requires no extra secrets.
148+
100149
### generate.yml — Dynamic Pipeline (100+ Databases)
101150

102151
For large-scale deployments driven by a SQL Server registry database. A single Python script (`scripts/generate_pipeline.py`) calls `dbo.usp_GetFlywayTargets`, builds JDBC URLs from the `dbserver` and `db` columns, and writes a child pipeline with one migrate job per target.
103152

104153
```yaml
105154
include:
106-
- project: 'your-group/flyway-ci-templates'
107-
ref: 'v1.0.0'
155+
- project: 'root/templatized-with-parser'
156+
ref: 'main'
108157
file:
109158
- '/.gitlab/ci/flyway.yml'
110159
- '/.gitlab/ci/generate.yml'
@@ -175,11 +224,17 @@ See [`usage-examples/`](usage-examples/) for complete `.gitlab-ci.yml` files:
175224
| File | Scenario |
176225
|------|----------|
177226
| `single-db-dev.gitlab-ci.yml` | Single database, dev branch |
227+
| `schema-model.gitlab-ci.yml` | Schema-model → generated migrations with approval gate |
228+
| `schema-model-dynamic.gitlab-ci.yml` | Schema-model + registry-driven dynamic deploy to all targets |
178229
| `staging-and-production.gitlab-ci.yml` | Staging → production with manual approval |
179230
| `multi-database.gitlab-ci.yml` | 2–10 databases with explicit jobs |
180-
181-
For parallel matrix (10–100 DBs), see `.gitlab-ci-example-matrix.yml`.
182-
For registry-driven dynamic pipelines (100+ DBs), see `.gitlab-ci-example-all-regions.yml`.
231+
| `variable-driven.gitlab-ci.yml` | Variable-driven, no registry or Python |
232+
| `matrix.gitlab-ci.yml` | Parallel matrix for 10–100 databases |
233+
| `dynamic-pipeline.gitlab-ci.yml` | Registry-driven dynamic child pipeline |
234+
| `all-regions.gitlab-ci.yml` | Dynamic pipeline — all regions |
235+
| `region-london.gitlab-ci.yml` | Dynamic pipeline — London only |
236+
| `region-new-york.gitlab-ci.yml` | Dynamic pipeline — New York only |
237+
| `region-tokyo.gitlab-ci.yml` | Dynamic pipeline — Tokyo only |
183238
184239
## Environment-Scoped Variables
185240

Gitlab-Templatized/SETUP_GUIDE.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -156,7 +156,7 @@ jdbc:oracle:thin:@//hostname:1521/service_name
156156
Copy the development example to create your active pipeline:
157157

158158
```bash
159-
cp .gitlab-ci-example-dev.yml .gitlab-ci.yml
159+
cp usage-examples/single-db-dev.gitlab-ci.yml .gitlab-ci.yml
160160
```
161161

162162
### Step 2: Review and Customize (Optional)
@@ -244,7 +244,7 @@ git push origin dev
244244
Use the multi-database example:
245245

246246
```bash
247-
cp .gitlab-ci-example-multi-db.yml .gitlab-ci.yml
247+
cp usage-examples/multi-database.gitlab-ci.yml .gitlab-ci.yml
248248
```
249249

250250
Edit `.gitlab-ci.yml` to match your databases:

Gitlab-Templatized/startup-services.ps1

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,26 @@ if ($runnerConfig -notmatch '\[\[runners\]\]') {
159159
}
160160
}
161161

162+
# ---------------------------------------------------------------------------
163+
# Configure Git SSH to use port 2222 for localhost (idempotent)
164+
# ---------------------------------------------------------------------------
165+
$sshDir = Join-Path $env:USERPROFILE ".ssh"
166+
if (-not (Test-Path $sshDir)) { New-Item -ItemType Directory -Path $sshDir -Force | Out-Null }
167+
$sshConfig = Join-Path $sshDir "config"
168+
$hostBlock = @"
169+
# GitLab local (added by startup-services.ps1)
170+
Host localhost
171+
Port 2222
172+
StrictHostKeyChecking no
173+
UserKnownHostsFile /dev/null
174+
"@
175+
if ((Test-Path $sshConfig) -and (Get-Content $sshConfig -Raw) -match 'GitLab local') {
176+
Write-Host "SSH config for localhost:2222 already exists." -ForegroundColor Gray
177+
} else {
178+
Add-Content -Path $sshConfig -Value "`n$hostBlock`n"
179+
Write-Host "Added SSH config: localhost → port 2222" -ForegroundColor Green
180+
}
181+
162182
# ---------------------------------------------------------------------------
163183
# Summary
164184
# ---------------------------------------------------------------------------

0 commit comments

Comments
 (0)