pyverge 0.2.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 (72) hide show
  1. pyverge-0.2.0/.editorconfig +15 -0
  2. pyverge-0.2.0/.github/workflows/ci.yml +32 -0
  3. pyverge-0.2.0/.github/workflows/release.yml +141 -0
  4. pyverge-0.2.0/.gitignore +225 -0
  5. pyverge-0.2.0/.pre-commit-config.yaml +46 -0
  6. pyverge-0.2.0/CHANGELOG.md +64 -0
  7. pyverge-0.2.0/CONTRIBUTING.md +97 -0
  8. pyverge-0.2.0/LICENSE +201 -0
  9. pyverge-0.2.0/PKG-INFO +149 -0
  10. pyverge-0.2.0/README.md +131 -0
  11. pyverge-0.2.0/docs/concepts.md +324 -0
  12. pyverge-0.2.0/docs/getting-started.md +142 -0
  13. pyverge-0.2.0/mkdocs.yml +22 -0
  14. pyverge-0.2.0/pyproject.toml +81 -0
  15. pyverge-0.2.0/showcases/README.md +38 -0
  16. pyverge-0.2.0/showcases/configuration-management.md +118 -0
  17. pyverge-0.2.0/showcases/document-storage.md +107 -0
  18. pyverge-0.2.0/showcases/etl-pipelines.md +124 -0
  19. pyverge-0.2.0/showcases/event-sourcing.md +133 -0
  20. pyverge-0.2.0/showcases/iot-telemetry.md +111 -0
  21. pyverge-0.2.0/showcases/kafka-consumers.md +118 -0
  22. pyverge-0.2.0/showcases/mqtt-iot.md +106 -0
  23. pyverge-0.2.0/showcases/rabbitmq-streams.md +112 -0
  24. pyverge-0.2.0/src/pyverge/__init__.py +9 -0
  25. pyverge-0.2.0/src/pyverge/_version.py +24 -0
  26. pyverge-0.2.0/src/pyverge/cli/__init__.py +1 -0
  27. pyverge-0.2.0/src/pyverge/cli/_helpers.py +206 -0
  28. pyverge-0.2.0/src/pyverge/cli/config.py +64 -0
  29. pyverge-0.2.0/src/pyverge/cli/main.py +175 -0
  30. pyverge-0.2.0/src/pyverge/migration/__init__.py +86 -0
  31. pyverge-0.2.0/src/pyverge/migration/adapters.py +164 -0
  32. pyverge-0.2.0/src/pyverge/migration/diff.py +243 -0
  33. pyverge-0.2.0/src/pyverge/migration/engine.py +518 -0
  34. pyverge-0.2.0/src/pyverge/migration/exceptions.py +244 -0
  35. pyverge-0.2.0/src/pyverge/migration/executor.py +242 -0
  36. pyverge-0.2.0/src/pyverge/migration/graph.py +331 -0
  37. pyverge-0.2.0/src/pyverge/migration/hooks.py +119 -0
  38. pyverge-0.2.0/src/pyverge/migration/manager.py +696 -0
  39. pyverge-0.2.0/src/pyverge/migration/models.py +120 -0
  40. pyverge-0.2.0/src/pyverge/migration/policy.py +125 -0
  41. pyverge-0.2.0/src/pyverge/migration/registry.py +434 -0
  42. pyverge-0.2.0/src/pyverge/migration/steps.py +51 -0
  43. pyverge-0.2.0/src/pyverge/migration/strategy.py +171 -0
  44. pyverge-0.2.0/src/pyverge/migration/types.py +391 -0
  45. pyverge-0.2.0/src/pyverge/migration/versioning.py +300 -0
  46. pyverge-0.2.0/src/pyverge/migration/walker.py +282 -0
  47. pyverge-0.2.0/src/pyverge/py.typed +0 -0
  48. pyverge-0.2.0/tests/__init__.py +0 -0
  49. pyverge-0.2.0/tests/__snapshots__/test_cli_config.ambr +4 -0
  50. pyverge-0.2.0/tests/__snapshots__/test_model_manager.ambr +325 -0
  51. pyverge-0.2.0/tests/cli/__snapshots__/test_cli.ambr +56 -0
  52. pyverge-0.2.0/tests/cli/test_cli.py +143 -0
  53. pyverge-0.2.0/tests/cli/test_config.py +56 -0
  54. pyverge-0.2.0/tests/conftest.py +165 -0
  55. pyverge-0.2.0/tests/examples/README.md +32 -0
  56. pyverge-0.2.0/tests/examples/__init__.py +0 -0
  57. pyverge-0.2.0/tests/examples/pydantic/base.py +50 -0
  58. pyverge-0.2.0/tests/examples/pydantic/chrono.py +106 -0
  59. pyverge-0.2.0/tests/examples/pydantic/chrono_nested.py +70 -0
  60. pyverge-0.2.0/tests/examples/pydantic/semver.py +115 -0
  61. pyverge-0.2.0/tests/examples/pydantic/semver_nested.py +124 -0
  62. pyverge-0.2.0/tests/test_engine.py +1089 -0
  63. pyverge-0.2.0/tests/test_executor.py +355 -0
  64. pyverge-0.2.0/tests/test_graph.py +762 -0
  65. pyverge-0.2.0/tests/test_manager.py +681 -0
  66. pyverge-0.2.0/tests/test_policy.py +151 -0
  67. pyverge-0.2.0/tests/test_registry.py +1295 -0
  68. pyverge-0.2.0/tests/test_strategy.py +231 -0
  69. pyverge-0.2.0/tests/test_version_property.py +782 -0
  70. pyverge-0.2.0/tests/test_walker.py +485 -0
  71. pyverge-0.2.0/tests/utils.py +125 -0
  72. pyverge-0.2.0/uv.lock +1911 -0
