Skip to content

Development Workflow

Development Setup

EpiModel is built with Rust and Python. To set up your development environment:

1. Install Prerequisites

# Install Rust toolchain
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh

# Install Python 3.11 or higher
# (use your system's package manager)

# Install Poetry
curl -sSL https://install.python-poetry.org | python3 -

2. Clone and Setup

git clone https://github.com/MUNQU/epimodel.git
cd epimodel

# Install Python dependencies
cd py-epimodel
poetry install --with dev

# Install pre-commit hooks (optional but recommended)
poetry run pre-commit install

3. Build the Project

# Build Rust workspace
cargo build --workspace

# Build Python extension
cd py-epimodel
maturin develop --release

Branching Strategy

We use a simplified Git Flow approach:

  • main: Production-ready code. Protected branch.
  • develop: Integration branch for features. Used for staging releases.
  • feature/*: New features, refactoring, or significant changes (e.g., feature/add-stochastic-model)
  • fix/*: Bug fixes (e.g., fix/calculation-error)
  • docs/*: Documentation-only changes (e.g., docs/update-api-reference)
  • test/*: Test additions or improvements (e.g., test/add-sir-model-tests)
  • chore/*: Maintenance tasks, dependencies (e.g., chore/update-dependencies)

Development Process

1. Starting New Work

# Always start from the latest develop branch
git checkout develop
git pull origin develop

# Create a feature branch
git checkout -b feature/your-feature-name

2. Making Changes

# Make your changes to the code

# Run quality checks before committing
cd py-epimodel
poetry run ruff check . --fix         # Auto-fix Python linting issues
poetry run ruff format .              # Format Python code
poetry run mypy epimodel              # Type checking

cd ..
cargo fmt --all                       # Format Rust code
cargo clippy --all-targets --fix      # Fix Rust linting issues

3. Testing Your Changes

# Run Python tests
cd py-epimodel
poetry run pytest -v

# Run Rust tests
cd ..
cargo test --workspace

4. Committing Changes

We follow Conventional Commits for commit messages:

# Commit format: <type>(<scope>): <description>

git add .
git commit -m "feat(transitions): add support for time-varying rates"
git commit -m "fix(simulation): correct population calculation"
git commit -m "docs(readme): update installation instructions"
git commit -m "test(models): add tests for stratified models"

Commit Types:

  • feat: New feature
  • fix: Bug fix
  • docs: Documentation only
  • style: Code style changes (formatting)
  • refactor: Code refactoring
  • test: Adding or updating tests
  • chore: Maintenance tasks, dependencies
  • perf: Performance improvements
  • ci: CI/CD changes
  • build: Build system changes

Branch vs Commit Type:

The branch name indicates the primary purpose of the work, while commit types describe individual changes:

  • A feature/refactor-api branch may have refactor, test, and docs commits
  • A test/* branch primarily adds tests but may have fix commits too
  • A docs/* branch is documentation-only, so all commits should use docs type

5. Pushing and Creating Pull Requests

# Push your branch
git push origin feature/your-feature-name

# Create a Pull Request on GitHub
# - Target the 'develop' branch
# - Fill in the PR template with:
#   - Description of changes
#   - Related issues
#   - Testing performed
#   - Screenshots (if UI changes)

6. Code Review and CI

When you create a PR, automated checks run:

  • Code Quality Pipeline: Linting, formatting, type checking for both Python and Rust
  • Build Pipeline: Builds on Ubuntu, Windows, and macOS with Python versions
  • Test Coverage: Runs all tests and reports coverage

PR Requirements:

  • All CI checks must pass
  • At least one approving review
  • No merge conflicts with target branch
  • Code coverage should be >95%

7. Merging to Develop

Once approved:

# Merge using "Squash and merge" or "Rebase and merge" on GitHub
# Delete the feature branch after merging

Continuous Integration Pipelines

The project uses three main CI/CD pipelines:

1. Code Quality (on every push/PR)

  • Python: Ruff (linting/formatting), MyPy (type checking), Pytest (testing)
  • Rust: rustfmt (formatting), Clippy (linting), cargo test
  • Runs on: Ubuntu
  • Purpose: Ensure code quality standards

2. Build (on every push/PR)

  • Builds on: Ubuntu, Windows, macOS
  • Python versions: 3.11, 3.12
  • Tests: Full test suite on all platforms
  • Artifacts: Wheel files for each platform
  • Purpose: Ensure cross-platform compatibility

3. Release (on GitHub release)

  • Builds wheels for all platforms
  • Creates source distribution
  • Publishes to PyPI using trusted publishing
  • Purpose: Automated deployment

Local Development Tips

Quick Commands

# Run all quality checks (Python)
cd py-epimodel && poetry run ruff check . && poetry run ruff format . && poetry run mypy epimodel && poetry run pytest

# Run all quality checks (Rust)
cargo fmt --all && cargo clippy --all-targets && cargo test --workspace

# Build and test the Python package locally
cd py-epimodel
maturin develop --release
poetry run pytest

Pre-commit Hooks

Install pre-commit hooks to automatically check code before committing:

cd py-epimodel
poetry run pre-commit install

This runs:

  • Ruff linting and formatting
  • MyPy type checking
  • Trailing whitespace removal
  • End-of-file fixing
  • Poetry lock file validation
  • Ensures poetry.lock stays in sync with pyproject.toml

Keeping poetry.lock in Sync

Important: Always ensure poetry.lock is synchronized with pyproject.toml before committing.

Pre-commit hooks handle this automatically:

cd py-epimodel
poetry run pre-commit install  # One-time setup

# Now git commit automatically updates poetry.lock if needed
git add pyproject.toml
git commit -m "chore: update dependencies"

Manual

If not using pre-commit hooks:

cd py-epimodel
poetry check  # Check if lock file is in sync
poetry lock   # Update lock file without changing versions
git add poetry.lock
git commit -m "chore: update poetry.lock"

Why this matters: Out-of-sync lock files cause CI/CD pipeline failures. The pre-commit hook prevents this automatically.

Debugging Rust from Python

# Build with debug symbols
maturin develop

# Run Python
cd py-epimodel
poetry run python -m your_test_script.py

Building Documentation

The project uses MkDocs Material for documentation:

# Install documentation dependencies
cd py-epimodel
poetry install --with docs

# Serve documentation locally
poetry run mkdocs serve

# Build documentation
poetry run mkdocs build

Visit http://127.0.0.1:8000 to view the documentation locally.

Next Steps