Files
MajoorWaldi cb171e826d feat: Add runtime bootstrap helpers and search request normalization
- Introduced runtime bootstrap helpers in `mjr_am_backend.features.runtime` for dependency initialization.
- Implemented `apply_startup_settings` to handle various startup configurations.
- Added search request normalization helpers in `mjr_am_backend.features.search` including `SearchRequestContext` and `parse_search_request` for better query handling.
- Created audit logging helpers in `mjr_am_backend.routes.core.audit_log` for state-changing route handlers.
- Developed a quality gate script in `scripts/run_quality_gate.py` to enforce code quality checks.
- Added comprehensive tests for audit routes, scan consistency, vendor routes, and code quality metrics.
2026-03-26 20:42:51 +01:00

117 lines
3.0 KiB
Python

"""Fail if repository text files use UTF-8 BOM or invalid UTF-8."""
from __future__ import annotations
import argparse
from pathlib import Path
UTF8_BOM = b"\xef\xbb\xbf"
SKIP_DIRS = {
".git",
".venv",
"node_modules",
"__pycache__",
".pytest_cache",
}
TEXT_EXTENSIONS = {
".md",
".py",
".toml",
".yml",
".yaml",
".json",
}
def _iter_text_files(root: Path):
for path in root.rglob("*"):
if not path.is_file():
continue
if any(part in SKIP_DIRS for part in path.parts):
continue
if path.suffix.lower() not in TEXT_EXTENSIONS:
continue
yield path
def _has_utf8_bom(path: Path) -> bool:
try:
with path.open("rb") as fh:
return fh.read(3) == UTF8_BOM
except OSError:
return False
def _is_valid_utf8(path: Path) -> bool:
try:
path.read_text(encoding="utf-8")
except (OSError, UnicodeDecodeError):
return False
return True
def _parse_args() -> argparse.Namespace:
parser = argparse.ArgumentParser(
description="Check text files for UTF-8 without BOM."
)
parser.add_argument(
"paths",
nargs="*",
default=["."],
help="Files or directories to scan (default: repository root).",
)
return parser.parse_args()
def main() -> int:
args = _parse_args()
cwd = Path.cwd().resolve()
bom_offenders: list[Path] = []
decode_offenders: list[Path] = []
for raw in args.paths:
target = Path(raw)
if not target.exists():
continue
if target.is_file():
if target.suffix.lower() in TEXT_EXTENSIONS and _has_utf8_bom(target):
bom_offenders.append(target)
if target.suffix.lower() in TEXT_EXTENSIONS and not _is_valid_utf8(target):
decode_offenders.append(target)
continue
for path in _iter_text_files(target):
if _has_utf8_bom(path):
bom_offenders.append(path)
if not _is_valid_utf8(path):
decode_offenders.append(path)
bom_offenders = sorted({p.resolve() for p in bom_offenders})
decode_offenders = sorted({p.resolve() for p in decode_offenders})
if bom_offenders or decode_offenders:
if bom_offenders:
print(f"Found UTF-8 BOM in {len(bom_offenders)} text file(s):")
for path in bom_offenders:
try:
rel = path.relative_to(cwd)
except ValueError:
rel = path
print(f"- {rel.as_posix()}")
if decode_offenders:
print(f"Found invalid UTF-8 in {len(decode_offenders)} text file(s):")
for path in decode_offenders:
try:
rel = path.relative_to(cwd)
except ValueError:
rel = path
print(f"- {rel.as_posix()}")
return 1
print("UTF-8 and BOM checks passed.")
return 0
if __name__ == "__main__":
raise SystemExit(main())