We welcome contributions to Ferro! This guide will help you get started with developing Ferro locally.
Before starting, ensure you have:
- Python 3.13+: Ferro requires Python 3.13 or later
- Rust toolchain: Required for building the Rust core
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
- UV: Fast Python package manager
curl -LsSf https://astral.sh/uv/install.sh | sh
git clone https://github.com/syn54x/ferro-orm.git
cd ferro-ormuv sync --group devThis 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)
# Install all hooks (file checks, linting, formatting)
uv run prek install
# Install commit message validation hook
uv run prek install --hook-type commit-msgThese 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
uv run maturin developThis compiles the Rust core and installs it in development mode. You'll need to re-run this command after making changes to Rust code.
# 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# 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# 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/Before submitting a PR, ensure:
-
All tests pass:
uv run pytest
-
All linters pass:
uv run prek run --all-files
-
Rust tests pass:
cargo test -
Code builds successfully:
uv run maturin develop
Ferro uses Conventional Commits for automated version bumping and changelog generation. All commit messages must follow this format:
<type>[optional scope]: <description>
[optional body]
[optional footer(s)]
- 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
# 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"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:
git commit --no-verify -m "message"-
Create a feature branch:
git checkout -b feat/my-new-feature
-
Make your changes and commit:
git add . git commit -m "feat: add my new feature"
-
Push to your fork:
git push origin feat/my-new-feature
-
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!
- ✅ All CI checks pass
- ✅ Conventional commit format followed
- ✅ Tests added for new features
- ✅ Documentation updated
- ✅ No merge conflicts with main
Ferro uses automated releases. You don't need to manually bump versions or update the changelog.
-
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):
gh workflow run release.yml
- 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.tomlandCargo.toml. - Finalize
CHANGELOG.md. - Create and push the Git tag.
- Trigger the Build & Publish workflow to upload wheels to PyPI.
- Via GitHub CLI (Recommended):
-
Package is automatically published to PyPI
- Cross-platform wheels are built
- Package is uploaded using trusted publishing
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
- 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
- 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
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"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");
}
}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
- Issues: GitHub Issues
- Discussions: GitHub Discussions
- Documentation: https://syn54x.github.io/ferro-orm
By contributing to Ferro, you agree that your contributions will be licensed under the same license as the project (Apache 2.0 OR MIT).