ferro-orm 0.3.0__tar.gz → 0.3.2__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 (122) hide show
  1. {ferro_orm-0.3.0 → ferro_orm-0.3.2}/.github/PERMISSIONS.md +3 -2
  2. {ferro_orm-0.3.0 → ferro_orm-0.3.2}/.github/workflows/publish.yml +4 -0
  3. ferro_orm-0.3.2/.github/workflows/release.yml +374 -0
  4. {ferro_orm-0.3.0 → ferro_orm-0.3.2}/.gitignore +1 -2
  5. {ferro_orm-0.3.0 → ferro_orm-0.3.2}/CHANGELOG.md +69 -4
  6. {ferro_orm-0.3.0 → ferro_orm-0.3.2}/Cargo.lock +158 -11
  7. {ferro_orm-0.3.0 → ferro_orm-0.3.2}/Cargo.toml +2 -2
  8. {ferro_orm-0.3.0 → ferro_orm-0.3.2}/PKG-INFO +6 -4
  9. {ferro_orm-0.3.0 → ferro_orm-0.3.2}/README.md +2 -3
  10. {ferro_orm-0.3.0 → ferro_orm-0.3.2}/docs/TEST_RESULTS.md +4 -2
  11. ferro_orm-0.3.2/docs/api/fields.md +21 -0
  12. {ferro_orm-0.3.0 → ferro_orm-0.3.2}/docs/coming-soon.md +6 -2
  13. {ferro_orm-0.3.0 → ferro_orm-0.3.2}/docs/concepts/architecture.md +4 -2
  14. {ferro_orm-0.3.0 → ferro_orm-0.3.2}/docs/concepts/performance.md +7 -3
  15. {ferro_orm-0.3.0 → ferro_orm-0.3.2}/docs/getting-started/tutorial.md +13 -13
  16. {ferro_orm-0.3.0 → ferro_orm-0.3.2}/docs/guide/database.md +43 -0
  17. {ferro_orm-0.3.0 → ferro_orm-0.3.2}/docs/guide/migrations.md +35 -7
  18. {ferro_orm-0.3.0 → ferro_orm-0.3.2}/docs/guide/models-and-fields.md +39 -26
  19. {ferro_orm-0.3.0 → ferro_orm-0.3.2}/docs/guide/queries.md +3 -3
  20. {ferro_orm-0.3.0 → ferro_orm-0.3.2}/docs/guide/relationships.md +1 -1
  21. {ferro_orm-0.3.0 → ferro_orm-0.3.2}/docs/howto/pagination.md +5 -3
  22. {ferro_orm-0.3.0 → ferro_orm-0.3.2}/docs/index.md +3 -3
  23. {ferro_orm-0.3.0 → ferro_orm-0.3.2}/docs/migration-sqlalchemy.md +9 -6
  24. {ferro_orm-0.3.0 → ferro_orm-0.3.2}/pyproject.toml +10 -7
  25. {ferro_orm-0.3.0 → ferro_orm-0.3.2}/src/ferro/__init__.py +2 -1
  26. ferro_orm-0.3.2/src/ferro/_annotation_utils.py +28 -0
  27. {ferro_orm-0.3.0 → ferro_orm-0.3.2}/src/ferro/base.py +50 -1
  28. {ferro_orm-0.3.0 → ferro_orm-0.3.2}/src/ferro/composite_uniques.py +3 -2
  29. {ferro_orm-0.3.0 → ferro_orm-0.3.2}/src/ferro/fields.py +18 -0
  30. {ferro_orm-0.3.0 → ferro_orm-0.3.2}/src/ferro/metaclass.py +7 -1
  31. ferro_orm-0.3.2/src/ferro/migrations/alembic.py +345 -0
  32. {ferro_orm-0.3.0 → ferro_orm-0.3.2}/src/ferro/models.py +18 -4
  33. ferro_orm-0.3.2/tests/test_alembic_autogenerate.py +190 -0
  34. {ferro_orm-0.3.0 → ferro_orm-0.3.2}/tests/test_alembic_bridge.py +21 -1
  35. ferro_orm-0.3.2/tests/test_alembic_nullability.py +270 -0
  36. {ferro_orm-0.3.0 → ferro_orm-0.3.2}/tests/test_alembic_type_mapping.py +3 -1
  37. {ferro_orm-0.3.0 → ferro_orm-0.3.2}/tests/test_composite_unique.py +16 -16
  38. ferro_orm-0.3.2/tests/test_field_wrapper.py +95 -0
  39. {ferro_orm-0.3.0 → ferro_orm-0.3.2}/uv.lock +10 -7
  40. ferro_orm-0.3.0/.github/workflows/release.yml +0 -491
  41. ferro_orm-0.3.0/docs/api/fields.md +0 -21
  42. ferro_orm-0.3.0/src/ferro/migrations/alembic.py +0 -233
  43. ferro_orm-0.3.0/tests/test_alembic_autogenerate.py +0 -94
  44. ferro_orm-0.3.0/tests/test_field_wrapper.py +0 -47
  45. {ferro_orm-0.3.0 → ferro_orm-0.3.2}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
  46. {ferro_orm-0.3.0 → ferro_orm-0.3.2}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
  47. {ferro_orm-0.3.0 → ferro_orm-0.3.2}/.github/PYPI_CHECKLIST.md +0 -0
  48. {ferro_orm-0.3.0 → ferro_orm-0.3.2}/.github/PYPI_SETUP.md +0 -0
  49. {ferro_orm-0.3.0 → ferro_orm-0.3.2}/.github/generated/wheels.generated.yml +0 -0
  50. {ferro_orm-0.3.0 → ferro_orm-0.3.2}/.github/pull_request_template.md +0 -0
  51. {ferro_orm-0.3.0 → ferro_orm-0.3.2}/.github/workflows/ci.yml +0 -0
  52. {ferro_orm-0.3.0 → ferro_orm-0.3.2}/.github/workflows/packaging-smoke.yml +0 -0
  53. {ferro_orm-0.3.0 → ferro_orm-0.3.2}/.github/workflows/publish-docs.yml +0 -0
  54. {ferro_orm-0.3.0 → ferro_orm-0.3.2}/.pre-commit-config.yaml +0 -0
  55. {ferro_orm-0.3.0 → ferro_orm-0.3.2}/.python-version +0 -0
  56. {ferro_orm-0.3.0 → ferro_orm-0.3.2}/CONTRIBUTING.md +0 -0
  57. {ferro_orm-0.3.0 → ferro_orm-0.3.2}/LICENSE +0 -0
  58. {ferro_orm-0.3.0 → ferro_orm-0.3.2}/docs/api/model.md +0 -0
  59. {ferro_orm-0.3.0 → ferro_orm-0.3.2}/docs/api/query.md +0 -0
  60. {ferro_orm-0.3.0 → ferro_orm-0.3.2}/docs/api/relationships.md +0 -0
  61. {ferro_orm-0.3.0 → ferro_orm-0.3.2}/docs/api/transactions.md +0 -0
  62. {ferro_orm-0.3.0 → ferro_orm-0.3.2}/docs/api/utilities.md +0 -0
  63. {ferro_orm-0.3.0 → ferro_orm-0.3.2}/docs/changelog.md +0 -0
  64. {ferro_orm-0.3.0 → ferro_orm-0.3.2}/docs/concepts/identity-map.md +0 -0
  65. {ferro_orm-0.3.0 → ferro_orm-0.3.2}/docs/concepts/type-safety.md +0 -0
  66. {ferro_orm-0.3.0 → ferro_orm-0.3.2}/docs/contributing.md +0 -0
  67. {ferro_orm-0.3.0 → ferro_orm-0.3.2}/docs/faq.md +0 -0
  68. {ferro_orm-0.3.0 → ferro_orm-0.3.2}/docs/getting-started/installation.md +0 -0
  69. {ferro_orm-0.3.0 → ferro_orm-0.3.2}/docs/getting-started/next-steps.md +0 -0
  70. {ferro_orm-0.3.0 → ferro_orm-0.3.2}/docs/guide/mutations.md +0 -0
  71. {ferro_orm-0.3.0 → ferro_orm-0.3.2}/docs/guide/transactions.md +0 -0
  72. {ferro_orm-0.3.0 → ferro_orm-0.3.2}/docs/howto/multiple-databases.md +0 -0
  73. {ferro_orm-0.3.0 → ferro_orm-0.3.2}/docs/howto/soft-deletes.md +0 -0
  74. {ferro_orm-0.3.0 → ferro_orm-0.3.2}/docs/howto/testing.md +0 -0
  75. {ferro_orm-0.3.0 → ferro_orm-0.3.2}/docs/howto/timestamps.md +0 -0
  76. {ferro_orm-0.3.0 → ferro_orm-0.3.2}/docs/stylesheets/extra.css +0 -0
  77. {ferro_orm-0.3.0 → ferro_orm-0.3.2}/docs/why-ferro.md +0 -0
  78. {ferro_orm-0.3.0 → ferro_orm-0.3.2}/justfile +0 -0
  79. {ferro_orm-0.3.0 → ferro_orm-0.3.2}/mkdocs.yml +0 -0
  80. {ferro_orm-0.3.0 → ferro_orm-0.3.2}/scripts/demo_queries.py +0 -0
  81. {ferro_orm-0.3.0 → ferro_orm-0.3.2}/src/connection.rs +0 -0
  82. {ferro_orm-0.3.0 → ferro_orm-0.3.2}/src/ferro/_core.pyi +0 -0
  83. {ferro_orm-0.3.0 → ferro_orm-0.3.2}/src/ferro/_shadow_fk_types.py +0 -0
  84. {ferro_orm-0.3.0 → ferro_orm-0.3.2}/src/ferro/migrations/__init__.py +0 -0
  85. {ferro_orm-0.3.0 → ferro_orm-0.3.2}/src/ferro/py.typed +0 -0
  86. {ferro_orm-0.3.0 → ferro_orm-0.3.2}/src/ferro/query/__init__.py +0 -0
  87. {ferro_orm-0.3.0 → ferro_orm-0.3.2}/src/ferro/query/builder.py +0 -0
  88. {ferro_orm-0.3.0 → ferro_orm-0.3.2}/src/ferro/query/nodes.py +0 -0
  89. {ferro_orm-0.3.0 → ferro_orm-0.3.2}/src/ferro/relations/__init__.py +0 -0
  90. {ferro_orm-0.3.0 → ferro_orm-0.3.2}/src/ferro/relations/descriptors.py +0 -0
  91. {ferro_orm-0.3.0 → ferro_orm-0.3.2}/src/ferro/state.py +0 -0
  92. {ferro_orm-0.3.0 → ferro_orm-0.3.2}/src/lib.rs +0 -0
  93. {ferro_orm-0.3.0 → ferro_orm-0.3.2}/src/operations.rs +0 -0
  94. {ferro_orm-0.3.0 → ferro_orm-0.3.2}/src/query.rs +0 -0
  95. {ferro_orm-0.3.0 → ferro_orm-0.3.2}/src/schema.rs +0 -0
  96. {ferro_orm-0.3.0 → ferro_orm-0.3.2}/src/state.rs +0 -0
  97. {ferro_orm-0.3.0 → ferro_orm-0.3.2}/tests/conftest.py +0 -0
  98. {ferro_orm-0.3.0 → ferro_orm-0.3.2}/tests/test_aggregation.py +0 -0
  99. {ferro_orm-0.3.0 → ferro_orm-0.3.2}/tests/test_auto_migrate.py +0 -0
  100. {ferro_orm-0.3.0 → ferro_orm-0.3.2}/tests/test_bulk_update.py +0 -0
  101. {ferro_orm-0.3.0 → ferro_orm-0.3.2}/tests/test_connection.py +0 -0
  102. {ferro_orm-0.3.0 → ferro_orm-0.3.2}/tests/test_constraints.py +0 -0
  103. {ferro_orm-0.3.0 → ferro_orm-0.3.2}/tests/test_crud.py +0 -0
  104. {ferro_orm-0.3.0 → ferro_orm-0.3.2}/tests/test_deletion.py +0 -0
  105. {ferro_orm-0.3.0 → ferro_orm-0.3.2}/tests/test_docs_examples.py +0 -0
  106. {ferro_orm-0.3.0 → ferro_orm-0.3.2}/tests/test_documentation_features.py +0 -0
  107. {ferro_orm-0.3.0 → ferro_orm-0.3.2}/tests/test_helpers.py +0 -0
  108. {ferro_orm-0.3.0 → ferro_orm-0.3.2}/tests/test_hydration.py +0 -0
  109. {ferro_orm-0.3.0 → ferro_orm-0.3.2}/tests/test_metaclass_internals.py +0 -0
  110. {ferro_orm-0.3.0 → ferro_orm-0.3.2}/tests/test_metadata.py +0 -0
  111. {ferro_orm-0.3.0 → ferro_orm-0.3.2}/tests/test_models.py +0 -0
  112. {ferro_orm-0.3.0 → ferro_orm-0.3.2}/tests/test_one_to_one.py +0 -0
  113. {ferro_orm-0.3.0 → ferro_orm-0.3.2}/tests/test_query_builder.py +0 -0
  114. {ferro_orm-0.3.0 → ferro_orm-0.3.2}/tests/test_refresh.py +0 -0
  115. {ferro_orm-0.3.0 → ferro_orm-0.3.2}/tests/test_relationship_engine.py +0 -0
  116. {ferro_orm-0.3.0 → ferro_orm-0.3.2}/tests/test_schema.py +0 -0
  117. {ferro_orm-0.3.0 → ferro_orm-0.3.2}/tests/test_schema_constraints.py +0 -0
  118. {ferro_orm-0.3.0 → ferro_orm-0.3.2}/tests/test_shadow_fk_types.py +0 -0
  119. {ferro_orm-0.3.0 → ferro_orm-0.3.2}/tests/test_string_search.py +0 -0
  120. {ferro_orm-0.3.0 → ferro_orm-0.3.2}/tests/test_structural_types.py +0 -0
  121. {ferro_orm-0.3.0 → ferro_orm-0.3.2}/tests/test_temporal_types.py +0 -0
  122. {ferro_orm-0.3.0 → ferro_orm-0.3.2}/tests/test_transactions.py +0 -0
