Skip to content

Commit e8a2ae4

Browse files
committed
Add GitHub workflow to validate migrations in the current branch to its base branch
1 parent 6c0a8e3 commit e8a2ae4

File tree

2 files changed

+221
-53
lines changed

2 files changed

+221
-53
lines changed

.github/workflows/database.yaml

Lines changed: 221 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,221 @@
1+
name: Validate database components
2+
3+
# Runs tests to validate database components. It's only triggered for new
4+
# commits. If any of the jobs fail for new commits they're not allowed to be
5+
# merged into the default branch.
6+
7+
on: [pull_request, push]
8+
9+
env:
10+
GITHUB_TOKEN: ${{ github.token }}
11+
12+
jobs:
13+
schema:
14+
name: Schema
15+
16+
runs-on: ubuntu-latest
17+
services:
18+
database:
19+
image: postgres:16-alpine
20+
ports:
21+
- "5432:5432"
22+
env:
23+
POSTGRES_DB: dirigent
24+
POSTGRES_PASSWORD: "!ChangeMe!"
25+
POSTGRES_USER: dirigent
26+
27+
steps:
28+
- name: Checkout code
29+
uses: actions/checkout@v4
30+
31+
- name: Install PHP with extensions
32+
uses: shivammathur/setup-php@v2
33+
with:
34+
php-version: 8.3
35+
tools: composer:v2
36+
37+
- name: Set Composer cache directory
38+
id: composer-cache
39+
run: echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT
40+
41+
- name: Cache Composer output
42+
uses: actions/cache@v4
43+
with:
44+
path: ${{ steps.composer-cache.outputs.dir }}
45+
key: ${{ runner.os }}-composer-${{ hashFiles('composer.lock') }}
46+
restore-keys: ${{ runner.os }}-composer-
47+
48+
- name: Install Composer dependencies
49+
run: composer install --ansi --no-interaction --no-progress
50+
51+
- name: Generate encryption keys
52+
run: bin/console encryption:generate-keys
53+
54+
- name: Validate mapping
55+
run: bin/console doctrine:schema:validate --skip-sync -vvv --ansi --no-interaction
56+
57+
- name: Execute migrations
58+
run: bin/console doctrine:migrations:migrate -vvv --ansi --no-interaction
59+
60+
- name: Validate schema
61+
run: bin/console doctrine:schema:validate --skip-mapping --skip-property-types -vvv --ansi --no-interaction
62+
63+
- name: Load fixtures
64+
run: bin/console doctrine:fixtures:load -vvv --ansi --no-interaction
65+
66+
migrations:
67+
name: Migrations
68+
69+
# Verifies that new database migrations are compatible with existing data.
70+
# When migration files changed, this workflow:
71+
# 1. Checks out the base branch and applies all existing migrations
72+
# 2. Loads fixtures to simulate a database with real-world data
73+
# 3. Switches to the current branch and applies the new migrations on top
74+
# 4. Reverts the current migrations to verify the down() methods work
75+
# 5. Compares the schema and data against the baseline to ensure the revert is clean
76+
77+
runs-on: ubuntu-latest
78+
services:
79+
database:
80+
image: postgres:16-alpine
81+
ports:
82+
- "5432:5432"
83+
env:
84+
POSTGRES_DB: dirigent
85+
POSTGRES_PASSWORD: "!ChangeMe!"
86+
POSTGRES_USER: dirigent
87+
88+
steps:
89+
- name: Checkout code
90+
uses: actions/checkout@v4
91+
with:
92+
fetch-depth: 0
93+
94+
- name: Check for migration changes
95+
id: check
96+
run: |
97+
if [ -n "${{ github.base_ref }}" ]; then
98+
BASE_REF="${{ github.base_ref }}"
99+
else
100+
git fetch --all --quiet
101+
BEST_BRANCH="main"
102+
BEST_DISTANCE=$(git rev-list --count origin/main..HEAD 2>/dev/null || echo 999999)
103+
for branch in $(git branch -r | grep -oE 'origin/[0-9]+\.x' | sed 's|origin/||'); do
104+
DISTANCE=$(git rev-list --count origin/$branch..HEAD 2>/dev/null || echo 999999)
105+
if [ "$DISTANCE" -lt "$BEST_DISTANCE" ]; then
106+
BEST_DISTANCE=$DISTANCE
107+
BEST_BRANCH=$branch
108+
fi
109+
done
110+
BASE_REF=$BEST_BRANCH
111+
fi
112+
echo "base_ref=$BASE_REF" >> $GITHUB_OUTPUT
113+
git fetch origin $BASE_REF
114+
CHANGED=$(git diff --name-only origin/$BASE_REF...HEAD -- migrations/)
115+
if [ -n "$CHANGED" ]; then
116+
echo "has_changes=true" >> $GITHUB_OUTPUT
117+
echo "New migration files:"
118+
echo "$CHANGED"
119+
else
120+
echo "has_changes=false" >> $GITHUB_OUTPUT
121+
echo "No migration changes detected, skipping."
122+
fi
123+
124+
- name: Install PHP with extensions
125+
if: steps.check.outputs.has_changes == 'true'
126+
uses: shivammathur/setup-php@v2
127+
with:
128+
php-version: 8.3
129+
tools: composer:v2
130+
131+
- name: Set Composer cache directory
132+
if: steps.check.outputs.has_changes == 'true'
133+
id: composer-cache
134+
run: echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT
135+
136+
- name: Cache Composer output
137+
if: steps.check.outputs.has_changes == 'true'
138+
uses: actions/cache@v4
139+
with:
140+
path: ${{ steps.composer-cache.outputs.dir }}
141+
key: ${{ runner.os }}-composer-${{ hashFiles('composer.lock') }}
142+
restore-keys: ${{ runner.os }}-composer-
143+
144+
- name: Checkout base branch
145+
if: steps.check.outputs.has_changes == 'true'
146+
run: git checkout origin/${{ steps.check.outputs.base_ref }}
147+
148+
- name: Install Composer dependencies for base branch
149+
if: steps.check.outputs.has_changes == 'true'
150+
run: composer install --ansi --no-interaction --no-progress
151+
152+
- name: Generate encryption keys
153+
if: steps.check.outputs.has_changes == 'true'
154+
run: bin/console encryption:generate-keys
155+
156+
- name: Execute migrations from base branch
157+
if: steps.check.outputs.has_changes == 'true'
158+
run: bin/console doctrine:migrations:migrate -vvv --ansi --no-interaction
159+
160+
- name: Capture base branch migration version
161+
id: base-version
162+
if: steps.check.outputs.has_changes == 'true'
163+
run: |
164+
VERSION=$(bin/console doctrine:migrations:latest --no-ansi | awk '{print $1}')
165+
echo "version=$VERSION" >> $GITHUB_OUTPUT
166+
167+
- name: Load fixtures
168+
if: steps.check.outputs.has_changes == 'true'
169+
run: bin/console doctrine:fixtures:load -vvv --ansi --no-interaction
170+
171+
- name: Snapshot baseline schema and data
172+
if: steps.check.outputs.has_changes == 'true'
173+
env:
174+
PGPASSWORD: "!ChangeMe!"
175+
run: |
176+
pg_dump -U dirigent -h localhost dirigent \
177+
--schema-only --no-comments --no-privileges --no-owner \
178+
| grep -vE '^(SET |SELECT |$)' \
179+
> /tmp/schema_baseline.sql
180+
pg_dump -U dirigent -h localhost dirigent \
181+
--data-only --no-comments --no-privileges --no-owner \
182+
| grep -vE '^(SET |SELECT |$)' \
183+
> /tmp/data_baseline.sql
184+
185+
- name: Checkout current branch
186+
if: steps.check.outputs.has_changes == 'true'
187+
run: git checkout ${{ github.sha }}
188+
189+
- name: Install Composer dependencies for current branch
190+
if: steps.check.outputs.has_changes == 'true'
191+
run: composer install --ansi --no-interaction --no-progress
192+
193+
- name: Execute new migrations
194+
if: steps.check.outputs.has_changes == 'true'
195+
run: bin/console doctrine:migrations:migrate -vvv --ansi --no-interaction
196+
197+
- name: Revert new migrations
198+
if: steps.check.outputs.has_changes == 'true'
199+
run: bin/console doctrine:migrations:migrate ${{ steps.base-version.outputs.version }} -vvv --ansi --no-interaction
200+
201+
- name: Snapshot reverted schema and data
202+
if: steps.check.outputs.has_changes == 'true'
203+
env:
204+
PGPASSWORD: "!ChangeMe!"
205+
run: |
206+
pg_dump -U dirigent -h localhost dirigent \
207+
--schema-only --no-comments --no-privileges --no-owner \
208+
| grep -vE '^(SET |SELECT |$)' \
209+
> /tmp/schema_reverted.sql
210+
pg_dump -U dirigent -h localhost dirigent \
211+
--data-only --no-comments --no-privileges --no-owner \
212+
| grep -vE '^(SET |SELECT |$)' \
213+
> /tmp/data_reverted.sql
214+
215+
- name: Verify schema is unchanged after revert
216+
if: steps.check.outputs.has_changes == 'true'
217+
run: diff /tmp/schema_baseline.sql /tmp/schema_reverted.sql
218+
219+
- name: Verify data is unchanged after revert
220+
if: steps.check.outputs.has_changes == 'true'
221+
run: diff /tmp/data_baseline.sql /tmp/data_reverted.sql

