#!/usr/bin/env python3 """Warn when the working tree has CR bytes but git index is LF-only. Tarballs copied from Windows often have CRLF on disk while `git pull` on Ubuntu deploys LF from the index. Run after unpack: python3 scripts/check_worktree_eol.py bash scripts/normalize_worktree_lf.sh # optional fix """ from __future__ import annotations import subprocess import sys from pathlib import Path ROOT = Path(__file__).resolve().parents[1] # Same critical paths as check_text_eol.py PATTERNS = [ "*.sh", "server/**/*.py", "scripts/**/*.py", "deploy/**/*.sh", "docker/**/*.py", "web/agent/**/*.py", "frontend/src/**/*.vue", "frontend/src/**/*.ts", ".cursor/rules/**/*.mdc", ] def git_ls_files(pattern: str) -> list[str]: r = subprocess.run( ["git", "ls-files", pattern], cwd=ROOT, capture_output=True, text=True, check=False, ) return [p for p in r.stdout.splitlines() if p.strip()] def index_has_cr(path: str) -> bool: r = subprocess.run( ["git", "show", f":{path}"], cwd=ROOT, capture_output=True, check=False, ) return r.returncode == 0 and b"\r" in r.stdout def worktree_has_cr(path: str) -> bool: full = ROOT / path if not full.is_file(): return False return b"\r" in full.read_bytes() def main() -> int: seen: set[str] = set() mismatch: list[str] = [] worktree_only: list[str] = [] for pattern in PATTERNS: for path in git_ls_files(pattern): if path in seen: continue seen.add(path) wt = worktree_has_cr(path) if not wt: continue if index_has_cr(path): worktree_only.append(path) else: mismatch.append(path) if not mismatch and not worktree_only: print(f"check_worktree_eol: OK ({len(seen)} paths, working tree matches LF index)") return 0 if mismatch: print( f"CRLF in working tree but LF in git index ({len(mismatch)} file(s)) — " "bash/scripts may fail on Ubuntu:", file=sys.stderr, ) for p in sorted(mismatch)[:50]: print(f" {p}", file=sys.stderr) if len(mismatch) > 50: print(f" ... and {len(mismatch) - 50} more", file=sys.stderr) if worktree_only: print(f"CR in both worktree and index ({len(worktree_only)} file(s)) — run check_text_eol.py", file=sys.stderr) print( "\nFix tracked files:\n" " git config core.autocrlf false\n" " bash scripts/normalize_worktree_lf.sh", file=sys.stderr, ) return 1 if __name__ == "__main__": sys.exit(main())