Skip to content

Commit 5c9b359

Browse files
build pipeline dynamically based on list of dbs
1 parent c42dea8 commit 5c9b359

7 files changed

Lines changed: 304 additions & 5 deletions

File tree

Gitlab-Templatized/.env.example

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
# Copy this file to .env and fill in your values.
44

55
GITLAB_RUNNER_REGISTRATION_TOKEN=your_runner_registration_token
6-
GITLAB_URL=http://172.17.0.2
6+
GITLAB_URL=http://172.30.0.2
77
GITLAB_EXTERNAL_URL=http://localhost:8080
88

99
# SQL Server (SQLEXPRESS) — used by Flyway CI/CD variables
@@ -13,6 +13,15 @@ SQL_PORT=1433
1313
SQL_USER=sa
1414
SQL_PASSWORD=your_sql_password
1515

16+
# Flyway Pipeline Generator - registry connection
17+
# These map to the same SQL Server used for Flyway targets.
18+
# In GitLab CI/CD, set these as project/group CI/CD variables instead.
19+
REGISTRY_SERVER=host.docker.internal
20+
REGISTRY_PORT=1433
21+
REGISTRY_USER=sa
22+
REGISTRY_PASSWORD=your_sql_password
23+
REGISTRY_DATABASE=flyway_registry
24+
1625
# Flyway Enterprise licensing (Personal Access Token)
1726
# See: https://documentation.red-gate.com/fd/licensing-164167730.html#Licensing-AccessToken(PAT)
1827
FLYWAY_EMAIL=your_email@example.com

Gitlab-Templatized/.gitignore

Lines changed: 56 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,57 @@
1+
# .gitignore for GitLab Flyway Pipeline Project
2+
3+
# Active pipeline file (users create this by copying an example)
4+
# Uncomment the line below if you want to keep .gitlab-ci.yml local
5+
# and not commit it to the repository
6+
#.gitlab-ci.yml
7+
8+
# User-specific SQL migrations (optional - comment out if you want to track them)
9+
# sql/*.sql
10+
11+
# Flyway metadata
12+
flyway.conf
13+
14+
# Environment files (if used)
115
.env
2-
!.env.example
16+
.env.local
17+
!.env.example
18+
19+
# IDE and Editor files
20+
.vscode/
21+
.idea/
22+
*.swp
23+
*.swo
24+
*~
25+
.DS_Store
26+
27+
# OS specific files
28+
Thumbs.db
29+
Desktop.ini
30+
31+
# Build artifacts
32+
dist/
33+
build/
34+
*.log
35+
36+
# Temporary files
37+
tmp/
38+
temp/
39+
*.tmp
40+
41+
# Personal notes (users might create these)
42+
notes.md
43+
TODO.md
44+
scratch.txt
45+
46+
# Backup files
47+
*.bak
48+
*.backup
49+
50+
# GitLab Runner specific
51+
.gitlab-runner/
52+
53+
# Docker volumes (if testing locally)
54+
gitlab-config/
55+
gitlab-logs/
56+
gitlab-data/
57+
rancher-data/

Gitlab-Templatized/.gitlab-ci.yml

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
# =============================================================================
2+
# Dynamic Pipeline — Registry-Driven Flyway Deployments
3+
# =============================================================================
4+
# Uses generate_pipeline.py to query a SQL Server registry database,
5+
# discover target databases, and produce a child pipeline with one
6+
# Flyway migrate job per target.
7+
#
8+
# CI/CD Variables required (Settings → CI/CD → Variables):
9+
# REGISTRY_SERVER = SQL Server hostname for the registry DB
10+
# REGISTRY_USER = Login for the registry DB
11+
# REGISTRY_PASSWORD = *** (Protected + Masked)
12+
# TARGET_DATABASE_USER = Login for target databases
13+
# TARGET_DATABASE_PASSWORD = *** (Protected + Masked)
14+
# FLYWAY_EMAIL = your@email.com
15+
# FLYWAY_TOKEN = *** (Protected + Masked)
16+
#
17+
# Optional CI/CD Variables:
18+
# REGISTRY_DATABASE = flyway_registry (default)
19+
# REGISTRY_PORT = 1433 (default)
20+
# JDBC_PORT = 1433 (default)
21+
# FILTER_LOCATION = all (default — or e.g. "London")
22+
# INCLUDE_REPLICAS = false (default)
23+
# FLYWAY_LOCATIONS = filesystem:./migrations (default)
24+
# RUNNER_TAG_MAP = JSON map of location → runner tag overrides
25+
#
26+
# Consumer repo structure:
27+
# my-flyway-project/
28+
# ├── migrations/
29+
# │ ├── V1__create_schema.sql
30+
# │ └── V2__add_users_table.sql
31+
# └── .gitlab-ci.yml ← this file
32+
#
33+
# Optional CI/CD Variables for template repo override:
34+
# TEMPLATE_PROJECT = root/templatized-with-parser (default)
35+
# TEMPLATE_REF = main (default)
36+
# =============================================================================
37+
38+
stages:
39+
- generate
40+
- deploy
41+
42+
variables:
43+
TEMPLATE_PROJECT: "root/templatized-with-parser"
44+
TEMPLATE_REF: "main"
45+
46+
# ---------------------------------------------------------------------------
47+
# Stage 1: Generate the child pipeline YAML from the registry
48+
# ---------------------------------------------------------------------------
49+
generate-pipeline:
50+
stage: generate
51+
image: python:3.12-slim
52+
script:
53+
- apt-get update -qq && apt-get install -y -qq git > /dev/null
54+
- git clone --depth 1 --branch "$TEMPLATE_REF"
55+
"http://gitlab-ci-token:${CI_JOB_TOKEN}@172.30.0.2/${TEMPLATE_PROJECT}.git"
56+
/tmp/templates
57+
- pip install --quiet -r /tmp/templates/scripts/requirements.txt
58+
- python /tmp/templates/scripts/generate_pipeline.py
59+
artifacts:
60+
paths:
61+
- dynamic-pipeline.yml
62+
expire_in: 1 hour
63+
rules:
64+
- if: $CI_COMMIT_BRANCH == "main"
65+
66+
# ---------------------------------------------------------------------------
67+
# Stage 2: Trigger the generated child pipeline
68+
# ---------------------------------------------------------------------------
69+
deploy-targets:
70+
stage: deploy
71+
trigger:
72+
include:
73+
- artifact: dynamic-pipeline.yml
74+
job: generate-pipeline
75+
strategy: depend
76+
rules:
77+
- if: $CI_COMMIT_BRANCH == "main"

