Skip to content

Commit db95995

Browse files
feat: pg-diff md report generation
1 parent 12ef2b7 commit db95995

File tree

5 files changed

+164
-0
lines changed

5 files changed

+164
-0
lines changed

.github/workflows/ci-cd.yml

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
name: CI/CD
2+
3+
on: [push]
4+
5+
env:
6+
REGISTRY: ghcr.io
7+
IMAGE_NAME_CACHE: ghcr.io/${{ github.repository }}:_buildcache
8+
9+
jobs:
10+
build-image:
11+
runs-on: ubuntu-20.04
12+
steps:
13+
- name: Checkout repository
14+
uses: actions/checkout@v3
15+
16+
- name: Set up Docker Buildx
17+
uses: docker/setup-buildx-action@v2
18+
19+
- name: Login to GitHub Container Registry
20+
uses: docker/login-action@v2
21+
with:
22+
registry: ${{ env.REGISTRY }}
23+
username: ${{ github.actor }}
24+
password: ${{ secrets.GITHUB_TOKEN }}
25+
26+
- name: Build Docker image
27+
uses: docker/build-push-action@v4
28+
with:
29+
tags: ghcr.io/${{ github.repository }}:latest
30+
push: true
31+
# platforms: linux/arm64,linux/amd64
32+
cache-from: type=registry,ref=${{ env.IMAGE_NAME_CACHE }}
33+
cache-to: type=registry,ref=${{ env.IMAGE_NAME_CACHE }},mode=max

Dockerfile

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
FROM python:3.12.9-slim
2+
3+
WORKDIR /app
4+
5+
# Download liquibase package and extract to WORKDIR
6+
RUN apt-get update \
7+
&& apt-get install -y --no-install-recommends \
8+
curl=7.* \
9+
unzip=6.* \
10+
openjdk-17-jre-headless \
11+
&& curl -o liquibase.zip -SL https://github.com/liquibase/liquibase/releases/download/v4.31.0/liquibase-4.31.0.zip \
12+
&& unzip liquibase.zip -d liquibase \
13+
&& rm liquibase.zip
14+
15+
COPY pg_diff.sh .
16+
COPY pg_diff.py .
17+
18+
RUN ["chmod", "+x", "pg_diff.sh"]
19+
20+
CMD "./pg_diff.sh"

pg_diff.py

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
import enum
2+
import re
3+
import sys
4+
5+
6+
class DBChanges(enum.StrEnum):
7+
ColumnAdded = "column added"
8+
ColumnDeleted = "column deleted"
9+
ColumnChanged = "column changed"
10+
TableAdded = "table added"
11+
TableDeleted = "table deleted"
12+
TableChanged = "table changed"
13+
14+
15+
TOC = {
16+
"Missing Column(s)": DBChanges.ColumnAdded,
17+
"Unexpected Column(s)": DBChanges.ColumnDeleted,
18+
"Changed Column(s)": DBChanges.ColumnChanged,
19+
"Missing Table(s)": DBChanges.TableAdded,
20+
"Unexpected Table(s)": DBChanges.TableDeleted,
21+
"Changed Table(s)": DBChanges.TableChanged,
22+
}
23+
24+
HEADING_PATTERN = r"(\w+.\w+\(s\)):"
25+
COLUMN_PATTERN = r"^\s{5}public.(\w+).(\w+)"
26+
TABLE_PATTERN = r"^\s{5}(\w+)"
27+
28+
29+
current_type = None
30+
columns_changes = {}
31+
tables_changes = {}
32+
33+
for line in sys.stdin.readlines():
34+
heading = re.match(HEADING_PATTERN, line)
35+
if heading:
36+
current_type = TOC.get(heading.group(1))
37+
elif current_type in {
38+
DBChanges.ColumnAdded,
39+
DBChanges.ColumnDeleted,
40+
DBChanges.ColumnChanged,
41+
}:
42+
column_name = re.match(COLUMN_PATTERN, line)
43+
columns_changes.setdefault(current_type, {}).setdefault(
44+
column_name.group(1),
45+
[],
46+
).append(column_name.group(2))
47+
else:
48+
table_name = re.match(TABLE_PATTERN, line)
49+
if table_name:
50+
tables_changes.setdefault(
51+
current_type,
52+
[],
53+
).append(table_name.group(1))
54+
55+
sys.stdout.write("*" * 100 + "\n")
56+
sys.stdout.write(" " * 42 + "DB changes report\n")
57+
sys.stdout.write("*" * 100 + "\n\n")
58+
59+
ignore_tables = set()
60+
for change_type, tables in tables_changes.items():
61+
ignore_tables.update(tables)
62+
sys.stdout.writelines(
63+
[
64+
"- {0} `{1}`\n".format(
65+
change_type.value,
66+
table,
67+
)
68+
for table in tables
69+
],
70+
)
71+
72+
for change_type, tables in columns_changes.items():
73+
for table, columns in tables.items():
74+
if table in ignore_tables:
75+
continue
76+
77+
sys.stdout.writelines(
78+
[
79+
"- {0} `{1}` table `{2}`\n".format(
80+
change_type.value,
81+
column,
82+
table,
83+
)
84+
for column in columns
85+
],
86+
)

pg_diff.sh

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
POSTGRES_SCHEMA=public
2+
3+
./liquibase/liquibase --show-banner=false \
4+
--url=$POSTGRES_TARGET_DB \
5+
--diffTypes=tables,columns \
6+
--defaultSchemaName=$POSTGRES_SCHEMA \
7+
diff \
8+
--referenceUrl=$POSTGRES_CURRENT_DB \
9+
--schemas=$POSTGRES_SCHEMA \
10+
| python pg_diff.py

readme.md

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
# Postgres DB diff report
2+
3+
## ENV variables
4+
5+
- `POSTGRES_TARGET_DB` - most updated DB state (ex: development DB)
6+
- `POSTGRES_CURRENT_DB` - outdated DB state (ex: PROD DB before release)
7+
8+
## Get report
9+
10+
```bash
11+
docker run --rm -it \
12+
-e "POSTGRES_TARGET_DB=jdbc:postgresql://postgres:5432/db-name?user=pg_user&password=secret" \
13+
-e "POSTGRES_CURRENT_DB=jdbc:postgresql://postgres:5432/db-name?user=pg_user&password=secret" \
14+
--network=pg-network pg-diff:latest
15+
```

0 commit comments

Comments
 (0)