coop-schema-compare 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.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Cooptimize / Aaron Jennings
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,178 @@
1
+ Metadata-Version: 2.4
2
+ Name: coop-schema-compare
3
+ Version: 0.1.0
4
+ Summary: Deterministic schema compare between SQL environments (Fabric Warehouse, Azure SQL) with an ordered promotion script. Reads only; applies nothing.
5
+ Author: Cooptimize
6
+ License: MIT License
7
+
8
+ Copyright (c) 2026 Cooptimize / Aaron Jennings
9
+
10
+ Permission is hereby granted, free of charge, to any person obtaining a copy
11
+ of this software and associated documentation files (the "Software"), to deal
12
+ in the Software without restriction, including without limitation the rights
13
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
14
+ copies of the Software, and to permit persons to whom the Software is
15
+ furnished to do so, subject to the following conditions:
16
+
17
+ The above copyright notice and this permission notice shall be included in all
18
+ copies or substantial portions of the Software.
19
+
20
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
23
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
25
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
26
+ SOFTWARE.
27
+
28
+ Project-URL: Homepage, https://github.com/kabukisensei/coop-schema-compare
29
+ Keywords: fabric,warehouse,azure-sql,schema,compare,deployment
30
+ Classifier: Programming Language :: Python :: 3
31
+ Classifier: License :: OSI Approved :: MIT License
32
+ Classifier: Operating System :: OS Independent
33
+ Classifier: Topic :: Database
34
+ Requires-Python: >=3.10
35
+ Description-Content-Type: text/markdown
36
+ License-File: LICENSE
37
+ Requires-Dist: pyodbc>=5.0
38
+ Dynamic: license-file
39
+
40
+ # coop-schema-compare
41
+
42
+ Deterministic schema compare between two SQL schemas, live or as code, with an
43
+ ordered promotion script. Built for Microsoft Fabric Warehouse, where the
44
+ built-in deployment pipeline gives you no script to read; it works the same on
45
+ Azure SQL Database, Fabric SQL database and SQL Server. A side is a live
46
+ database (by SQL endpoint or by Fabric workspace and warehouse name), a
47
+ committed snapshot, or a repo folder of `.sql` files, in any pairing: repo
48
+ against repo, live against live, repo against live. It **reads only and applies
49
+ nothing**: the output is a report and a script a person reviews and runs through
50
+ their own deployment path.
51
+
52
+ Part of the coop suite (see [coop-agent](https://github.com/kabukisensei/coop-agent)).
53
+ coop calls it as its `schema_compare` tool; you can run it yourself.
54
+
55
+ ## Install
56
+
57
+ ```powershell
58
+ pipx install coop-schema-compare
59
+ ```
60
+
61
+ Needs Python 3.10+, the Microsoft ODBC Driver 18 for SQL Server, and either an
62
+ access token in `SQL_ACCESS_TOKEN` or the Azure CLI signed in (`az login`).
63
+
64
+ ## Use
65
+
66
+ ```powershell
67
+ # dev against prod, live on both sides, named by workspace and warehouse
68
+ coop-schema-compare compare "fabric://Contoso Dev/QE_WH" "fabric://Contoso Prod/QE_WH" --source-environment dev --target-environment prod
69
+
70
+ # the same by SQL endpoint host
71
+ coop-schema-compare compare contoso-dev.datawarehouse.fabric.microsoft.com/QE_WH contoso-prod.datawarehouse.fabric.microsoft.com/QE_WH --source-environment dev --target-environment prod
72
+
73
+ # the repo (schema as code) against the live prod Warehouse
74
+ coop-schema-compare compare .\warehouse\QE_WH.Warehouse "fabric://Contoso Prod/QE_WH" --target-environment prod
75
+
76
+ # two branches of the repo, checked out side by side: no connection
77
+ coop-schema-compare diff ..\QE_WH-main\warehouse ..\QE_WH-feature\warehouse
78
+
79
+ # commit a snapshot of each side, then diff the folders offline (no connection)
80
+ coop-schema-compare snapshot "fabric://Contoso Dev/QE_WH" --out catalog/dev --environment dev
81
+ coop-schema-compare snapshot "fabric://Contoso Prod/QE_WH" --out catalog/prod --environment prod
82
+ coop-schema-compare diff catalog/dev catalog/prod
83
+ ```
84
+
85
+ A side is one of:
86
+
87
+ | Form | Reads |
88
+ | --- | --- |
89
+ | `server/database` | the live catalog over an encrypted, token-authenticated connection |
90
+ | `fabric://<workspace>/<warehouse>` | the same, with the SQL endpoint looked up through the Fabric REST API; workspace and warehouse by id or display name |
91
+ | a snapshot folder | the folder `snapshot` or coop's `coop catalog snapshot` wrote (it holds `manifest.json`); no connection |
92
+ | any other folder | the repo: every `.sql` file under it, read as code; no connection |
93
+
94
+ The snapshot layout is the one coop's `coop catalog snapshot` writes, so either
95
+ tool's folder is a valid side for the other.
96
+
97
+ ### The repo side
98
+
99
+ A repo side is a folder of T-SQL source: a Fabric Warehouse Git export or SQL
100
+ database project (one file per object under folders such as `dbo/Tables/`), or
101
+ a hand-kept `sql/` folder. Files are read in path order and split on `GO`; a
102
+ batch counts when its first statement is `CREATE SCHEMA`, `CREATE TABLE`, or
103
+ `CREATE [OR ALTER] VIEW | PROCEDURE | FUNCTION`. Everything else (post-deployment
104
+ scripts, `ALTER`, `INSERT`, `.sqlproj`, the `bin/` and `obj/` folders) is
105
+ ignored, and the report notes how many files held no object, so a mis-pointed
106
+ folder is visible. Table columns are read from the column list with the type
107
+ rendered as the live catalog renders it (`decimal(18, 2)`, `varchar(max)`; a
108
+ length or precision the source leaves out gets the engine default, `varchar(1)`
109
+ or `datetime2(7)`, as the engine itself would store it; write them out for an
110
+ exact compare). Constraints, defaults, identity and computed-column expressions
111
+ are out of scope, as on the live side. A view, procedure or function compares by
112
+ its text with a leading comment banner, `OR ALTER` and a trailing `;` ignored,
113
+ so a file and the catalog's copy of the same object match.
114
+
115
+ `compare` and `diff` write two files under `--out` (default
116
+ `./schema-compare/<source>-to-<target>/`):
117
+
118
+ - `report.md`: both sides' object counts and a sorted table of every difference
119
+ (kind, object, change, detail), destructive ones marked.
120
+ - `deploy.sql`: the promotion script in run order: new schemas, new tables,
121
+ in-place column additions, then functions, views and procedures as
122
+ `CREATE OR ALTER` in dependency order. Every DROP and every change that can
123
+ lose data (a dropped or retyped column, a `NOT NULL` column added to an
124
+ existing table, a name whose kind differs between the sides) is at the end,
125
+ commented out under a DESTRUCTIVE banner. On a Fabric Warehouse or Lakehouse
126
+ a column change is a rebuild note with the source shape, because those
127
+ targets do not alter columns in place.
128
+
129
+ Direction is "what the target needs to match the source". A view or procedure
130
+ reformatted by an editor is not a change: definitions are compared with line
131
+ endings and runs of whitespace folded.
132
+
133
+ ## What is compared
134
+
135
+ Schemas; tables (each column's name, type, nullability and position); views,
136
+ procedures and functions by definition. Constraints, indexes, permissions and
137
+ row data are out of scope and the report says so.
138
+
139
+ ## Exit codes and output
140
+
141
+ `0` the compare ran; `--fail-on-diff` gives `2` on any difference and
142
+ `--fail-on-destructive` gives `3` on a destructive one, for CI gates; `1` on an
143
+ error, printed as a short state (`connection_failed`, `side_invalid`,
144
+ `pyodbc_unavailable`, `odbc_driver_unavailable`, `az_unavailable`,
145
+ `snapshot_missing`, `output_not_snapshot`, `workspace_not_found`,
146
+ `warehouse_not_found`, `fabric_forbidden`). `--json` prints the result as one
147
+ object. No token, credential, server name, driver text or exception text is ever
148
+ printed or written.
149
+
150
+ ## Authentication
151
+
152
+ The token for `https://database.windows.net/` is read from `SQL_ACCESS_TOKEN`
153
+ (or the variable `--token-env` names). When it is empty, the Azure CLI mints one
154
+ (`az account get-access-token`, `--tenant` to pin the tenant). The token goes to
155
+ the driver as a connection attribute and nowhere else. A `fabric://` side also
156
+ needs a token for `https://api.fabric.microsoft.com`, from `FABRIC_ACCESS_TOKEN`
157
+ (or `--fabric-token-env`) or the Azure CLI the same way; it is used for two
158
+ read-only GETs (list workspaces, list the workspace's warehouses) and nothing
159
+ else. Folder sides need no token.
160
+
161
+ ## Why not SqlPackage or a deployment pipeline
162
+
163
+ Fabric deployment pipelines and Git integration promote a Warehouse through
164
+ DacFx: no script to read or edit, a column change drops and recreates the table,
165
+ permissions do not travel, and both sides must be workspaces in one tenant and
166
+ pipeline. SqlPackage supports the Warehouse but is a separate .NET tool on each
167
+ machine with a publish you cannot review line by line. This tool gives you the
168
+ plain script instead. SqlPackage remains the right tool for a full-fidelity
169
+ dacpac of a large estate.
170
+
171
+ ## Develop
172
+
173
+ ```sh
174
+ python -m pip install -e .
175
+ python -m unittest discover -s tests -v
176
+ ```
177
+
178
+ Tests run offline with fake cursors; no database or network is needed.
@@ -0,0 +1,139 @@
1
+ # coop-schema-compare
2
+
3
+ Deterministic schema compare between two SQL schemas, live or as code, with an
4
+ ordered promotion script. Built for Microsoft Fabric Warehouse, where the
5
+ built-in deployment pipeline gives you no script to read; it works the same on
6
+ Azure SQL Database, Fabric SQL database and SQL Server. A side is a live
7
+ database (by SQL endpoint or by Fabric workspace and warehouse name), a
8
+ committed snapshot, or a repo folder of `.sql` files, in any pairing: repo
9
+ against repo, live against live, repo against live. It **reads only and applies
10
+ nothing**: the output is a report and a script a person reviews and runs through
11
+ their own deployment path.
12
+
13
+ Part of the coop suite (see [coop-agent](https://github.com/kabukisensei/coop-agent)).
14
+ coop calls it as its `schema_compare` tool; you can run it yourself.
15
+
16
+ ## Install
17
+
18
+ ```powershell
19
+ pipx install coop-schema-compare
20
+ ```
21
+
22
+ Needs Python 3.10+, the Microsoft ODBC Driver 18 for SQL Server, and either an
23
+ access token in `SQL_ACCESS_TOKEN` or the Azure CLI signed in (`az login`).
24
+
25
+ ## Use
26
+
27
+ ```powershell
28
+ # dev against prod, live on both sides, named by workspace and warehouse
29
+ coop-schema-compare compare "fabric://Contoso Dev/QE_WH" "fabric://Contoso Prod/QE_WH" --source-environment dev --target-environment prod
30
+
31
+ # the same by SQL endpoint host
32
+ coop-schema-compare compare contoso-dev.datawarehouse.fabric.microsoft.com/QE_WH contoso-prod.datawarehouse.fabric.microsoft.com/QE_WH --source-environment dev --target-environment prod
33
+
34
+ # the repo (schema as code) against the live prod Warehouse
35
+ coop-schema-compare compare .\warehouse\QE_WH.Warehouse "fabric://Contoso Prod/QE_WH" --target-environment prod
36
+
37
+ # two branches of the repo, checked out side by side: no connection
38
+ coop-schema-compare diff ..\QE_WH-main\warehouse ..\QE_WH-feature\warehouse
39
+
40
+ # commit a snapshot of each side, then diff the folders offline (no connection)
41
+ coop-schema-compare snapshot "fabric://Contoso Dev/QE_WH" --out catalog/dev --environment dev
42
+ coop-schema-compare snapshot "fabric://Contoso Prod/QE_WH" --out catalog/prod --environment prod
43
+ coop-schema-compare diff catalog/dev catalog/prod
44
+ ```
45
+
46
+ A side is one of:
47
+
48
+ | Form | Reads |
49
+ | --- | --- |
50
+ | `server/database` | the live catalog over an encrypted, token-authenticated connection |
51
+ | `fabric://<workspace>/<warehouse>` | the same, with the SQL endpoint looked up through the Fabric REST API; workspace and warehouse by id or display name |
52
+ | a snapshot folder | the folder `snapshot` or coop's `coop catalog snapshot` wrote (it holds `manifest.json`); no connection |
53
+ | any other folder | the repo: every `.sql` file under it, read as code; no connection |
54
+
55
+ The snapshot layout is the one coop's `coop catalog snapshot` writes, so either
56
+ tool's folder is a valid side for the other.
57
+
58
+ ### The repo side
59
+
60
+ A repo side is a folder of T-SQL source: a Fabric Warehouse Git export or SQL
61
+ database project (one file per object under folders such as `dbo/Tables/`), or
62
+ a hand-kept `sql/` folder. Files are read in path order and split on `GO`; a
63
+ batch counts when its first statement is `CREATE SCHEMA`, `CREATE TABLE`, or
64
+ `CREATE [OR ALTER] VIEW | PROCEDURE | FUNCTION`. Everything else (post-deployment
65
+ scripts, `ALTER`, `INSERT`, `.sqlproj`, the `bin/` and `obj/` folders) is
66
+ ignored, and the report notes how many files held no object, so a mis-pointed
67
+ folder is visible. Table columns are read from the column list with the type
68
+ rendered as the live catalog renders it (`decimal(18, 2)`, `varchar(max)`; a
69
+ length or precision the source leaves out gets the engine default, `varchar(1)`
70
+ or `datetime2(7)`, as the engine itself would store it; write them out for an
71
+ exact compare). Constraints, defaults, identity and computed-column expressions
72
+ are out of scope, as on the live side. A view, procedure or function compares by
73
+ its text with a leading comment banner, `OR ALTER` and a trailing `;` ignored,
74
+ so a file and the catalog's copy of the same object match.
75
+
76
+ `compare` and `diff` write two files under `--out` (default
77
+ `./schema-compare/<source>-to-<target>/`):
78
+
79
+ - `report.md`: both sides' object counts and a sorted table of every difference
80
+ (kind, object, change, detail), destructive ones marked.
81
+ - `deploy.sql`: the promotion script in run order: new schemas, new tables,
82
+ in-place column additions, then functions, views and procedures as
83
+ `CREATE OR ALTER` in dependency order. Every DROP and every change that can
84
+ lose data (a dropped or retyped column, a `NOT NULL` column added to an
85
+ existing table, a name whose kind differs between the sides) is at the end,
86
+ commented out under a DESTRUCTIVE banner. On a Fabric Warehouse or Lakehouse
87
+ a column change is a rebuild note with the source shape, because those
88
+ targets do not alter columns in place.
89
+
90
+ Direction is "what the target needs to match the source". A view or procedure
91
+ reformatted by an editor is not a change: definitions are compared with line
92
+ endings and runs of whitespace folded.
93
+
94
+ ## What is compared
95
+
96
+ Schemas; tables (each column's name, type, nullability and position); views,
97
+ procedures and functions by definition. Constraints, indexes, permissions and
98
+ row data are out of scope and the report says so.
99
+
100
+ ## Exit codes and output
101
+
102
+ `0` the compare ran; `--fail-on-diff` gives `2` on any difference and
103
+ `--fail-on-destructive` gives `3` on a destructive one, for CI gates; `1` on an
104
+ error, printed as a short state (`connection_failed`, `side_invalid`,
105
+ `pyodbc_unavailable`, `odbc_driver_unavailable`, `az_unavailable`,
106
+ `snapshot_missing`, `output_not_snapshot`, `workspace_not_found`,
107
+ `warehouse_not_found`, `fabric_forbidden`). `--json` prints the result as one
108
+ object. No token, credential, server name, driver text or exception text is ever
109
+ printed or written.
110
+
111
+ ## Authentication
112
+
113
+ The token for `https://database.windows.net/` is read from `SQL_ACCESS_TOKEN`
114
+ (or the variable `--token-env` names). When it is empty, the Azure CLI mints one
115
+ (`az account get-access-token`, `--tenant` to pin the tenant). The token goes to
116
+ the driver as a connection attribute and nowhere else. A `fabric://` side also
117
+ needs a token for `https://api.fabric.microsoft.com`, from `FABRIC_ACCESS_TOKEN`
118
+ (or `--fabric-token-env`) or the Azure CLI the same way; it is used for two
119
+ read-only GETs (list workspaces, list the workspace's warehouses) and nothing
120
+ else. Folder sides need no token.
121
+
122
+ ## Why not SqlPackage or a deployment pipeline
123
+
124
+ Fabric deployment pipelines and Git integration promote a Warehouse through
125
+ DacFx: no script to read or edit, a column change drops and recreates the table,
126
+ permissions do not travel, and both sides must be workspaces in one tenant and
127
+ pipeline. SqlPackage supports the Warehouse but is a separate .NET tool on each
128
+ machine with a publish you cannot review line by line. This tool gives you the
129
+ plain script instead. SqlPackage remains the right tool for a full-fidelity
130
+ dacpac of a large estate.
131
+
132
+ ## Develop
133
+
134
+ ```sh
135
+ python -m pip install -e .
136
+ python -m unittest discover -s tests -v
137
+ ```
138
+
139
+ Tests run offline with fake cursors; no database or network is needed.
@@ -0,0 +1,29 @@
1
+ [build-system]
2
+ requires = ["setuptools>=68"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "coop-schema-compare"
7
+ version = "0.1.0"
8
+ description = "Deterministic schema compare between SQL environments (Fabric Warehouse, Azure SQL) with an ordered promotion script. Reads only; applies nothing."
9
+ readme = "README.md"
10
+ license = { file = "LICENSE" }
11
+ requires-python = ">=3.10"
12
+ authors = [{ name = "Cooptimize" }]
13
+ keywords = ["fabric", "warehouse", "azure-sql", "schema", "compare", "deployment"]
14
+ classifiers = [
15
+ "Programming Language :: Python :: 3",
16
+ "License :: OSI Approved :: MIT License",
17
+ "Operating System :: OS Independent",
18
+ "Topic :: Database",
19
+ ]
20
+ dependencies = ["pyodbc>=5.0"]
21
+
22
+ [project.scripts]
23
+ coop-schema-compare = "coop_schema_compare.cli:main"
24
+
25
+ [project.urls]
26
+ Homepage = "https://github.com/kabukisensei/coop-schema-compare"
27
+
28
+ [tool.setuptools.packages.find]
29
+ where = ["src"]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,3 @@
1
+ """coop-schema-compare: deterministic schema compare and promotion script for Fabric Warehouse and Azure SQL."""
2
+
3
+ __version__ = "0.1.0"