.github/workflows/tests.yaml

Lines changed: 0 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -11,59 +11,6 @@ env:
1111
PHPUNIT_FLAGS: ""
1212

1313
jobs:
14-
database:
15-
name: Validate database components
16-
17-
runs-on: ubuntu-latest
18-
services:
19-
database:
20-
image: postgres:16-alpine
21-
ports:
22-
- "5432:5432"
23-
env:
24-
POSTGRES_DB: dirigent
25-
POSTGRES_PASSWORD: "!ChangeMe!"
26-
POSTGRES_USER: dirigent
27-
28-
steps:
29-
- name: Checkout code
30-
uses: actions/checkout@v4
31-
32-
- name: Install PHP with extensions
33-
uses: shivammathur/setup-php@v2
34-
with:
35-
php-version: 8.3
36-
tools: composer:v2
37-
38-
- name: Set Composer cache directory
39-
id: composer-cache
40-
run: echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT
41-
42-
- name: Cache Composer output
43-
uses: actions/cache@v4
44-
with:
45-
path: ${{ steps.composer-cache.outputs.dir }}
46-
key: ${{ runner.os }}-composer-${{ hashFiles('composer.lock') }}
47-
restore-keys: ${{ runner.os }}-composer-
48-
49-
- name: Install Composer dependencies
50-
run: composer install --ansi --no-interaction --no-progress
51-
52-
- name: Generate encryption keys
53-
run: bin/console encryption:generate-keys
54-
55-
- name: Validate mapping
56-
run: bin/console doctrine:schema:validate --skip-sync -vvv --ansi --no-interaction
57-
58-
- name: Execute migrations
59-
run: bin/console doctrine:migrations:migrate -vvv --ansi --no-interaction
60-
61-
- name: Validate schema
62-
run: bin/console doctrine:schema:validate --skip-mapping --skip-property-types -vvv --ansi --no-interaction
63-
64-
- name: Load fixtures
65-
run: bin/console doctrine:fixtures:load -vvv --ansi --no-interaction
66-
6714
phpunit:
6815
name: "PHPUnit (PHP ${{ matrix.php-version }})"
6916

0 commit comments

Comments
 (0)