ducktracker 0.1.0__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.
- ducktracker-0.1.0/LICENSE +21 -0
- ducktracker-0.1.0/PKG-INFO +205 -0
- ducktracker-0.1.0/README.md +187 -0
- ducktracker-0.1.0/pyproject.toml +46 -0
- ducktracker-0.1.0/setup.cfg +4 -0
- ducktracker-0.1.0/src/ducktracker/__init__.py +3 -0
- ducktracker-0.1.0/src/ducktracker/__main__.py +5 -0
- ducktracker-0.1.0/src/ducktracker/backends.py +29 -0
- ducktracker-0.1.0/src/ducktracker/cli.py +328 -0
- ducktracker-0.1.0/src/ducktracker/config.py +136 -0
- ducktracker-0.1.0/src/ducktracker/connection.py +69 -0
- ducktracker-0.1.0/src/ducktracker/drift.py +264 -0
- ducktracker-0.1.0/src/ducktracker/history/__init__.py +91 -0
- ducktracker-0.1.0/src/ducktracker/history/ducklake.py +217 -0
- ducktracker-0.1.0/src/ducktracker/initializer.py +59 -0
- ducktracker-0.1.0/src/ducktracker/introspection/__init__.py +21 -0
- ducktracker-0.1.0/src/ducktracker/introspection/ducklake.py +231 -0
- ducktracker-0.1.0/src/ducktracker/migrator.py +357 -0
- ducktracker-0.1.0/src/ducktracker/models.py +197 -0
- ducktracker-0.1.0/src/ducktracker/resolver.py +124 -0
- ducktracker-0.1.0/src/ducktracker/sql_utils.py +13 -0
- ducktracker-0.1.0/src/ducktracker/templates/.gitignore +3 -0
- ducktracker-0.1.0/src/ducktracker/templates/README.md +63 -0
- ducktracker-0.1.0/src/ducktracker/templates/__init__.py +0 -0
- ducktracker-0.1.0/src/ducktracker/templates/ducktracker.ducklake-duckdb.toml +13 -0
- ducktracker-0.1.0/src/ducktracker/templates/ducktracker.ducklake-postgres.toml +17 -0
- ducktracker-0.1.0/src/ducktracker.egg-info/PKG-INFO +205 -0
- ducktracker-0.1.0/src/ducktracker.egg-info/SOURCES.txt +35 -0
- ducktracker-0.1.0/src/ducktracker.egg-info/dependency_links.txt +1 -0
- ducktracker-0.1.0/src/ducktracker.egg-info/entry_points.txt +2 -0
- ducktracker-0.1.0/src/ducktracker.egg-info/requires.txt +8 -0
- ducktracker-0.1.0/src/ducktracker.egg-info/top_level.txt +1 -0
- ducktracker-0.1.0/tests/test_backends.py +40 -0
- ducktracker-0.1.0/tests/test_config.py +184 -0
- ducktracker-0.1.0/tests/test_init.py +94 -0
- ducktracker-0.1.0/tests/test_models.py +254 -0
- ducktracker-0.1.0/tests/test_resolver.py +121 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Nick
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: ducktracker
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: DDL migration and schema drift detection for DuckLake
|
|
5
|
+
Author: ducktracker contributors
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Requires-Python: >=3.12
|
|
8
|
+
Description-Content-Type: text/markdown
|
|
9
|
+
License-File: LICENSE
|
|
10
|
+
Requires-Dist: click>=8.1
|
|
11
|
+
Requires-Dist: duckdb>=1.2
|
|
12
|
+
Requires-Dist: rich>=13.0
|
|
13
|
+
Provides-Extra: dev
|
|
14
|
+
Requires-Dist: pytest>=8.0; extra == "dev"
|
|
15
|
+
Requires-Dist: pytest-cov>=5.0; extra == "dev"
|
|
16
|
+
Requires-Dist: ruff>=0.9; extra == "dev"
|
|
17
|
+
Dynamic: license-file
|
|
18
|
+
|
|
19
|
+
# ducktracker
|
|
20
|
+
|
|
21
|
+
Simple tool to keep your ducks in a row.
|
|
22
|
+
|
|
23
|
+
DDL migration and schema drift detection for DuckLake.
|
|
24
|
+
|
|
25
|
+
## Install
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
pip install -e .
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
For development (includes pytest and ruff):
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
pip install -e ".[dev]"
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
## Initialize
|
|
38
|
+
|
|
39
|
+
Create a directory and run `init` to scaffold the config and migrations folder:
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
mkdir my-ducklake
|
|
43
|
+
ducktracker init my-ducklake --backend ducklake-duckdb
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
If you omit `--backend`, you'll be prompted. Two options:
|
|
47
|
+
|
|
48
|
+
| Backend | Use when |
|
|
49
|
+
|---|---|
|
|
50
|
+
| `ducklake-duckdb` | DuckLake catalog in a local DuckDB file. Good for local dev. |
|
|
51
|
+
| `ducklake-postgres` | DuckLake catalog in PostgreSQL. Good for teams. |
|
|
52
|
+
|
|
53
|
+
`init` creates `ducktracker.toml`, `migrations/`, `.gitignore`, and a `README.md` in the target directory. The directory must already exist — `init` won't create it.
|
|
54
|
+
|
|
55
|
+
## Writing migrations
|
|
56
|
+
|
|
57
|
+
Two file types, both live in your `migrations/` directory:
|
|
58
|
+
|
|
59
|
+
- **Versioned:** `V{n}__{description}.sql` — applied once, in version order
|
|
60
|
+
- **Repeatable:** `R__{description}.sql` — re-applied whenever the file content changes
|
|
61
|
+
|
|
62
|
+
Use `create` to scaffold the next file:
|
|
63
|
+
|
|
64
|
+
```bash
|
|
65
|
+
ducktracker create "create users table"
|
|
66
|
+
# → migrations/V2__create_users_table.sql
|
|
67
|
+
|
|
68
|
+
ducktracker create "refresh views" --repeatable
|
|
69
|
+
# → migrations/R__refresh_views.sql
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
Then open the file and add your DDL.
|
|
73
|
+
|
|
74
|
+
## Running migrations
|
|
75
|
+
|
|
76
|
+
```bash
|
|
77
|
+
ducktracker migrate
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
Flags worth knowing:
|
|
81
|
+
|
|
82
|
+
```
|
|
83
|
+
--dry-run Show what would be applied without executing any migration SQL
|
|
84
|
+
--target N Only apply migrations up to version N (inclusive)
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
By default, checksums of previously applied versioned migrations are validated before anything runs. If a migration file has been modified after it was applied, `migrate` will refuse to proceed.
|
|
88
|
+
|
|
89
|
+
## Checking status
|
|
90
|
+
|
|
91
|
+
```bash
|
|
92
|
+
ducktracker info
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
Prints a table with version, description, type, state, applied timestamp, and checksum for every migration it knows about. States: `applied`, `pending`, `failed`, `outdated`, `missing`.
|
|
96
|
+
|
|
97
|
+
To check only checksums without touching the database state:
|
|
98
|
+
|
|
99
|
+
```bash
|
|
100
|
+
ducktracker validate
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
Exits 1 if any applied migration's file has changed since it was applied.
|
|
104
|
+
|
|
105
|
+
## Detecting drift
|
|
106
|
+
|
|
107
|
+
```bash
|
|
108
|
+
ducktracker drift
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
Compares the schema snapshot captured at the last `migrate` or `baseline` run against the live catalog. Exits 1 if differences are found — useful in CI to catch out-of-band schema changes.
|
|
112
|
+
|
|
113
|
+
Output shows added (`+`), removed (`-`), and modified (`~`) objects.
|
|
114
|
+
|
|
115
|
+
## Baselining an existing database
|
|
116
|
+
|
|
117
|
+
If you're bringing an existing database under ducktracker management, use `baseline` before running any migrations:
|
|
118
|
+
|
|
119
|
+
```bash
|
|
120
|
+
ducktracker baseline --version 3
|
|
121
|
+
ducktracker baseline --version 3 --description "after initial setup"
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
This captures the current schema as a snapshot and marks the database at the given version. It will refuse to run if the history table already contains any records (including a prior baseline).
|
|
125
|
+
|
|
126
|
+
## Configuration
|
|
127
|
+
|
|
128
|
+
ducktracker looks for `ducktracker.toml` in the current directory by default. Override with `-c`:
|
|
129
|
+
|
|
130
|
+
```bash
|
|
131
|
+
ducktracker -c path/to/ducktracker.toml migrate
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
A minimal config looks like this:
|
|
135
|
+
|
|
136
|
+
```toml
|
|
137
|
+
[connection]
|
|
138
|
+
catalog_name = "my_ducklake"
|
|
139
|
+
catalog_backend = "duckdb"
|
|
140
|
+
duckdb_metadata_path = "ducklake_metadata.db"
|
|
141
|
+
|
|
142
|
+
[migrations]
|
|
143
|
+
directory = "migrations"
|
|
144
|
+
target_schema = "main"
|
|
145
|
+
|
|
146
|
+
[behavior]
|
|
147
|
+
validate_on_migrate = true
|
|
148
|
+
out_of_order = false
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
### Using a DuckDB secret
|
|
152
|
+
|
|
153
|
+
Instead of putting connection details directly in the config file, you can reference a named DuckDB persistent secret. Set `secret_name` in place of `duckdb_metadata_path` (duckdb backend) or `postgres_connection` (postgres backend):
|
|
154
|
+
|
|
155
|
+
```toml
|
|
156
|
+
[connection]
|
|
157
|
+
catalog_name = "my_ducklake"
|
|
158
|
+
catalog_backend = "postgres"
|
|
159
|
+
secret_name = "my_ducklake_secret"
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
The secret must already exist in DuckDB before ducktracker runs.
|
|
163
|
+
|
|
164
|
+
If your secrets are stored outside DuckDB's default location (`~/.duckdb/stored_secrets/`), add `secret_directory` to the same `[connection]` block:
|
|
165
|
+
|
|
166
|
+
```toml
|
|
167
|
+
[connection]
|
|
168
|
+
catalog_name = "my_ducklake"
|
|
169
|
+
catalog_backend = "postgres"
|
|
170
|
+
secret_name = "my_ducklake_secret"
|
|
171
|
+
secret_directory = "/run/secrets/duckdb"
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
ducktracker issues `SET secret_directory` before opening any catalog connection, so the path is in effect for the entire session.
|
|
175
|
+
|
|
176
|
+
### Config layers
|
|
177
|
+
|
|
178
|
+
Settings are resolved in this order, with later layers winning:
|
|
179
|
+
|
|
180
|
+
1. `ducktracker.toml`
|
|
181
|
+
2. Environment variables
|
|
182
|
+
3. CLI flags
|
|
183
|
+
|
|
184
|
+
### Key environment variables
|
|
185
|
+
|
|
186
|
+
| Variable | Config equivalent |
|
|
187
|
+
|---|---|
|
|
188
|
+
| `DUCKTRACKER_CATALOG_NAME` | `connection.catalog_name` |
|
|
189
|
+
| `DUCKTRACKER_CATALOG_BACKEND` | `connection.catalog_backend` |
|
|
190
|
+
| `DUCKTRACKER_DUCKDB_METADATA_PATH` | `connection.duckdb_metadata_path` |
|
|
191
|
+
| `DUCKTRACKER_POSTGRES_CONNECTION` | `connection.postgres_connection` |
|
|
192
|
+
| `DUCKTRACKER_SECRET_DIRECTORY` | `connection.secret_directory` |
|
|
193
|
+
|
|
194
|
+
### Global CLI flags
|
|
195
|
+
|
|
196
|
+
These apply to all commands except `init`:
|
|
197
|
+
|
|
198
|
+
```
|
|
199
|
+
-c, --config PATH Path to ducktracker.toml
|
|
200
|
+
--catalog NAME Override catalog name
|
|
201
|
+
--backend TYPE Override backend (duckdb, postgres) — note these differ from init's backend values
|
|
202
|
+
--metadata PATH Override DuckDB metadata file path
|
|
203
|
+
--connection STRING Override PostgreSQL connection string
|
|
204
|
+
--secrets-dir PATH Override DuckDB persistent secrets directory
|
|
205
|
+
```
|
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
# ducktracker
|
|
2
|
+
|
|
3
|
+
Simple tool to keep your ducks in a row.
|
|
4
|
+
|
|
5
|
+
DDL migration and schema drift detection for DuckLake.
|
|
6
|
+
|
|
7
|
+
## Install
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
pip install -e .
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
For development (includes pytest and ruff):
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
pip install -e ".[dev]"
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## Initialize
|
|
20
|
+
|
|
21
|
+
Create a directory and run `init` to scaffold the config and migrations folder:
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
mkdir my-ducklake
|
|
25
|
+
ducktracker init my-ducklake --backend ducklake-duckdb
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
If you omit `--backend`, you'll be prompted. Two options:
|
|
29
|
+
|
|
30
|
+
| Backend | Use when |
|
|
31
|
+
|---|---|
|
|
32
|
+
| `ducklake-duckdb` | DuckLake catalog in a local DuckDB file. Good for local dev. |
|
|
33
|
+
| `ducklake-postgres` | DuckLake catalog in PostgreSQL. Good for teams. |
|
|
34
|
+
|
|
35
|
+
`init` creates `ducktracker.toml`, `migrations/`, `.gitignore`, and a `README.md` in the target directory. The directory must already exist — `init` won't create it.
|
|
36
|
+
|
|
37
|
+
## Writing migrations
|
|
38
|
+
|
|
39
|
+
Two file types, both live in your `migrations/` directory:
|
|
40
|
+
|
|
41
|
+
- **Versioned:** `V{n}__{description}.sql` — applied once, in version order
|
|
42
|
+
- **Repeatable:** `R__{description}.sql` — re-applied whenever the file content changes
|
|
43
|
+
|
|
44
|
+
Use `create` to scaffold the next file:
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
ducktracker create "create users table"
|
|
48
|
+
# → migrations/V2__create_users_table.sql
|
|
49
|
+
|
|
50
|
+
ducktracker create "refresh views" --repeatable
|
|
51
|
+
# → migrations/R__refresh_views.sql
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
Then open the file and add your DDL.
|
|
55
|
+
|
|
56
|
+
## Running migrations
|
|
57
|
+
|
|
58
|
+
```bash
|
|
59
|
+
ducktracker migrate
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
Flags worth knowing:
|
|
63
|
+
|
|
64
|
+
```
|
|
65
|
+
--dry-run Show what would be applied without executing any migration SQL
|
|
66
|
+
--target N Only apply migrations up to version N (inclusive)
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
By default, checksums of previously applied versioned migrations are validated before anything runs. If a migration file has been modified after it was applied, `migrate` will refuse to proceed.
|
|
70
|
+
|
|
71
|
+
## Checking status
|
|
72
|
+
|
|
73
|
+
```bash
|
|
74
|
+
ducktracker info
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
Prints a table with version, description, type, state, applied timestamp, and checksum for every migration it knows about. States: `applied`, `pending`, `failed`, `outdated`, `missing`.
|
|
78
|
+
|
|
79
|
+
To check only checksums without touching the database state:
|
|
80
|
+
|
|
81
|
+
```bash
|
|
82
|
+
ducktracker validate
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
Exits 1 if any applied migration's file has changed since it was applied.
|
|
86
|
+
|
|
87
|
+
## Detecting drift
|
|
88
|
+
|
|
89
|
+
```bash
|
|
90
|
+
ducktracker drift
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
Compares the schema snapshot captured at the last `migrate` or `baseline` run against the live catalog. Exits 1 if differences are found — useful in CI to catch out-of-band schema changes.
|
|
94
|
+
|
|
95
|
+
Output shows added (`+`), removed (`-`), and modified (`~`) objects.
|
|
96
|
+
|
|
97
|
+
## Baselining an existing database
|
|
98
|
+
|
|
99
|
+
If you're bringing an existing database under ducktracker management, use `baseline` before running any migrations:
|
|
100
|
+
|
|
101
|
+
```bash
|
|
102
|
+
ducktracker baseline --version 3
|
|
103
|
+
ducktracker baseline --version 3 --description "after initial setup"
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
This captures the current schema as a snapshot and marks the database at the given version. It will refuse to run if the history table already contains any records (including a prior baseline).
|
|
107
|
+
|
|
108
|
+
## Configuration
|
|
109
|
+
|
|
110
|
+
ducktracker looks for `ducktracker.toml` in the current directory by default. Override with `-c`:
|
|
111
|
+
|
|
112
|
+
```bash
|
|
113
|
+
ducktracker -c path/to/ducktracker.toml migrate
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
A minimal config looks like this:
|
|
117
|
+
|
|
118
|
+
```toml
|
|
119
|
+
[connection]
|
|
120
|
+
catalog_name = "my_ducklake"
|
|
121
|
+
catalog_backend = "duckdb"
|
|
122
|
+
duckdb_metadata_path = "ducklake_metadata.db"
|
|
123
|
+
|
|
124
|
+
[migrations]
|
|
125
|
+
directory = "migrations"
|
|
126
|
+
target_schema = "main"
|
|
127
|
+
|
|
128
|
+
[behavior]
|
|
129
|
+
validate_on_migrate = true
|
|
130
|
+
out_of_order = false
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
### Using a DuckDB secret
|
|
134
|
+
|
|
135
|
+
Instead of putting connection details directly in the config file, you can reference a named DuckDB persistent secret. Set `secret_name` in place of `duckdb_metadata_path` (duckdb backend) or `postgres_connection` (postgres backend):
|
|
136
|
+
|
|
137
|
+
```toml
|
|
138
|
+
[connection]
|
|
139
|
+
catalog_name = "my_ducklake"
|
|
140
|
+
catalog_backend = "postgres"
|
|
141
|
+
secret_name = "my_ducklake_secret"
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
The secret must already exist in DuckDB before ducktracker runs.
|
|
145
|
+
|
|
146
|
+
If your secrets are stored outside DuckDB's default location (`~/.duckdb/stored_secrets/`), add `secret_directory` to the same `[connection]` block:
|
|
147
|
+
|
|
148
|
+
```toml
|
|
149
|
+
[connection]
|
|
150
|
+
catalog_name = "my_ducklake"
|
|
151
|
+
catalog_backend = "postgres"
|
|
152
|
+
secret_name = "my_ducklake_secret"
|
|
153
|
+
secret_directory = "/run/secrets/duckdb"
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
ducktracker issues `SET secret_directory` before opening any catalog connection, so the path is in effect for the entire session.
|
|
157
|
+
|
|
158
|
+
### Config layers
|
|
159
|
+
|
|
160
|
+
Settings are resolved in this order, with later layers winning:
|
|
161
|
+
|
|
162
|
+
1. `ducktracker.toml`
|
|
163
|
+
2. Environment variables
|
|
164
|
+
3. CLI flags
|
|
165
|
+
|
|
166
|
+
### Key environment variables
|
|
167
|
+
|
|
168
|
+
| Variable | Config equivalent |
|
|
169
|
+
|---|---|
|
|
170
|
+
| `DUCKTRACKER_CATALOG_NAME` | `connection.catalog_name` |
|
|
171
|
+
| `DUCKTRACKER_CATALOG_BACKEND` | `connection.catalog_backend` |
|
|
172
|
+
| `DUCKTRACKER_DUCKDB_METADATA_PATH` | `connection.duckdb_metadata_path` |
|
|
173
|
+
| `DUCKTRACKER_POSTGRES_CONNECTION` | `connection.postgres_connection` |
|
|
174
|
+
| `DUCKTRACKER_SECRET_DIRECTORY` | `connection.secret_directory` |
|
|
175
|
+
|
|
176
|
+
### Global CLI flags
|
|
177
|
+
|
|
178
|
+
These apply to all commands except `init`:
|
|
179
|
+
|
|
180
|
+
```
|
|
181
|
+
-c, --config PATH Path to ducktracker.toml
|
|
182
|
+
--catalog NAME Override catalog name
|
|
183
|
+
--backend TYPE Override backend (duckdb, postgres) — note these differ from init's backend values
|
|
184
|
+
--metadata PATH Override DuckDB metadata file path
|
|
185
|
+
--connection STRING Override PostgreSQL connection string
|
|
186
|
+
--secrets-dir PATH Override DuckDB persistent secrets directory
|
|
187
|
+
```
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=75.0", "wheel"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "ducktracker"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "DDL migration and schema drift detection for DuckLake"
|
|
9
|
+
requires-python = ">=3.12"
|
|
10
|
+
license = "MIT"
|
|
11
|
+
readme = "README.md"
|
|
12
|
+
authors = [
|
|
13
|
+
{ name = "ducktracker contributors" }
|
|
14
|
+
]
|
|
15
|
+
dependencies = [
|
|
16
|
+
"click>=8.1",
|
|
17
|
+
"duckdb>=1.2",
|
|
18
|
+
"rich>=13.0",
|
|
19
|
+
]
|
|
20
|
+
|
|
21
|
+
[project.optional-dependencies]
|
|
22
|
+
dev = [
|
|
23
|
+
"pytest>=8.0",
|
|
24
|
+
"pytest-cov>=5.0",
|
|
25
|
+
"ruff>=0.9",
|
|
26
|
+
]
|
|
27
|
+
|
|
28
|
+
[project.scripts]
|
|
29
|
+
ducktracker = "ducktracker.cli:cli"
|
|
30
|
+
|
|
31
|
+
[tool.setuptools.packages.find]
|
|
32
|
+
where = ["src"]
|
|
33
|
+
|
|
34
|
+
[tool.setuptools.package-data]
|
|
35
|
+
"ducktracker" = ["templates/*", "templates/.*"]
|
|
36
|
+
|
|
37
|
+
[tool.pytest.ini_options]
|
|
38
|
+
testpaths = ["tests"]
|
|
39
|
+
pythonpath = ["src"]
|
|
40
|
+
|
|
41
|
+
[tool.ruff]
|
|
42
|
+
line-length = 120
|
|
43
|
+
target-version = "py312"
|
|
44
|
+
|
|
45
|
+
[tool.ruff.lint]
|
|
46
|
+
select = ["E", "F", "I", "N", "W", "UP"]
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
"""Factory functions to select the right backend implementations from config."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from ducktracker.config import DuckTrackerConfig
|
|
6
|
+
from ducktracker.history import HistoryManagerBase
|
|
7
|
+
from ducktracker.history.ducklake import DuckLakeHistoryManager
|
|
8
|
+
from ducktracker.introspection import IntrospectorBase
|
|
9
|
+
from ducktracker.introspection.ducklake import DuckLakeIntrospector
|
|
10
|
+
|
|
11
|
+
_INTROSPECTORS: dict[str, type[IntrospectorBase]] = {
|
|
12
|
+
"duckdb": DuckLakeIntrospector,
|
|
13
|
+
"postgres": DuckLakeIntrospector,
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
_HISTORY_MANAGERS: dict[str, type[HistoryManagerBase]] = {
|
|
17
|
+
"duckdb": DuckLakeHistoryManager,
|
|
18
|
+
"postgres": DuckLakeHistoryManager,
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def get_introspector(config: DuckTrackerConfig) -> IntrospectorBase:
|
|
23
|
+
"""Return the appropriate IntrospectorBase implementation for this config."""
|
|
24
|
+
return _INTROSPECTORS[config.catalog_backend]()
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def get_history_manager(config: DuckTrackerConfig) -> HistoryManagerBase:
|
|
28
|
+
"""Return the appropriate HistoryManagerBase implementation for this config."""
|
|
29
|
+
return _HISTORY_MANAGERS[config.catalog_backend]()
|