Contributing to Ferro¶
We welcome contributions to Ferro! This guide will help you get started with developing Ferro locally.
Prerequisites¶
Before starting, ensure you have:
- Python 3.13+: Ferro requires Python 3.13 or later
- Rust toolchain: Required for building the Rust core
- UV: Fast Python package manager
Getting Started¶
1. Clone the Repository¶
2. Install Dependencies¶
This will install all development dependencies including: - Testing tools (pytest, pytest-asyncio, pytest-cov) - Linting and formatting tools (ruff, prek) - Build tools (maturin) - Documentation tools (mkdocs-material) - Release tools (commitizen, python-semantic-release)
3. Install Pre-commit Hooks¶
# Install all hooks (file checks, linting, formatting)
uv run prek install
# Install commit message validation hook
uv run prek install --hook-type commit-msg
These hooks will automatically: - Check for trailing whitespace - Fix end-of-file issues - Validate YAML, TOML, and JSON files - Format Python code with Ruff - Format Rust code with rustfmt - Lint Rust code with clippy - Validate conventional commit messages
4. Build the Rust Extension¶
This compiles the Rust core and installs it in development mode. You'll need to re-run this command after making changes to Rust code.
Development Workflow¶
Running Tests¶
# Run all tests with coverage
uv run pytest
# Run specific test file
uv run pytest tests/test_models.py
# Run with verbose output
uv run pytest -v
# Run tests and generate coverage report
uv run pytest --cov=src --cov-report=html
Running Linters¶
# Run all pre-commit hooks
uv run prek run --all-files
# Run specific hooks
uv run ruff check . # Python linting
uv run ruff format . # Python formatting
cargo fmt # Rust formatting
cargo clippy # Rust linting
Building Documentation¶
# Serve documentation locally (with live reload)
uv run mkdocs serve
# Build documentation
uv run mkdocs build
# Documentation will be available at http://127.0.0.1:8000/
Testing Your Changes¶
Before submitting a PR, ensure:
-
All tests pass:
-
All linters pass:
-
Rust tests pass:
-
Code builds successfully:
Conventional Commits¶
Ferro uses Conventional Commits for automated version bumping and changelog generation. All commit messages must follow this format:
Commit Types¶
- feat: New feature (triggers minor version bump)
- fix: Bug fix (triggers patch version bump)
- docs: Documentation changes only
- refactor: Code refactoring (no functional changes)
- test: Adding or updating tests
- perf: Performance improvements (triggers patch version bump)
- build: Build system changes
- ci: CI/CD configuration changes
- chore: Other changes that don't modify src or test files
Examples¶
# Feature commits
git commit -m "feat: add support for many-to-many relations"
git commit -m "feat(queries): implement OR operator for filters"
# Bug fix commits
git commit -m "fix: resolve connection pool deadlock"
git commit -m "fix(migrations): handle nullable foreign keys correctly"
# Documentation commits
git commit -m "docs: update installation instructions"
git commit -m "docs(api): add examples for transaction usage"
# Breaking changes (triggers major version bump)
git commit -m "feat!: change Model.create() to require explicit save()"
# OR
git commit -m "feat: redesign query API
BREAKING CHANGE: Query.filter() now requires Q objects instead of kwargs"
Commit Validation¶
The pre-commit hook will automatically validate your commit message format. Invalid commits will be rejected with an error message.
If you need to bypass the hook (not recommended), use:
Pull Request Process¶
-
Create a feature branch:
-
Make your changes and commit:
-
Push to your fork:
-
Open a Pull Request on GitHub
-
Wait for CI checks to pass:
- All linters must pass
- All tests must pass
-
Code must build on all platforms
-
Address review feedback if any
-
Merge once approved!
PR Requirements¶
- ✅ All CI checks pass
- ✅ Conventional commit format followed
- ✅ Tests added for new features
- ✅ Documentation updated
- ✅ No merge conflicts with main
Release Process¶
Ferro uses automated releases. You don't need to manually bump versions or update the changelog.
How Releases Work¶
- Commits are merged to main
-
Changelog is automatically updated with unreleased changes
-
Maintainer triggers the release workflow
The release process is fully automated. To trigger a new release:
- Via GitHub CLI (Recommended):
- Via GitHub Web UI: Go to the Actions tab, select the Release workflow, and click Run workflow.
This will:
- Automatically determine the next version from conventional commits.
- Update pyproject.toml and Cargo.toml.
- Finalize CHANGELOG.md.
- Create and push the Git tag.
- Trigger the Build & Publish workflow to upload wheels to PyPI.
- Package is automatically published to PyPI
- Cross-platform wheels are built
- Package is uploaded using trusted publishing
Version Bumping¶
Version bumps are determined by commit types:
- Major (1.0.0 → 2.0.0): Commits with
BREAKING CHANGE:in body or!after type - Minor (1.0.0 → 1.1.0): Commits with
feat:type - Patch (1.0.0 → 1.0.1): Commits with
fix:orperf:type
Code Style¶
Python¶
- Follow PEP 8 style guide
- Use type hints for all functions
- Maximum line length: 100 characters (enforced by Ruff)
- Use Pydantic for data validation
- Write docstrings for all public APIs
Rust¶
- Follow Rust style guidelines (enforced by rustfmt)
- Use
cargo clippywarnings as errors - Write documentation for public APIs
- Use descriptive variable names
- Prefer explicit types over inference in function signatures
Testing¶
Python Tests¶
Located in tests/ directory. Use pytest with async support:
import pytest
from ferro import Model, FerroField, connect
@pytest.mark.asyncio
async def test_create_model():
await connect("sqlite::memory:")
user = await User.create(name="Alice")
assert user.name == "Alice"
Rust Tests¶
Located alongside Rust code with #[cfg(test)]:
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_sql_generation() {
let query = generate_select_query("users");
assert_eq!(query, "SELECT * FROM users");
}
}
Project Structure¶
ferro/
├── src/
│ ├── ferro/ # Python package
│ │ ├── __init__.py
│ │ ├── models.py
│ │ ├── queries.py
│ │ └── ...
│ └── lib.rs # Rust core
├── tests/ # Python tests
├── docs/ # Documentation
├── .github/
│ └── workflows/ # CI/CD workflows
├── Cargo.toml # Rust dependencies
├── pyproject.toml # Python dependencies
└── README.md
Getting Help¶
- Issues: GitHub Issues
- Discussions: GitHub Discussions
- Documentation: https://syn54x.github.io/ferro-orm
License¶
By contributing to Ferro, you agree that your contributions will be licensed under the same license as the project (Apache 2.0 OR MIT).