Skip to content

Latest commit

 

History

History
223 lines (170 loc) · 7.46 KB

File metadata and controls

223 lines (170 loc) · 7.46 KB

Code Standards & Structure

Development guidelines, architectural patterns, and best practices for trobz_local.

Project Architecture

Five-layer modular design with clear separation of concerns:

Layer Module(s) Responsibility
CLI Layer main.py Command routing, user interaction, newcomer mode
Diagnostics doctor.py Environment health checks, status enums, check result dataclasses
Implementation installers.py, postgres.py Installation strategies (script, system, npm, uv); PostgreSQL user management
Utility Layer utils.py Config validation, platform detection, helpers
Infrastructure concurrency.py, exceptions.py Parallel execution, custom exceptions

Code Style Guidelines

Python Conventions

  • Standard: PEP 8 compliance
  • Formatter: ruff (fast, deterministic)
  • Line Length: Max 120 characters (ruff.toml config)
  • Import Order: stdlib → third-party → local (ruff-managed)
  • Active Rules: YTT, S (security), B, A, C4, T10, SIM, I, C90, E, W, F, PGH, UP, RUF, TRY
  • UI Library: Rich (progress bars, colored output, trees)

Type Safety (Mandatory)

  • All function signatures must have type hints
  • Complex variables must be annotated
  • Static analysis via mypy (ty wrapper)
  • Pydantic for external data validation (config.toml)

Naming Rules

Element Pattern Example
Modules snake_case installers.py
Classes PascalCase ConfigModel
Functions snake_case pull_repos
Constants SCREAMING_SNAKE_CASE ARCH_PACKAGES
Private _snake_case _run_installers

Architectural Patterns

1. Declarative Configuration

System state defined in TOML. Tool reconciles local environment with definition. Pydantic validates before any side effects.

2. Strategy Pattern

installers.py: Multiple strategies per tool source (script, system packages, npm, uv) with OS-aware selection.

3. Command Pattern

main.py: Each CLI command is discrete but shares context (newcomer mode, dry-run). Easy to add new commands.

4. Observer Pattern

GitProgress and run_tasks(): Real-time progress callbacks to Rich UI. Decoupled from execution logic.

5. Diagnostic Pattern

doctor.py: Grouped health checks returning CheckResult objects with CheckStatus enum. Enables modular diagnostics that can be combined or reused independently.

6. Fail-Fast Validation

Config validated at startup. Early detection prevents side effects on invalid input.


Security Requirements

Subprocess Safety

  • Never use shell=True - Always pass arguments as list
  • Full paths only - Use shutil.which() instead of relying on PATH
  • No user input in commands - Build commands from validated config only
  • SQL injection prevention - Use psql variable binding (:\"identifier\" for names, :'string' for values) instead of string interpolation

Download Security

  • HTTPS enforcement - Pydantic validator rejects non-HTTPS URLs
  • Trusted sources only - Script URLs must be whitelisted or reviewed

Input Validation

  • Regex patterns - All config fields validated with specific patterns:
    • Versions: ^(?:\d+\.\d+|master)$ (supports semver and "master" branch)
    • UV tools: ^[a-zA-Z0-9][a-zA-Z0-9._\-\[\]@=<>!,]*$
    • Repo names: ^[a-zA-Z0-9._-]+$
    • PostgreSQL usernames: ^[a-zA-Z_][a-zA-Z0-9_]{0,62}$ (max 63 chars, alphanumeric + underscore)
  • Pydantic models - No ad-hoc parsing of config
  • Ruff S rules* - Security linting enabled in make check

Credential Handling

  • No credentials stored in code
  • No sensitive data logged
  • Environment variables used for user-specific paths only

Error Handling Patterns

Custom Exceptions

Use specialized exceptions from exceptions.py with context:

raise DownloadError(url=script_url, reason=str(e))
raise ExecutableNotFoundError(executable="wget")

Fail Paths

  1. Config errors: Print message + example, exit 1
  2. Task failures: Catch exception, record in TaskResult, continue
  3. Validation errors: Pydantic prints field-level details, exit 1

Cleanup

  • Use context managers for temporary files
  • Temporary directories auto-deleted on function exit
  • No partial state on failure

Testing Requirements

Framework

  • Tool: pytest
  • Mocking: unittest.mock for filesystem, network, subprocesses
  • Coverage: All new features must include tests
  • No real I/O: Tests must not actually download, install, or git-clone

Test Organization

  • Test file naming: test_{module}.py
  • Test function naming: test_{function}_case_description
  • Fixtures for common setup
  • Parametrized tests for multiple scenarios

Running Tests

make test              # Full suite
pytest -xvs file.py  # Single file with verbose output
pytest --cov         # Coverage report

Development Workflow

Setup

uv sync              # Install dependencies
uv run pre-commit install  # Setup git hooks
make check           # Verify setup (lint + type check)

Code Quality Checklist

  • Code follows naming conventions and style guidelines
  • Type hints on all function signatures
  • Tests written and passing (make test)
  • Linting passes (make check)
  • No security issues (ruff S rules)
  • Error handling implemented
  • Documentation updated

Before Committing

make check  # Linters + type checkers
make test   # Full test suite

Commit Message Format

Follow Conventional Commits:

  • feat: description - New feature
  • fix: description - Bug fix
  • docs: description - Documentation
  • refactor: description - Code restructuring
  • test: description - Test additions
  • chore: description - Maintenance

File Structure

Target file size: 500 LOC; main.py at 592 LOC is exception due to command density (6 command implementations + orchestration).

  • main.py: CLI commands and orchestration (592 LOC - 6 commands: init, pull-repos, create-venvs, install-tools, ensure-db-user, doctor)
  • doctor.py: Environment health checks (~200 LOC - CheckStatus enum, CheckResult dataclass, check_* functions, run_doctor orchestrator)
  • installers.py: Installation strategies (391 LOC - 5 installation strategies: PostgreSQL repo, scripts, system packages, NPM, UV)
  • postgres.py: PostgreSQL user management (173 LOC - user validation, creation, testing)
  • utils.py: Config, platform detection, helpers (246 LOC - Pydantic models, OS detection, utilities)
  • concurrency.py: Task runner with progress (60 LOC - ThreadPoolExecutor wrapper, TaskResult dataclass)
  • exceptions.py: Custom exception classes (38 LOC - installer exceptions)
  • tests/: pytest unit tests for all modules (1006 LOC total - 6 test files)

Imports in each module:

  • No circular imports
  • Public functions documented
  • Private functions start with underscore

Documentation Standards

Docstrings (Google Style)

def function(param: Type) -> ReturnType:
    """Brief one-line description.

    Longer description if needed.

    Args:
        param: Description

    Returns:
        Description of return value

    Raises:
        CustomError: When this happens
    """

Code Comments

  • Explain "why", not "what"
  • Comment complex logic only
  • Keep comments synchronized with code

Inline Documentation

  • Update docs/ files when changing features
  • Keep code examples in docs/ current
  • Reference specific line numbers when helpful