# Refactoring №2: roko-backup.py — улучшенная версия ## Исходный код `/home/openclaw/.openclaw/scripts/roko-backup.py` (текущая версия — 8 классов, 380 строк) ## ✅ Сохранить - Архитектуру (конвейер: Config → LockFile → Collector → Archiver → Uploader → Verifier → Cleaner → Reporter → BackupPipeline) - Python 3.10+, только stdlib + rclone subprocess - argparse (--config, --dry-run, --verbose) - LockFile (защита от параллельных запусков) - NO_PROXY для Yandex Cloud в subprocess env - Exponential backoff (10s, 30s, 60s) на retry ## 🆕 Новый функционал ### 1. Streaming rclone progress - Сейчас: `subprocess.run(capture_output=True)` — ждём завершения, потом показываем вывод - Надо: `subprocess.Popen` с построчным чтением stderr в реальном времени - rclone с `--progress` пишет progress строки в stderr - **Формат вывода в логах:** ``` 2026-07-07 10:30:15 INFO Upload: 15% (95 MiB / 622 MiB) @ 2.1 MiB/s ``` - Парсить `Transferred: ... MiB / ... MiB, ...%` из rclone stderr - Не flood-ить лог — логировать прогресс раз в 30 секунд или при каждом новом проценте (кратном 5%) - По завершении показать финальную статистику (скорость, время) ### 2. Ротация логов - Лог-файл: `/home/openclaw/backups/roko-backup.log` (НА ДИСКЕ, не в /tmp!) - Максимальный размер: 1 МБ (после этого ротация) - Хранить: 5 последних ротированных логов (`roko-backup.log.1`, `.2`, ... `.5`) - Использовать `logging.handlers.RotatingFileHandler` из stdlib - При `--verbose` — дублировать в stdout тоже (для отладки вручную) ### 3. Улучшения кода - **Type hints** полные, включая `collections.abc.Generator`, `ContextManager` - **Меньше повторений** — RCLONE_ENV дублируется в Uploader/Verifier/Cleaner, вынести в конфиг или хелпер - **Документация** — docstrings по PEP 257 на английском (краткие, по делу) - **Error handling** — отдельный класс BackupError(Exception) вместо голых return 3 - **Cleanup гарантированный** — даже при падении архиватор должен подчищать staging ## Структура (сохраняем конвейер, улучшаем детали) ```python class BackupError(Exception): ... class LockError(BackupError): ... @dataclass(frozen=True) class Config: # добавляем log_file: Path, max_log_size: int, max_log_backups: int ... class LockFile(ContextManager): # добавить __enter__/__exit__ ... class Collector: EXCLUDE_PATTERNS = (...) # можно сделать конфигурируемым в Config ... class Archiver: # архивирует через tarfile (не subprocess tar) ... class Uploader: # subprocess.Popen + streaming stderr для progress UPLOAD_PROGRESS_INTERVAL = 30 # секунд между логами прогресса PROGRESS_PERCENT_STEP = 5 # логировать каждые N процентов ... class Verifier: ... class Cleaner: ... class Reporter: ... class BackupPipeline: ... def setup_logging(verbose, log_file, max_size, max_backups): ... def main(): ... ``` ## Требования к качеству - `python3 -c "import ast; ast.parse(open('roko-backup.py').read())"` — OK - `--dry-run --verbose` — работает, показывает все источники - Без аргументов — полный цикл без ошибок - `--config X` — работает - `python3 -c "from roko_backup import Config, BackupPipeline"` — импортируемый модуль ## Файлы - Пишем: `/home/openclaw/.openclaw/scripts/roko-backup.py` (перезаписать) - Сохраняем: `roko-backup.py.bak` (текущая версия) - README: обновить `/home/openclaw/.openclaw/scripts/README.md` (добавить про логи и streaming) ## Модели для OpenCode - **Основная:** mimo-v2.5-pro (OpenCode Go) - **Сравнение:** kimi-k2.7-code (OpenCode Go) - **Запасные при зависании (до 3 попыток):** minimax-m3 (OpenCode Go), deepseek-v4-pro (OpenCode Go)