dbly 0.0.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- dbly-0.0.1/.github/workflows/ci.yml +58 -0
- dbly-0.0.1/.github/workflows/publish.yml +53 -0
- dbly-0.0.1/.gitignore +27 -0
- dbly-0.0.1/PKG-INFO +149 -0
- dbly-0.0.1/README.md +111 -0
- dbly-0.0.1/docs/dbly_head.png +0 -0
- dbly-0.0.1/examples/ci/README.md +35 -0
- dbly-0.0.1/examples/ci/bitbucket-pipelines.yml +97 -0
- dbly-0.0.1/examples/ci/github-actions.yml +49 -0
- dbly-0.0.1/pyproject.toml +76 -0
- dbly-0.0.1/src/dbly/__init__.py +7 -0
- dbly-0.0.1/src/dbly/adapters/__init__.py +35 -0
- dbly-0.0.1/src/dbly/adapters/base.py +93 -0
- dbly-0.0.1/src/dbly/adapters/mssql.py +112 -0
- dbly-0.0.1/src/dbly/adapters/oracle.py +160 -0
- dbly-0.0.1/src/dbly/adapters/postgres.py +103 -0
- dbly-0.0.1/src/dbly/adapters/sqlite.py +94 -0
- dbly-0.0.1/src/dbly/cli.py +245 -0
- dbly-0.0.1/src/dbly/config.py +101 -0
- dbly-0.0.1/src/dbly/engine.py +138 -0
- dbly-0.0.1/src/dbly/hooks.py +62 -0
- dbly-0.0.1/src/dbly/initializer.py +19 -0
- dbly-0.0.1/src/dbly/model.py +119 -0
- dbly-0.0.1/src/dbly/parsing.py +173 -0
- dbly-0.0.1/src/dbly/planner.py +143 -0
- dbly-0.0.1/src/dbly/py.typed +0 -0
- dbly-0.0.1/src/dbly/repo.py +113 -0
- dbly-0.0.1/src/dbly/report.py +135 -0
- dbly-0.0.1/tests/test_integration.py +108 -0
- dbly-0.0.1/tests/test_mssql.py +47 -0
- dbly-0.0.1/tests/test_mssql_e2e.py +111 -0
- dbly-0.0.1/tests/test_oracle.py +67 -0
- dbly-0.0.1/tests/test_oracle_e2e.py +136 -0
- dbly-0.0.1/tests/test_parsing.py +105 -0
- dbly-0.0.1/uv.lock +639 -0
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
pull_request:
|
|
7
|
+
branches: [main]
|
|
8
|
+
|
|
9
|
+
# Cancel in-progress runs for the same ref when a new commit arrives.
|
|
10
|
+
concurrency:
|
|
11
|
+
group: ci-${{ github.ref }}
|
|
12
|
+
cancel-in-progress: true
|
|
13
|
+
|
|
14
|
+
jobs:
|
|
15
|
+
test:
|
|
16
|
+
name: Tests (Python ${{ matrix.python }})
|
|
17
|
+
runs-on: ubuntu-latest
|
|
18
|
+
strategy:
|
|
19
|
+
fail-fast: false
|
|
20
|
+
matrix:
|
|
21
|
+
python: ['3.12', '3.13']
|
|
22
|
+
|
|
23
|
+
steps:
|
|
24
|
+
- uses: actions/checkout@v4
|
|
25
|
+
|
|
26
|
+
- name: Install uv
|
|
27
|
+
uses: astral-sh/setup-uv@v6
|
|
28
|
+
with:
|
|
29
|
+
enable-cache: true
|
|
30
|
+
|
|
31
|
+
- name: Set up Python ${{ matrix.python }}
|
|
32
|
+
run: uv python install ${{ matrix.python }}
|
|
33
|
+
|
|
34
|
+
- name: Install project + dev dependencies
|
|
35
|
+
run: uv sync --python ${{ matrix.python }}
|
|
36
|
+
|
|
37
|
+
# Live database tests are gated on the presence of local *.connection.properties
|
|
38
|
+
# files and the optional drivers (oracledb / pymssql). On CI runners they skip
|
|
39
|
+
# automatically, so this runs the unit suite only.
|
|
40
|
+
- name: Run tests
|
|
41
|
+
run: uv run python -m pytest -q --tb=short
|
|
42
|
+
|
|
43
|
+
build:
|
|
44
|
+
name: Build sdist + wheel
|
|
45
|
+
runs-on: ubuntu-latest
|
|
46
|
+
needs: test
|
|
47
|
+
steps:
|
|
48
|
+
- uses: actions/checkout@v4
|
|
49
|
+
- uses: astral-sh/setup-uv@v6
|
|
50
|
+
with:
|
|
51
|
+
enable-cache: true
|
|
52
|
+
- run: uv build
|
|
53
|
+
- name: Upload artifacts
|
|
54
|
+
uses: actions/upload-artifact@v4
|
|
55
|
+
with:
|
|
56
|
+
name: dist-${{ github.sha }}
|
|
57
|
+
path: dist/
|
|
58
|
+
retention-days: 7
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
name: Publish to PyPI
|
|
2
|
+
|
|
3
|
+
# Triggered when a GitHub Release is published. Tag the release with
|
|
4
|
+
# the version (e.g. `v0.1.0`) — the workflow builds wheel + sdist and
|
|
5
|
+
# publishes via PyPI Trusted Publishing (OIDC, no API token).
|
|
6
|
+
#
|
|
7
|
+
# One-time setup on PyPI (Trusted Publisher), with these values:
|
|
8
|
+
# * PyPI Project name: dbly
|
|
9
|
+
# * Owner: angrydat
|
|
10
|
+
# * Repository name: dbly
|
|
11
|
+
# * Workflow name: publish.yml
|
|
12
|
+
# * Environment: pypi
|
|
13
|
+
# For the FIRST release (project does not exist on PyPI yet) add a *pending*
|
|
14
|
+
# publisher at https://pypi.org/manage/account/publishing/ — the first publish
|
|
15
|
+
# then creates the project. Afterwards manage it under
|
|
16
|
+
# https://pypi.org/manage/project/dbly/settings/publishing/
|
|
17
|
+
on:
|
|
18
|
+
release:
|
|
19
|
+
types: [published]
|
|
20
|
+
|
|
21
|
+
# Allow manual runs for emergencies / re-publishes from a tag.
|
|
22
|
+
workflow_dispatch:
|
|
23
|
+
|
|
24
|
+
jobs:
|
|
25
|
+
build:
|
|
26
|
+
name: Build distributions
|
|
27
|
+
runs-on: ubuntu-latest
|
|
28
|
+
steps:
|
|
29
|
+
- uses: actions/checkout@v4
|
|
30
|
+
- uses: astral-sh/setup-uv@v6
|
|
31
|
+
- run: uv build
|
|
32
|
+
- uses: actions/upload-artifact@v4
|
|
33
|
+
with:
|
|
34
|
+
name: dist
|
|
35
|
+
path: dist/
|
|
36
|
+
|
|
37
|
+
publish:
|
|
38
|
+
name: Publish to PyPI
|
|
39
|
+
needs: build
|
|
40
|
+
runs-on: ubuntu-latest
|
|
41
|
+
environment:
|
|
42
|
+
name: pypi
|
|
43
|
+
url: https://pypi.org/p/dbly
|
|
44
|
+
permissions:
|
|
45
|
+
id-token: write # required for Trusted Publishing (OIDC)
|
|
46
|
+
steps:
|
|
47
|
+
- uses: actions/download-artifact@v4
|
|
48
|
+
with:
|
|
49
|
+
name: dist
|
|
50
|
+
path: dist/
|
|
51
|
+
- uses: pypa/gh-action-pypi-publish@release/v1
|
|
52
|
+
with:
|
|
53
|
+
verify-metadata: true
|
dbly-0.0.1/.gitignore
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# Python
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
*.egg-info/
|
|
5
|
+
.eggs/
|
|
6
|
+
build/
|
|
7
|
+
dist/
|
|
8
|
+
.venv/
|
|
9
|
+
venv/
|
|
10
|
+
|
|
11
|
+
# uv
|
|
12
|
+
.uv/
|
|
13
|
+
|
|
14
|
+
# Test / coverage
|
|
15
|
+
.pytest_cache/
|
|
16
|
+
.ruff_cache/
|
|
17
|
+
.coverage
|
|
18
|
+
htmlcov/
|
|
19
|
+
|
|
20
|
+
# IDE (never committed — see project policy)
|
|
21
|
+
.idea/
|
|
22
|
+
.vscode/
|
|
23
|
+
|
|
24
|
+
# Local secrets / connection profiles with credentials
|
|
25
|
+
*.local.properties
|
|
26
|
+
*.connection.properties
|
|
27
|
+
secrets/
|
dbly-0.0.1/PKG-INFO
ADDED
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: dbly
|
|
3
|
+
Version: 0.0.1
|
|
4
|
+
Summary: State-based, cross-engine database deployment — git-driven, parser-assisted, SQL-first.
|
|
5
|
+
Project-URL: Homepage, https://angrydata.info/dbly
|
|
6
|
+
Project-URL: Repository, https://github.com/angrydat/dbly
|
|
7
|
+
Project-URL: Issues, https://github.com/angrydat/dbly/issues
|
|
8
|
+
Author-email: Jürgen Zornig <info@angrydata.info>
|
|
9
|
+
License: MIT
|
|
10
|
+
Keywords: ci-cd,database,deployment,git,migration,oracle,postgres,schema,sql,sqlite,sqlserver,state-based
|
|
11
|
+
Classifier: Development Status :: 3 - Alpha
|
|
12
|
+
Classifier: Environment :: Console
|
|
13
|
+
Classifier: Intended Audience :: Developers
|
|
14
|
+
Classifier: Intended Audience :: Information Technology
|
|
15
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
16
|
+
Classifier: Operating System :: OS Independent
|
|
17
|
+
Classifier: Programming Language :: Python
|
|
18
|
+
Classifier: Programming Language :: Python :: 3
|
|
19
|
+
Classifier: Programming Language :: SQL
|
|
20
|
+
Classifier: Topic :: Database
|
|
21
|
+
Classifier: Typing :: Typed
|
|
22
|
+
Requires-Python: >=3.12
|
|
23
|
+
Requires-Dist: pathspec>=0.12
|
|
24
|
+
Requires-Dist: psycopg[binary]>=3.1
|
|
25
|
+
Requires-Dist: pyyaml>=6.0
|
|
26
|
+
Requires-Dist: rich>=13.7
|
|
27
|
+
Requires-Dist: sqlalchemy>=2.0
|
|
28
|
+
Requires-Dist: sqlglot>=25.0
|
|
29
|
+
Requires-Dist: typer>=0.12
|
|
30
|
+
Provides-Extra: all
|
|
31
|
+
Requires-Dist: oracledb>=2.0; extra == 'all'
|
|
32
|
+
Requires-Dist: pymssql>=2.3; extra == 'all'
|
|
33
|
+
Provides-Extra: mssql
|
|
34
|
+
Requires-Dist: pymssql>=2.3; extra == 'mssql'
|
|
35
|
+
Provides-Extra: oracle
|
|
36
|
+
Requires-Dist: oracledb>=2.0; extra == 'oracle'
|
|
37
|
+
Description-Content-Type: text/markdown
|
|
38
|
+
|
|
39
|
+
<p align="center">
|
|
40
|
+
<img src="docs/dbly_head.png" alt="dbly — state-based database deployment" width="100%">
|
|
41
|
+
</p>
|
|
42
|
+
|
|
43
|
+
<p align="center"><b>Declare your desired database state in git. dbly makes it real.</b></p>
|
|
44
|
+
|
|
45
|
+
---
|
|
46
|
+
|
|
47
|
+
**dbly** deploys your database objects — tables, views, functions, procedures, packages,
|
|
48
|
+
triggers, grants — from version control to **PostgreSQL, SQL Server, Oracle and SQLite**.
|
|
49
|
+
You keep one SQL file per object, like normal source code; dbly works out what changed and
|
|
50
|
+
brings the target database in sync. Think *Terraform for your database*: **declarative,
|
|
51
|
+
predictable, repeatable.**
|
|
52
|
+
|
|
53
|
+
## Why
|
|
54
|
+
|
|
55
|
+
- **Declarative** — your repo is the source of truth. No hand-written migration chains, no
|
|
56
|
+
version-number collisions on parallel branches.
|
|
57
|
+
- **Plan before apply** — preview exactly what will run, then execute. No surprises.
|
|
58
|
+
- **Idempotent & safe** — only the necessary changes are applied. Additive changes go
|
|
59
|
+
automatically; destructive ones (dropping columns, etc.) are flagged and never run unless
|
|
60
|
+
you explicitly allow them.
|
|
61
|
+
- **Multi-database** — one workflow across all four engines.
|
|
62
|
+
|
|
63
|
+
## Install
|
|
64
|
+
|
|
65
|
+
```sh
|
|
66
|
+
uv sync # PostgreSQL + SQLite work out of the box
|
|
67
|
+
uv sync --extra oracle # add the Oracle driver
|
|
68
|
+
uv sync --extra mssql # add the SQL Server driver
|
|
69
|
+
uv sync --extra all # both
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
## Organize your repo
|
|
73
|
+
|
|
74
|
+
One file per object; folder names map to database schemas. Use any extension you like
|
|
75
|
+
(`.sql`, `.tbl`, `.vw`, `.prc`, …) — dbly reads the SQL to know what each object is.
|
|
76
|
+
|
|
77
|
+
```
|
|
78
|
+
db/
|
|
79
|
+
sales/
|
|
80
|
+
customer.tbl
|
|
81
|
+
v_open_orders.vw
|
|
82
|
+
grants.sql
|
|
83
|
+
init/ # optional: privileged greenfield groundwork (CREATE DATABASE/ROLE…)
|
|
84
|
+
hooks/pre/ hooks/post/ # optional: .sql or .py hooks (e.g. ArcGIS/ArcPy steps)
|
|
85
|
+
.dbignore # files in the repo that should not be deployed
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
## Connect
|
|
89
|
+
|
|
90
|
+
A connection profile (reuses the familiar `connection.properties` format):
|
|
91
|
+
|
|
92
|
+
```properties
|
|
93
|
+
environment=postgres # postgres | sqlserver | oracle | sqlite
|
|
94
|
+
service=db.example.com:5432
|
|
95
|
+
username=app
|
|
96
|
+
password=${DB_PASSWORD} # ${ENV} placeholders → keep secrets out of the repo (CI/CD-safe)
|
|
97
|
+
database=appdb
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
## Use
|
|
101
|
+
|
|
102
|
+
```sh
|
|
103
|
+
# preview the changes between the deployed state and a git ref
|
|
104
|
+
dbly plan --to main --target prod.connection.properties
|
|
105
|
+
|
|
106
|
+
# apply them
|
|
107
|
+
dbly apply --to main --target prod.connection.properties
|
|
108
|
+
|
|
109
|
+
# export a plain SQL script to run by hand (e.g. through a customer VPN, no dbly needed)
|
|
110
|
+
dbly plan --to main --target prod.connection.properties --sql deploy.sql
|
|
111
|
+
|
|
112
|
+
# what is currently deployed?
|
|
113
|
+
dbly status --target prod.connection.properties
|
|
114
|
+
|
|
115
|
+
# has the database drifted from the desired state?
|
|
116
|
+
dbly check --target prod.connection.properties
|
|
117
|
+
|
|
118
|
+
# greenfield only: run privileged groundwork once under a superuser profile
|
|
119
|
+
dbly init --init-target super.connection.properties
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
**Typical workflow:** edit your object files → commit → `dbly plan` to review → `dbly apply`.
|
|
123
|
+
|
|
124
|
+
Deploying a *subset* of features is just choosing the git ref you deploy (a release tag or
|
|
125
|
+
branch). Destructive steps require `--allow-destructive`.
|
|
126
|
+
|
|
127
|
+
## Built for trunk-based development
|
|
128
|
+
|
|
129
|
+
Database teams are usually locked out of trunk-based development: migration scripts collide
|
|
130
|
+
on parallel branches, and "merge" effectively means "deploy to the customer". dbly is
|
|
131
|
+
designed to break that deadlock for teams who write their logic **in SQL, in the database**:
|
|
132
|
+
|
|
133
|
+
- **No migration numbers, no collisions.** Two developers edit different objects — git
|
|
134
|
+
merges them like any other code. Integrate early, every day.
|
|
135
|
+
- **Merge ≠ deploy.** The trunk is your desired state; `dbly apply --to <tag>` ships the ref
|
|
136
|
+
you choose, in the maintenance window you choose. Release *what* you want, *when* you want.
|
|
137
|
+
- **One trunk, every customer.** dbly reads each database's real state, so customers on
|
|
138
|
+
different versions are no problem.
|
|
139
|
+
|
|
140
|
+
Integrate continuously, deploy on your own schedule — trunk-based development, finally
|
|
141
|
+
practical for state-based database developers.
|
|
142
|
+
|
|
143
|
+
## Status
|
|
144
|
+
|
|
145
|
+
Early alpha — all four engines are implemented and tested against live databases.
|
|
146
|
+
|
|
147
|
+
## License
|
|
148
|
+
|
|
149
|
+
MIT
|
dbly-0.0.1/README.md
ADDED
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
<p align="center">
|
|
2
|
+
<img src="docs/dbly_head.png" alt="dbly — state-based database deployment" width="100%">
|
|
3
|
+
</p>
|
|
4
|
+
|
|
5
|
+
<p align="center"><b>Declare your desired database state in git. dbly makes it real.</b></p>
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
**dbly** deploys your database objects — tables, views, functions, procedures, packages,
|
|
10
|
+
triggers, grants — from version control to **PostgreSQL, SQL Server, Oracle and SQLite**.
|
|
11
|
+
You keep one SQL file per object, like normal source code; dbly works out what changed and
|
|
12
|
+
brings the target database in sync. Think *Terraform for your database*: **declarative,
|
|
13
|
+
predictable, repeatable.**
|
|
14
|
+
|
|
15
|
+
## Why
|
|
16
|
+
|
|
17
|
+
- **Declarative** — your repo is the source of truth. No hand-written migration chains, no
|
|
18
|
+
version-number collisions on parallel branches.
|
|
19
|
+
- **Plan before apply** — preview exactly what will run, then execute. No surprises.
|
|
20
|
+
- **Idempotent & safe** — only the necessary changes are applied. Additive changes go
|
|
21
|
+
automatically; destructive ones (dropping columns, etc.) are flagged and never run unless
|
|
22
|
+
you explicitly allow them.
|
|
23
|
+
- **Multi-database** — one workflow across all four engines.
|
|
24
|
+
|
|
25
|
+
## Install
|
|
26
|
+
|
|
27
|
+
```sh
|
|
28
|
+
uv sync # PostgreSQL + SQLite work out of the box
|
|
29
|
+
uv sync --extra oracle # add the Oracle driver
|
|
30
|
+
uv sync --extra mssql # add the SQL Server driver
|
|
31
|
+
uv sync --extra all # both
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
## Organize your repo
|
|
35
|
+
|
|
36
|
+
One file per object; folder names map to database schemas. Use any extension you like
|
|
37
|
+
(`.sql`, `.tbl`, `.vw`, `.prc`, …) — dbly reads the SQL to know what each object is.
|
|
38
|
+
|
|
39
|
+
```
|
|
40
|
+
db/
|
|
41
|
+
sales/
|
|
42
|
+
customer.tbl
|
|
43
|
+
v_open_orders.vw
|
|
44
|
+
grants.sql
|
|
45
|
+
init/ # optional: privileged greenfield groundwork (CREATE DATABASE/ROLE…)
|
|
46
|
+
hooks/pre/ hooks/post/ # optional: .sql or .py hooks (e.g. ArcGIS/ArcPy steps)
|
|
47
|
+
.dbignore # files in the repo that should not be deployed
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
## Connect
|
|
51
|
+
|
|
52
|
+
A connection profile (reuses the familiar `connection.properties` format):
|
|
53
|
+
|
|
54
|
+
```properties
|
|
55
|
+
environment=postgres # postgres | sqlserver | oracle | sqlite
|
|
56
|
+
service=db.example.com:5432
|
|
57
|
+
username=app
|
|
58
|
+
password=${DB_PASSWORD} # ${ENV} placeholders → keep secrets out of the repo (CI/CD-safe)
|
|
59
|
+
database=appdb
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
## Use
|
|
63
|
+
|
|
64
|
+
```sh
|
|
65
|
+
# preview the changes between the deployed state and a git ref
|
|
66
|
+
dbly plan --to main --target prod.connection.properties
|
|
67
|
+
|
|
68
|
+
# apply them
|
|
69
|
+
dbly apply --to main --target prod.connection.properties
|
|
70
|
+
|
|
71
|
+
# export a plain SQL script to run by hand (e.g. through a customer VPN, no dbly needed)
|
|
72
|
+
dbly plan --to main --target prod.connection.properties --sql deploy.sql
|
|
73
|
+
|
|
74
|
+
# what is currently deployed?
|
|
75
|
+
dbly status --target prod.connection.properties
|
|
76
|
+
|
|
77
|
+
# has the database drifted from the desired state?
|
|
78
|
+
dbly check --target prod.connection.properties
|
|
79
|
+
|
|
80
|
+
# greenfield only: run privileged groundwork once under a superuser profile
|
|
81
|
+
dbly init --init-target super.connection.properties
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
**Typical workflow:** edit your object files → commit → `dbly plan` to review → `dbly apply`.
|
|
85
|
+
|
|
86
|
+
Deploying a *subset* of features is just choosing the git ref you deploy (a release tag or
|
|
87
|
+
branch). Destructive steps require `--allow-destructive`.
|
|
88
|
+
|
|
89
|
+
## Built for trunk-based development
|
|
90
|
+
|
|
91
|
+
Database teams are usually locked out of trunk-based development: migration scripts collide
|
|
92
|
+
on parallel branches, and "merge" effectively means "deploy to the customer". dbly is
|
|
93
|
+
designed to break that deadlock for teams who write their logic **in SQL, in the database**:
|
|
94
|
+
|
|
95
|
+
- **No migration numbers, no collisions.** Two developers edit different objects — git
|
|
96
|
+
merges them like any other code. Integrate early, every day.
|
|
97
|
+
- **Merge ≠ deploy.** The trunk is your desired state; `dbly apply --to <tag>` ships the ref
|
|
98
|
+
you choose, in the maintenance window you choose. Release *what* you want, *when* you want.
|
|
99
|
+
- **One trunk, every customer.** dbly reads each database's real state, so customers on
|
|
100
|
+
different versions are no problem.
|
|
101
|
+
|
|
102
|
+
Integrate continuously, deploy on your own schedule — trunk-based development, finally
|
|
103
|
+
practical for state-based database developers.
|
|
104
|
+
|
|
105
|
+
## Status
|
|
106
|
+
|
|
107
|
+
Early alpha — all four engines are implemented and tested against live databases.
|
|
108
|
+
|
|
109
|
+
## License
|
|
110
|
+
|
|
111
|
+
MIT
|
|
Binary file
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# CI: the trunk-based-development gate
|
|
2
|
+
|
|
3
|
+
These pipelines make *“the trunk is always deployable”* an automatic, enforced fact —
|
|
4
|
+
the one guarantee trunk-based development depends on. Drop one into the repo that holds
|
|
5
|
+
your database object files (not the dbly repo).
|
|
6
|
+
|
|
7
|
+
On every change, against a **throwaway database**, the pipeline proves:
|
|
8
|
+
|
|
9
|
+
| Check | Command | Proves |
|
|
10
|
+
|---|---|---|
|
|
11
|
+
| **Greenfield** | `dbly apply --to HEAD` on an empty DB | the trunk builds a correct database from scratch |
|
|
12
|
+
| **Drift** | `dbly check --to HEAD` | desired state == the live DB after apply |
|
|
13
|
+
| **Upgrade** (PRs) | `dbly apply --to origin/main` → `dbly plan/apply --from origin/main --to HEAD` | the path from the released baseline applies cleanly and stays additive |
|
|
14
|
+
| **Behaviour** | `dbression run ./tests` | no regressions in schema or business logic |
|
|
15
|
+
|
|
16
|
+
Together: `dbly` deploys, `dbression` verifies → a green gate that keeps the trunk
|
|
17
|
+
releasable. Developers integrate early and often; deployment to customers stays a separate,
|
|
18
|
+
scheduled act (`dbly apply --to <release-tag>` in the maintenance window, or
|
|
19
|
+
`dbly plan --sql` for a hand-deploy through a VPN).
|
|
20
|
+
|
|
21
|
+
## Notes
|
|
22
|
+
|
|
23
|
+
- **No secrets in the repo.** CI starts an ephemeral Postgres and writes the
|
|
24
|
+
`connection.properties` at runtime. For real targets, use repository/pipeline secrets and
|
|
25
|
+
`${ENV}` placeholders in the profile.
|
|
26
|
+
- **One profile, two tools.** dbly reads it via `--target`; dbression discovers it next to
|
|
27
|
+
the suite. The extra `environment=` key is ignored by dbression.
|
|
28
|
+
- **The release artifact** (`dbly plan --sql`) is only an accurate *diff* when generated
|
|
29
|
+
against a copy of production (read access). Against an empty CI DB it is the full bootstrap
|
|
30
|
+
script — fine for a fresh customer, not for an upgrade.
|
|
31
|
+
- Postgres is dbly’s reference engine here; the same pattern works for SQL Server / Oracle by
|
|
32
|
+
swapping the service image, the `environment=` value, and installing the matching extra
|
|
33
|
+
(`uv tool install "dbly[mssql]"` / `"dbly[oracle]"`).
|
|
34
|
+
|
|
35
|
+
Files: [`bitbucket-pipelines.yml`](bitbucket-pipelines.yml) · [`github-actions.yml`](github-actions.yml)
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
# ─────────────────────────────────────────────────────────────────────────────
|
|
2
|
+
# Trunk-based-development gate for a dbly-managed *database* repository.
|
|
3
|
+
#
|
|
4
|
+
# Place this at the ROOT of the repo that holds your SQL object files (not the
|
|
5
|
+
# dbly repo itself). On every change it proves — against a throwaway Postgres —
|
|
6
|
+
# that the trunk is always deployable and green:
|
|
7
|
+
#
|
|
8
|
+
# • GREENFIELD : the trunk builds a correct database from scratch
|
|
9
|
+
# • UPGRADE : the path from the released baseline (main) applies cleanly
|
|
10
|
+
# • BEHAVIOUR : the dbression regression suite passes
|
|
11
|
+
#
|
|
12
|
+
# That is exactly the guarantee trunk-based development needs. No secrets live in
|
|
13
|
+
# the repo — CI spins up an ephemeral database and writes the connection at runtime.
|
|
14
|
+
# ─────────────────────────────────────────────────────────────────────────────
|
|
15
|
+
|
|
16
|
+
image: python:3.12-slim
|
|
17
|
+
|
|
18
|
+
clone:
|
|
19
|
+
depth: full # dbly diffs git refs (origin/main..HEAD) — needs history
|
|
20
|
+
|
|
21
|
+
definitions:
|
|
22
|
+
services:
|
|
23
|
+
postgres:
|
|
24
|
+
image: postgres:16
|
|
25
|
+
variables:
|
|
26
|
+
POSTGRES_DB: ci
|
|
27
|
+
POSTGRES_USER: ci
|
|
28
|
+
POSTGRES_PASSWORD: ci
|
|
29
|
+
|
|
30
|
+
pipelines:
|
|
31
|
+
# Every feature branch: prove it is integrable AND deployable from scratch.
|
|
32
|
+
branches:
|
|
33
|
+
'**':
|
|
34
|
+
- step:
|
|
35
|
+
name: "dbly · trunk is deployable + green (greenfield)"
|
|
36
|
+
services: [postgres]
|
|
37
|
+
script:
|
|
38
|
+
- pip install -q uv
|
|
39
|
+
- uv tool install "git+https://github.com/angrydat/dbly@main"
|
|
40
|
+
- uv tool install dbression
|
|
41
|
+
# one profile for BOTH dbly (--target) and dbression (auto-discovered)
|
|
42
|
+
- |
|
|
43
|
+
cat > connection.properties <<'EOF'
|
|
44
|
+
environment=postgres
|
|
45
|
+
connection-string=postgresql://ci:ci@127.0.0.1:5432/ci
|
|
46
|
+
EOF
|
|
47
|
+
# empty DB → `apply` bootstraps the full desired state
|
|
48
|
+
- dbly apply --to HEAD --target connection.properties
|
|
49
|
+
# desired state must now equal the live DB (no drift)
|
|
50
|
+
- dbly check --to HEAD --target connection.properties
|
|
51
|
+
# behaviour gate — JUnit feeds Bitbucket's test report (adjust the path)
|
|
52
|
+
- dbression run ./tests --junit-xml test-reports/dbression.xml
|
|
53
|
+
|
|
54
|
+
# Pull requests into main: also prove the UPGRADE path from production baseline.
|
|
55
|
+
pull-requests:
|
|
56
|
+
'**':
|
|
57
|
+
- step:
|
|
58
|
+
name: "dbly · upgrade path from main applies cleanly"
|
|
59
|
+
services: [postgres]
|
|
60
|
+
script:
|
|
61
|
+
- pip install -q uv
|
|
62
|
+
- uv tool install "git+https://github.com/angrydat/dbly@main"
|
|
63
|
+
- uv tool install dbression
|
|
64
|
+
- git fetch origin main:refs/remotes/origin/main
|
|
65
|
+
- |
|
|
66
|
+
cat > connection.properties <<'EOF'
|
|
67
|
+
environment=postgres
|
|
68
|
+
connection-string=postgresql://ci:ci@127.0.0.1:5432/ci
|
|
69
|
+
EOF
|
|
70
|
+
# 1) build the released baseline (production state == main)
|
|
71
|
+
- dbly apply --to origin/main --target connection.properties
|
|
72
|
+
# 2) review the upgrade — gate here if destructive steps appear
|
|
73
|
+
- dbly plan --from origin/main --to HEAD --target connection.properties
|
|
74
|
+
# 3) apply the upgrade and re-check behaviour on the upgraded DB
|
|
75
|
+
- dbly apply --from origin/main --to HEAD --target connection.properties
|
|
76
|
+
- dbression run ./tests --junit-xml test-reports/dbression.xml
|
|
77
|
+
|
|
78
|
+
# On a release tag: emit the hand-deploy SQL as a downloadable artifact.
|
|
79
|
+
tags:
|
|
80
|
+
'v*':
|
|
81
|
+
- step:
|
|
82
|
+
name: "dbly · build hand-deploy SQL for the maintenance window"
|
|
83
|
+
services: [postgres]
|
|
84
|
+
script:
|
|
85
|
+
- pip install -q uv
|
|
86
|
+
- uv tool install "git+https://github.com/angrydat/dbly@main"
|
|
87
|
+
- |
|
|
88
|
+
cat > connection.properties <<'EOF'
|
|
89
|
+
environment=postgres
|
|
90
|
+
connection-string=postgresql://ci:ci@127.0.0.1:5432/ci
|
|
91
|
+
EOF
|
|
92
|
+
# NOTE: for an accurate additive table-diff, point --target at a *copy of
|
|
93
|
+
# production* (read access). Against an empty CI DB this yields the full
|
|
94
|
+
# bootstrap script instead.
|
|
95
|
+
- dbly plan --to "$BITBUCKET_TAG" --target connection.properties --sql "deploy-$BITBUCKET_TAG.sql"
|
|
96
|
+
artifacts:
|
|
97
|
+
- "deploy-*.sql"
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
# GitHub Actions variant of the dbly trunk-based-development gate.
|
|
2
|
+
# Place at .github/workflows/dbly.yml in your *database* repo.
|
|
3
|
+
# Greenfield + behaviour on every push; upgrade path on pull requests.
|
|
4
|
+
|
|
5
|
+
name: dbly trunk gate
|
|
6
|
+
|
|
7
|
+
on: [push, pull_request]
|
|
8
|
+
|
|
9
|
+
jobs:
|
|
10
|
+
deployable:
|
|
11
|
+
runs-on: ubuntu-latest
|
|
12
|
+
services:
|
|
13
|
+
postgres:
|
|
14
|
+
image: postgres:16
|
|
15
|
+
env:
|
|
16
|
+
POSTGRES_DB: ci
|
|
17
|
+
POSTGRES_USER: ci
|
|
18
|
+
POSTGRES_PASSWORD: ci
|
|
19
|
+
ports: ["5432:5432"]
|
|
20
|
+
options: >-
|
|
21
|
+
--health-cmd "pg_isready -U ci" --health-interval 5s
|
|
22
|
+
--health-timeout 5s --health-retries 10
|
|
23
|
+
steps:
|
|
24
|
+
- uses: actions/checkout@v4
|
|
25
|
+
with:
|
|
26
|
+
fetch-depth: 0 # dbly diffs git refs — needs full history
|
|
27
|
+
- uses: astral-sh/setup-uv@v5
|
|
28
|
+
- run: uv tool install "git+https://github.com/angrydat/dbly@main"
|
|
29
|
+
- run: uv tool install dbression
|
|
30
|
+
- run: |
|
|
31
|
+
cat > connection.properties <<'EOF'
|
|
32
|
+
environment=postgres
|
|
33
|
+
connection-string=postgresql://ci:ci@127.0.0.1:5432/ci
|
|
34
|
+
EOF
|
|
35
|
+
|
|
36
|
+
- name: Greenfield — trunk builds a correct DB from scratch
|
|
37
|
+
run: |
|
|
38
|
+
dbly apply --to HEAD --target connection.properties
|
|
39
|
+
dbly check --to HEAD --target connection.properties
|
|
40
|
+
|
|
41
|
+
- name: Upgrade path from main (pull requests only)
|
|
42
|
+
if: github.event_name == 'pull_request'
|
|
43
|
+
run: |
|
|
44
|
+
git fetch origin main
|
|
45
|
+
# fresh DB instance recommended; here we just validate the plan is clean
|
|
46
|
+
dbly plan --from origin/main --to HEAD --target connection.properties
|
|
47
|
+
|
|
48
|
+
- name: Behaviour gate (dbression)
|
|
49
|
+
run: dbression run ./tests --junit-xml test-reports/dbression.xml
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "dbly"
|
|
3
|
+
version = "0.0.1"
|
|
4
|
+
description = "State-based, cross-engine database deployment — git-driven, parser-assisted, SQL-first."
|
|
5
|
+
readme = "README.md"
|
|
6
|
+
license = { text = "MIT" }
|
|
7
|
+
authors = [
|
|
8
|
+
{ name = "Jürgen Zornig", email = "info@angrydata.info" }
|
|
9
|
+
]
|
|
10
|
+
requires-python = ">=3.12"
|
|
11
|
+
keywords = [
|
|
12
|
+
"database",
|
|
13
|
+
"deployment",
|
|
14
|
+
"migration",
|
|
15
|
+
"state-based",
|
|
16
|
+
"schema",
|
|
17
|
+
"postgres",
|
|
18
|
+
"oracle",
|
|
19
|
+
"sqlserver",
|
|
20
|
+
"sqlite",
|
|
21
|
+
"sql",
|
|
22
|
+
"git",
|
|
23
|
+
"ci-cd",
|
|
24
|
+
]
|
|
25
|
+
classifiers = [
|
|
26
|
+
"Development Status :: 3 - Alpha",
|
|
27
|
+
"Environment :: Console",
|
|
28
|
+
"Intended Audience :: Developers",
|
|
29
|
+
"Intended Audience :: Information Technology",
|
|
30
|
+
"License :: OSI Approved :: MIT License",
|
|
31
|
+
"Operating System :: OS Independent",
|
|
32
|
+
"Programming Language :: Python",
|
|
33
|
+
"Programming Language :: Python :: 3",
|
|
34
|
+
"Programming Language :: SQL",
|
|
35
|
+
"Topic :: Database",
|
|
36
|
+
"Typing :: Typed",
|
|
37
|
+
]
|
|
38
|
+
dependencies = [
|
|
39
|
+
"typer>=0.12",
|
|
40
|
+
"rich>=13.7",
|
|
41
|
+
"sqlalchemy>=2.0",
|
|
42
|
+
"sqlglot>=25.0",
|
|
43
|
+
"pyyaml>=6.0",
|
|
44
|
+
"pathspec>=0.12",
|
|
45
|
+
"psycopg[binary]>=3.1",
|
|
46
|
+
]
|
|
47
|
+
|
|
48
|
+
[project.optional-dependencies]
|
|
49
|
+
oracle = ["oracledb>=2.0"]
|
|
50
|
+
mssql = ["pymssql>=2.3"]
|
|
51
|
+
all = ["oracledb>=2.0", "pymssql>=2.3"]
|
|
52
|
+
|
|
53
|
+
[project.urls]
|
|
54
|
+
Homepage = "https://angrydata.info/dbly"
|
|
55
|
+
Repository = "https://github.com/angrydat/dbly"
|
|
56
|
+
Issues = "https://github.com/angrydat/dbly/issues"
|
|
57
|
+
|
|
58
|
+
[project.scripts]
|
|
59
|
+
dbly = "dbly.cli:main"
|
|
60
|
+
|
|
61
|
+
[build-system]
|
|
62
|
+
requires = ["hatchling"]
|
|
63
|
+
build-backend = "hatchling.build"
|
|
64
|
+
|
|
65
|
+
[tool.hatch.build.targets.wheel]
|
|
66
|
+
packages = ["src/dbly"]
|
|
67
|
+
|
|
68
|
+
[dependency-groups]
|
|
69
|
+
dev = [
|
|
70
|
+
"pytest>=8.0",
|
|
71
|
+
"ruff>=0.6",
|
|
72
|
+
]
|
|
73
|
+
|
|
74
|
+
[tool.ruff]
|
|
75
|
+
line-length = 100
|
|
76
|
+
target-version = "py312"
|