Gitlab-Templatized/README.md

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,9 +142,32 @@ Optional `generate_pipeline.py` settings:
142142
| `FILTER_LOCATION` | `all` | Region filter (`London`, `New York`, `Tokyo`) |
143143
| `INCLUDE_REPLICAS` | `false` | Include replicated databases |
144144
| `JDBC_PORT` | `1433` | Port in generated JDBC URLs |
145+
| `RUNNER_TAG_DEFAULT` | _(empty)_ | Single runner tag for all generated jobs (overrides per-location tags) |
145146
| `TEMPLATE_PROJECT` | _(empty)_ | Templates repo path for child pipeline include |
146147
| `TEMPLATE_REF` | `main` | Git ref for cross-project child pipeline include |
147148

149+
## Runner Tags
150+
151+
Generated pipeline jobs are assigned runner tags based on each target's `location`. By default, a location like `Production` produces the tag `runner-production`.
152+
153+
**Override all tags with one value** — set `RUNNER_TAG_DEFAULT` as a CI/CD variable:
154+
155+
```
156+
RUNNER_TAG_DEFAULT = local-runner
157+
```
158+
159+
Every generated job will use `local-runner` instead of per-location tags.
160+
161+
**Per-location overrides** — set `RUNNER_TAG_MAP` as a JSON CI/CD variable:
162+
163+
```
164+
RUNNER_TAG_MAP = {"Production": "prod-runner", "Development": "dev-runner"}
165+
```
166+
167+
Or use individual variables: `RUNNER_TAG_LONDON`, `RUNNER_TAG_NEW_YORK`, `RUNNER_TAG_TOKYO`.
168+
169+
**Priority order:** `RUNNER_TAG_DEFAULT` → `RUNNER_TAG_MAP` → individual `RUNNER_TAG_*` → auto-generated from location name.
170+
148171
## Usage Examples
149172
150173
See [`usage-examples/`](usage-examples/) for complete `.gitlab-ci.yml` files:

Gitlab-Templatized/scripts/generate_pipeline.py

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@
3939
TEMPLATE_PROJECT Templates repo path for child pipeline include
4040
TEMPLATE_REF Templates repo ref for child pipeline include
4141
OUTPUT_FILE Output YAML filename (default: dynamic-pipeline.yml)
42+
RUNNER_TAG_DEFAULT Single runner tag for all jobs (overrides per-location tags)
4243
RUNNER_TAG_MAP JSON map of location->runner tag overrides
4344
RUNNER_TAG_LONDON Runner tag for London (default: runner-london)
4445
RUNNER_TAG_NEW_YORK Runner tag for New York (default: runner-new-york)
@@ -53,6 +54,37 @@
5354
import yaml
5455

5556

57+
# ---------------------------------------------------------------------------
58+
# Load .env file if present (local development)
59+
# In GitLab CI/CD, variables come from project/group CI/CD settings natively.
60+
# ---------------------------------------------------------------------------
61+
62+
def load_dotenv(path=None):
63+
"""Load a .env file into os.environ without overwriting existing values."""
64+
if path is None:
65+
# Look for .env in the repo root (one level up from scripts/)
66+
path = os.path.join(os.path.dirname(os.path.abspath(__file__)), os.pardir, ".env")
67+
path = os.path.normpath(path)
68+
if not os.path.isfile(path):
69+
return
70+
with open(path) as f:
71+
for line in f:
72+
line = line.strip()
73+
if not line or line.startswith("#"):
74+
continue
75+
if "=" not in line:
76+
continue
77+
key, _, value = line.partition("=")
78+
key = key.strip()
79+
value = value.strip()
80+
# Don't overwrite — GitLab CI/CD variables take precedence
81+
if key not in os.environ:
82+
os.environ[key] = value
83+
84+
85+
load_dotenv()
86+
87+
5688
# ---------------------------------------------------------------------------
5789
# Configuration from environment
5890
# ---------------------------------------------------------------------------
@@ -75,7 +107,8 @@
75107
TEMPLATE_PROJECT = os.environ.get("TEMPLATE_PROJECT", "")
76108
TEMPLATE_REF = os.environ.get("TEMPLATE_REF", "main")
77109

