This document explains the internal architecture, development workflow, and implementation details of Path Header Scanner.
Path Header Scanner is designed as a modular and extensible CLI utility for:
- recursive source file scanning
- path header validation
- missing header insertion
- invalid header replacement
- multi-language support
- Docker-based execution
The architecture emphasizes:
- separation of concerns
- strategy-based language handling
- safe file updates
- reusable utilities
- maintainable CLI tooling
The project is divided into multiple layers.
| Layer | Responsibility |
|---|---|
| CLI | User commands and argument parsing |
| Core | Processing orchestration |
| Languages | Language-specific behaviors |
| Models | Shared result models and enums |
| Utils | Shared helpers and utilities |
| Constants | Global constants and configuration |
See full structure in project_structure.md.
Responsible for:
- recursive directory scanning
- ignored directory filtering
- supported extension filtering
- returning discovered source files
Main responsibilities:
scanner.scan()Features:
- recursive scanning using
Path.rglob() - ignored directory support
- extension-based matching
- sorted file output
Central orchestration layer.
Responsible for:
- strategy resolution
- file processing delegation
- result aggregation
- logging and summaries
Main responsibilities:
processor.process_files()Features:
- strategy lookup
- updater delegation
- centralized reporting
- result collection
Responsible for:
- safe file updates
- header insertion
- invalid header replacement
- preserving special lines
Main responsibilities:
updater.process_file()Features:
- shebang preservation
- encoding preservation
- PHP opening tag preservation
- newline-safe rebuilding
Path resolution is handled by:
resolve_target_path()Features:
- absolute path support
- Docker workspace fallback
- custom working directory support
- normalized path resolution
Resolution order:
- absolute path
- working directory
- current working directory
- Docker workspace
- direct relative path
The project uses the Strategy Pattern.
Each language implementation defines:
- supported extensions
- comment syntax
- insertion behavior
- header extraction logic
Base interface:
class BaseLanguageStrategy(ABC):| Strategy | Extensions |
|---|---|
| PythonLanguageStrategy | .py |
| JavaScriptLanguageStrategy | .js, .jsx, .ts, .tsx |
| ShellLanguageStrategy | .sh, .bash, .zsh |
| PhpLanguageStrategy | .php |
| HtmlLanguageStrategy | .html, .htm |
| MarkdownLanguageStrategy | .md, .markdown |
Each strategy can implement:
| Method | Required | Purpose |
|---|---|---|
extensions |
Yes | Supported extensions |
comment_prefix |
Yes | Header comment syntax |
extract_header() |
Yes | Detect existing headers |
get_insertion_index() |
Optional | Preserve special lines |
build_header() |
Optional | Custom header formatting |
class PythonLanguageStrategy(
BaseLanguageStrategy
):Features:
- Python comment syntax
- shebang preservation
- encoding declaration preservation
Example:
class RustLanguageStrategy(
BaseLanguageStrategy
):Required:
@property
def extensions(self) -> list[str]:@property
def comment_prefix(self) -> str:def extract_header(
self,
lines: list[str],
) -> Optional[str]:Optional:
def get_insertion_index(
self,
lines: list[str],
) -> int:Headers are generated from relative paths.
Example:
# app/core/scanner.pyThe scanner supports:
- including target directory
- excluding target directory
Examples:
Included:
# app/cli/main.pyExcluded:
# cli/main.pyControlled via:
--include-target-directoryor:
--exclude-target-directoryHigh-level workflow:
scan files
↓
resolve strategy
↓
extract header
↓
validate header
↓
insert/update if needed
↓
collect results
↓
display summary
The project uses structured logging.
Recommended levels:
| Level | Purpose |
|---|---|
| INFO | summaries and updates |
| DEBUG | detailed diagnostics |
| WARNING | recoverable issues |
| ERROR | failures |
Examples:
INFO:
Updated: app/core/main.py
DEBUG:
Processing: app/core/scanner.py
The project supports:
- direct Docker execution
- Docker Compose
- mounted workspace development
Example:
docker run -it --rm \
-w /workspace \
-v "${PWD}:/workspace" \
path-header-scanner \
scan appThe project includes Makefile helpers.
Examples:
make docker-buildmake docker-scanmake docker-applymake docker-debugpip install -e .[dev]pytestpytest --cov=appruff check .ruff format .py -m app scan apppy -m app scan app --applypy -m app scan app --debugBuild image:
docker compose buildRun scanner:
docker compose run --rm \
path-header-scanner \
scan appOpen shell:
docker compose run --rm \
path-header-scanner \
bashThe project follows several design principles.
Each layer has a focused responsibility.
Examples:
- scanner only scans
- updater only updates
- strategies only define language behavior
Strategies are composed into processors instead of tightly coupled inheritance chains.
The project consistently uses:
pathlib.Pathinstead of:
os.pathBenefits:
- cleaner APIs
- cross-platform compatibility
- easier path manipulation
The project avoids:
print()in favor of:
logger.info()Benefits:
- configurable verbosity
- CI/CD friendliness
- debug support
File rebuilding preserves:
- shebangs
- encoding declarations
- PHP opening tags
- line ordering
Every:
- module
- class
- method
- public function
must contain docstrings.
Preferred style:
- descriptive summaries
- typed arguments
- examples where appropriate
Recommended testing layers:
| Test Type | Purpose |
|---|---|
| Unit Tests | strategies and utilities |
| Integration Tests | processing workflow |
| CLI Tests | Typer command behavior |
| Docker Tests | container execution |
Potential future enhancements:
- configurable ignored directories
.path-header-ignore- custom header templates
- progress bars
- parallel processing
- TOML configuration support
- pre-commit integration
- CI validation mode
- Git hooks
- All paths use POSIX separators in headers.
- File updates preserve trailing newlines.
- Logging is centralized through the processor layer.
- Docker support is designed for mounted workspace development.
- Strategies should remain lightweight and isolated.