Skip to content

Contributing To Protostar

Architecture & Implementation Rules

Protostar has one job: save the user time on setup they would have done anyway. When evaluating a new feature, ask:

  • Would most users want this, or just some?
  • Would a user plausibly revert this manually after running the tool?

If the answer to either of the first two is "maybe not", the feature probably doesn't belong in the tool.

1. Manifest-first, side-effects-last

Modules declare intent into the manifest during build(). The orchestrator executes all side effects afterward in a single, ordered phase. Never call subprocess.run or write to disk inside a module's build() method.

2. Fail loud, fail early

All system dependency checks happen in pre_flight(), before the manifest is built and before anything is written. If a preflight check fails, the environment is untouched. This is a guarantee, not a coincidence.

3. Non-destructive by default

Protostar never overwrites existing work. .gitignore entries are appended and deduplicated. IDE settings are merged. It must be safe to run against a repo that is already partially configured.

4. Modules are composable, not coupled

A module only interacts with the manifest interface. It must not inspect what other modules are loaded, assume a particular run order, or conditionally change behaviour based on the presence of sibling modules.

5. Presets are Independent Pipeline Injections

Presets inherit from the PresetModule abstract base class and evaluate independently during the manifest aggregation phase. They do not override language modules; they strictly append domain-specific dependencies and directory scaffolding to the EnvironmentManifest.

Coding Standards

  1. Type Hinting: All new application functions and methods must include strict Python 3.12 type hints. We use mypy to statically enforce this (disallow_untyped_defs = true). The test suite (tests/*) is granted an exemption from strict untyped definition checks.

  2. Docstrings: Use Google-style docstrings for public functions, classes, and methods. Module-level, package-level, and __init__ docstrings are exempt from linting checks.

  3. Formatting & Linting: Code is formatted and linted using ruff.

    • Use 4-space indentation and double quotes.
    • The formatter enforces an 88-character line length.
    • Do not bypass the pre-commit hooks, as they will automatically apply the required isort block ordering and formatting rules.

Testing Guidelines

Because Protostar is a scaffolding tool, its execution inherently interacts with the host filesystem and shell. To maintain a deterministic and isolated test suite:

  1. Relaxed Linting: The test suite (tests/*) is exempt from docstring requirements and print statement linting restrictions (T201).

  2. Disk I/O: Never write to the actual host filesystem during tests. Always use the pytest tmp_path fixture to sandbox generated artifacts.

  3. Subprocesses: Use pytest-mock to patch subprocess.run. Do not allow the test suite to execute unmocked shell commands (e.g., uv init or cargo init) on the host machine.

  4. Coverage: Ensure new modules or generators maintain or improve the current test coverage metrics (measured via pytest-cov).

How to Contribute

Reporting Bugs

  1. Check if the issue has already been reported.
  2. Open a new issue with a clear title and description.
  3. Include the command that caused the error and the resulting traceback.

Development Setup

To contribute to this project, you will need the following system-level dependencies installed:

  • Python 3.12+
  • uv: For dependency and environment management.
  • just: Our command runner.

(For macOS users with homebrew: brew install uv just)

  1. Fork & Clone Fork the repo and clone it locally:

    git clone https://github.com/JacksonFergusonDev/protostar.git
    cd protostar
    
  2. Environment Setup We use uv to manage the virtual environment and dependencies. Running sync will install the core application alongside the dev dependency group (which includes build, bump-my-version, mypy, pre-commit, pytest, pytest-cov, pytest-mock, and ruff).

    uv sync
    
  3. Install Hooks Set up pre-commit hooks to handle linting and type checking automatically.

    uv run pre-commit install
    

Running Tests & Tooling

We use just as our command runner to standardize test execution, linting, and formatting.

To see all available commands and their descriptions, run just in the repository root:

just

To execute the standard test matrix:

just test

Pull Requests

  1. Create a Branch

    git checkout -b feature/my-amazing-feature
    
  2. Make Changes

    Write your code. Ensure your changes are tightly scoped to a single feature, preset, or bug fix. Avoid monolithic pull requests that mix refactoring with new logic.

  3. Verify

    Ensure your code passes the linter, type checker, and test suite locally. We provide a single command that emulates the GitHub Actions CI pipeline. Run this before pushing:

    just ci
    

    (Pre-commit will also run ruff and mypy when you commit).

  4. Commit & Push

    Use clear, descriptive commit messages.

    git commit -m "feat: added something cool"
    git push origin feature/my-amazing-feature
    
  5. Open a Pull Request

    Submit your PR against the main branch.