Contributing to FuelLib

We welcome contributions! This page covers how to set up your development environment, make changes, and submit pull requests.

Development Setup

Clone the repository and install in editable mode with development dependencies:

git clone https://github.com/NatLabRockies/FuelLib.git
cd FuelLib
pip install -e '.[dev]'

This installs FuelLib with all development tools:

  • Documentation: Sphinx, sphinx-rtd-theme, sphinxcontrib-bibtex

  • Code formatting & linting: Ruff

  • Type checking: ty

  • Testing: pytest, pytest-cov

Optional: Conda Environment

To use a specific conda environment:

conda create --name fuellib-dev-env
conda activate fuellib-dev-env
conda install -c conda-forge rust
pip install -e '.[dev]'

Developer Tasks

Common developer tasks are defined with taskipy in the [tool.taskipy.tasks] table of pyproject.toml, and can be run with task <name> (or uv run task <name> if using uv):

Task

Command

Description

format

ruff format

Format the codebase

lint

ruff check --fix

Lint the codebase, auto-fixing issues where possible

types

ty check

Run static type checking

test

pytest

Run the test suite

pre-commit

lefthook run pre-commit

Run the full pre-commit suite (format, lint, types, test)

build-docs

sphinx-build -M html docs/ docs/_build

Build the documentation with Sphinx

view-docs

bash fuellib/cli/view-docs.sh

Open the built documentation in a browser

Pre-commit Hooks

This repository uses lefthook to run the formatting, linting, type-checking, and test tasks automatically before each commit. After installing the dev dependencies, enable the hooks with:

lefthook install

You can also run the full pre-commit suite manually with lefthook run pre-commit --all-files. Note that the import-linter module-layering check (lint-imports) is not yet part of the pre-commit suite, since the codebase doesn’t fully match the target module layering yet.

Test coverage is currently enforced at a low threshold (fail_under = 20 in pyproject.toml) while the codebase is migrated; this will be raised over time.

Optional: uv

uv is included as a dev dependency and offers a much faster alternative to pip for installing dependencies and managing a virtual environment:

uv venv
uv pip install -e '.[dev]'

Run uv lock to regenerate uv.lock after changing dependencies in pyproject.toml, keeping installs reproducible across machines.

Working with Units (pint)

FuelLib’s public API is unit-aware: physical quantities (e.g. Fuel.MW, Fuel.Tc, Fuel.Pc) are pint Quantity objects rather than bare float/np.ndarray values. When contributing new code that creates or consumes physical quantities:

  • Always build quantities using the shared fuellib.Units registry (also

    available as fuellib.utils.Units), for example Units.Quantity(value, "K"). Do not instantiate a new pint.UnitRegistry(): quantities from different registries are not compatible with one another.

  • Convert between units with .to("target_unit"); access the raw numeric value with .magnitude only at boundaries (e.g. plotting, exporting, passing to non-pint-aware libraries).

  • For annotations, import types from fuellib.utils and use its

    aliases: Array1D and Array2D for one- and two-dimensional float64 NumPy arrays, and Quantity0D, Quantity1D, and Quantity2D for Pint quantities with scalar, one-dimensional, and two-dimensional magnitudes, respectively.

  • Do not reintroduce bare-float return types for existing public Fuel/utility APIs; this would reverse an intentional breaking change (see CHANGELOG.md).

Updating the Changelog

This project keeps CHANGELOG.md in the Keep a Changelog format. The keepachangelog package is a dependency used to parse and validate entries against that format. When making a change, add a bullet under the ## [Unreleased] section using the appropriate category (Added, Changed, Deprecated, Removed, Fixed, or Security).

Contributing Guidelines

New contributions are always welcome! To contribute:

  1. Fork the main repository on GitHub

  2. Create a new branch for your feature: git checkout -b newFeature

  3. Make your changes and update documentation as needed

  4. Ensure development dependencies are installed (see Development Setup above)

  5. Format, lint, and test your code using the provided development tasks (e.g., uv run task pre-commit).

  6. Update the CHANGELOG (under [UNRELEASED]) with detailed descriptions of the contributions.

  7. Open a Pull Request (PR) from your fork to the main FuelLib repository

Building and Viewing Documentation Locally

To build the documentation after installing with pip install -e '.[dev]':

fl-build-docs

The built documentation will be in docs/_build/html/. Open index.html in your browser to view it.

To clean the build artifacts:

fl-clean-docs

Releasing

Releases are maintainer-gated. A maintainer first opens a release pull request that manually sets a new PEP 440 version in [project].version in pyproject.toml and moves all release entries from ## [Unreleased] into a dated ## [<version>] - YYYY-MM-DD section. Review and merge this pull request only after the normal CI checks pass. The [Unreleased] section must be empty when the release workflow runs.

An authorized maintainer then opens the repository’s Actions tab, selects the Release workflow, chooses the protected main branch, and dispatches it. The workflow validates the version and changelog, reruns the test suite, builds and checks the source distribution and wheel, verifies that the wheel installs, and publishes the tested artifacts. After a successful PyPI upload it creates the v<version> tag and GitHub release.

Release security setup (repository administrator)

Configure PyPI trusted publishing for the fuellib project with this repository, the release.yml workflow, and the pypi environment. Do not create or store a PyPI API token in repository secrets; the workflow uses a short-lived OpenID Connect token instead.

In GitHub, create a pypi environment and require approval from one or more release maintainers. Keep main protected: require pull requests and successful CI checks, restrict who can push, and restrict who can run or approve deployments. The workflow only runs its release jobs when dispatched from main; its PyPI credential is scoped to the protected environment, and its GitHub write permission is scoped to the final tag-and-release job.