78-
# Runner tags — JSON map takes priority, then individual env vars, then defaults
110+
# Runner tags — default overrides all, then JSON map, then individual env vars, then fallbacks
111+
RUNNER_TAG_DEFAULT = os.environ.get("RUNNER_TAG_DEFAULT", "")
79112
_runner_tag_map_raw = os.environ.get("RUNNER_TAG_MAP", "")
80113
if _runner_tag_map_raw:
81114
RUNNER_TAGS = json.loads(_runner_tag_map_raw)
@@ -178,6 +211,8 @@ def safe_job_name(value):
178211

179212

180213
def runner_tag_for(location):
214+
if RUNNER_TAG_DEFAULT:
215+
return RUNNER_TAG_DEFAULT
181216
return RUNNER_TAGS.get(location, f"runner-{location.lower().replace(' ', '-')}")
182217

183218

Gitlab-Templatized/startup-services.ps1

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,17 @@ if (Test-Path $envFile) {
1414
Write-Host "Starting services..." -ForegroundColor Yellow
1515
Write-Host ""
1616

17+
# Create a dedicated Docker network with a fixed subnet (idempotent)
18+
$networkName = "gitlab-net"
19+
$subnet = "172.30.0.0/24"
20+
$networkExists = docker network ls --filter "name=^${networkName}$" --format '{{.Name}}' 2>&1
21+
if ($networkExists -ne $networkName) {
22+
Write-Host "Creating Docker network '$networkName' (subnet $subnet)..." -ForegroundColor Cyan
23+
docker network create --subnet $subnet $networkName | Out-Null
24+
} else {
25+
Write-Host "Docker network '$networkName' already exists." -ForegroundColor Gray
26+
}
27+
1728
# Ensure Rancher Desktop is running
1829
if (-not (Get-Process "Rancher Desktop" -ErrorAction SilentlyContinue)) {
1930
Write-Host "Rancher Desktop is not running. Starting it..." -ForegroundColor Yellow
@@ -38,6 +49,7 @@ if (-not (Get-Process "Rancher Desktop" -ErrorAction SilentlyContinue)) {
3849
Write-Host "Starting Rancher..." -ForegroundColor Cyan
3950
docker run -d --restart=unless-stopped `
4051
--name rancher `
52+
--network gitlab-net --ip 172.30.0.10 `
4153
-p 80:80 -p 443:443 `
4254
--privileged `
4355
rancher/rancher:latest
@@ -46,6 +58,7 @@ docker run -d --restart=unless-stopped `
4658
Write-Host "Starting GitLab..." -ForegroundColor Cyan
4759
docker run -d --restart=unless-stopped `
4860
--name gitlab `
61+
--network gitlab-net --ip 172.30.0.2 `
4962
-p 8080:80 -p 8443:443 -p 2222:22 `
5063
-v gitlab-config:/etc/gitlab `
5164
-v gitlab-logs:/var/log/gitlab `
@@ -56,6 +69,7 @@ docker run -d --restart=unless-stopped `
5669
Write-Host "Starting GitLab Runner..." -ForegroundColor Cyan
5770
docker run -d --restart=unless-stopped `
5871
--name gitlab-runner `
72+
--network gitlab-net --ip 172.30.0.3 `
5973
-v gitlab-runner-config:/etc/gitlab-runner `
6074
-v //var/run/docker.sock:/var/run/docker.sock `
6175
gitlab/gitlab-runner:latest
@@ -74,10 +88,13 @@ if ($runnerConfig -notmatch '\[\[runners\]\]') {
7488
--executor "docker" `
7589
--docker-image "redgate/flyway:12-enterprise-alpine" `
7690
--description "local-runner" `
91+
--tag-list "local-runner" `
7792
--run-untagged="true" `
78-
--locked="false"
93+
--locked="false" `
94+
--docker-network-mode "gitlab-net" `
95+
--clone-url $url
7996
} else {
80-
Write-Host "WARNING: GITLAB_RUNNER_REGISTRATION_TOKEN or GITLAB_URL not set in .env skipping registration." -ForegroundColor Yellow
97+
Write-Host "WARNING: GITLAB_RUNNER_REGISTRATION_TOKEN or GITLAB_URL not set in .env - skipping registration." -ForegroundColor Yellow
8198
}
8299
}
83100

0 commit comments

Comments
 (0)