Skip to content

Project Development

Jacob Callahan edited this page Apr 16, 2026 · 1 revision

Project Development

This page covers the recommended local development workflow for Hussh contributors. It is based on the repository's GitHub Actions workflows, pyproject.toml, tox.toml, and the test fixtures in tests/.

Tooling Overview

Hussh is a mixed Rust/Python project built with:

  • maturin for building and installing the PyO3 extension module
  • uv for managing the local Python environment and editable installs
  • tox for running the supported test and lint environments
  • pre-commit for the same formatting and lint checks that CI runs first

Initial Local Setup

  1. Install Rust and Cargo.
  2. Install Python and uv.
  3. Create a virtual environment and install the development dependencies:
uv venv
source .venv/bin/activate
uv pip install -e .[dev]

That editable install gives you the Python dependencies used by local development, including tox, pre-commit, pytest, Docker bindings, and maturin.

Rebuilding After Changes

For a fast local development loop, reinstall the extension into your active environment after Rust or PyO3-facing changes:

maturin develop

If you want to mirror the wheel-building step used in GitHub Actions, build first and then reinstall:

maturin build
pip install .[dev] --find-links dist --force-reinstall

If you want to stay on uv for the reinstall step locally, use:

uv pip install --force-reinstall -e .[dev]

CI builds wheels with maturin and then installs the project before running the test suite. Locally, maturin develop is usually the quickest option while iterating, and maturin build is useful when you want to verify the distributable wheel still builds cleanly.

Pre-commit Checks

Run the pre-commit hooks manually before pushing changes:

pre-commit run --all-files

This matches the first CI gate. The configured hooks currently run:

  • cargo fmt
  • clippy
  • ruff-format
  • ruff --fix --exit-non-zero-on-fix

You can also run the same check through tox:

tox -e lint

Running Tests

The project standard is to run tests through tox.

Full test matrix

tox -m test

This requires the matching Python interpreters to be available locally. If you only have one interpreter installed, run that specific tox environment instead.

Single Python version

tox -e py312

Filter to a subset of pytest tests

tox -e py312 -- -k proxy_jump

The tox test environments use uv as the installer and execute:

pytest -v tests/

If you specifically need to run pytest directly while debugging, make sure you already refreshed the extension with maturin develop or reinstalled a freshly built wheel first.

Test Server Requirements

Most tests depend on Dockerized SSH test servers. The fixtures in tests/conftest.py will automatically pull and start:

ghcr.io/jacobcallahan/hussh/hussh-test-server:latest

The default tests use Docker ports:

  • 8022 for the primary SSH test server
  • 8023 for the secondary server used in proxy and copy scenarios

Make sure Docker is running before you start the test suite.

MultiConnection Test Hostnames

The MultiConnection tests also require local hostnames that resolve to 127.0.0.1. CI adds them automatically; locally you should add them yourself:

echo "127.0.0.1 hussh-server-1.test hussh-server-2.test hussh-server-3.test hussh-server-4.test hussh-server-5.test" | sudo tee -a /etc/hosts

If these hostnames are missing, the MultiConnection tests are skipped.

You can also choose how many multi-host test servers to launch:

tox -e py312 -- --num-servers 5

PyO3 and Native Build Notes

  • The extension is built through maturin with the pyo3/extension-module feature enabled.
  • Python-only edits do not always require a rebuild, but Rust changes do.
  • If imports behave unexpectedly after native changes, rerun maturin develop.
  • Build artifacts land under target/, so stale native artifacts can sometimes explain confusing local behavior.

On Linux and macOS, the CI workflows also install OpenSSL-related system packages before building. If a local native build fails during dependency detection or linking, check that your machine has the equivalent OpenSSL and pkg-config tooling available.

Useful Commands

# editable install for development
uv pip install -e .[dev]

# rebuild the extension into the active environment
maturin develop

# verify the wheel build
maturin build

# run the same pre-commit gate used by CI
pre-commit run --all-files

# run lint through tox
tox -e lint

# run tests
tox -e py312
tox -m test