Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
File renamed without changes.
248 changes: 248 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,248 @@
# Contributing to Exosphere

Thank you for considering a contribution to Exosphere!

We absolutely welcome contributions. If you are making something cool with Exosphere,
please let us know in the [GitHub Discussions](https://github.com/mrdaemon/exosphere/discussions)!

If what you'd like to implement is a fundamental change with ramifications that touch
on the core functionality of Providers, Objects or the way Exosphere is designed, please
[open an issue](https://github.com/mrdaemon/exosphere/issues/new?template=feature_request.md) to discuss it first,
mainly to avoid clashing with projected features and the general design philosophy of the program.

Bugfixes are always welcome.

Documentation fixes are also welcome! If anything is unclear, we'd consider that
a bug, and [filing an issue](https://github.com/mrdaemon/exosphere/issues/new?template=documentation_suggestion.md)
would be correct, and desirable.

## Guidelines

### Code Style

Code style should try to mimic the existing form where possible.
We use type annotations everywhere, and run a typechecker as standard.
The code should pass the 'check' poe task (see below), which generally should
encompass the basic stuff.

### Test Suites

Exosphere has an exhaustive test suite, comprised of unit tests, smoke tests,
and integration tests for all of the core functionality and most of the UI.

If you add a feature, we would ask for minimum test coverage to ensure the
feature does not regress across supported versions and dependencies.

Submitting a PR will run the test suite across all supported Python versions
as well as the minimum dependencies specified in `pyproject.toml`.

We standardize on pytest and pytest-mock, essentially. The tests directory
in the project loosely mirrors the source tree and should be self-explanatory.

When possible, favor `create_autospec` and/or `Mock(spec=...)` for fixtures,
or (preferably) reuse the existing ones.

### Targeted Python versions

We currently target a minimum version of **Python 3.13**, with no hard upper
boundary. New Python releases may take a moment to be added to CI, but this
is generally handled swiftly.

Your code should always be compatible with the lowest common denominator,
which is Python 3.13, at the time of writing. CI matrices will ensure this
through the test suites.

### Packaging Exosphere for Operating System Distributions

Our official means of distribution is through PyPI, but we do welcome,
and will absolutely support packaging efforts for various operating systems
for inclusion in their repositories, and will subsequently integrate them in
the installation documentation.

If the platform you are packaging for has specific needs, we are likely to
try to accommodate them through code changes, if possible!

### LLM/"AI" Contributions Policy

We do not endorse, recommend, or want contributions from Large Language Models
(LLMs) or "AI" code generation tools. We like our bugs human made and ethically
sourced, with a verifiable license.

Anything you wish to contribute should be something you can explain and defend in
code review, assert the licensing of, and take responsibility for as the human who
owns the change. If you cannot explain it, or would defer to asking an LLM, please
don't bother, and save everyone the time.

If you want the author to bother reviewing your pull request, take the time to
write it yourself. Automated PRs, or code generated by Claude, ChatGPT, Copilot
or by any Large Language Model or similar will be summarily rejected.

### Licensing

Content submitted to the project should be compatible with its licenses.
The short version is: MIT for code, CC BY 4.0 for documentation prose, per LICENSE
and .reuse/dep5. See those files for details.

## Development Guide

### Using Exosphere as a Library

This use case is not currently well supported, but it is possible to use Exosphere as a library.
The documentation for this (alongside actual examples) is still a WIP, but you can
refer to the [Online API Documentation](https://docs.exosphere.tools/en/stable/api/index.html)
for the core functionality and objects that are considered public.

### Development Quick Start

The project uses [uv](https://docs.astral.sh/uv/getting-started/installation/) to
manage dependencies, and run tasks (through [poe](https://poethepoet.natn.io/)).

It will fetch and manage its own Python if you don't have a compatible runtime available, and maintain its own venv automatically.

```bash
uv sync --dev
uv run exosphere
```

Linting, formatting and testing can be done with poe tasks.

**This should be done systematically before submitting a PR**.

```bash
uv run poe format
uv run poe check
uv run poe test
```

For more details, and available tasks, run:

```bash
uv run poe --help
```

### UI Development Quick Start

The UI is built with [Textual](https://textual.textualize.io/).

A quick start for running the UI with live editing and reloading, plus debug
console, is as follows:

```bash
# Ensure you have the dev dependencies
uv sync --dev
# Run the console
uv run textual console
# In another terminal, run the UI
uv run textual run --dev -c exosphere ui
```

Congratulations! Editing any of the `.tcss` files in the `ui/` directory will
reflect changes immediately.

### Documentation Editing Quick Start

To edit the documentation, you can use the following commands:

```bash
uv sync --dev
uv run poe docs-serve
```

This will start a local server at `http://localhost:8000` where you can view the
documentation. You can edit the files in the `docs/source` directory, and the changes
will be reflected in real-time.

To check the documentation for spelling errors, you can run:

```bash
uv run poe docs-spellcheck
```

Linting is performed as part of the `poe docs` task, which also builds the
documentation, but can also be invoked separately:

```bash
uv run poe docs-lint
```

### Project Structure

The project is managed via uv and `pyproject.toml`, which contains all dependencies,
scripts, and metadata for the application.

Exosphere uses [Poe the Poet](https://poethepoet.natn.io/) as a task runner, and all
tasks are defined in the `pyproject.toml` file under the `[tool.poe.tasks]` table.

#### Root Directory

| path | description |
| ---- | ----------- |
| `LICENSES/` | Full text of every license used by the project and its assets |
| `docs/` | Sphinx documentation source tree |
| `docs/source/_ext/` | Custom Sphinx extensions for the project |
| `docs/source/changelog/` | Per-release changelog entries (markdown) and their assets |
| `examples/` | Example configuration files and reports |
| `scripts/` | Utilitarian scripts for dev and maintenance |
| `src/` | Main source code for the application |
| `tests/` | Test suite for the application |

#### Source Tree

| path | description |
| ---- | ----------- |
| `src/exosphere/` | Main application source code |
| `src/exosphere/commands/` | CLI command implementations |
| `src/exosphere/providers/` | Package Manager Provider implementations (e.g. debian, freebsd, redhat, etc) |
| `src/exosphere/schema/` | Reporting JSON schema definitions |
| `src/exosphere/setup/` | Discovery and platform detection module |
| `src/exosphere/templates/` | Jinja2 templates for reporting |
| `src/exosphere/ui/` | Textual UI source code |
| `src/exosphere/ui/style.tcss` | Textual CSS for styling the UI |

The rest of the source tree should be fairly self-explanatory.

#### Core Modules

Paths below are relative to `src/exosphere/` unless otherwise noted.

| module | description |
| ------ | ----------- |
| `main.py` | Main entry point for the application |
| `providers/api.py` | Package manager provider API and base classes |
| `providers/factory.py` | Concrete provider factory for creation of Package Managers |
| `cli.py` | CLI interface entry point |
| `config.py` | Configuration subsystem, including defaults |
| `context.py` | Context management for shared state across commands and UI |
| `data.py` | Data models and structures for serialization and exchange |
| `database.py` | Cache system for serialization |
| `editing.py` | External editor helper module, shared between CLI and UI |
| `errors.py` | Exception classes and general error messages |
| `fspaths.py` | Platform-appropriate filesystem paths module, for state |
| `inventory.py` | Inventory management subsystem |
| `migrations.py` | Cache format migration processes |
| `objects.py` | Main objects for representing Hosts, and most of the relevant logic |
| `pipelining.py` | SSH pipelining implementation, including reaper thread |
| `repl.py` | REPL module for interactive CLI usage |
| `reporting.py` | Reporting subsystem, including templates and formatters |
| `runners.py` | SSH runners module, handles setting up remote POSIX environment |
| `security.py` | Sudo management subsystem, including policy and utilities |

Generally, most of the things Exosphere does to hosts (including connection management
and operations) are going to be found in `objects.py`.

#### UI Modules

Paths below are relative to `src/exosphere/` unless otherwise noted.

| module | description |
| ------ | ----------- |
| `ui/app.py` | Main Textual application class and entry point for the UI |
| `ui/context.py` | UI Context management for shared state across UI components |
| `ui/elements.py` | Shared UI elements, including task runners |
| `ui/dashboard.py` | Dashboard screen implementation |
| `ui/inventory.py` | Inventory screen implementation |
| `ui/logs.py` | Logs screen implementation |
| `ui/messages.py` | Screen refresh and message passing system |
| `ui/palette.py` | Command palette providers and implementations |

The TCSS for all of it is in a single file under `ui/style.tcss`.
Loading
Loading