a2ae74d582
- Backend: auth refresh reuse, schedule/retry/sync/agent/files fixes - Frontend: push WS, files ETag browse, vite build assets - Docs: audit/changelog, production deploy gate, bug scan registry B7-77 - Deploy: deploy-production.sh, prune assets, gate logs
112 lines
2.9 KiB
Python
112 lines
2.9 KiB
Python
#!/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())
|