@@ -0,0 +1,15 @@
1
+ root = true
2
+
3
+ [*]
4
+ charset = utf-8
5
+ end_of_line = lf
6
+ insert_final_newline = true
7
+ trim_trailing_whitespace = true
8
+
9
+ [*.py]
10
+ indent_size = 4
11
+ indent_style = space
12
+
13
+ [*.{md,yaml,yml,toml,json}]
14
+ indent_size = 2
15
+ indent_style = space
@@ -0,0 +1,32 @@
1
+ name: CI
2
+
3
+ on:
4
+ pull_request:
5
+ branches: [main]
6
+ push:
7
+ branches: [main]
8
+
9
+ env:
10
+ UV_FROZEN: "1"
11
+
12
+ jobs:
13
+ check:
14
+ runs-on: ubuntu-latest
15
+ steps:
16
+ - uses: actions/checkout@v7.0.1
17
+ with:
18
+ fetch-depth: 0
19
+ - uses: astral-sh/setup-uv@v10.0.1
20
+ with:
21
+ python-version: "3.11"
22
+ - run: uv sync --group dev --all-extras
23
+ - run: uv run pre-commit run --all-files
24
+ - name: Check conventional commits
25
+ if: github.event_name == 'pull_request'
26
+ run: |
27
+ BASE=$(git merge-base origin/main HEAD)
28
+ uv run cz check --rev-range "$BASE..HEAD"
29
+ - name: Validate changelog (dry-run)
30
+ if: github.event_name == 'pull_request'
31
+ run: |
32
+ uv run cz changelog --dry-run
@@ -0,0 +1,141 @@
1
+ name: GitHub Release
2
+
3
+ on:
4
+ workflow_dispatch:
5
+ pull_request:
6
+ types:
7
+ - closed
8
+ branches:
9
+ - main
10
+
11
+ concurrency:
12
+ group: release-${{ github.ref }}
13
+ cancel-in-progress: false
14
+
15
+ jobs:
16
+ tag:
17
+ if: ${{ github.event_name == 'workflow_dispatch' || github.event.pull_request.merged == true }}
18
+ runs-on: ubuntu-latest
19
+ permissions:
20
+ contents: write
21
+ packages: write
22
+ steps:
23
+ - uses: actions/checkout@v7.0.1
24
+ with:
25
+ fetch-depth: 0
26
+ token: ${{ secrets.PUBLISHING_PAT }}
27
+ - uses: astral-sh/setup-uv@v10.0.1
28
+ with:
29
+ python-version: "3.11"
30
+ - run: uv sync --group dev --all-extras
31
+ - name: Extract ticket ID from latest commit
32
+ run: |
33
+ TICKET_ID=$(git log -1 --pretty=format:"%s" | grep -oE "#[0-9]+" | head -1)
34
+ if [ -z "$TICKET_ID" ]; then
35
+ echo "No ticket ID found in latest commit. Aborting."
36
+ exit 1
37
+ fi
38
+ echo "TICKET_ID=$TICKET_ID" >> "$GITHUB_ENV"
39
+ echo "ticket_id=$TICKET_ID"
40
+ - name: Bump version, changelog, and tag
41
+ id: version
42
+ run: |
43
+ git config user.name "github-actions[bot]"
44
+ git config user.email "github-actions[bot]@users.noreply.github.com"
45
+
46
+ # Remember the previous tag so we can list the changes in this release
47
+ PREV_TAG=$(git describe --tags --abbrev=0 2>/dev/null || echo "")
48
+
49
+ uv run cz bump --yes --changelog --bump-message "chore(${{ env.TICKET_ID }}): release \$new_version [skip ci]"
50
+
51
+ VERSION=$(git describe --tags --abbrev=0)
52
+ echo "version=$VERSION" >> "$GITHUB_OUTPUT"
53
+ echo "next_version=$VERSION"
54
+
55
+ # Build a human-readable list of changes since the previous release
56
+ if [ -n "$PREV_TAG" ] && [ "$PREV_TAG" != "$VERSION" ]; then
57
+ FEATURES=$(git log --no-merges --pretty=format:"- %s" "$PREV_TAG..HEAD~1" || true)
58
+ else
59
+ FEATURES="No previous tag found."
60
+ fi
61
+ [ -z "$FEATURES" ] && FEATURES="- No conventional changes since last release."
62
+
63
+ # Amend the release commit so it actually describes what was deployed
64
+ git commit --amend \
65
+ -m "chore(${{ env.TICKET_ID }}): release $VERSION [skip ci]" \
66
+ -m "Features deployed:" \
67
+ -m "$FEATURES"
68
+
69
+ # Move the lightweight version tag to the amended commit
70
+ git tag -f "$VERSION"
71
+
72
+ git push origin HEAD
73
+ git push origin "$VERSION" --force
74
+ - name: Build package
75
+ run: uv build
76
+ - name: Upload build artifacts
77
+ uses: actions/upload-artifact@v7.0.1
78
+ with:
79
+ name: dist
80
+ path: dist/
81
+ if-no-files-found: error
82
+ outputs:
83
+ version: ${{ steps.version.outputs.version }}
84
+
85
+ release:
86
+ needs: tag
87
+ if: ${{ needs.tag.outputs.version != 'none' && needs.tag.outputs.version != '' }}
88
+ runs-on: ubuntu-latest
89
+ permissions:
90
+ contents: write
91
+ packages: write
92
+ steps:
93
+ - uses: actions/checkout@v7.0.1
94
+ with:
95
+ fetch-depth: 0
96
+ ref: ${{ needs.tag.outputs.version }}
97
+ - uses: astral-sh/setup-uv@v10.0.1
98
+ with:
99
+ python-version: "3.11"
100
+ - run: uv sync --group dev --all-extras
101
+ - name: Download build artifacts
102
+ uses: actions/download-artifact@v8.0.1
103
+ with:
104
+ name: dist
105
+ path: dist/
106
+ - name: Generate release notes
107
+ run: uv run cz changelog "${{ needs.tag.outputs.version }}" > RELEASE_NOTES.md
108
+ - name: Create GitHub Release
109
+ env:
110
+ GH_TOKEN: ${{ secrets.PUBLISHING_PAT }}
111
+ run: |
112
+ gh release create "${{ needs.tag.outputs.version }}" \
113
+ --title "Release ${{ needs.tag.outputs.version }}" \
114
+ --notes-file RELEASE_NOTES.md \
115
+ dist/*
116
+
117
+ publish:
118
+ needs: [tag, release]
119
+ if: ${{ needs.tag.outputs.version != 'none' && needs.tag.outputs.version != '' }}
120
+ runs-on: ubuntu-latest
121
+ permissions:
122
+ contents: read
123
+ steps:
124
+ - uses: actions/checkout@v7.0.1
125
+ with:
126
+ fetch-depth: 0
127
+ ref: ${{ needs.tag.outputs.version }}
128
+ - uses: astral-sh/setup-uv@v10.0.1
129
+ with:
130
+ python-version: "3.11"
131
+ - run: uv sync --group dev --all-extras
132
+ - name: Download build artifacts
133
+ uses: actions/download-artifact@v8.0.1
134
+ with:
135
+ name: dist
136
+ path: dist/
137
+ - name: Publish to PyPI
138
+ env:
139
+ UV_PUBLISH_URL: https://upload.pypi.org/legacy/
140
+ UV_PUBLISH_TOKEN: ${{ secrets.PYPI_TOKEN }}
141
+ run: uv publish dist/*
@@ -0,0 +1,225 @@
1
+ # Byte-compiled / optimized / DLL files
2
+ __pycache__/
3
+ *.py[codz]
4
+ *$py.class
5
+
6
+ # C extensions
7
+ *.so
8
+
9
+ # Distribution / packaging
10
+ .Python
11
+ build/
12
+ develop-eggs/
13
+ dist/
14
+ downloads/
15
+ eggs/
16
+ .eggs/
17
+ lib/
18
+ lib64/
19
+ parts/
20
+ sdist/
21
+ var/
22
+ wheels/
23
+ share/python-wheels/
24
+ *.egg-info/
25
+ .installed.cfg
26
+ *.egg
27
+ MANIFEST
28
+
29
+ # PyInstaller
30
+ # Usually these files are written by a python script from a template
31
+ # before PyInstaller builds the exe, so as to inject date/other infos into it.
32
+ *.manifest
33
+ *.spec
34
+
35
+ # Installer logs
36
+ pip-log.txt
37
+ pip-delete-this-directory.txt
38
+
39
+ # Unit test / coverage reports
40
+ htmlcov/
41
+ .tox/
42
+ .nox/
43
+ .coverage
44
+ .coverage.*
45
+ .cache
46
+ nosetests.xml
47
+ coverage.xml
48
+ *.cover
49
+ *.py.cover
50
+ .hypothesis/
51
+ .pytest_cache/
52
+ cover/
53
+
54
+ # Translations
55
+ *.mo
56
+ *.pot
57
+
58
+ # Django stuff:
59
+ *.log
60
+ local_settings.py
61
+ db.sqlite3
62
+ db.sqlite3-journal
63
+
64
+ # Flask stuff:
65
+ instance/
66
+ .webassets-cache
67
+
68
+ # Scrapy stuff:
69
+ .scrapy
70
+
71
+ # Sphinx documentation
72
+ docs/_build/
73
+
74
+ # PyBuilder
75
+ .pybuilder/
76
+ target/
77
+
78
+ # Jupyter Notebook
79
+ .ipynb_checkpoints
80
+
81
+ # IPython
82
+ profile_default/
83
+ ipython_config.py
84
+
85
+ # Hatch vcs
86
+ src/pyverge/_version.py
87
+
88
+ # pyenv
89
+ # For a library or package, you might want to ignore these files since the code is
90
+ # intended to run in multiple environments; otherwise, check them in:
91
+ # .python-version
92
+
93
+ # pipenv
94
+ # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
95
+ # However, in case of collaboration, if having platform-specific dependencies or dependencies
96
+ # having no cross-platform support, pipenv may install dependencies that don't work, or not
97
+ # install all needed dependencies.
98
+ # Pipfile.lock
99
+
100
+ # UV
101
+ # Similar to Pipfile.lock, it is generally recommended to include uv.lock in version control.
102
+ # This is especially recommended for binary packages to ensure reproducibility, and is more
103
+ # commonly ignored for libraries.
104
+ # uv.lock
105
+
106
+ # poetry
107
+ # Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
108
+ # This is especially recommended for binary packages to ensure reproducibility, and is more
109
+ # commonly ignored for libraries.
110
+ # https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
111
+ # poetry.lock
112
+ # poetry.toml
113
+
114
+ # pdm
115
+ # Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
116
+ # pdm recommends including project-wide configuration in pdm.toml, but excluding .pdm-python.
117
+ # https://pdm-project.org/en/latest/usage/project/#working-with-version-control
118
+ # pdm.lock
119
+ # pdm.toml
120
+ .pdm-python
121
+ .pdm-build/
122
+
123
+ # pixi
124
+ # Similar to Pipfile.lock, it is generally recommended to include pixi.lock in version control.
125
+ # pixi.lock
126
+ # Pixi creates a virtual environment in the .pixi directory, just like venv module creates one
127
+ # in the .venv directory. It is recommended not to include this directory in version control.
128
+ .pixi
129
+
130
+ # PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
131
+ __pypackages__/
132
+
133
+ # Celery stuff
134
+ celerybeat-schedule
135
+ celerybeat.pid
136
+
137
+ # Redis
138
+ *.rdb
139
+ *.aof
140
+ *.pid
141
+
142
+ # RabbitMQ
143
+ mnesia/
144
+ rabbitmq/
145
+ rabbitmq-data/
146
+
147
+ # ActiveMQ
148
+ activemq-data/
149
+
150
+ # SageMath parsed files
151
+ *.sage.py
152
+
153
+ # Environments
154
+ .env
155
+ .envrc
156
+ .venv
157
+ env/
158
+ venv/
159
+ ENV/
160
+ env.bak/
161
+ venv.bak/
162
+
163
+ # Spyder project settings
164
+ .spyderproject
165
+ .spyproject
166
+
167
+ # Rope project settings
168
+ .ropeproject
169
+
170
+ # mkdocs documentation
171
+ /site
172
+
173
+ # mypy
174
+ .mypy_cache/
175
+ .dmypy.json
176
+ dmypy.json
177
+
178
+ # Pyre type checker
179
+ .pyre/
180
+
181
+ # pytype static type analyzer
182
+ .pytype/
183
+
184
+ # Cython debug symbols
185
+ cython_debug/
186
+
187
+ # PyCharm
188
+ # JetBrains specific template is maintained in a separate JetBrains.gitignore that can
189
+ # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
190
+ # and can be added to the global gitignore or merged into this file. For a more nuclear
191
+ # option (not recommended) you can uncomment the following to ignore the entire idea folder.
192
+ # .idea/
193
+
194
+ # Abstra
195
+ # Abstra is an AI-powered process automation framework.
196
+ # Ignore directories containing user credentials, local state, and settings.
197
+ # Learn more at https://abstra.io/docs
198
+ .abstra/
199
+
200
+ # Visual Studio Code
201
+ # Visual Studio Code specific template is maintained in a separate VisualStudioCode.gitignore
202
+ # that can be found at https://github.com/github/gitignore/blob/main/Global/VisualStudioCode.gitignore
203
+ # and can be added to the global gitignore or merged into this file. However, if you prefer,
204
+ # you could uncomment the following to ignore the entire vscode folder
205
+ .vscode/
206
+ .zed/
207
+ # Temporary file for partial code execution
208
+ tempCodeRunnerFile.py
209
+
210
+ # Ruff stuff:
211
+ .ruff_cache/
212
+
213
+ # Hatch-vcs generated version file
214
+ src/pydantic_migrator/_version.py
215
+
216
+ # PyPI configuration file
217
+ .pypirc
218
+
219
+ # Marimo
220
+ marimo/_static/
221
+ marimo/_lsp/
222
+ __marimo__/
223
+
224
+ # Streamlit
225
+ .streamlit/secrets.toml
@@ -0,0 +1,46 @@
1
+ repos:
2
+ - repo: local
3
+ hooks:
4
+ - id: ruff-format
5
+ name: ruff format
6
+ entry: uv run ruff format
7
+ language: system
8
+ types_or: [python, pyi]
9
+ - id: ruff-check
10
+ name: ruff check
11
+ entry: uv run ruff check
12
+ language: system
13
+ types_or: [python, pyi]
14
+ - id: toml-sort
15
+ name: toml sort
16
+ entry: uv run toml-sort --in-place
17
+ language: system
18
+ types: [toml]
19
+ - id: ty
20
+ name: ty check
21
+ entry: uv run ty check
22
+ always_run: true
23
+ language: system
24
+ pass_filenames: false
25
+ types: [python, pyi]
26
+ - id: pytest
27
+ name: pytest
28
+ entry: uv run pytest -vv
29
+ language: system
30
+ pass_filenames: false
31
+ stages: [pre-push]
32
+ - repo: https://github.com/commitizen-tools/commitizen
33
+ rev: v4.4.1
34
+ hooks:
35
+ - id: commitizen
36
+ stages: [commit-msg]
37
+ - repo: https://github.com/rhysd/actionlint
38
+ rev: v1.7.12
39
+ hooks:
40
+ - id: actionlint
41
+ stages: [pre-commit]
42
+ types: [yaml]
43
+ pass_filenames: true
44
+ ci:
45
+ skip:
46
+ - commitizen
@@ -0,0 +1,64 @@
1
+ ## v0.2.0 (2026-08-18)
2
+
3
+ ### Feat
4
+
5
+ - **#42**: include deployed changes in release commit body
6
+
7
+ ### Fix
8
+
9
+ - **#42**: publish release artifacts to PyPI
10
+ - **#42**: migrate release workflow from Node 20 actions
11
+
12
+ ## v0.1.3 (2026-08-17)
13
+
14
+ ### Fix
15
+
16
+ - **#42**: escape $new_version in cz bump message
17
+
18
+ ## v0.1.2 (2026-08-18)
19
+
20
+ ### Fix
21
+
22
+ - **#42**: use correct cz bump placeholder
23
+ - **#42**: merge publish back into release workflow
24
+ - **#42**: use correct version placeholder in cz bump message
25
+ - **#42**: fix uv publish command syntax
26
+
27
+ ## v0.1.1 (2026-08-18)
28
+
29
+ ### Fix
30
+
31
+ - **#42**: fix uv publish command syntax
32
+
33
+ ## v0.1.0 (2026-08-18)
34
+
35
+ ### Feat
36
+
37
+ - **#42**: release (#43)
38
+ - **#40**: policy (#41)
39
+ - add ModelManager lookup helpers (#32)
40
+ - **#38**: cli (#39)
41
+ - **#24**: docs (#29)
42
+ - **#19**: manager (#28)
43
+ - **#17**: engine (#18)
44
+ - **#12**: versioning (#16)
45
+ - **#12**: registry (#13) - introduced registry
46
+ - **#10**: versioning (#11)
47
+ - **#1**: Add pydantic-migrator (#4)
48
+ - **#1**: Add pydantic-migrator package
49
+ - **#1**: Add tests
50
+ - **#1**: Add pytest and syrupy
51
+ - **#1**: Add ci workflow
52
+ - **#1**: Add project configurations
53
+
54
+ ### Fix
55
+
56
+ - **#33**: pre commit (#37)
57
+ - **#33**: migration (#36)
58
+ - **#33**: core (#35)
59
+ - **#33**: versioning (#34)
60
+ - **#30**: typed migration (#31)
61
+
62
+ ### Refactor
63
+
64
+ - **#7**: remove Avro, Protobuf, and TypeScript export (#8)
@@ -0,0 +1,97 @@
1
+ # Contributing to pyverge
2
+
3
+ Thank you for considering contributing to pyverge! This document outlines the
4
+ process for contributing code, documentation, and ideas.
5
+
6
+ ## Getting started
7
+
8
+ 1. **Fork the repository** and clone it locally.
9
+ 2. **Create a virtual environment** and install dependencies:
10
+ ```bash
11
+ uv sync --dev
12
+ ```
13
+ 3. **Run the tests** to ensure everything works:
14
+ ```bash
15
+ uv run pytest
16
+ ```
17
+
18
+ ## How to contribute
19
+
20
+ ### Reporting issues
21
+
22
+ - Use the issue tracker to report bugs, request features, or ask questions.
23
+ - For bugs, include:
24
+ - A minimal reproducible example
25
+ - Expected vs. actual behavior
26
+ - Python version and pyverge version
27
+ - Stack traces if applicable
28
+
29
+ ### Submitting code
30
+
31
+ 1. **Create a branch** from `main`:
32
+ ```bash
33
+ git checkout -b feature/your-feature-name
34
+ ```
35
+
36
+ 2. **Make your changes** following the existing code style.
37
+
38
+ 3. **Write tests** for new functionality. Aim for high coverage.
39
+
40
+ 4. **Run checks** before committing:
41
+ ```bash
42
+ uv run ruff check .
43
+ uv run ruff format .
44
+ uv run pytest
45
+ uv run ty check src/pyverge
46
+ ```
47
+
48
+ 5. **Commit** with a clear message following [Conventional Commits](https://www.conventionalcommits.org/):
49
+ ```bash
50
+ git commit -m "feat: add new migration hook type"
51
+ ```
52
+
53
+ 6. **Push** and open a pull request.
54
+
55
+ ### Documentation
56
+
57
+ - Documentation lives in `docs/`.
58
+ - Use reStructuredText or Markdown consistently.
59
+ - Include examples for new features.
60
+ - Update the changelog if applicable.
61
+
62
+ ## Code style
63
+
64
+ - **Formatting**: `ruff format` (Black-compatible)
65
+ - **Linting**: `ruff check`
66
+ - **Type checking**: `ty check`
67
+ - **Testing**: `pytest` with `-vv` for verbose output
68
+
69
+ Follow existing patterns in the codebase. When in doubt, match the surrounding
70
+ code.
71
+
72
+ ## Pull request process
73
+
74
+ 1. **CI must pass** — all checks (ruff, ty, pytest) must be green.
75
+ 2. **Review** — at least one maintainer must approve.
76
+ 3. **Squash** — if your branch has multiple commits, squash them into logical
77
+ units before merging.
78
+ 4. **Merge** — maintainers will merge your PR once approved.
79
+
80
+ ## Release process
81
+
82
+ Releases are automated via [commitizen](https://commitizen-tools.github.io/commitizen/):
83
+
84
+ 1. Commit messages determine the next version:
85
+ - `feat:` → minor version bump
86
+ - `fix:` → patch version bump
87
+ - `BREAKING CHANGE:` → major version bump
88
+
89
+ 2. Pushing to `main` triggers the release pipeline.
90
+
91
+ 3. The changelog is auto-generated from commit messages.
92
+
93
+ ## Questions?
94
+
95
+ - Open an issue for discussion.
96
+ - Check existing issues and PRs for similar topics.
97
+ - Read the [documentation](docs/) for usage guidance.