@@ -71,6 +71,7 @@ permissions:
71
71
  - Add labels or comments to PRs
72
72
 
73
73
  **What It Does:**
74
+ - **Preflight** builds all release wheels at the workflow SHA (same matrix as post-release wheel builds), in parallel with the CI gate and packaging smoke jobs. If any platform fails, the Create Release job does not run.
74
75
  - Analyzes conventional commits
75
76
  - Determines next version
76
77
  - Updates version in both Python and Rust files
@@ -88,7 +89,7 @@ permissions:
88
89
 
89
90
  ### 2. Build & Publish (jobs in `release.yml`)
90
91
 
91
- **Trigger:** Part of `release.yml` after the release job (no reusable workflow).
92
+ **Trigger:** Part of `release.yml` after the Create Release job (no reusable workflow). Wheel preflight runs in parallel with CI and packaging smoke, before that job.
92
93
 
93
94
  Build, test, and publish jobs are defined directly in `release.yml` so PyPI Trusted Publishing receives a token with `workflow_ref: release.yml` (reusable workflows are not supported by PyPI).
94
95
 
@@ -248,7 +249,7 @@ Permission to manage GitHub Pages deployments:
248
249
 
249
250
  ### Release Completed But Publish/Docs Did Not Run
250
251
 
251
- **Cause:** `release.yml` gates downstream jobs. If CI, packaging smoke, or release creation fails, publish/docs are skipped.
252
+ **Cause:** `release.yml` gates downstream jobs. If CI, packaging smoke, preflight wheel builds, or release creation fails, publish/docs are skipped.
252
253
 
