Skip to content

Latest commit

 

History

History
218 lines (166 loc) · 6.72 KB

File metadata and controls

218 lines (166 loc) · 6.72 KB

Code Standards & Structure

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

Project Architecture

Four-layer modular design with clear separation of concerns:

Layer Module(s) Responsibility
CLI Layer main.py Command routing, user interaction, newcomer mode
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. 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 523 LOC is exception due to command density.

  • main.py: CLI commands and orchestration (523 LOC - consolidates 5 command implementations)
  • installers.py: Installation strategies (389 LOC - 5 installation strategies)
  • postgres.py: PostgreSQL user management (173 LOC)
  • utils.py: Config, platform detection, helpers (277 LOC)
  • concurrency.py: Task runner with progress (60 LOC)
  • exceptions.py: Custom exception classes (38 LOC)
  • tests/: pytest unit tests for all modules

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