Development guidelines, architectural patterns, and best practices for trobz_local.
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 |
- 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)
- 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)
| 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 |
System state defined in TOML. Tool reconciles local environment with definition. Pydantic validates before any side effects.
installers.py: Multiple strategies per tool source (script, system packages, npm, uv) with OS-aware selection.
main.py: Each CLI command is discrete but shares context (newcomer mode, dry-run). Easy to add new commands.
GitProgress and run_tasks(): Real-time progress callbacks to Rich UI. Decoupled from execution logic.
Config validated at startup. Early detection prevents side effects on invalid input.
- 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
- HTTPS enforcement - Pydantic validator rejects non-HTTPS URLs
- Trusted sources only - Script URLs must be whitelisted or reviewed
- 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)
- Versions:
- Pydantic models - No ad-hoc parsing of config
- Ruff S rules* - Security linting enabled in make check
- No credentials stored in code
- No sensitive data logged
- Environment variables used for user-specific paths only
Use specialized exceptions from exceptions.py with context:
raise DownloadError(url=script_url, reason=str(e))
raise ExecutableNotFoundError(executable="wget")- Config errors: Print message + example, exit 1
- Task failures: Catch exception, record in TaskResult, continue
- Validation errors: Pydantic prints field-level details, exit 1
- Use context managers for temporary files
- Temporary directories auto-deleted on function exit
- No partial state on failure
- 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 file naming:
test_{module}.py - Test function naming:
test_{function}_case_description - Fixtures for common setup
- Parametrized tests for multiple scenarios
make test # Full suite
pytest -xvs file.py # Single file with verbose output
pytest --cov # Coverage reportuv sync # Install dependencies
uv run pre-commit install # Setup git hooks
make check # Verify setup (lint + type check)- 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
make check # Linters + type checkers
make test # Full test suiteFollow Conventional Commits:
feat: description- New featurefix: description- Bug fixdocs: description- Documentationrefactor: description- Code restructuringtest: description- Test additionschore: description- Maintenance
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
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
"""- Explain "why", not "what"
- Comment complex logic only
- Keep comments synchronized with code
- Update docs/ files when changing features
- Keep code examples in docs/ current
- Reference specific line numbers when helpful