fester/CONTRIBUTING.md

4.4 KiB

Contributing to Fester

Thanks for your interest in improving Fester! This document covers the basics.

🚀 Quick Start for Contributors

# 1. Fork + clone
git clone https://git.dcos.net/dcosnet/fester.git
cd fester

# 2. Bootstrap dev environment (creates venv, installs deps + dev tools)
./bootstrap.sh --dev

# 3. Run the backend in dev mode (auto-reload on changes)
./run.sh --dev

# 4. Run tests (when we have them)
pytest

# 5. Lint + format
ruff check .
ruff format .

🏗️ Architecture Overview

Read README.md first — it has the full architecture diagram and project layout.

The key insight: Fester has a strict separation between:

  • EventBus — the singleton message bus (all events flow through it)
  • Subscribers — bus listeners that index/journal/broadcast events (TimelineStore, cause_graph, FailurePropagator, the WS broadcaster)
  • Routers — FastAPI APIRouters under backend/api/ that expose REST endpoints
  • UI — vanilla HTML/JS pages under ui/ that subscribe to the WS stream and call REST endpoints

When you add a new feature, ask:

  1. Does this emit events? → Use bus.emit(EventType.X, ...) from backend/events/schema.py
  2. Does this need persistence? → Add a method to backend/storage/sqlite_db.py:Storage
  3. Does this need a UI? → Add a page under ui/ and a nav entry in ui/app.js:Fester.mountShell()
  4. Does this need a CLI command? → Add a subcommand to cli/fester.py

📝 Coding Standards

Python

  • Python 3.12+ — use modern syntax (match statements, type hints, etc.)
  • Type hints — add them to new code; don't worry about retrofitting old code
  • Imports — use from backend.X.Y import Z (not bare from X.Y import Z)
  • EventBus — always emit events via bus.emit(), never mutate state directly from a request handler
  • Pydantic — use BaseModel for request bodies in routers
  • Async — use async def for endpoints that touch the DB or do I/O

JavaScript (UI)

  • Vanilla JS — no frameworks (React, Vue, etc.). Keep it dependency-free.
  • Shared shell — every page calls Fester.mountShell('page-id') to get the topbar + WS connection
  • API calls — use Fester.api(path, opts) (returns parsed JSON)
  • Toasts — use Fester.toast(msg, kind) for user notifications
  • No build step — pages load directly via <script src="/ui/app.js">

CSS

  • Design tokens — use var(--token) (defined in style.css). Don't hardcode colors.
  • Dark theme only — no light mode. Tokens are already dark.

🧪 Testing

# Run all tests
pytest

# Run a specific test file
pytest tests/test_pipeline.py

# Run with coverage
pytest --cov=backend --cov-report=html

# Run only fast tests
pytest -m "not slow"

Tests live in tests/ (we don't have many yet — contributions welcome!).

🔍 Code Quality

# Lint
ruff check .

# Format
ruff format .

# Type check
mypy backend/

# Check for syntax errors in inline <script> blocks
node scripts/check_syntax.js ui/*.html index.html

📦 Commit Conventions

Use Conventional Commits:

feat: add tmux output viewer to sessions page
fix: pass real cwd to execute_action instead of empty dict
docs: add quickstart guide
chore: update dependencies
refactor: split BuildRunner from PipelineEngine
test: add tests for failure_propagation

🐛 Bug Reports

When filing an issue, include:

  1. Fester version (fester health output)
  2. Python version (python3 --version)
  3. OS + distro
  4. Steps to reproduce
  5. Expected vs actual behavior
  6. Relevant log output (/tmp/fester_backend.log)
  7. Screenshots if UI-related

Pull Requests

  1. Fork the repo + create a branch: git checkout -b feat/my-feature
  2. Make your changes
  3. Run ruff check . && ruff format . before committing
  4. Write a clear commit message (Conventional Commits)
  5. Open a PR with:
    • What changed + why
    • Screenshots (if UI changes)
    • Test results
    • Any breaking changes

🆘 Getting Help

📜 License

By contributing, you agree that your contributions will be licensed under the AGPL-3.0 — see LICENSE.