# tgit — git в Телеграме Телеграм-бот для команды, которая ведёт общий репозиторий небольших Python-сервисов. Прямо из чата можно поправить код и закоммитить его, посмотреть, живы ли контейнеры, поискать по логам — своим grep или вопросом LLM-агенту, — и получать алерты, когда что-то упало. Учебный проект, курс Python, НИУ ВШЭ. Основной репозиторий и задачи — [Gitea `HSE/tgit`](https://gitea.24studbank.ru/HSE/tgit); [SourceCraft](https://sourcecraft.dev/rogachevegorushka/tgit) — зеркало. | Документ | О чём | |---|---| | [`docs/SPEC.md`](docs/SPEC.md) | ТЗ: требования A-, M-, C-, S-, L-, AL- | | [`docs/ARCHITECTURE.md`](docs/ARCHITECTURE.md) | модули, потоки данных, хранилище | | [`docs/DECISIONS.md`](docs/DECISIONS.md) | решения ADR-001…015 с альтернативами | | [`docs/dev/DEPLOY.md`](docs/dev/DEPLOY.md) | CI/CD и деплой на сервер | | [`examples/README.md`](examples/README.md) | демо-сервисы и сценарии для проверки | --- ## 1. Цели и задачи **Цель** — сделать так, чтобы мелкие правки в общем коде и разбор инцидентов не требовали ноутбука с git и SSH: всё, что обычно делают в терминале, доступно из Телеграма, причём безопасно для сервера. **Задачи:** - пускать в бота только своих — по одноразовому QR-приглашению, без регистрации; - редактировать файлы репозитория и коммитить их из чата с проверкой до пуша; - показывать состояние контейнеров и запускать проверки здоровья по кнопке; - давать логи — хвост, grep и вопрос на естественном языке, — не открывая shell и не отдавая логи в LLM; - сообщать всей команде о падениях и перегрузках без спама; - защищаться от «случайных людей» и попыток взлома через бота. ## 2. Что реализовано | Раздел | Возможности | Требования | |---|---|---| | **Доступ** | вход только по одноразовому QR (TTL 24 ч), роли админ/участник, отзыв доступа сразу, молчание на перебор токенов (> 5 неудач в час) | A-1…A-8 | | **📝 Коммит** | навигация по папкам, просмотр файла, новый файл/папка, замена (текстом или документом), удаление; проверка имени, размера, UTF-8 и синтаксиса Python; черновик до 50 изменений; сообщение коммита; push с проверкой конфликтов и «Перезаписать» | C-1…C-34 | | **🩺 Статус** | все контейнеры 🟢/🟡/🔴/⚪; карточка — health, аптайм, рестарты, CPU/RAM от лимита, ревизия и время деплоя; «🩺 Запустить проверки» — 7 независимых проверок с таймаутами | S-1…S-4 | | **📜 Логи** | хвост 50/200 строк или 5 мин/1 ч/24 ч; свой grep (`-i -v -E -c -C N`), выполняется в Python, не в shell; длинный ответ — файлом; журнал поисков | L-1…L-6 | | **🤖 Агент** | вопрос словами → фильтр → LLM-страж → LLM строит только `LogQuery` → валидация → grep; показывает построенную команду и строки; атака — отказ и 🛡 админам; 30 вопросов в сутки | L-10…L-17 | | **🚨 Алерты** | 💥 краш, 🧠 OOM, 🔁 цикл рестартов, 🩺 unhealthy, 🔥 перегрузка, 💾 мало места, 🔌 Docker недоступен, 🛡 атака; всем участникам, с хвостом лога и кнопками; повторы — счётчик, «✅ восстановлено» ответом; список за 7 дней | AL-1…AL-6 | | **CI/CD бота** | Gitea Actions: ruff, mypy strict, миграции up/down/up на PostgreSQL, pytest с порогом покрытия 80 %; автодеплой `dev` на сервер с проверкой health | ADR-015 | Качество: 296 тестов, покрытие 92 %, `mypy --strict`, `ruff`. Telegram в тестах — фейковая сессия aiogram, Docker — фейковый Engine API на `httpx.MockTransport`, LLM — сценарный фейк. ## 3. Развёртывание и запуск ### Требования - Python 3.12 и [uv](https://docs.astral.sh/uv/) — для запуска без Docker и тестов; - Docker 24+ с Compose v2 — для полного стека; - токен бота от [@BotFather](https://t.me/BotFather) и ваш Telegram ID (узнать — у [@userinfobot](https://t.me/userinfobot)); - для агента логов — OpenAI-совместимый LLM с structured output (`response_format: json_schema`). Один токен — один запущенный экземпляр: Telegram отдаёт обновления только одному процессу. ### Вариант А — локально за 5 минут (без Docker для бота) ```bash git clone https://gitea.24studbank.ru/HSE/tgit.git && cd tgit make install # uv sync cp .env.example .env # впишите TELEGRAM_BOT_TOKEN и ADMIN_IDS make demo-repo # локальный репозиторий загрузок из шаблона # → допишите напечатанную строку GIT_UPLOADS_URL=... в .env make run # бот запущен, база — SQLite ./tgit.db ``` Чтобы заработали «Статус», «Логи» и алерты, нужен Docker API: ```bash make local-proxy # read-only прокси Docker на 127.0.0.1:23750 echo DOCKER_URL=http://127.0.0.1:23750 >> .env make demo-up # демо-сервисы: здоровый, падающий, CPU-hog, unhealthy make run ``` ### Вариант Б — полный стек в Docker ```bash cp .env.example .env # токен, ADMIN_IDS, репозиторий загрузок и ключ (см. ниже) make up # бот + PostgreSQL 16 + docker-socket-proxy make demo-up # по желанию: демо-сервисы рядом с ботом make logs # логи бота ``` В Docker бот ходит в репозиторий загрузок по SSH: нужен отдельный репозиторий (шаблон — [`infra/uploads-template/`](infra/uploads-template/)) и deploy-ключ с правом записи. Ключ передаётся в `GIT_SSH_KEY_B64` (`base64 -w0 <ключ>`): смонтированный файл контейнер прочитать не сможет. ### Сервер CD из Gitea Actions разворачивает `dev` на сервер скриптом `infra/deploy/deploy.sh` — его же можно запускать руками для любого хоста. Подробно — в [`docs/dev/DEPLOY.md`](docs/dev/DEPLOY.md). ### Переменные окружения Полный список с комментариями — [`.env.example`](.env.example). Секреты хранятся только в `.env`, в git не попадают. | Переменная | Обязательна | Что задаёт | |---|---|---| | `TELEGRAM_BOT_TOKEN` | да | токен бота | | `ADMIN_IDS` | да | Telegram ID первых администраторов через запятую | | `TELEGRAM_PROXY_URL` | нет | HTTP/SOCKS-прокси до Telegram (если сервер не видит api.telegram.org) | | `GIT_UPLOADS_URL` | да | репозиторий загрузок: путь, `ssh://…` или `git@…` | | `GIT_SSH_KEY_B64` / `GIT_SSH_KEY_PATH` | для SSH | deploy-ключ бота | | `DATABASE_URL` | нет | по умолчанию SQLite; в Compose — PostgreSQL | | `DOCKER_URL` | нет | docker-socket-proxy; без него «Статус», «Логи» и алерты отвечают «Docker недоступен» | | `LLM_PROVIDER`, `LLM_BASE_URL`, `LLM_MODEL`, `LLM_API_KEY` | нет | LLM для агента; при `mock` агент выключен | ### Проверки ```bash make lint # ruff + mypy --strict make test # pytest make cov # pytest с покрытием (порог 80 %) ``` ## 4. Входные и выходные данные ### Команды и кнопки | Ввод | Что происходит | |---|---| | `/start <токен>` (ссылка из QR) | регистрация по приглашению, главное меню | | `/start`, `/menu` | главное меню; из любого шага диалога | | `/help` | справка | | 📝 Коммит · 🩺 Статус · 📜 Логи · 🚨 Алерты | разделы; у админа ещё 👥 Участники (приглашения, отзыв) | Внутри разделов — инлайн-кнопки. Текстом бот принимает имя файла или папки, содержимое файла (текстом, в том числе в блоке ```` ``` ````, или документом до 100 КБ), сообщение коммита, выражение grep и вопрос агенту. ### Выходные данные - **В Телеграме**: экраны разделов, файлы (`.log` для длинных результатов, исходник для больших файлов), QR-приглашения PNG, алерты. - **В репозитории загрузок**: коммиты от `tgit-bot` с автором из Телеграма; push в `main` запускает CI репозитория. - **В БД** (13 таблиц, миграции Alembic): участники и приглашения, черновики, состояние диалогов, снапшоты контейнеров, алерты и их доставки, журналы поисков по логам и вопросов агенту. ### Используемые API | API | Зачем | Как | |---|---|---| | Telegram Bot API | весь интерфейс | aiogram 3, long polling | | git (SSH) | чтение и коммиты в репозиторий загрузок | `git` подпроцессом со своим ключом, без shell | | Docker Engine API | контейнеры, stats, логи | только GET через `docker-socket-proxy` | | OpenAI-совместимый `/chat/completions` | страж и планировщик агента | httpx, `response_format: json_schema` | | SourceCraft REST API | статус прогонов CI репозитория загрузок | httpx, клиент готов, в UI — #11 | ## 5. Ограничения и особенности **Безопасность:** - Docker-сокет есть только у прокси, и тот пропускает лишь `GET /containers/*`. Бот показывает только контейнеры с меткой `tgit.managed=true` и отказывает по чужому id ещё до запроса: на сервере живут и другие проекты. - grep разбирается `shlex.split` и выполняется модулем `regex` с таймаутом на строку и общим бюджетом 3 с; катастрофический шаблон вроде `(a|aa)+$` бота не вешает. - Агент: логи в LLM **не передаются** (только вопрос и имена контейнеров), поэтому prompt injection из логов невозможен. LLM может вернуть только `LogQuery` — контейнер из белого списка, период ≤ 7 дней, шаблон ≤ 200 символов без вложенных квантификаторов. Даже обманутый страж даёт в худшем случае медленный grep. - Пути `.git/`, `.sourcecraft/`, `.tgit/` и `ruff.toml` из Телеграма не редактируются. Бот проверяет синтаксис до пуша, но код сервисов не исполняет. **Лимиты:** | Что | Лимит | |---|---| | файл из Телеграма | 100 КБ, UTF-8 | | черновик | 50 изменений | | push | 20 в час на участника | | шаблон grep | 200 символов; вывод — 500 строк | | чтение логов | 50 000 строк и 5 МБ (новейшие) | | вопрос агенту | 500 символов, 30 в сутки | | повтор алерта | в течение 10 мин — счётчик, а не новое сообщение | **Что пока не сделано** (открытые задачи): - запуск сервисов из репозитория загрузок в песочнице (`--cap-drop ALL`, read-only FS, лимиты) и перезапуск из бота — `tgit-runner`, #13. Сейчас сервисы поднимаются Compose, как в `examples/`; - уведомления о прогонах CI/CD репозитория загрузок и алерты 📦/⛔ — #11, ждут возврата к SourceCraft; - «🔕 Тишина на час» для алертов (P2); - prod-окружение: нужен отдельный бот, деплой `main` пропускается, пока его нет. ## Структура репозитория ``` app/ access/ приглашения, QR, проверка доступа bot/ диспетчер, меню, роутеры разделов, FSM в БД repo/ клон репозитория загрузок, валидация, черновики, push runtime/ Docker API, проверки, снапшоты logs/ grep-движок и журнал agent/ фильтр, страж, планировщик, схема LogQuery alerts/ детекторы, сервис алертов, рассылка, монитор llm/ провайдеры: OpenAI-совместимый, mock sourcecraft/ клиент API прогонов db/ модели, миграции Alembic, очистка config/ тексты интерфейса, пороги проверок и алертов, правила и промпты агента tests/ pytest; фейки Telegram, Docker и LLM examples/ демо-сервисы и сценарии infra/ деплой на сервер, шаблон репозитория загрузок docs/ ТЗ, архитектура, ADR, процесс ```