253
254
  **Solution:**
254
255
  - Open the failed `release.yml` run and inspect the first failed job.
@@ -60,6 +60,10 @@ jobs:
60
60
 
61
61
  - name: Build wheels
62
62
  uses: PyO3/maturin-action@v1.48.0
63
+ env:
64
+ # ring (via rustls/sqlx): manylinux2014-cross gcc does not define __ARM_ARCH for .S asm.
65
+ # https://github.com/briansmith/ring/issues/1728
66
+ CFLAGS_aarch64_unknown_linux_gnu: ${{ matrix.platform.name == 'linux-aarch64' && '-D__ARM_ARCH=8' || '' }}
63
67
  with:
64
68
  target: ${{ matrix.platform.target }}
65
69
  args: --release --out dist --find-interpreter
@@ -0,0 +1,374 @@
1
+ name: Release
2
+
3
+ on:
4
+ workflow_dispatch:
5
+ inputs:
6
+ prerelease:
7
+ description: 'Create a pre-release'
8
+ required: false
9
+ type: boolean
10
+ default: false
11
+
12
+ concurrency:
13
+ group: release-${{ github.workflow }}-${{ github.ref }}
14
+ cancel-in-progress: true
15
+
16
+ jobs:
17
+ # Same maturin targets as build-wheels, but against the current ref *before*
18
+ # semantic-release mutates the repo. Runs in parallel with CI gate and
19
+ # packaging smoke so the wall-clock pipeline stays shorter.
20
+ preflight-build-wheels:
21
+ name: Preflight build wheels (${{ matrix.platform.name }})
22
+ runs-on: ${{ matrix.platform.runner }}
23
+ strategy:
24
+ fail-fast: false
25
+ matrix:
26
+ platform:
27
+ - name: linux-x86_64
28
+ runner: ubuntu-latest
29
+ target: x86_64
30
+ manylinux: auto
31
+ - name: linux-aarch64
32
+ runner: ubuntu-latest
33
+ target: aarch64
34
+ manylinux: auto
35
+ - name: macos-aarch64
36
+ runner: macos-latest
37
+ target: aarch64
38
+ manylinux: ''
39
+ - name: windows-x64
40
+ runner: windows-latest
41
+ target: x64
42
+ manylinux: ''
43
+
44
+ steps:
45
+ - name: Checkout repository
46
+ uses: actions/checkout@v4
47
+ with:
48
+ ref: ${{ github.sha }}
49
+
50
+ - name: Set up Python
51
+ uses: actions/setup-python@v5
52
+ with:
53
+ python-version: '3.13'
54
+
55
+ - name: Build wheels (preflight)
56
+ uses: PyO3/maturin-action@v1.48.0
57
+ env:
58
+ # ring (via rustls/sqlx): manylinux2014-cross gcc does not define __ARM_ARCH for .S asm.
59
+ # https://github.com/briansmith/ring/issues/1728
60
+ CFLAGS_aarch64_unknown_linux_gnu: ${{ matrix.platform.name == 'linux-aarch64' && '-D__ARM_ARCH=8' || '' }}
61
+ with:
62
+ target: ${{ matrix.platform.target }}
63
+ args: --release --out dist --find-interpreter
64
+ manylinux: ${{ matrix.platform.manylinux }}
65
+ sccache: 'true'
66
+
67
+ ci-gate:
68
+ name: CI Gate
69
+ uses: ./.github/workflows/ci.yml
70
+
71
+ packaging-smoke:
72
+ name: Packaging Smoke Gate
73
+ uses: ./.github/workflows/packaging-smoke.yml
74
+
75
+ release:
76
+ name: Create Release
77
+ needs: [ci-gate, packaging-smoke, preflight-build-wheels]
78
+ runs-on: ubuntu-latest
79
+ outputs:
80
+ release_created: ${{ steps.verify_release.outputs.release_created }}
81
+ release_ref: ${{ steps.release_ref.outputs.release_ref }}
82
+ permissions:
83
+ contents: write
84
+ issues: write
85
+ pull-requests: write
86
+
87
+ steps:
88
+ - name: Checkout repository
89
+ uses: actions/checkout@v4
90
+ with:
91
+ fetch-depth: 0
92
+ ssh-key: ${{ secrets.RELEASE_DEPLOY_KEY }}
93
+ persist-credentials: false
94
+
95
+ - name: Set up Python
96
+ uses: actions/setup-python@v5
97
+ with:
98
+ python-version: '3.13'
99
+
100
+ - name: Install UV
101
+ uses: astral-sh/setup-uv@v5
102
+ with:
103
+ enable-cache: true
104
+
105
+ - name: Set up Rust
106
+ uses: dtolnay/rust-toolchain@stable
107
+
108
+ - name: Cache Rust dependencies
109
+ uses: Swatinem/rust-cache@v2
110
+ with:
111
+ prefix-key: "v1-rust"
112
+
113
+ - name: Install dependencies
114
+ run: |
115
+ uv sync --only-group release --no-install-project --python 3.13
116
+
117
+ - name: Configure Git
118
+ run: |
119
+ git config --local user.email "github-actions[bot]@users.noreply.github.com"
120
+ git config --local user.name "github-actions[bot]"
121
+ git remote set-url origin "git@github.com:${{ github.repository }}.git"
122
+
123
+ - name: Run semantic-release
124
+ env:
125
+ # Prefer a PAT so release events can trigger downstream workflows.
126
+ GH_TOKEN: ${{ secrets.RELEASE_TOKEN || secrets.GITHUB_TOKEN }}
127
+ run: |
128
+ # Use the --prerelease flag if requested via manual trigger
129
+ PRERELEASE_FLAG=""
130
+ if [ "${{ github.event.inputs.prerelease }}" == "true" ]; then
131
+ PRERELEASE_FLAG="--prerelease"
132
+ fi
133
+
134
+ # This command will:
135
+ # 1. Bump version in pyproject.toml and Cargo.toml
136
+ # 2. Update CHANGELOG.md
137
+ # 3. Create a commit and push it
138
+ # 4. Create a tag and push it
139
+ # 5. Create a GitHub Release (publishing is handled by the wheels/publish workflow)
140
+ uv run semantic-release version $PRERELEASE_FLAG
141
+
142
+ - name: Verify release commit updates changelog
143
+ id: verify_release
144
+ shell: bash
145
+ run: |
146
+ LAST_SUBJECT="$(git log -1 --pretty=%s)"
147
+ if [[ "$LAST_SUBJECT" != chore\(release\):* ]]; then
148
+ echo "No release commit created in this run; skipping changelog validation."
149
+ echo "release_created=false" >> "$GITHUB_OUTPUT"
150
+ echo "changelog_validated=skipped" >> "$GITHUB_OUTPUT"
151
+ echo "release_subject=$LAST_SUBJECT" >> "$GITHUB_OUTPUT"
152
+ exit 0
153
+ fi
154
+
155
+ echo "release_created=true" >> "$GITHUB_OUTPUT"
156
+ echo "release_subject=$LAST_SUBJECT" >> "$GITHUB_OUTPUT"
157
+
158
+ CHANGED_FILES="$(git show --name-only --pretty='' HEAD)"
159
+ if [[ "$CHANGED_FILES" != *"CHANGELOG.md"* ]]; then
160
+ echo "WARNING: release commit does not include CHANGELOG.md update."
161
+ echo "Changed files:"
162
+ echo "$CHANGED_FILES"
163
+ echo "changelog_validated=missing" >> "$GITHUB_OUTPUT"
164
+ exit 0
165
+ fi
166
+
167
+ echo "changelog_validated=passed" >> "$GITHUB_OUTPUT"
168
+ echo "Validated: release commit includes CHANGELOG.md."
169
+
170
+ - name: Capture released ref
171
+ id: release_ref
172
+ shell: bash
173
+ run: |
174
+ if [[ "${{ steps.verify_release.outputs.release_created }}" != "true" ]]; then
175
+ echo "release_ref=" >> "$GITHUB_OUTPUT"
176
+ exit 0
177
+ fi
178
+
179
+ RELEASE_TAG="$(git tag --points-at HEAD | head -n1)"
180
+ if [[ -z "$RELEASE_TAG" ]]; then
181
+ echo "ERROR: expected release tag on HEAD but none was found."
182
+ exit 1
183
+ fi
184
+
185
+ echo "release_ref=$RELEASE_TAG" >> "$GITHUB_OUTPUT"
186
+ echo "Using release ref: $RELEASE_TAG"
187
+
188
+ - name: Release summary
189
+ if: always()
190
+ shell: bash
191
+ run: |
192
+ {
193
+ echo "## Release workflow summary"
194
+ echo ""
195
+ echo "- Trigger: \`${{ github.event_name }}\`"
196
+ echo "- Ref: \`${{ github.ref_name }}\`"
197
+ echo "- Release commit created: \`${{ steps.verify_release.outputs.release_created || 'unknown' }}\`"
198
+ echo "- Changelog validation: \`${{ steps.verify_release.outputs.changelog_validated || 'unknown' }}\`"
199
+ echo "- Release ref: \`${{ steps.release_ref.outputs.release_ref || 'n/a' }}\`"
200
+ echo "- Last commit subject: \`${{ steps.verify_release.outputs.release_subject || 'n/a' }}\`"
201
+ } >> "$GITHUB_STEP_SUMMARY"
202
+
203
+ # Build and publish jobs are inlined here (not in a reusable workflow) so that
204
+ # PyPI Trusted Publishing sees workflow_ref = release.yml. Reusable workflows
205
+ # are not supported by PyPI Trusted Publishing.
206
+ build-wheels:
207
+ name: Build wheels (${{ matrix.platform.name }})
208
+ needs: [release]
209
+ if: needs.release.outputs.release_created == 'true'
210
+ runs-on: ${{ matrix.platform.runner }}
211
+ strategy:
212
+ fail-fast: false
213
+ matrix:
214
+ platform:
215
+ - name: linux-x86_64
216
+ runner: ubuntu-latest
217
+ target: x86_64
218
+ manylinux: auto
219
+ - name: linux-aarch64
220
+ runner: ubuntu-latest
221
+ target: aarch64
222
+ manylinux: auto
223
+ - name: macos-aarch64
224
+ runner: macos-latest
225
+ target: aarch64
226
+ manylinux: ''
227
+ - name: windows-x64
228
+ runner: windows-latest
229
+ target: x64
230
+ manylinux: ''
231
+
232
+ steps:
233
+ - name: Checkout repository
234
+ uses: actions/checkout@v4
235
+ with:
236
+ ref: ${{ needs.release.outputs.release_ref }}
237
+
238
+ - name: Set up Python
239
+ uses: actions/setup-python@v5
240
+ with:
241
+ python-version: '3.13'
242
+
243
+ - name: Build wheels
244
+ uses: PyO3/maturin-action@v1.48.0
245
+ env:
246
+ # ring (via rustls/sqlx): manylinux2014-cross gcc does not define __ARM_ARCH for .S asm.
247
+ # https://github.com/briansmith/ring/issues/1728
248
+ CFLAGS_aarch64_unknown_linux_gnu: ${{ matrix.platform.name == 'linux-aarch64' && '-D__ARM_ARCH=8' || '' }}
249
+ with:
250
+ target: ${{ matrix.platform.target }}
251
+ args: --release --out dist --find-interpreter
252
+ manylinux: ${{ matrix.platform.manylinux }}
253
+ sccache: 'true'
254
+
255
+ - name: Upload wheels
256
+ uses: actions/upload-artifact@v4
257
+ with:
258
+ name: wheels-${{ matrix.platform.name }}
259
+ path: dist/*.whl
260
+
261
+ build-sdist:
262
+ name: Build sdist
263
+ needs: [release]
264
+ if: needs.release.outputs.release_created == 'true'
265
+ runs-on: ubuntu-latest
266
+ steps:
267
+ - name: Checkout repository
268
+ uses: actions/checkout@v4
269
+ with:
270
+ ref: ${{ needs.release.outputs.release_ref }}
271
+
272
+ - name: Build sdist
273
+ uses: PyO3/maturin-action@v1.48.0
274
+ with:
275
+ command: sdist
276
+ args: --out dist
277
+
278
+ - name: Upload sdist
279
+ uses: actions/upload-artifact@v4
280
+ with:
281
+ name: sdist
282
+ path: dist/*.tar.gz
283
+
284
+ test-wheels:
285
+ name: Test wheels (${{ matrix.os }})
286
+ needs: [release, build-wheels]
287
+ if: needs.release.outputs.release_created == 'true'
288
+ runs-on: ${{ matrix.os }}
289
+ strategy:
290
+ fail-fast: false
291
+ matrix:
292
+ os: [ubuntu-latest, windows-latest, macos-latest]
293
+ steps:
294
+ - name: Checkout repository
295
+ uses: actions/checkout@v4
296
+ with:
297
+ ref: ${{ needs.release.outputs.release_ref }}
298
+
299
+ - name: Set up Python
300
+ uses: actions/setup-python@v5
301
+ with:
302
+ python-version: '3.13'
303
+
304
+ - name: Download wheels
305
+ uses: actions/download-artifact@v4
306
+ with:
307
+ path: dist
308
+ pattern: wheels-*
309
+ merge-multiple: true
310
+
311
+ - name: Install wheel
312
+ shell: bash
313
+ run: |
314
+ python -m pip install --upgrade pip
315
+ python -m pip install --find-links dist ferro-orm
316
+
317
+ - name: Test import
318
+ shell: bash
319
+ run: |
320
+ python -c "import ferro; print('Ferro imported successfully')"
321
+
322
+ - name: Run basic smoke test
323
+ shell: bash
324
+ run: |
325
+ python -c "
326
+ import asyncio
327
+ from ferro import Model, FerroField, connect
328
+ from typing import Annotated
329
+
330
+ class TestModel(Model):
331
+ id: Annotated[int, FerroField(primary_key=True)]
332
+ name: str
333
+
334
+ async def test():
335
+ await connect('sqlite::memory:')
336
+ print('Connection test passed')
337
+
338
+ asyncio.run(test())
339
+ "
340
+
341
+ publish-pypi:
342
+ name: Publish to PyPI
343
+ needs: [release, build-wheels, build-sdist, test-wheels]
344
+ if: needs.release.outputs.release_created == 'true'
345
+ runs-on: ubuntu-latest
346
+ environment:
347
+ name: pypi
348
+ url: https://pypi.org/p/ferro-orm
349
+ permissions:
350
+ id-token: write
351
+ steps:
352
+ - name: Download all artifacts
353
+ uses: actions/download-artifact@v4
354
+ with:
355
+ path: dist
356
+ merge-multiple: true
357
+
358
+ - name: Publish to PyPI
359
+ uses: pypa/gh-action-pypi-publish@release/v1
360
+ with:
361
+ skip-existing: true
362
+ verbose: true
363
+
364
+ deploy-docs:
365
+ name: Deploy Documentation
366
+ needs: [release, publish-pypi]
367
+ if: needs.release.outputs.release_created == 'true'
368
+ uses: ./.github/workflows/publish-docs.yml
369
+ permissions:
370
+ contents: read
371
+ pages: write
372
+ id-token: write
373
+ with:
374
+ ref: ${{ needs.release.outputs.release_ref }}
@@ -243,5 +243,4 @@ playground.ipynb
243
243
  IMPLEMENTATION.md
244
244
  Cargo.lock
245
245
  demo.db
246
- docs/superpowers/plans/2026-04-22-shadow-fk-types.md
247
- docs/superpowers/specs/2026-04-22-shadow-fk-types-design.md
246
+ docs/superpowers
@@ -1,7 +1,75 @@
1
1
  # CHANGELOG
2
2
 
3
3
 
4
- ## v0.3.0 (2026-04-22)
4
+ ## v0.3.2 (2026-04-24)
5
+
6
+ ### Bug Fixes
7
+
8
+ - Move alembic reqs to optional dependencies
9
+ ([`87f0e81`](https://github.com/syn54x/ferro-orm/commit/87f0e8157640ac9984da20e0c4c7290dbfcf4bfd))
10
+
11
+ ### Build System
12
+
13
+ - **sqlx**: Enable rustls TLS for PostgreSQL connections
14
+ ([`807fa81`](https://github.com/syn54x/ferro-orm/commit/807fa8196a3742a5a50380fe6dbf727045798cc3))
15
+
16
+ ### Chores
17
+
18
+ - Sync uv.lock with project version 0.3.1
19
+ ([`c3c9f91`](https://github.com/syn54x/ferro-orm/commit/c3c9f91907a3ece8ce7bc70f08979f1dd269a87c))
20
+
21
+ ### Continuous Integration
22
+
23
+ - Build preflight wheels earlier to fail faster
24
+ ([`475c93c`](https://github.com/syn54x/ferro-orm/commit/475c93caa1d51fb07d7eda875205086761d64e8f))
25
+
26
+ - Fix linux-aarch64 wheel builds for ring/rustls asm
27
+ ([`5eadddc`](https://github.com/syn54x/ferro-orm/commit/5eadddc922b51448ad4da3841a84fd4931b19814))
28
+
29
+ - Gate release on preflight wheel builds for all platforms
30
+ ([`6ec48a2`](https://github.com/syn54x/ferro-orm/commit/6ec48a275b8dc9868612a3f374595ce63fe151ca))
31
+
32
+ - Restore legacy release workflow
33
+ ([`d3ee87c`](https://github.com/syn54x/ferro-orm/commit/d3ee87c68995a1163480eb9be8a3111032e94842))
34
+
35
+ ### Documentation
36
+
37
+ - Add Supabase PostgreSQL connection and TLS guidance
38
+ ([`b1d61ad`](https://github.com/syn54x/ferro-orm/commit/b1d61ad4395a17c7c2270c1d4776500c436c22d1))
39
+
40
+
41
+ ## v0.3.1 (2026-04-23)
42
+
43
+ ### Bug Fixes
44
+
45
+ - Alembic autogenerate named SQLAlchemy enums for PostgreSQL
46
+ ([`25a00e8`](https://github.com/syn54x/ferro-orm/commit/25a00e84502ae1f8ba502718934d93eedfa4ce09))
47
+
48
+ - **migrations**: Align nullable inference with field types
49
+ ([`885f0fe`](https://github.com/syn54x/ferro-orm/commit/885f0fe155dfa643e29b9425ff1ede62f3f0b269))
50
+
51
+ - **migrations**: Propagate ForeignKey(unique=True) to Alembic metadata
52
+ ([#22](https://github.com/syn54x/ferro-orm/pull/22),
53
+ [`9329e8f`](https://github.com/syn54x/ferro-orm/commit/9329e8fba2f0efd201bea4545393654c7d1dd34e))
54
+
55
+ ### Continuous Integration
56
+
57
+ - Fix release
58
+ ([`e2822f6`](https://github.com/syn54x/ferro-orm/commit/e2822f6c9bacc6fc955e56b2ca8e120cc22b0b72))
59
+
60
+ - Fix release
61
+ ([`e5c1adc`](https://github.com/syn54x/ferro-orm/commit/e5c1adcc10eb44845ef95d78226840ecdbfd0ebd))
62
+
63
+ - Fix release
64
+ ([`688d01b`](https://github.com/syn54x/ferro-orm/commit/688d01bdae0aff1f82e4a1bb60dd1b8ab35e1d01))
65
+
66
+ ### Documentation
67
+
68
+ - Prefer Field over FerroField
69
+ ([`3385cfa`](https://github.com/syn54x/ferro-orm/commit/3385cfadf0951f80827dac1aa08f73430a02023f))
70
+
71
+
72
+ ## v0.3.0 (2026-04-23)
5
73
 
6
74
  ### Bug Fixes
7
75
 
@@ -18,9 +86,6 @@
18
86
 
19
87
  ### Continuous Integration
20
88
 
21
- - Fix release
22
- ([`688d01b`](https://github.com/syn54x/ferro-orm/commit/688d01bdae0aff1f82e4a1bb60dd1b8ab35e1d01))
23
-
24
89
  - Fix release
25
90
  ([`249e460`](https://github.com/syn54x/ferro-orm/commit/249e46058bac87215920083a9d45557f3c58b62f))
26
91