ferro-orm 0.1.1__tar.gz → 0.2.1__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 (133) hide show
  1. ferro_orm-0.2.1/.github/ISSUE_TEMPLATE/bug_report.md +43 -0
  2. ferro_orm-0.2.1/.github/ISSUE_TEMPLATE/feature_request.md +37 -0
  3. {ferro_orm-0.1.1 → ferro_orm-0.2.1}/.github/PERMISSIONS.md +51 -58
  4. {ferro_orm-0.1.1 → ferro_orm-0.2.1}/.github/PYPI_CHECKLIST.md +8 -11
  5. {ferro_orm-0.1.1 → ferro_orm-0.2.1}/.github/PYPI_SETUP.md +17 -10
  6. ferro_orm-0.2.1/.github/pull_request_template.md +36 -0
  7. {ferro_orm-0.1.1 → ferro_orm-0.2.1}/.github/workflows/ci.yml +7 -56
  8. ferro_orm-0.2.1/.github/workflows/packaging-smoke.yml +62 -0
  9. {ferro_orm-0.1.1 → ferro_orm-0.2.1}/.github/workflows/publish-docs.yml +10 -2
  10. {ferro_orm-0.1.1 → ferro_orm-0.2.1}/.github/workflows/publish.yml +15 -58
  11. ferro_orm-0.2.1/.github/workflows/release.yml +320 -0
  12. {ferro_orm-0.1.1 → ferro_orm-0.2.1}/.gitignore +3 -2
  13. ferro_orm-0.2.1/CHANGELOG.md +172 -0
  14. {ferro_orm-0.1.1 → ferro_orm-0.2.1}/Cargo.lock +330 -229
  15. {ferro_orm-0.1.1 → ferro_orm-0.2.1}/Cargo.toml +1 -1
  16. ferro_orm-0.2.1/DOCS_COMPLETE.md +226 -0
  17. ferro_orm-0.2.1/DOCS_RESTRUCTURE_SUMMARY.md +213 -0
  18. {ferro_orm-0.1.1 → ferro_orm-0.2.1}/PKG-INFO +3 -3
  19. {ferro_orm-0.1.1 → ferro_orm-0.2.1}/README.md +2 -2
  20. ferro_orm-0.2.1/docs/TEST_RESULTS.md +208 -0
  21. ferro_orm-0.2.1/docs/api/fields.md +21 -0
  22. ferro_orm-0.2.1/docs/api/model.md +29 -0
  23. ferro_orm-0.2.1/docs/api/query.md +24 -0
  24. ferro_orm-0.2.1/docs/api/relationships.md +28 -0
  25. ferro_orm-0.2.1/docs/api/transactions.md +36 -0
  26. ferro_orm-0.2.1/docs/api/utilities.md +75 -0
  27. ferro_orm-0.2.1/docs/changelog.md +36 -0
  28. ferro_orm-0.2.1/docs/coming-soon.md +514 -0
  29. ferro_orm-0.2.1/docs/concepts/architecture.md +330 -0
  30. ferro_orm-0.2.1/docs/concepts/identity-map.md +285 -0
  31. ferro_orm-0.2.1/docs/concepts/performance.md +221 -0
  32. ferro_orm-0.2.1/docs/concepts/type-safety.md +159 -0
  33. ferro_orm-0.2.1/docs/faq.md +255 -0
  34. ferro_orm-0.2.1/docs/getting-started/installation.md +90 -0
  35. ferro_orm-0.2.1/docs/getting-started/next-steps.md +159 -0
  36. ferro_orm-0.2.1/docs/getting-started/tutorial.md +388 -0
  37. ferro_orm-0.2.1/docs/guide/database.md +266 -0
  38. ferro_orm-0.2.1/docs/guide/migrations.md +411 -0
  39. ferro_orm-0.2.1/docs/guide/models-and-fields.md +184 -0
  40. ferro_orm-0.2.1/docs/guide/mutations.md +461 -0
  41. ferro_orm-0.2.1/docs/guide/queries.md +329 -0
  42. ferro_orm-0.2.1/docs/guide/relationships.md +332 -0
  43. ferro_orm-0.2.1/docs/guide/transactions.md +357 -0
  44. ferro_orm-0.2.1/docs/howto/multiple-databases.md +74 -0
  45. ferro_orm-0.2.1/docs/howto/pagination.md +353 -0
  46. ferro_orm-0.2.1/docs/howto/soft-deletes.md +86 -0
  47. ferro_orm-0.2.1/docs/howto/testing.md +123 -0
  48. ferro_orm-0.2.1/docs/howto/timestamps.md +57 -0
  49. ferro_orm-0.2.1/docs/index.md +142 -0
  50. ferro_orm-0.2.1/docs/migration-sqlalchemy.md +150 -0
  51. ferro_orm-0.2.1/docs/stylesheets/extra.css +27 -0
  52. ferro_orm-0.2.1/docs/why-ferro.md +206 -0
  53. ferro_orm-0.2.1/mkdocs.yml +161 -0
  54. {ferro_orm-0.1.1 → ferro_orm-0.2.1}/pyproject.toml +20 -3
  55. {ferro_orm-0.1.1 → ferro_orm-0.2.1}/scripts/demo_queries.py +3 -3
  56. {ferro_orm-0.1.1 → ferro_orm-0.2.1}/src/ferro/__init__.py +4 -2
  57. ferro_orm-0.2.1/src/ferro/fields.py +496 -0
  58. ferro_orm-0.2.1/src/ferro/metaclass.py +391 -0
  59. {ferro_orm-0.1.1 → ferro_orm-0.2.1}/src/ferro/models.py +9 -0
  60. ferro_orm-0.2.1/src/ferro/query/__init__.py +6 -0
  61. {ferro_orm-0.1.1 → ferro_orm-0.2.1}/src/ferro/query/builder.py +2 -2
  62. {ferro_orm-0.1.1 → ferro_orm-0.2.1}/src/ferro/relations/__init__.py +15 -13
  63. {ferro_orm-0.1.1 → ferro_orm-0.2.1}/src/ferro/relations/descriptors.py +22 -24
  64. {ferro_orm-0.1.1 → ferro_orm-0.2.1}/tests/conftest.py +4 -3
  65. {ferro_orm-0.1.1 → ferro_orm-0.2.1}/tests/test_alembic_bridge.py +4 -4
  66. ferro_orm-0.2.1/tests/test_auto_migrate.py +78 -0
  67. {ferro_orm-0.1.1 → ferro_orm-0.2.1}/tests/test_crud.py +25 -25
  68. {ferro_orm-0.1.1 → ferro_orm-0.2.1}/tests/test_docs_examples.py +3 -0
  69. ferro_orm-0.2.1/tests/test_documentation_features.py +830 -0
  70. ferro_orm-0.2.1/tests/test_field_wrapper.py +47 -0
  71. ferro_orm-0.2.1/tests/test_metaclass_internals.py +340 -0
  72. {ferro_orm-0.1.1 → ferro_orm-0.2.1}/tests/test_one_to_one.py +2 -2
  73. {ferro_orm-0.1.1 → ferro_orm-0.2.1}/tests/test_relationship_engine.py +76 -11
  74. {ferro_orm-0.1.1 → ferro_orm-0.2.1}/tests/test_schema_constraints.py +8 -6
  75. {ferro_orm-0.1.1 → ferro_orm-0.2.1}/uv.lock +31 -1
  76. ferro_orm-0.1.1/.github/workflows/release.yml +0 -76
  77. ferro_orm-0.1.1/.github/workflows/update-changelog.yml +0 -76
  78. ferro_orm-0.1.1/CHANGELOG.md +0 -48
  79. ferro_orm-0.1.1/docs/api/core-models.md +0 -6
  80. ferro_orm-0.1.1/docs/api/field-metadata.md +0 -13
  81. ferro_orm-0.1.1/docs/api/global-functions.md +0 -21
  82. ferro_orm-0.1.1/docs/api/query-builder.md +0 -11
  83. ferro_orm-0.1.1/docs/api.md +0 -10
  84. ferro_orm-0.1.1/docs/connection.md +0 -48
  85. ferro_orm-0.1.1/docs/fields.md +0 -59
  86. ferro_orm-0.1.1/docs/index.md +0 -3
  87. ferro_orm-0.1.1/docs/migrations.md +0 -52
  88. ferro_orm-0.1.1/docs/models.md +0 -53
  89. ferro_orm-0.1.1/docs/queries.md +0 -89
  90. ferro_orm-0.1.1/docs/relations.md +0 -85
  91. ferro_orm-0.1.1/docs/transactions.md +0 -57
  92. ferro_orm-0.1.1/mkdocs.yml +0 -104
  93. ferro_orm-0.1.1/src/ferro/metaclass.py +0 -200
  94. ferro_orm-0.1.1/src/ferro/query/__init__.py +0 -6
  95. ferro_orm-0.1.1/tests/test_auto_migrate.py +0 -37
  96. {ferro_orm-0.1.1 → ferro_orm-0.2.1}/.github/generated/wheels.generated.yml +0 -0
  97. {ferro_orm-0.1.1 → ferro_orm-0.2.1}/.pre-commit-config.yaml +0 -0
  98. {ferro_orm-0.1.1 → ferro_orm-0.2.1}/.python-version +0 -0
  99. {ferro_orm-0.1.1 → ferro_orm-0.2.1}/CONTRIBUTING.md +0 -0
  100. {ferro_orm-0.1.1 → ferro_orm-0.2.1}/LICENSE +0 -0
  101. {ferro_orm-0.1.1 → ferro_orm-0.2.1}/docs/contributing.md +0 -0
  102. {ferro_orm-0.1.1 → ferro_orm-0.2.1}/justfile +0 -0
  103. {ferro_orm-0.1.1 → ferro_orm-0.2.1}/src/connection.rs +0 -0
  104. {ferro_orm-0.1.1 → ferro_orm-0.2.1}/src/ferro/_core.pyi +0 -0
  105. {ferro_orm-0.1.1 → ferro_orm-0.2.1}/src/ferro/base.py +0 -0
  106. {ferro_orm-0.1.1 → ferro_orm-0.2.1}/src/ferro/migrations/__init__.py +0 -0
  107. {ferro_orm-0.1.1 → ferro_orm-0.2.1}/src/ferro/migrations/alembic.py +0 -0
  108. {ferro_orm-0.1.1 → ferro_orm-0.2.1}/src/ferro/py.typed +0 -0
  109. {ferro_orm-0.1.1 → ferro_orm-0.2.1}/src/ferro/query/nodes.py +0 -0
  110. {ferro_orm-0.1.1 → ferro_orm-0.2.1}/src/ferro/state.py +0 -0
  111. {ferro_orm-0.1.1 → ferro_orm-0.2.1}/src/lib.rs +0 -0
  112. {ferro_orm-0.1.1 → ferro_orm-0.2.1}/src/operations.rs +0 -0
  113. {ferro_orm-0.1.1 → ferro_orm-0.2.1}/src/query.rs +0 -0
  114. {ferro_orm-0.1.1 → ferro_orm-0.2.1}/src/schema.rs +0 -0
  115. {ferro_orm-0.1.1 → ferro_orm-0.2.1}/src/state.rs +0 -0
  116. {ferro_orm-0.1.1 → ferro_orm-0.2.1}/tests/test_aggregation.py +0 -0
  117. {ferro_orm-0.1.1 → ferro_orm-0.2.1}/tests/test_alembic_autogenerate.py +0 -0
  118. {ferro_orm-0.1.1 → ferro_orm-0.2.1}/tests/test_alembic_type_mapping.py +0 -0
  119. {ferro_orm-0.1.1 → ferro_orm-0.2.1}/tests/test_bulk_update.py +0 -0
  120. {ferro_orm-0.1.1 → ferro_orm-0.2.1}/tests/test_connection.py +0 -0
  121. {ferro_orm-0.1.1 → ferro_orm-0.2.1}/tests/test_constraints.py +0 -0
  122. {ferro_orm-0.1.1 → ferro_orm-0.2.1}/tests/test_deletion.py +0 -0
  123. {ferro_orm-0.1.1 → ferro_orm-0.2.1}/tests/test_helpers.py +0 -0
  124. {ferro_orm-0.1.1 → ferro_orm-0.2.1}/tests/test_hydration.py +0 -0
  125. {ferro_orm-0.1.1 → ferro_orm-0.2.1}/tests/test_metadata.py +0 -0
  126. {ferro_orm-0.1.1 → ferro_orm-0.2.1}/tests/test_models.py +0 -0
  127. {ferro_orm-0.1.1 → ferro_orm-0.2.1}/tests/test_query_builder.py +0 -0
  128. {ferro_orm-0.1.1 → ferro_orm-0.2.1}/tests/test_refresh.py +0 -0
  129. {ferro_orm-0.1.1 → ferro_orm-0.2.1}/tests/test_schema.py +0 -0
  130. {ferro_orm-0.1.1 → ferro_orm-0.2.1}/tests/test_string_search.py +0 -0
  131. {ferro_orm-0.1.1 → ferro_orm-0.2.1}/tests/test_structural_types.py +0 -0
  132. {ferro_orm-0.1.1 → ferro_orm-0.2.1}/tests/test_temporal_types.py +0 -0
  133. {ferro_orm-0.1.1 → ferro_orm-0.2.1}/tests/test_transactions.py +0 -0
