# Design: Инкрементальный SFTP-деплой ## Проблема `tools/deploy_sftp.py` mirror-заливал ВСЕ 524 файла (66M) каждый деплой, включая неизменные тяжёлые (21M PDF, 11M×2 M4V) — деплой занимал ~36 мин (замер №1: run создан 16:54:19Z → прод виден 17:30:20Z). Плюс: при обрыве SFTP-соединения скрипт вис бесконечно (нет таймаутов) — оба раза висел на `2026/gotosocial-relay-match-by-default/hero.svg`. ## Решение Единственный файл `tools/deploy_sftp.py`: 1. **Инкремент по (size + SHA1)**: для каждого локального файла — `sftp.stat()`; если удалённый существует и (размер совпадает И SHA1 содержимого совпадает) — пропуск; иначе `sftp.put()`. - mtime НЕ используется: в CI (Gitea Actions) checkout ставит свежий mtime всем файлам → mtime-сравнение бесполезно. - SHA1 читается потоково с удалённого через `sftp.open()` (1MB чанками) — корректно для 21M PDF (несколько секунд). - Для несовпадающих размеров хэш не считается (быстрый путь). 2. **Защита от зависаний**: - `Transport.set_keepalive(15)` — пинги; мёртвое соединение падает само. - `sftp.get_channel().settimeout(120)` — операция дольше 120с прерывается. - `with_retry()` — при исключении переподключение (до 3 попыток, sleep 2с), `put`/`remove` повторяются; после исчерпания — выход с ненулевым кодом. 3. **Mirror-удаление сохранено**: `remote_files - local_files` → `remove()`. 4. **CI**: `python3-paramiko` ставится через **apt** (не pip в venv) — не зависит от PyPI, быстрее (установка случайно занимала 15-20 мин). ## Почему не mtime В Gitea Actions `actions/checkout` распаковывает репозиторий с текущим mtime → все файлы «свежие» относительно сервера → mtime-сравнение никогда не пропускает. SHA1 — единственный надёжный индикатор неизменности для CI. ## Альтернативы, отклонённые - **rsync**: Jino SFTP-only, удалённый exec запрещён. - **Манифест (JSON) прошлого деплоя**: усложняет, требует хранения состояния; SHA1-сравнение с сервером самодостаточно. - **Только размер**: риск ложного пропуска при изменении содержимого без смены размера (маловероятно для статики, но SHA1 дешевле ошибки). ## Открытые вопросы - Точное время залива (замер №2) зависит от пропускной способности Jino SFTP. - SHA1 крупных файлов при каждом деплое: 21M PDF → ~2-5с на чтение с сервера; приемлемо по сравнению с перезаливкой.