#!/usr/bin/env python3 """Fail if git-indexed deploy-critical files contain CR (CRLF). Checks blobs in the git index (what Ubuntu gets on git pull), not the working tree. Usage: python scripts/check_text_eol.py python scripts/check_text_eol.py --fix-hint """ from __future__ import annotations import argparse import subprocess import sys from pathlib import Path ROOT = Path(__file__).resolve().parents[1] # Paths that must be LF-only in the repository (Ubuntu bash / Python shebang) GLOB_PATTERNS = [ "*.sh", "server/**/*.py", "deploy/**/*.py", "scripts/**/*.py", "web/agent/**/*.py", "web/agent/**/*.json", "tests/**/*.py", "mcp/**/*.py", "frontend/src/**/*.vue", "frontend/src/**/*.ts", "frontend/src/**/*.css", "frontend/e2e/**/*.mjs", ".cursor/rules/**/*.mdc", "web/agent/**/*.py", ".gitattributes", ".env.example", "ruff.toml", "requirements*.txt", ] def git_ls_files(pattern: str) -> list[str]: r = subprocess.run( ["git", "ls-files", pattern], cwd=ROOT, capture_output=True, text=True, check=False, ) if r.returncode != 0: return [] return [line for line in r.stdout.splitlines() if line.strip()] def blob_has_cr(path: str) -> bool: r = subprocess.run( ["git", "show", f":{path}"], cwd=ROOT, capture_output=True, check=False, ) if r.returncode != 0: return False return b"\r" in r.stdout def main() -> int: parser = argparse.ArgumentParser(description=__doc__) parser.add_argument( "--fix-hint", action="store_true", help="Print git renormalize commands on failure", ) args = parser.parse_args() if subprocess.run(["git", "rev-parse", "--is-inside-work-tree"], cwd=ROOT, capture_output=True).returncode != 0: print("check_text_eol: not a git repository", file=sys.stderr) return 1 seen: set[str] = set() bad: list[str] = [] for pattern in GLOB_PATTERNS: for path in git_ls_files(pattern): if path in seen: continue seen.add(path) if blob_has_cr(path): bad.append(path) bad.sort() if bad: print(f"CR byte in git index ({len(bad)} file(s)):", file=sys.stderr) for p in bad: print(f" {p}", file=sys.stderr) if args.fix_hint: print( "\nFix:\n" " git config core.autocrlf false\n" " git add --renormalize '*.sh' 'server/' 'deploy/' 'scripts/' 'web/agent/' 'frontend/src/'\n" " git commit -m 'chore: normalize line endings to LF'", file=sys.stderr, ) return 1 print(f"check_text_eol: OK ({len(seen)} tracked paths, LF-only in git index)") return 0 if __name__ == "__main__": sys.exit(main())