ormguard 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.
- ormguard-0.1.0/.coderabbit.yaml +18 -0
- ormguard-0.1.0/.github/ISSUE_TEMPLATE/bug_report.yml +50 -0
- ormguard-0.1.0/.github/ISSUE_TEMPLATE/config.yml +5 -0
- ormguard-0.1.0/.github/ISSUE_TEMPLATE/feature_request.yml +19 -0
- ormguard-0.1.0/.github/PULL_REQUEST_TEMPLATE.md +23 -0
- ormguard-0.1.0/.github/dependabot.yml +16 -0
- ormguard-0.1.0/.github/workflows/ci.yml +112 -0
- ormguard-0.1.0/.github/workflows/release.yml +49 -0
- ormguard-0.1.0/.gitignore +15 -0
- ormguard-0.1.0/.pre-commit-config.yaml +17 -0
- ormguard-0.1.0/.pre-commit-hooks.yaml +31 -0
- ormguard-0.1.0/CHANGELOG.md +82 -0
- ormguard-0.1.0/CLAUDE.md +44 -0
- ormguard-0.1.0/CONTRIBUTING.md +57 -0
- ormguard-0.1.0/LICENSE +21 -0
- ormguard-0.1.0/PKG-INFO +197 -0
- ormguard-0.1.0/README.md +174 -0
- ormguard-0.1.0/action.yml +54 -0
- ormguard-0.1.0/docs/DESIGN.md +119 -0
- ormguard-0.1.0/docs/USAGE.md +93 -0
- ormguard-0.1.0/docs/V2_OFFLINE_REPLAY.md +126 -0
- ormguard-0.1.0/examples/fastapi_app.py +38 -0
- ormguard-0.1.0/examples/validate_aace_tenant.py +66 -0
- ormguard-0.1.0/ormguard/__init__.py +42 -0
- ormguard-0.1.0/ormguard/__main__.py +5 -0
- ormguard-0.1.0/ormguard/_schema.py +75 -0
- ormguard-0.1.0/ormguard/_version.py +24 -0
- ormguard-0.1.0/ormguard/cli.py +120 -0
- ormguard-0.1.0/ormguard/config.py +78 -0
- ormguard-0.1.0/ormguard/core.py +71 -0
- ormguard-0.1.0/ormguard/diff.py +256 -0
- ormguard-0.1.0/ormguard/integrations/__init__.py +0 -0
- ormguard-0.1.0/ormguard/integrations/fastapi.py +41 -0
- ormguard-0.1.0/ormguard/model.py +111 -0
- ormguard-0.1.0/ormguard/notify.py +44 -0
- ormguard-0.1.0/ormguard/orm.py +80 -0
- ormguard-0.1.0/ormguard/reflect.py +126 -0
- ormguard-0.1.0/ormguard/selfcheck.py +63 -0
- ormguard-0.1.0/ormguard/types.py +120 -0
- ormguard-0.1.0/pyproject.toml +56 -0
- ormguard-0.1.0/tests/test_aace_drift_cases.py +76 -0
- ormguard-0.1.0/tests/test_checks_enums.py +81 -0
- ormguard-0.1.0/tests/test_cli.py +19 -0
- ormguard-0.1.0/tests/test_core_sqlite.py +84 -0
- ormguard-0.1.0/tests/test_defaults.py +50 -0
- ormguard-0.1.0/tests/test_foreign_keys.py +62 -0
- ormguard-0.1.0/tests/test_indexes.py +55 -0
- ormguard-0.1.0/tests/test_mysql_integration.py +66 -0
- ormguard-0.1.0/tests/test_notify.py +45 -0
- ormguard-0.1.0/tests/test_postgres_integration.py +69 -0
- ormguard-0.1.0/tests/test_types.py +89 -0
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# CodeRabbit configuration — https://docs.coderabbit.ai/reference/configuration
|
|
2
|
+
# AI code review runs via the CodeRabbit GitHub App (install at
|
|
3
|
+
# https://github.com/apps/coderabbitai). No API key or workflow needed.
|
|
4
|
+
language: en-US
|
|
5
|
+
reviews:
|
|
6
|
+
profile: chill
|
|
7
|
+
request_changes_workflow: false
|
|
8
|
+
high_level_summary: true
|
|
9
|
+
poem: false
|
|
10
|
+
review_status: true
|
|
11
|
+
auto_review:
|
|
12
|
+
enabled: true
|
|
13
|
+
drafts: false
|
|
14
|
+
base_branches:
|
|
15
|
+
- develop
|
|
16
|
+
- main
|
|
17
|
+
chat:
|
|
18
|
+
auto_reply: true
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
name: Bug report
|
|
2
|
+
description: Report incorrect behavior in ormguard
|
|
3
|
+
labels: ["bug"]
|
|
4
|
+
body:
|
|
5
|
+
- type: markdown
|
|
6
|
+
attributes:
|
|
7
|
+
value: Thanks for filing a bug. Please fill in the details below so it can be reproduced.
|
|
8
|
+
- type: input
|
|
9
|
+
id: version
|
|
10
|
+
attributes:
|
|
11
|
+
label: ormguard version
|
|
12
|
+
placeholder: "e.g. 0.1.0 (pip show ormguard)"
|
|
13
|
+
validations:
|
|
14
|
+
required: true
|
|
15
|
+
- type: input
|
|
16
|
+
id: python
|
|
17
|
+
attributes:
|
|
18
|
+
label: Python version
|
|
19
|
+
placeholder: "e.g. 3.11"
|
|
20
|
+
validations:
|
|
21
|
+
required: true
|
|
22
|
+
- type: input
|
|
23
|
+
id: sqlalchemy
|
|
24
|
+
attributes:
|
|
25
|
+
label: SQLAlchemy version
|
|
26
|
+
placeholder: "e.g. 2.0.30"
|
|
27
|
+
validations:
|
|
28
|
+
required: true
|
|
29
|
+
- type: input
|
|
30
|
+
id: database
|
|
31
|
+
attributes:
|
|
32
|
+
label: Database / driver
|
|
33
|
+
placeholder: "e.g. PostgreSQL 16 (psycopg), SQLite"
|
|
34
|
+
- type: textarea
|
|
35
|
+
id: what-happened
|
|
36
|
+
attributes:
|
|
37
|
+
label: What happened, and what did you expect?
|
|
38
|
+
validations:
|
|
39
|
+
required: true
|
|
40
|
+
- type: textarea
|
|
41
|
+
id: repro
|
|
42
|
+
attributes:
|
|
43
|
+
label: Minimal reproduction
|
|
44
|
+
description: Model definition plus how you invoke ormguard.
|
|
45
|
+
render: python
|
|
46
|
+
- type: textarea
|
|
47
|
+
id: output
|
|
48
|
+
attributes:
|
|
49
|
+
label: ormguard output / traceback
|
|
50
|
+
render: shell
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
name: Feature request
|
|
2
|
+
description: Suggest an idea or improvement for ormguard
|
|
3
|
+
labels: ["enhancement"]
|
|
4
|
+
body:
|
|
5
|
+
- type: textarea
|
|
6
|
+
id: problem
|
|
7
|
+
attributes:
|
|
8
|
+
label: Problem / motivation
|
|
9
|
+
description: What are you trying to do that ormguard doesn't support today?
|
|
10
|
+
validations:
|
|
11
|
+
required: true
|
|
12
|
+
- type: textarea
|
|
13
|
+
id: proposal
|
|
14
|
+
attributes:
|
|
15
|
+
label: Proposed solution
|
|
16
|
+
- type: textarea
|
|
17
|
+
id: alternatives
|
|
18
|
+
attributes:
|
|
19
|
+
label: Alternatives considered
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
## Summary
|
|
2
|
+
|
|
3
|
+
<!-- What does this PR change, and why? -->
|
|
4
|
+
|
|
5
|
+
## Related issue
|
|
6
|
+
|
|
7
|
+
<!-- e.g. Closes #123 -->
|
|
8
|
+
|
|
9
|
+
## Type of change
|
|
10
|
+
|
|
11
|
+
- [ ] Bug fix
|
|
12
|
+
- [ ] New feature
|
|
13
|
+
- [ ] Refactor / internal
|
|
14
|
+
- [ ] Docs
|
|
15
|
+
- [ ] CI / tooling
|
|
16
|
+
|
|
17
|
+
## Checklist
|
|
18
|
+
|
|
19
|
+
- [ ] Tests pass locally (`pytest`)
|
|
20
|
+
- [ ] Lint passes (`ruff check .`)
|
|
21
|
+
- [ ] Added/updated tests for the change
|
|
22
|
+
- [ ] Updated `CHANGELOG.md` under "Unreleased"
|
|
23
|
+
- [ ] Updated docs / README if behavior changed
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
version: 2
|
|
2
|
+
updates:
|
|
3
|
+
- package-ecosystem: pip
|
|
4
|
+
directory: "/"
|
|
5
|
+
schedule:
|
|
6
|
+
interval: weekly
|
|
7
|
+
groups:
|
|
8
|
+
python-dependencies:
|
|
9
|
+
patterns: ["*"]
|
|
10
|
+
- package-ecosystem: github-actions
|
|
11
|
+
directory: "/"
|
|
12
|
+
schedule:
|
|
13
|
+
interval: weekly
|
|
14
|
+
groups:
|
|
15
|
+
github-actions:
|
|
16
|
+
patterns: ["*"]
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [develop, main]
|
|
6
|
+
pull_request:
|
|
7
|
+
|
|
8
|
+
jobs:
|
|
9
|
+
test:
|
|
10
|
+
name: test (py${{ matrix.python-version }})
|
|
11
|
+
runs-on: ubuntu-latest
|
|
12
|
+
strategy:
|
|
13
|
+
fail-fast: false
|
|
14
|
+
matrix:
|
|
15
|
+
python-version: ["3.9", "3.10", "3.11", "3.12"]
|
|
16
|
+
steps:
|
|
17
|
+
- uses: actions/checkout@v7
|
|
18
|
+
with:
|
|
19
|
+
fetch-depth: 0 # hatch-vcs needs tags to compute the version
|
|
20
|
+
- uses: actions/setup-python@v6
|
|
21
|
+
with:
|
|
22
|
+
python-version: ${{ matrix.python-version }}
|
|
23
|
+
- run: pip install -e ".[dev]"
|
|
24
|
+
- name: Lint
|
|
25
|
+
run: ruff check .
|
|
26
|
+
- name: Unit tests (SQLite, no external DB)
|
|
27
|
+
run: pytest -q
|
|
28
|
+
- name: Self-check smoke test
|
|
29
|
+
run: python -m ormguard --selfcheck --warn-only # selfcheck intentionally finds drift; --warn-only keeps exit 0
|
|
30
|
+
|
|
31
|
+
postgres:
|
|
32
|
+
name: integration (postgres)
|
|
33
|
+
runs-on: ubuntu-latest
|
|
34
|
+
services:
|
|
35
|
+
postgres:
|
|
36
|
+
image: postgres:16
|
|
37
|
+
env:
|
|
38
|
+
POSTGRES_USER: ormguard
|
|
39
|
+
POSTGRES_PASSWORD: ormguard
|
|
40
|
+
POSTGRES_DB: ormguard_test
|
|
41
|
+
ports: ["5432:5432"]
|
|
42
|
+
options: >-
|
|
43
|
+
--health-cmd pg_isready
|
|
44
|
+
--health-interval 10s
|
|
45
|
+
--health-timeout 5s
|
|
46
|
+
--health-retries 5
|
|
47
|
+
steps:
|
|
48
|
+
- uses: actions/checkout@v7
|
|
49
|
+
with:
|
|
50
|
+
fetch-depth: 0 # hatch-vcs needs tags to compute the version
|
|
51
|
+
- uses: actions/setup-python@v6
|
|
52
|
+
with:
|
|
53
|
+
python-version: "3.12"
|
|
54
|
+
- run: pip install -e ".[dev]" "psycopg[binary]"
|
|
55
|
+
- name: Integration tests (real Postgres)
|
|
56
|
+
env:
|
|
57
|
+
DATABASE_URL: postgresql+psycopg://ormguard:ormguard@localhost:5432/ormguard_test
|
|
58
|
+
run: pytest -q -m postgres
|
|
59
|
+
|
|
60
|
+
mysql:
|
|
61
|
+
name: integration (mysql)
|
|
62
|
+
runs-on: ubuntu-latest
|
|
63
|
+
services:
|
|
64
|
+
mysql:
|
|
65
|
+
image: mysql:8
|
|
66
|
+
env:
|
|
67
|
+
MYSQL_ROOT_PASSWORD: ormguard
|
|
68
|
+
MYSQL_DATABASE: ormguard_test
|
|
69
|
+
MYSQL_USER: ormguard
|
|
70
|
+
MYSQL_PASSWORD: ormguard
|
|
71
|
+
ports: ["3306:3306"]
|
|
72
|
+
options: >-
|
|
73
|
+
--health-cmd="mysqladmin ping -h 127.0.0.1 -uroot -pormguard"
|
|
74
|
+
--health-interval=10s
|
|
75
|
+
--health-timeout=5s
|
|
76
|
+
--health-retries=10
|
|
77
|
+
steps:
|
|
78
|
+
- uses: actions/checkout@v7
|
|
79
|
+
with:
|
|
80
|
+
fetch-depth: 0 # hatch-vcs needs tags to compute the version
|
|
81
|
+
- uses: actions/setup-python@v6
|
|
82
|
+
with:
|
|
83
|
+
python-version: "3.12"
|
|
84
|
+
- run: pip install -e ".[dev]" "PyMySQL"
|
|
85
|
+
- name: Integration tests (real MySQL)
|
|
86
|
+
env:
|
|
87
|
+
DATABASE_URL_MYSQL: mysql+pymysql://ormguard:ormguard@127.0.0.1:3306/ormguard_test
|
|
88
|
+
run: pytest -q -m mysql
|
|
89
|
+
|
|
90
|
+
# Posts to Slack/Discord only when CI fails AND the matching webhook secret
|
|
91
|
+
# exists — otherwise every step is skipped, so it stays dormant until set.
|
|
92
|
+
notify-failure:
|
|
93
|
+
name: Notify on failure
|
|
94
|
+
needs: [test, postgres, mysql]
|
|
95
|
+
if: failure()
|
|
96
|
+
runs-on: ubuntu-latest
|
|
97
|
+
env:
|
|
98
|
+
SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }}
|
|
99
|
+
DISCORD_WEBHOOK_URL: ${{ secrets.DISCORD_WEBHOOK_URL }}
|
|
100
|
+
steps:
|
|
101
|
+
- name: Slack notification
|
|
102
|
+
if: ${{ env.SLACK_WEBHOOK_URL != '' }}
|
|
103
|
+
run: |
|
|
104
|
+
curl -sf -X POST -H 'Content-type: application/json' \
|
|
105
|
+
--data "{\"text\":\":red_circle: ormguard CI failed on \`${{ github.ref_name }}\` — ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}\"}" \
|
|
106
|
+
"$SLACK_WEBHOOK_URL"
|
|
107
|
+
- name: Discord notification
|
|
108
|
+
if: ${{ env.DISCORD_WEBHOOK_URL != '' }}
|
|
109
|
+
run: |
|
|
110
|
+
curl -sf -X POST -H 'Content-type: application/json' \
|
|
111
|
+
--data "{\"content\":\":red_circle: ormguard CI failed on \`${{ github.ref_name }}\` — ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}\"}" \
|
|
112
|
+
"$DISCORD_WEBHOOK_URL"
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
name: Release
|
|
2
|
+
|
|
3
|
+
# Publish to PyPI when a version tag (vX.Y.Z) is pushed.
|
|
4
|
+
# Uses PyPI Trusted Publishing (OIDC) — no API token stored.
|
|
5
|
+
# One-time setup: create the project on PyPI and add this repo as a
|
|
6
|
+
# Trusted Publisher (workflow: release.yml, environment: pypi).
|
|
7
|
+
# Until then this workflow is inert (it only runs on tag push).
|
|
8
|
+
|
|
9
|
+
on:
|
|
10
|
+
push:
|
|
11
|
+
tags:
|
|
12
|
+
- "v*"
|
|
13
|
+
|
|
14
|
+
permissions:
|
|
15
|
+
contents: read
|
|
16
|
+
|
|
17
|
+
jobs:
|
|
18
|
+
build:
|
|
19
|
+
name: Build distributions
|
|
20
|
+
runs-on: ubuntu-latest
|
|
21
|
+
steps:
|
|
22
|
+
- uses: actions/checkout@v7
|
|
23
|
+
with:
|
|
24
|
+
fetch-depth: 0 # hatch-vcs derives the version from git tags
|
|
25
|
+
- uses: actions/setup-python@v6
|
|
26
|
+
with:
|
|
27
|
+
python-version: "3.12"
|
|
28
|
+
- run: python -m pip install --upgrade build
|
|
29
|
+
- run: python -m build
|
|
30
|
+
- uses: actions/upload-artifact@v7
|
|
31
|
+
with:
|
|
32
|
+
name: dist
|
|
33
|
+
path: dist/
|
|
34
|
+
|
|
35
|
+
publish:
|
|
36
|
+
name: Publish to PyPI
|
|
37
|
+
needs: build
|
|
38
|
+
runs-on: ubuntu-latest
|
|
39
|
+
environment:
|
|
40
|
+
name: pypi
|
|
41
|
+
url: https://pypi.org/p/ormguard
|
|
42
|
+
permissions:
|
|
43
|
+
id-token: write # required for Trusted Publishing
|
|
44
|
+
steps:
|
|
45
|
+
- uses: actions/download-artifact@v8
|
|
46
|
+
with:
|
|
47
|
+
name: dist
|
|
48
|
+
path: dist/
|
|
49
|
+
- uses: pypa/gh-action-pypi-publish@release/v1
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# Dev hooks for this repo. Install with: pre-commit install
|
|
2
|
+
repos:
|
|
3
|
+
- repo: https://github.com/astral-sh/ruff-pre-commit
|
|
4
|
+
rev: v0.6.9
|
|
5
|
+
hooks:
|
|
6
|
+
- id: ruff
|
|
7
|
+
args: [--fix]
|
|
8
|
+
- id: ruff-format
|
|
9
|
+
|
|
10
|
+
- repo: local
|
|
11
|
+
hooks:
|
|
12
|
+
- id: ormguard-selfcheck
|
|
13
|
+
name: ormguard selfcheck
|
|
14
|
+
entry: python -m ormguard --selfcheck --warn-only
|
|
15
|
+
language: system
|
|
16
|
+
pass_filenames: false
|
|
17
|
+
always_run: true
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# Hooks that downstream projects can use by adding ormguard to their
|
|
2
|
+
# .pre-commit-config.yaml:
|
|
3
|
+
#
|
|
4
|
+
# - repo: https://github.com/gogo1414/ormguard
|
|
5
|
+
# rev: v0.1.0
|
|
6
|
+
# hooks:
|
|
7
|
+
# - id: ormguard
|
|
8
|
+
# args: ["--url", "sqlite:///app.db", "--metadata", "myapp.db:Base"]
|
|
9
|
+
#
|
|
10
|
+
# Or the zero-config smoke test:
|
|
11
|
+
#
|
|
12
|
+
# - repo: https://github.com/gogo1414/ormguard
|
|
13
|
+
# rev: v0.1.0
|
|
14
|
+
# hooks:
|
|
15
|
+
# - id: ormguard-selfcheck
|
|
16
|
+
|
|
17
|
+
- id: ormguard
|
|
18
|
+
name: ormguard schema validation
|
|
19
|
+
description: Validate that the database schema matches your SQLAlchemy models.
|
|
20
|
+
entry: ormguard
|
|
21
|
+
language: python
|
|
22
|
+
pass_filenames: false
|
|
23
|
+
always_run: true
|
|
24
|
+
|
|
25
|
+
- id: ormguard-selfcheck
|
|
26
|
+
name: ormguard selfcheck
|
|
27
|
+
description: Run ormguard's self-contained SQLite demo (no database required).
|
|
28
|
+
entry: ormguard --selfcheck --warn-only
|
|
29
|
+
language: python
|
|
30
|
+
pass_filenames: false
|
|
31
|
+
always_run: true
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project are documented here. The format is based on
|
|
4
|
+
[Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project
|
|
5
|
+
adheres to [Semantic Versioning](https://semver.org/).
|
|
6
|
+
|
|
7
|
+
## [Unreleased]
|
|
8
|
+
|
|
9
|
+
### Fixed
|
|
10
|
+
|
|
11
|
+
- CLI no longer crashes with `UnicodeEncodeError` on legacy consoles (e.g.
|
|
12
|
+
Windows cp949); stdout/stderr are reconfigured to UTF-8. (#12)
|
|
13
|
+
|
|
14
|
+
### Documentation
|
|
15
|
+
|
|
16
|
+
- Added a top-of-README "Quickstart (60 seconds)" with copy-paste install,
|
|
17
|
+
`--selfcheck`, FastAPI guard, and CI snippets. (#11)
|
|
18
|
+
|
|
19
|
+
### Added
|
|
20
|
+
|
|
21
|
+
- Optional enum validation (`Config(check_enums=True)`): emits `enum_mismatch`
|
|
22
|
+
when an enum column's allowed values differ between the ORM and the database
|
|
23
|
+
(native enums on Postgres/MySQL), naming the differing values. Opt-in, WARN,
|
|
24
|
+
and skipped when either side exposes no enum values. (#5)
|
|
25
|
+
- Optional CHECK-constraint validation (`Config(check_constraints=True)`): emits
|
|
26
|
+
`check_missing` / `check_extra`, compared by constraint *name* only (the
|
|
27
|
+
expression text is dialect-rewritten on reflection, so it is not diffed);
|
|
28
|
+
unnamed constraints are skipped. Opt-in, WARN. (#5)
|
|
29
|
+
- Webhook notifications: `--notify-webhook URL` (and `--notify-on error|any`)
|
|
30
|
+
POST the report to a Slack- or Discord-compatible incoming webhook when drift
|
|
31
|
+
is found — one payload works for both, standard library only, best-effort.
|
|
32
|
+
Also exposed as a `notify-webhook` input on the GitHub Action. (#22)
|
|
33
|
+
- Reusable GitHub Action (`action.yml`): run ormguard in any repo's CI with a
|
|
34
|
+
few lines — inputs for `database-url`, `metadata`, extra `args`, install
|
|
35
|
+
`version`, and `python-version`. (#8)
|
|
36
|
+
- Optional server-default validation (`Config(check_server_defaults=True)` /
|
|
37
|
+
`--check-defaults`): emits `default_missing` / `default_extra` by comparing
|
|
38
|
+
*presence* of a DB default (not its value, which is dialect-dependent),
|
|
39
|
+
skipping primary keys. Opt-in, defaults to WARN. (#4)
|
|
40
|
+
- MySQL integration test + CI job (`mysql` marker) alongside the existing
|
|
41
|
+
Postgres one, covering the MySQL dialect's reflection. (#7)
|
|
42
|
+
- Optional index validation (`Config(check_indexes=True)` / `--check-indexes`):
|
|
43
|
+
emits `index_missing` / `index_extra`, compared by column set + uniqueness,
|
|
44
|
+
skipping PK/unique-constraint-backed indexes. Opt-in, defaults to WARN. (#2)
|
|
45
|
+
- Optional foreign-key validation (`Config(check_foreign_keys=True)` /
|
|
46
|
+
`--check-foreign-keys`): emits `fk_missing` / `fk_extra`, compared by local
|
|
47
|
+
columns + referred table + referred columns. Opt-in, defaults to WARN. (#3)
|
|
48
|
+
|
|
49
|
+
- Contribution scaffolding: issue forms (bug / feature), PR template,
|
|
50
|
+
`CONTRIBUTING.md`, `CHANGELOG.md`, `CLAUDE.md`.
|
|
51
|
+
- Dependabot for pip and GitHub Actions.
|
|
52
|
+
- Release workflow: publishes to PyPI via Trusted Publishing on `v*` tags.
|
|
53
|
+
- CodeRabbit AI code review on pull requests (via the CodeRabbit GitHub App,
|
|
54
|
+
configured by `.coderabbit.yaml`).
|
|
55
|
+
- CI failure notifications to Slack/Discord (dormant until a webhook is set).
|
|
56
|
+
- `pre-commit` hooks: `ormguard-selfcheck` and `ormguard` for downstream users,
|
|
57
|
+
plus a dev config (ruff + selfcheck).
|
|
58
|
+
|
|
59
|
+
### Changed
|
|
60
|
+
|
|
61
|
+
- `type_mismatch` now normalizes types per dialect before comparing — folds
|
|
62
|
+
`INT(11)`/`INTEGER`, `CHARACTER VARYING`/`VARCHAR`, `DOUBLE PRECISION`/`DOUBLE`,
|
|
63
|
+
`TINYINT(1)`/`BOOLEAN`, and `TIMESTAMP` spellings — so `check_types` produces
|
|
64
|
+
far fewer false positives (genuine length/precision/tz differences still
|
|
65
|
+
flagged). (#6)
|
|
66
|
+
- Version is now derived from git tags via `hatch-vcs` (single source of truth);
|
|
67
|
+
removed the duplicated hardcoded version in `pyproject.toml` and
|
|
68
|
+
`ormguard/__init__.py`.
|
|
69
|
+
|
|
70
|
+
## [0.1.0] - 2026-06
|
|
71
|
+
|
|
72
|
+
### Added
|
|
73
|
+
|
|
74
|
+
- Initial proof of concept: fail-fast schema validation for SQLAlchemy.
|
|
75
|
+
- `validate`, `assert_schema`, `validate_many`, `format_matrix` public API.
|
|
76
|
+
- FastAPI startup guard (`schema_guard_lifespan`).
|
|
77
|
+
- CLI (`python -m ormguard`) with `--selfcheck`.
|
|
78
|
+
- Checks: `table_missing`, `column_missing`, `column_extra`,
|
|
79
|
+
`nullable_mismatch`, opt-in `type_mismatch`.
|
|
80
|
+
|
|
81
|
+
[Unreleased]: https://github.com/gogo1414/ormguard/compare/v0.1.0...HEAD
|
|
82
|
+
[0.1.0]: https://github.com/gogo1414/ormguard/releases/tag/v0.1.0
|
ormguard-0.1.0/CLAUDE.md
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# CLAUDE.md
|
|
2
|
+
|
|
3
|
+
Guidance for Claude Code (and other AI assistants) working in this repo.
|
|
4
|
+
|
|
5
|
+
## What ormguard is
|
|
6
|
+
|
|
7
|
+
Fail-fast schema validation for SQLAlchemy — the equivalent of Hibernate's
|
|
8
|
+
`hibernate.ddl-auto=validate`. At startup (or in CI) it reflects the connected
|
|
9
|
+
database and checks that it matches the ORM entities, catching entity↔DB drift
|
|
10
|
+
at boot instead of as a runtime `column does not exist` error.
|
|
11
|
+
|
|
12
|
+
## Commands
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
python -m pip install -e ".[dev]" # setup
|
|
16
|
+
pytest # unit tests (SQLite, no external DB)
|
|
17
|
+
ruff check . # lint (line-length 110)
|
|
18
|
+
python -m ormguard --selfcheck # self-contained end-to-end demo
|
|
19
|
+
pytest -m postgres # integration tests (needs DATABASE_URL)
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
## Layout
|
|
23
|
+
|
|
24
|
+
- `ormguard/core.py` — `validate()` / `assert_schema()` orchestration.
|
|
25
|
+
- `ormguard/reflect.py` — reflect the live DB schema.
|
|
26
|
+
- `ormguard/orm.py`, `ormguard/model.py` — read ORM metadata; `Finding`,
|
|
27
|
+
`Severity`, `ValidationReport`, `SchemaValidationError`.
|
|
28
|
+
- `ormguard/diff.py` — compare reflected schema vs ORM, produce findings.
|
|
29
|
+
- `ormguard/config.py` — `Config` (schemas, ignores, severity toggles).
|
|
30
|
+
- `ormguard/cli.py` — `python -m ormguard` argument parsing.
|
|
31
|
+
- `ormguard/selfcheck.py` — in-memory SQLite demo with deliberate drift.
|
|
32
|
+
- `ormguard/integrations/` — framework hooks (e.g. FastAPI lifespan).
|
|
33
|
+
- `tests/` — SQLite by default; `@pytest.mark.postgres` for real Postgres.
|
|
34
|
+
- `docs/` — DESIGN.md, USAGE.md, V2_OFFLINE_REPLAY.md.
|
|
35
|
+
|
|
36
|
+
## Conventions
|
|
37
|
+
|
|
38
|
+
- Public API is what `ormguard/__init__.py` exports — keep it stable; note
|
|
39
|
+
breaking changes in `CHANGELOG.md`.
|
|
40
|
+
- Support SQLAlchemy >= 1.4 and Python 3.9–3.12 (the CI matrix).
|
|
41
|
+
- Type/dialect comparisons are dialect-sensitive — keep `type_mismatch` opt-in.
|
|
42
|
+
- Version is derived from git tags via `hatch-vcs`; never hardcode a version.
|
|
43
|
+
- Work on a branch and open a PR; do not push to `main` directly.
|
|
44
|
+
- Add tests for behavior changes; `pytest` and `ruff check .` must pass.
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
# Contributing to ormguard
|
|
2
|
+
|
|
3
|
+
Thanks for your interest in improving ormguard! This project is small and
|
|
4
|
+
welcomes issues and pull requests.
|
|
5
|
+
|
|
6
|
+
## Development setup
|
|
7
|
+
|
|
8
|
+
```bash
|
|
9
|
+
git clone https://github.com/gogo1414/ormguard.git
|
|
10
|
+
cd ormguard
|
|
11
|
+
python -m pip install -e ".[dev]"
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
## Everyday commands
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
pytest # unit tests (in-memory SQLite, no external DB)
|
|
18
|
+
ruff check . # lint
|
|
19
|
+
python -m ormguard --selfcheck # end-to-end smoke test against SQLite drift
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
Postgres integration tests are opt-in (they need a real database):
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
export DATABASE_URL="postgresql+psycopg://user:pass@localhost:5432/db"
|
|
26
|
+
pytest -m postgres
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
### Optional: pre-commit
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
pip install pre-commit
|
|
33
|
+
pre-commit install # runs ruff + selfcheck on every commit
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
## Pull request workflow
|
|
37
|
+
|
|
38
|
+
1. Branch from `main` (`feat/...`, `fix/...`, `chore/...`).
|
|
39
|
+
2. Make the change with tests. Keep the public API in `ormguard/__init__.py` stable.
|
|
40
|
+
3. Add an entry to `CHANGELOG.md` under **Unreleased**.
|
|
41
|
+
4. Open a PR — the template checklist covers tests, lint, and changelog.
|
|
42
|
+
5. CI (Python 3.9–3.12 + Postgres integration) must pass before merge.
|
|
43
|
+
|
|
44
|
+
Direct pushes to `main` are avoided; everything goes through a PR.
|
|
45
|
+
|
|
46
|
+
## Versioning & releases
|
|
47
|
+
|
|
48
|
+
Versions come from git tags via `hatch-vcs` — there is **no version string to
|
|
49
|
+
edit by hand**. To cut a release:
|
|
50
|
+
|
|
51
|
+
```bash
|
|
52
|
+
git tag v0.2.0
|
|
53
|
+
git push origin v0.2.0
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
The `Release` workflow builds the package and publishes it to PyPI via Trusted
|
|
57
|
+
Publishing. We follow [Semantic Versioning](https://semver.org/).
|
ormguard-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 JuneSoo
|
|
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.
|