@@ -0,0 +1,43 @@
1
+ ---
2
+ name: Bug report
3
+ about: Report a bug or unexpected behavior
4
+ title: "`bug` "
5
+ labels: bug
6
+ assignees: Ox54
7
+
8
+ ---
9
+
10
+ ## Summary
11
+
12
+ <!-- Brief description of the bug. -->
13
+
14
+ ## Environment
15
+
16
+ - **Ferro version:** <!-- e.g. 0.1.0 or git commit -->
17
+ - **Python version:**
18
+ - **OS:** <!-- e.g. macOS 14, Ubuntu 22.04 -->
19
+ - **Database:** <!-- e.g. SQLite 3.43, PostgreSQL 15 -->
20
+
21
+ ## Steps to reproduce
22
+
23
+ 1.
24
+ 2.
25
+ 3.
26
+
27
+ ## Expected behavior
28
+
29
+ <!-- What should happen. -->
30
+
31
+ ## Actual behavior
32
+
33
+ <!-- What actually happens. Include error messages or tracebacks if relevant. -->
34
+
35
+ ## Minimal example (optional)
36
+
37
+ ```python
38
+ # Minimal code that reproduces the issue
39
+ ```
40
+
41
+ ## Additional context
42
+
43
+ <!-- Logs, screenshots, or other details that might help. -->
@@ -0,0 +1,37 @@
1
+ ---
2
+ name: Feature request
3
+ about: Suggest a new feature or enhancement for Ferro
4
+ title: "`feat` "
5
+ labels: feature
6
+ assignees: Ox54
7
+
8
+ ---
9
+
10
+ ## Summary
11
+
12
+ <!-- One or two sentences describing the feature and why it would be useful. -->
13
+
14
+ ## Current state
15
+
16
+ **Status:** <!-- e.g. Not implemented / Partially implemented -->
17
+
18
+ <!-- If partially implemented, list what works and what does not. -->
19
+
20
+ **Documentation references:**
21
+ - <!-- e.g. docs/coming-soon.md, docs/guide/queries.md (lines X–Y) -->
22
+
23
+ **Current workaround:** <!-- How can users achieve this today, if at all? -->
24
+
25
+ ## Proposed API (examples)
26
+
27
+ ```python
28
+ # Example of how the feature would be used
29
+ ```
30
+
31
+ <!-- Describe how this would integrate with the existing API (query builder, models, etc.). -->
32
+
33
+ ## Acceptance criteria
34
+
35
+ - [ ]
36
+ - [ ]
37
+ - [ ] Docs updated (e.g. remove from Coming Soon, add to relevant guide).
@@ -41,31 +41,9 @@ All workflows use explicit, fine-grained permissions (principle of least privile
41
41
 
42
42
  ---
43
43
 
44
- ### 1. Update Changelog (`update-changelog.yml`)
44
+ ### 1. Release (`release.yml`)
45
45
 
46
- **Trigger:** Push to `main` branch
47
-
48
- **Permissions:**
49
- ```yaml
50
- permissions:
51
- contents: write
52
- ```
53
-
54
- **Why These Permissions:**
55
- - `contents: write` - Allows the workflow to:
56
- - Commit updated CHANGELOG.md back to the repository
57
- - Push changes to the `main` branch
58
-
59
- **What It Does:**
60
- - Reads conventional commits since last release
61
- - Updates the `[Unreleased]` section of CHANGELOG.md
62
- - Commits and pushes the updated changelog
63
-
64
- ---
65
-
66
- ### 2. Release (`release.yml`)
67
-
68
- **Trigger:** Manual workflow dispatch OR release published
46
+ **Trigger:** Manual workflow dispatch
69
47
 
70
48
  **Permissions:**
71
49
  ```yaml
@@ -100,22 +78,23 @@ permissions:
100
78
  - Creates git tag
101
79
  - Creates GitHub release
102
80
  - Triggers publish workflow
81
+ - Reports whether release commits include `CHANGELOG.md`
82
+
83
+ **Required Secrets:**
84
+ - `RELEASE_DEPLOY_KEY` (private SSH key for a write-enabled deploy key)
85
+ - `RELEASE_TOKEN` (recommended PAT for release API actions and downstream workflow triggering, falls back to `GITHUB_TOKEN`)
103
86
 
104
87
  ---
105
88
 
106
- ### 3. Build & Publish (`publish.yml`)
89
+ ### 2. Build & Publish (jobs in `release.yml`)
90
+
91
+ **Trigger:** Part of `release.yml` after the release job (no reusable workflow).
107
92
 
108
- **Trigger:** Workflow call, manual dispatch, or release published
93
+ 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).
109
94
 
110
95
  **Permissions:**
111
96
 
112
- **For build/test jobs:** (default - read-only)
113
- ```yaml
114
- # No explicit permissions needed
115
- # Uses default read permissions to:
116
- # - Checkout code
117
- # - Read repository contents
118
- ```
97
+ **For build-wheels, build-sdist, test-wheels:** (default - read-only)
119
98
 
120
99
  **For publish-pypi job:**
121
100
  ```yaml
@@ -123,6 +102,27 @@ permissions:
123
102
  id-token: write
124
103
  ```
125
104
 
105
+ **Why These Permissions:**
106
+ - `id-token: write` - Allows the job to request an OIDC token and authenticate with PyPI using Trusted Publishing.
107
+
108
+ **What It Does:**
109
+ - Builds wheels for multiple platforms from the release tag
110
+ - Builds source distribution
111
+ - Tests built packages
112
+ - Publishes to PyPI using OIDC authentication
113
+
114
+ ---
115
+
116
+ ### 3. Publish Docs (`publish-docs.yml`)
117
+
118
+ **Trigger:** Workflow call from `release.yml`
119
+
120
+ **Permissions:**
121
+ ```yaml
122
+ permissions:
123
+ contents: read
124
+ ```
125
+
126
126
  **For deploy-docs job:**
127
127
  ```yaml
128
128
  permissions:
@@ -131,21 +131,13 @@ permissions:
131
131
  ```
132
132
 
133
133
  **Why These Permissions:**
134
- - `id-token: write` - Allows the workflow to:
135
- - Request an OIDC token from GitHub
136
- - Authenticate with PyPI using Trusted Publishing
137
- - Publish packages without API tokens
138
-
139
134
  - `pages: write` - Allows the workflow to:
140
135
  - Deploy the generated MkDocs static site to GitHub Pages
141
136
  - Update the Pages deployment for the repository
137
+ - `id-token: write` - Allows secure OIDC auth for Pages deployment
142
138
 
143
139
  **What It Does:**
144
- - Builds wheels for multiple platforms
145
- - Builds source distribution
146
- - Tests built packages
147
- - Publishes to PyPI using OIDC authentication
148
- - Builds MkDocs docs on release
140
+ - Builds MkDocs docs
149
141
  - Deploys docs to GitHub Pages
150
142
 
151
143
  ---
@@ -254,14 +246,22 @@ Permission to manage GitHub Pages deployments:
254
246
  - `pull-requests: write` for PR comments
255
247
  - `issues: write` for issue comments
256
248
 
257
- ### Release Created But Publish Workflow Doesn't Start
249
+ ### Release Completed But Publish/Docs Did Not Run
258
250
 
259
- **Cause:** The release was created by a workflow using the default `GITHUB_TOKEN`. GitHub can suppress follow-on workflow triggers for events generated by that token to prevent recursive workflow chains.
251
+ **Cause:** `release.yml` gates downstream jobs. If CI, packaging smoke, or release creation fails, publish/docs are skipped.
260
252
 
261
253
  **Solution:**
262
- - Create a repo secret named `RELEASE_TOKEN` (classic PAT or fine-grained PAT) with permission to create releases/tags in this repo.
263
- - The `release.yml` workflow is configured to prefer `RELEASE_TOKEN` and fall back to `GITHUB_TOKEN`.
264
- - Re-run the Release workflow after adding the secret.
254
+ - Open the failed `release.yml` run and inspect the first failed job.
255
+ - Re-run `release.yml` after fixing the upstream failure.
256
+
257
+ ### Release Fails to Push to `main` with GH013 Ruleset Error
258
+
259
+ **Cause:** Repository rules require pull requests for `main`, and the release actor is not allowed to bypass.
260
+
261
+ **Solution:**
262
+ - Ensure the workflow uses the `RELEASE_DEPLOY_KEY` secret (configured in `release.yml`).
263
+ - In repo rulesets for `main`, add the deploy key actor (or your release bot user) to bypass "Changes must be made through a pull request".
264
+ - Re-run the Release workflow.
265
265
 
266
266
  ### PyPI Publishing Fails with Authentication Error
267
267
 
@@ -278,13 +278,6 @@ Permission to manage GitHub Pages deployments:
278
278
 
279
279
  To verify permissions are working:
280
280
 
281
- ### Test Update Changelog
282
- ```bash
283
- git commit --allow-empty -m "feat: test changelog workflow"
284
- git push
285
- # Check Actions tab - should see commit from github-actions[bot]
286
- ```
287
-
288
281
  ### Test Release
289
282
  ```bash
290
283
  gh workflow run release.yml
@@ -293,8 +286,8 @@ gh workflow run release.yml
293
286
 
294
287
  ### Test Publish
295
288
  ```bash
296
- # Triggered automatically by release workflow
297
- # Or manually: gh workflow run publish.yml
289
+ # Triggered by release.yml after a successful release
290
+ gh run list --workflow=release.yml
298
291
  ```
299
292
 
300
293
  ---
@@ -321,5 +314,5 @@ gh workflow run release.yml
321
314
 
322
315
  ---
323
316
 
324
- **Last Updated:** 2026-01-27
317
+ **Last Updated:** 2026-02-13
325
318
  **Status:** ✅ All workflows properly configured with fine-grained permissions
@@ -15,7 +15,7 @@ Use this checklist to track your progress setting up PyPI Trusted Publishing.
15
15
  - [ ] Added trusted publisher with correct details:
16
16
  - [ ] Owner: `syn54x`
17
17
  - [ ] Repository: `ferro-orm`
18
- - [ ] Workflow: `publish.yml`
18
+ - [ ] Workflow: `release.yml` (must be the top-level workflow that runs the job; do not use `publish.yml` when it is called from release.yml)
19
19
  - [ ] Environment: `pypi` (optional)
20
20
  - [ ] Verified configuration appears in publisher list
21
21
 
@@ -33,14 +33,14 @@ Use this checklist to track your progress setting up PyPI Trusted Publishing.
33
33
 
34
34
  ## Workflow Verification
35
35
 
36
- - [ ] Confirmed `.github/workflows/publish.yml` exists
37
- - [ ] Verified workflow has `id-token: write` permission
38
- - [ ] Verified workflow has `environment: pypi` (if using environment)
36
+ - [ ] Confirmed `.github/workflows/release.yml` and `.github/workflows/publish.yml` exist
37
+ - [ ] Verified the job that publishes to PyPI has `id-token: write` (in publish.yml, used by release.yml)
38
+ - [ ] Verified publish-pypi job has `environment: pypi` (if using environment)
39
39
  - [ ] All workflows pass pre-commit hooks
40
40
 
41
41
  ## Testing
42
42
 
43
- - [ ] Test workflow triggered manually (optional)
43
+ - [ ] Test release workflow triggered manually
44
44
  - [ ] Reviewed workflow logs for authentication
45
45
  - [ ] No OIDC errors in logs
46
46
 
@@ -64,14 +64,11 @@ Use this checklist to track your progress setting up PyPI Trusted Publishing.
64
64
  ## Quick Commands
65
65
 
66
66
  ```bash
67
- # Manual workflow trigger
68
- gh workflow run publish.yml
69
-
70
- # Create a test release
71
- gh release create v0.1.1 --generate-notes
67
+ # Manual release workflow trigger
68
+ gh workflow run release.yml
72
69
 
73
70
  # Check workflow status
74
- gh run list --workflow=publish.yml
71
+ gh run list --workflow=release.yml
75
72
 
76
73
  # Install and test
77
74
  pip install ferro-orm
@@ -61,9 +61,10 @@ If you want to reserve the package name before publishing:
61
61
  PyPI Project Name: ferro-orm
62
62
  Owner: syn54x
63
63
  Repository name: ferro-orm
64
- Workflow filename: publish.yml
64
+ Workflow filename: release.yml
65
65
  Environment name: pypi (optional but recommended)
66
66
  ```
67
+ **Important:** Use `release.yml`, not `publish.yml`. The release workflow calls publish.yml; PyPI's OIDC token reflects the top-level workflow (release.yml), so the trusted publisher must match.
67
68
 
68
69
  4. **Click "Add"**
69
70
 
@@ -86,9 +87,10 @@ If you want to reserve the package name before publishing:
86
87
  ```
87
88
  Owner: syn54x
88
89
  Repository: ferro-orm
89
- Workflow: publish.yml
90
+ Workflow: release.yml
90
91
  Environment: pypi (optional)
91
92
  ```
93
+ Use `release.yml` (the workflow that triggers the run). Do not use `publish.yml` when it is invoked via workflow_call from release.yml, or uploads will fail with "Build Config URI does not match expected Trusted Publisher".
92
94
 
93
95
  6. **Click "Add"**
94
96
 
@@ -103,7 +105,7 @@ After adding the trusted publisher, you should see:
103
105
 
104
106
  Owner: syn54x
105
107
  Repository: ferro-orm
106
- Workflow: publish.yml
108
+ Workflow: release.yml
107
109
  Environment: pypi
108
110
  ```
109
111
 
@@ -175,7 +177,7 @@ Before doing a real release, test the workflow:
175
177
 
176
178
  2. **Trigger the workflow manually:**
177
179
  ```bash
178
- gh workflow run publish.yml
180
+ gh workflow run release.yml
179
181
  ```
180
182
 
181
183
  3. **Check the logs:**
@@ -208,6 +210,12 @@ Before doing a real release, test the workflow:
208
210
 
209
211
  ## Troubleshooting
210
212
 
213
+ ### Error: "Certificate's Build Config URI ... does not match expected Trusted Publisher (publish.yml @ ...)"
214
+
215
+ **Cause:** PyPI Trusted Publishing uses the **top-level** workflow for OIDC. This repo runs `publish.yml` via `workflow_call` from `release.yml`, so the token identifies `release.yml`, not `publish.yml`. If PyPI is configured for `publish.yml`, uploads fail.
216
+
217
+ **Solution:** On PyPI, set the trusted publisher **Workflow** to `release.yml` (not `publish.yml`). Update or add the publisher: Owner `syn54x`, Repository `ferro-orm`, Workflow **release.yml**, Environment `pypi` (optional).
218
+
211
219
  ### Error: "Trusted publishing authentication failed"
212
220
 
213
221
  **Cause:** OIDC token is rejected by PyPI
@@ -216,7 +224,7 @@ Before doing a real release, test the workflow:
216
224
  1. Verify all fields match exactly on PyPI:
217
225
  - Owner name (case-sensitive)
218
226
  - Repository name (case-sensitive)
219
- - Workflow filename (must include `.yml`)
227
+ - Workflow filename: use **release.yml** (the workflow that starts the run)
220
228
  - Environment name (if specified)
221
229
 
222
230
  2. Check GitHub environment exists:
@@ -241,10 +249,9 @@ Before doing a real release, test the workflow:
241
249
  **Cause:** PyPI can't find the workflow file
242
250
 
243
251
  **Solutions:**
244
- 1. Ensure `publish.yml` exists in `.github/workflows/`
245
- 2. Verify filename matches exactly (case-sensitive)
252
+ 1. Ensure `release.yml` exists in `.github/workflows/` (this is the workflow PyPI should reference)
253
+ 2. Verify the workflow filename on PyPI matches exactly (case-sensitive): `release.yml`
246
254
  3. Check workflow is committed to `main` branch
247
- 4. Workflow must be in the repository (not a reusable workflow)
248
255
 
249
256
  ### Error: "Environment not found"
250
257
 
@@ -318,7 +325,7 @@ After completing setup, verify:
318
325
  - [ ] PyPI trusted publisher configured
319
326
  - [ ] GitHub environment `pypi` created (if using)
320
327
  - [ ] Workflow permissions set to read/write
321
- - [ ] Workflow file `publish.yml` exists
328
+ - [ ] Workflow file `release.yml` exists (trusted publisher must use this name)
322
329
  - [ ] Workflow has `id-token: write` permission
323
330
  - [ ] Test workflow runs successfully
324
331
  - [ ] Can authenticate with PyPI (check logs)
@@ -342,7 +349,7 @@ After completing setup, verify:
342
349
  Project: ferro-orm
343
350
  Owner: syn54x
344
351
  Repository: ferro-orm
345
- Workflow: publish.yml
352
+ Workflow: release.yml
346
353
  Environment: pypi (optional)
347
354
  ```
348
355
 
@@ -0,0 +1,36 @@
1
+ ## Description
2
+
3
+ <!-- What problem does this PR solve? Why is this change needed? -->
4
+
5
+ ## Changes
6
+
7
+ -
8
+ -
9
+ -
10
+
11
+ ## Bridge and Schema Impact
12
+
13
+ <!-- Complete if this PR touches model/schema/FFI behavior -->
14
+
15
+ - [ ] No Rust/Python bridge changes
16
+ - [ ] Python model/schema changed
17
+ - [ ] Rust core or SQL generation changed
18
+ - [ ] `src/ferro/_core.pyi` updated (if needed)
19
+ - [ ] Integration test added first for new behavior
20
+
21
+ ## Migration / Breaking Changes
22
+
23
+ - [ ] No breaking changes
24
+ - [ ] Breaking changes included (details below)
25
+
26
+ <!-- If breaking, describe user impact and upgrade path -->
27
+
28
+ ## Documentation and Changelog
29
+
30
+ - [ ] No docs update needed
31
+ - [ ] Docs updated (README/docs/inline docs)
32
+ - [ ] Changelog entry needed
33
+
34
+ ## Related Issues
35
+
36
+ <!-- Example: Closes #123 -->
@@ -1,6 +1,7 @@
1
1
  name: CI
2
2
 
3
3
  on:
4
+ workflow_call:
4
5
  pull_request:
5
6
  branches: [main]
6
7
  push:
@@ -53,11 +54,11 @@ jobs:
53
54
 
54
55
  - name: Install dependencies
55
56
  run: |
56
- uv sync --group dev
57
+ uv sync --only-group ci-lint --no-install-project --python 3.13
57
58
 
58
59
  - name: Run all pre-commit hooks
59
60
  run: |
60
- uv run prek run --all-files
61
+ uv run --no-sync --python 3.13 prek run --all-files
61
62
 
62
63
  test-rust:
63
64
  name: Rust tests
@@ -108,7 +109,7 @@ jobs:
108
109
 
109
110
  - name: Install dependencies
110
111
  run: |
111
- uv sync --group dev
112
+ uv sync --only-group ci-test --no-install-project --python 3.13
112
113
 
113
114
  - name: Build Rust extension
114
115
  run: |
@@ -151,7 +152,7 @@ jobs:
151
152
 
152
153
  - name: Install dependencies
153
154
  run: |
154
- uv sync --group dev
155
+ uv sync --only-group ci-test --no-install-project --python ${{ matrix.python-version }}
155
156
 
156
157
  - name: Build Rust extension
157
158
  run: |
@@ -170,55 +171,6 @@ jobs:
170
171
  name: python-${{ matrix.python-version }}
171
172
  continue-on-error: true
172
173
 
173
- packaging-smoke:
174
- name: Packaging smoke check (${{ matrix.platform.name }})
175
- # Only run the cross-OS packaging check on main / manual.
176
- if: github.event_name != 'pull_request'
177
- runs-on: ${{ matrix.platform.runner }}
178
- strategy:
179
- fail-fast: false
180
- matrix:
181
- platform:
182
- - name: linux
183
- runner: ubuntu-latest
184
- target: x86_64
185
- manylinux: auto
186
- - name: macos
187
- runner: macos-latest
188
- target: aarch64
189
- manylinux: ''
190
- - name: windows
191
- runner: windows-latest
192
- target: x64
193
- manylinux: ''
194
- steps:
195
- - name: Checkout repository
196
- uses: actions/checkout@v4
197
-
198
- - name: Set up Python
199
- uses: actions/setup-python@v5
200
- with:
201
- python-version: '3.13'
202
-
203
- - name: Build wheel
204
- uses: PyO3/maturin-action@v1.48.0
205
- with:
206
- target: ${{ matrix.platform.target }}
207
- args: --release --out dist --find-interpreter
208
- manylinux: ${{ matrix.platform.manylinux }}
209
- sccache: 'true'
210
-
211
- - name: Install wheel
212
- shell: bash
213
- run: |
214
- python -m pip install --upgrade pip
215
- python -m pip install --find-links dist ferro-orm
216
-
217
- - name: Import check
218
- shell: bash
219
- run: |
220
- python -c "import ferro; print('Ferro imported successfully')"
221
-
222
174
  check-conventional-commits:
223
175
  name: Check Conventional Commits
224
176
  runs-on: ubuntu-latest
@@ -258,7 +210,7 @@ jobs:
258
210
  fi
259
211
 
260
212
  echo "Checking commit: $MESSAGE"
261
- if ! uv run cz check --commit-msg-file <(git log -1 --pretty=format:"%s%n%n%b" $commit); then
213
+ if ! uv run --no-sync --python 3.13 cz check --commit-msg-file <(git log -1 --pretty=format:"%s%n%n%b" $commit); then
262
214
  echo "❌ Commit message does not follow conventional format: $MESSAGE"
263
215
  exit 1
264
216
  fi
@@ -268,7 +220,7 @@ jobs:
268
220
 
269
221
  all-checks:
270
222
  name: All Checks Passed
271
- needs: [lint-and-format, test-python-pr, test-python-main, test-rust, packaging-smoke]
223
+ needs: [lint-and-format, test-python-pr, test-python-main, test-rust]
272
224
  runs-on: ubuntu-latest
273
225
  if: always()
274
226
  steps:
@@ -281,6 +233,5 @@ jobs:
281
233
  if ! ok "${{ needs.test-python-pr.result }}"; then exit 1; fi
282
234
  if ! ok "${{ needs.test-python-main.result }}"; then exit 1; fi
283
235
  if ! ok "${{ needs.test-rust.result }}"; then exit 1; fi
284
- if ! ok "${{ needs.packaging-smoke.result }}"; then exit 1; fi
285
236
 
286
237
  echo "All checks passed!"
@@ -0,0 +1,62 @@
1
+ name: Packaging Smoke
2
+
3
+ on:
4
+ workflow_call:
5
+ push:
6
+ branches: [main]
7
+ workflow_dispatch:
8
+
9
+ concurrency:
10
+ group: packaging-${{ github.workflow }}-${{ github.ref }}
11
+ cancel-in-progress: true
12
+
13
+ permissions:
14
+ contents: read
15
+
16
+ jobs:
17
+ packaging-smoke:
18
+ name: Packaging smoke check (${{ matrix.platform.name }})
19
+ runs-on: ${{ matrix.platform.runner }}
20
+ strategy:
21
+ fail-fast: false
22
+ matrix:
23
+ platform:
24
+ - name: linux
25
+ runner: ubuntu-latest
26
+ target: x86_64
27
+ manylinux: auto
28
+ - name: macos
29
+ runner: macos-latest
30
+ target: aarch64
31
+ manylinux: ''
32
+ - name: windows
33
+ runner: windows-latest
34
+ target: x64
35
+ manylinux: ''
36
+ steps:
37
+ - name: Checkout repository
38
+ uses: actions/checkout@v4
39
+
40
+ - name: Set up Python
41
+ uses: actions/setup-python@v5
42
+ with:
43
+ python-version: '3.13'
44
+
45
+ - name: Build wheel
46
+ uses: PyO3/maturin-action@v1.48.0
47
+ with:
48
+ target: ${{ matrix.platform.target }}
49
+ args: --release --out dist --find-interpreter
50
+ manylinux: ${{ matrix.platform.manylinux }}
51
+ sccache: 'true'
52
+
53
+ - name: Install wheel
54
+ shell: bash
55
+ run: |
56
+ python -m pip install --upgrade pip
57
+ python -m pip install --find-links dist ferro-orm
58
+
59
+ - name: Import check
60
+ shell: bash
61
+ run: |
62
+ python -c "import ferro; print('Ferro imported successfully')"
@@ -2,6 +2,12 @@ name: Publish Docs
2
2
 
3
3
  on:
4
4
  workflow_dispatch:
5
+ workflow_call:
6
+ inputs:
7
+ ref:
8
+ description: Git ref (branch, tag, or SHA) to build docs from
9
+ required: true
10
+ type: string
5
11
 
6
12
  concurrency:
7
13
  group: docs-${{ github.workflow }}-${{ github.ref }}
@@ -17,6 +23,8 @@ jobs:
17
23
  steps:
18
24
  - name: Checkout repository
19
25
  uses: actions/checkout@v4
26
+ with:
27
+ ref: ${{ inputs.ref }}
20
28
 
21
29
  - name: Set up Python
22
30
  uses: actions/setup-python@v5
@@ -30,11 +38,11 @@ jobs:
30
38
 
31
39
  - name: Install documentation dependencies
32
40
  run: |
33
- uv sync --group dev
41
+ uv sync --only-group docs --no-install-project --python 3.13
34
42
 
35
43
  - name: Build MkDocs site
36
44
  run: |
37
- uv run mkdocs build
45
+ uv run --no-sync mkdocs build
38
46
 
39
47
  - name: Upload Pages artifact
40
48
  uses: actions/upload-pages-artifact@v3