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.
Files changed (51) hide show
  1. ormguard-0.1.0/.coderabbit.yaml +18 -0
  2. ormguard-0.1.0/.github/ISSUE_TEMPLATE/bug_report.yml +50 -0
  3. ormguard-0.1.0/.github/ISSUE_TEMPLATE/config.yml +5 -0
  4. ormguard-0.1.0/.github/ISSUE_TEMPLATE/feature_request.yml +19 -0
  5. ormguard-0.1.0/.github/PULL_REQUEST_TEMPLATE.md +23 -0
  6. ormguard-0.1.0/.github/dependabot.yml +16 -0
  7. ormguard-0.1.0/.github/workflows/ci.yml +112 -0
  8. ormguard-0.1.0/.github/workflows/release.yml +49 -0
  9. ormguard-0.1.0/.gitignore +15 -0
  10. ormguard-0.1.0/.pre-commit-config.yaml +17 -0
  11. ormguard-0.1.0/.pre-commit-hooks.yaml +31 -0
  12. ormguard-0.1.0/CHANGELOG.md +82 -0
  13. ormguard-0.1.0/CLAUDE.md +44 -0
  14. ormguard-0.1.0/CONTRIBUTING.md +57 -0
  15. ormguard-0.1.0/LICENSE +21 -0
  16. ormguard-0.1.0/PKG-INFO +197 -0
  17. ormguard-0.1.0/README.md +174 -0
  18. ormguard-0.1.0/action.yml +54 -0
  19. ormguard-0.1.0/docs/DESIGN.md +119 -0
  20. ormguard-0.1.0/docs/USAGE.md +93 -0
  21. ormguard-0.1.0/docs/V2_OFFLINE_REPLAY.md +126 -0
  22. ormguard-0.1.0/examples/fastapi_app.py +38 -0
  23. ormguard-0.1.0/examples/validate_aace_tenant.py +66 -0
  24. ormguard-0.1.0/ormguard/__init__.py +42 -0
  25. ormguard-0.1.0/ormguard/__main__.py +5 -0
  26. ormguard-0.1.0/ormguard/_schema.py +75 -0
  27. ormguard-0.1.0/ormguard/_version.py +24 -0
  28. ormguard-0.1.0/ormguard/cli.py +120 -0
  29. ormguard-0.1.0/ormguard/config.py +78 -0
  30. ormguard-0.1.0/ormguard/core.py +71 -0
  31. ormguard-0.1.0/ormguard/diff.py +256 -0
  32. ormguard-0.1.0/ormguard/integrations/__init__.py +0 -0
  33. ormguard-0.1.0/ormguard/integrations/fastapi.py +41 -0
  34. ormguard-0.1.0/ormguard/model.py +111 -0
  35. ormguard-0.1.0/ormguard/notify.py +44 -0
  36. ormguard-0.1.0/ormguard/orm.py +80 -0
  37. ormguard-0.1.0/ormguard/reflect.py +126 -0
  38. ormguard-0.1.0/ormguard/selfcheck.py +63 -0
  39. ormguard-0.1.0/ormguard/types.py +120 -0
  40. ormguard-0.1.0/pyproject.toml +56 -0
  41. ormguard-0.1.0/tests/test_aace_drift_cases.py +76 -0
  42. ormguard-0.1.0/tests/test_checks_enums.py +81 -0
  43. ormguard-0.1.0/tests/test_cli.py +19 -0
  44. ormguard-0.1.0/tests/test_core_sqlite.py +84 -0
  45. ormguard-0.1.0/tests/test_defaults.py +50 -0
  46. ormguard-0.1.0/tests/test_foreign_keys.py +62 -0
  47. ormguard-0.1.0/tests/test_indexes.py +55 -0
  48. ormguard-0.1.0/tests/test_mysql_integration.py +66 -0
  49. ormguard-0.1.0/tests/test_notify.py +45 -0
  50. ormguard-0.1.0/tests/test_postgres_integration.py +69 -0
  51. 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,5 @@
1
+ blank_issues_enabled: false
2
+ contact_links:
3
+ - name: Question / discussion
4
+ url: https://github.com/gogo1414/ormguard/discussions
5
+ about: Ask usage questions and share ideas (enable Discussions in repo settings).
@@ -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,15 @@
1
+ __pycache__/
2
+ *.py[cod]
3
+ *.egg-info/
4
+ .eggs/
5
+ build/
6
+ dist/
7
+ .venv/
8
+ venv/
9
+ .pytest_cache/
10
+ .ruff_cache/
11
+ *.db
12
+ .DS_Store
13
+
14
+ # Generated by hatch-vcs at build time (version derived from git tags)
15
+ ormguard/_version.py
@@ -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
@@ -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.