# Design: Инкрементальный SFTP-деплой ## Подход Модифицируем единственный файл `tools/deploy_sftp.py`: 1. **Сравнение файлов (size + mtime)**: - Для каждого локального файла вызываем `sftp.stat(remote_path)`. - Если stat успешен И `(st_size, int(st_mtime))` совпадает с локальным `(os.stat(lpath).st_size, int(os.stat(lpath).st_mtime))` — пропускаем (не льём). - Если stat бросает IOError (нет файла) или не совпадает — заливаем. - Метрику печатаем: `= пропущено, + залито`. 2. **Таймауты против зависания**: - `paramiko.Transport` создаём с `banner_timeout=30`, `timeout=30`. - Для каждого `put` используем `paramiko` окно: оборачиваем в `socket.setdefaulttimeout(120)` (SFTP-канал унаследует) — если операция длится дольше 2 мин, канал падает с ошибкой, скрипт падает с понятным сообщением вместо бесконечного висения. - Удаление тоже не должно висеть: те же таймауты. 3. **Что НЕ меняется**: - Mirror-логика (всё, чего нет локально, удаляется) — сохранена. - Рекурсивное создание каталогов — сохранено. - Интерфейс: `tools/deploy_sftp.py [--dry-run]`, переменные DEDINIT_* / SSHPASS. - Вызов из `.gitea/workflows/deploy.yml` не меняется. ## Риски - **mtime на сервере** (Jino) может отличаться от локального (часовой пояс, точность). SFTP stat возвращает mtime в секундах; локально `int(st_mtime)` — тоже секунды. Допуск: считаем «не изменился», если `|remote_mtime - local_mtime| <= 120` И размеры равны. (Больше 2 мин расхождения не бывает при честном сравнении; зато гарантируем, что файл, записанный «только что» локально и залитый минуту назад, не перельётся без нужды.) Решение: допуск на mtime = 300 сек (5 мин) при равенстве размеров. - **Ложные пропуски**: если сервер отдаёт mtime 0 (некоторые SFTP-сервера) — считаем файл изменённым (льём всегда). Проверка `if remote_mtime > 0`. - **Большой дерево**: rlist рекурсивно обходит всё дерево — оставляем как есть (работает, это доли секунды на 500 файлов). ## Верификация - `python3 tools/deploy_sftp.py --dry-run` локально: показывает список изменившихся (должно быть 0 после сборки в тот же public/... фактически покажет все «+», т.к. на сервере старый mtime — это ок, первый деплой льёт всё). - После первого инкрементального деплоя повторный `--dry-run` покажет «=» (пропуски).