#!/usr/bin/env python3 # -*- coding: utf-8 -*- """ ПОСТАВИТЬ /opt/arena ПОД GIT — безопасно и без секретов в истории. Зачем: 5.7 ГБ боевого торгового кода без истории версий. Откатить неудачную правку нечем, диффа нет, авторство не установить. Что делает: 1. .gitignore — тяжёлое и приватное в репозиторий не попадает (runs/ 5.1 ГБ, _killed_variants/, модели, базы, логи, файлы секретов) 2. СКАНИРУЕТ то, что реально попадёт в коммит, на зашитые секреты. Печатает только ИМЯ файла и ТИП находки — сами значения не выводятся. 3. Если находки есть — по умолчанию НЕ коммитит. Токен, попавший в историю git, оттуда просто так не убирается. 4. Базовый коммит. python3 git_init.py <путь> сухой прогон python3 git_init.py <путь> --apply сделать python3 git_init.py <путь> --apply --allow-secrets коммит несмотря на находки """ import os, re, subprocess, sys PATH = sys.argv[1] if len(sys.argv) > 1 else "/opt/arena" APPLY = "--apply" in sys.argv FORCE = "--allow-secrets" in sys.argv IGNORE = """# тяжёлое — в историю не нужно runs/ _killed_variants/ local_ml_models/ backups/ __pycache__/ *.pyc # данные, а не код *.db *.db-journal *.sqlite *.log *.jsonl *.csv *.parquet # СЕКРЕТЫ — никогда .key_secret *secrets*.json .env *.pem *.key tg_acl.json """ # что считаем секретом. Значения НЕ печатаем — только факт и тип. PATTERNS = [ ("телеграм-токен", re.compile(r"\b\d{9,10}:[A-Za-z0-9_-]{30,}")), ("токен Vercel", re.compile(r"\bvcp_[A-Za-z0-9]{20,}")), ("приватный ключ", re.compile(r"-----BEGIN [A-Z ]*PRIVATE KEY-----")), ("пароль в коде", re.compile(r"(?i)(password|passwd|pwd)\s*=\s*['\"][^'\"]{6,}")), ("ключ Grafana", re.compile(r"\bglsa_[A-Za-z0-9]{20,}")), ("ключ AWS", re.compile(r"\bAKIA[0-9A-Z]{16}\b")), ] def sh(cmd, cwd=PATH): r = subprocess.run(cmd, shell=True, cwd=cwd, capture_output=True, text=True, timeout=120) return (r.stdout or r.stderr or "").strip() def would_track(): """Файлы, которые попадут в коммит с учётом .gitignore.""" out = sh("git ls-files --others --exclude-standard --cached") return [f for f in out.splitlines() if f.strip()] print(f"каталог: {PATH}") if not os.path.isdir(PATH): sys.exit("нет такого каталога") is_repo = os.path.isdir(os.path.join(PATH, ".git")) print(f"уже под git: {'да' if is_repo else 'нет'}") # 1. .gitignore gi = os.path.join(PATH, ".gitignore") have_gi = os.path.exists(gi) print(f".gitignore: {'есть' if have_gi else 'будет создан'}") if APPLY and not have_gi: open(gi, "w", encoding="utf-8").write(IGNORE) if APPLY and not is_repo: print(sh("git init -q && git config user.email arena@local && " "git config user.name 'arena' && echo 'git init ok'")) # 2. что попадёт if os.path.isdir(os.path.join(PATH, ".git")): files = would_track() else: files = [] for root, dirs, fs in os.walk(PATH): dirs[:] = [d for d in dirs if d not in ("runs", "_killed_variants", "local_ml_models", "backups", "__pycache__", ".git")] for f in fs: if f.endswith((".py", ".json", ".md", ".sh", ".service", ".html", ".yml", ".yaml")): files.append(os.path.relpath(os.path.join(root, f), PATH)) total_mb = 0.0 for f in files: p = os.path.join(PATH, f) if os.path.isfile(p): total_mb += os.path.getsize(p) / 1048576 print(f"\nпопадёт в репозиторий: {len(files)} файлов, {total_mb:.1f} МБ") print(f"НЕ попадёт: runs/, _killed_variants/, local_ml_models/, backups/, " f"*.db, *.log, *.jsonl, файлы секретов") # 3. поиск секретов print("\nпроверка на зашитые секреты:") found = {} for f in files: p = os.path.join(PATH, f) if not os.path.isfile(p) or os.path.getsize(p) > 2_000_000: continue try: txt = open(p, encoding="utf-8", errors="replace").read() except Exception: continue for name, rx in PATTERNS: if rx.search(txt): found.setdefault(f, set()).add(name) if found: print(f" ⚠️ НАЙДЕНО в {len(found)} файлах (значения не показываю):") for f, kinds in sorted(found.items()): print(f" {f:44} {', '.join(sorted(kinds))}") print("\n Токен, попавший в историю git, оттуда просто так не убирается.") print(" Правильный порядок: вынести секреты в .env → добавить .env в .gitignore") print(" → РОТИРОВАТЬ засветившиеся токены → только потом коммит.") else: print(" ✅ ничего похожего на секреты не найдено") # 4. коммит if not APPLY: print("\nсухой прогон. Для выполнения добавьте --apply") sys.exit() if found and not FORCE: print("\n⛔ КОММИТ НЕ СДЕЛАН — сначала уберите секреты.") print(" Репозиторий и .gitignore созданы, история пуста.") print(" Осознанно закоммитить как есть: --apply --allow-secrets") sys.exit() sh("git add -A") print(sh("git -c user.email=arena@local -c user.name=arena commit -q -m " "'baseline: снимок боевого кода арены' && echo 'коммит сделан'")) print(sh("git log --oneline -1")) print(sh("git count-objects -vH | grep size-pack"))