Files
tgit/README.md
T
ElebobaandClaude Opus 5.5 e63698ffe8
CI / ruff · mypy · pytest (pull_request) Successful in 1m47s
docs: презентация и сценарий защиты
- docs/presentation.pdf (12 слайдов): задача → разделы → архитектура → коммит/CI/CD →
  песочница → LLM-агент → алерты → безопасность → качество → демо → итоги;
  исходник docs/presentation/slides.html, пересборка — make slides
- docs/DEMO.md: подготовка, 16 шагов по сценариям SPEC §6 с таймингом и репликами,
  запасной план (нет сети / CI в очереди / нет LLM / нет сервера)
- ссылки в README и CLAUDE.md

Refs #20

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-19 13:31:17 +03:00

18 KiB
Raw Blame History

tgit — git в Телеграме

Телеграм-бот для команды, которая ведёт общий репозиторий небольших Python-сервисов. Прямо из чата можно поправить код и закоммитить его, посмотреть, живы ли контейнеры, поискать по логам — своим grep или вопросом LLM-агенту, — и получать алерты, когда что-то упало.

Учебный проект, курс Python, НИУ ВШЭ. Основной репозиторий и задачи — Gitea HSE/tgit; SourceCraft — зеркало.

Документ О чём
docs/SPEC.md ТЗ: требования A-, M-, C-, S-, L-, AL-
docs/ARCHITECTURE.md модули, потоки данных, хранилище
docs/DECISIONS.md решения ADR-001…016 с альтернативами
docs/dev/DEPLOY.md CI/CD и деплой на сервер
examples/README.md демо-сервисы и сценарии для проверки
docs/presentation.pdf презентация: принцип работы, архитектура, безопасность (исходник — docs/presentation/slides.html)
docs/DEMO.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 после push сообщение о push обновляется на месте: CI ⏳ → ✅/❌ → CD 🚀/⛔; CI репозитория загрузок — Gitea Actions (compile · ruff · bandit · pytest), бот находит прогон по sha и деплоит только после зелёного; автору — «✅ CI пройден» со ссылкой, упавший шаг с логом (длинный — файлом), «🚀 CD: сервисы … развёрнуты»; таймаут 30 мин; после рестарта бот продолжает с того же места C-24, C-30…C-34, #11
🚀 Деплой сервисов после CI бот отправляет изменённые services/<name>/ в tgit-runner на хосте: сборка образа, запуск в песочнице (uid 10001, read-only, без capabilities и сети, 128 МБ / 0.25 CPU / 64 процесса); старая версия заменяется только здоровой новой, иначе ⛔ с логом; «♻️ Перезапустить» в «Статусе» для админа #13, S-5
CI/CD бота Gitea Actions: ruff, mypy strict, миграции up/down/up на PostgreSQL, pytest с порогом покрытия 80 %; автодеплой dev на сервер с проверкой health ADR-015

Качество: 357 тестов, покрытие 92 %, mypy --strict, ruff. Telegram в тестах — фейковая сессия aiogram, Docker — фейковый Engine API на httpx.MockTransport, LLM — сценарный фейк.

3. Развёртывание и запуск

Требования

  • Python 3.12 и uv — для запуска без Docker и тестов;
  • Docker 24+ с Compose v2 — для полного стека;
  • токен бота от @BotFather и ваш Telegram ID (узнать — у @userinfobot);
  • для агента логов — OpenAI-совместимый LLM с structured output (response_format: json_schema).

Один токен — один запущенный экземпляр: Telegram отдаёт обновления только одному процессу.

Вариант А — локально за 5 минут (без Docker для бота)

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:

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

cp .env.example .env              # токен, ADMIN_IDS, репозиторий загрузок и ключ (см. ниже)
make up                           # бот + PostgreSQL 16 + docker-socket-proxy
make demo-up                      # по желанию: демо-сервисы рядом с ботом
make logs                         # логи бота

В Docker бот ходит в репозиторий загрузок по SSH: нужен отдельный репозиторий (шаблон — infra/uploads-template/) и deploy-ключ с правом записи. Ключ передаётся в GIT_SSH_KEY_B64 (base64 -w0 <ключ>): смонтированный файл контейнер прочитать не сможет.

Сервер

CD из Gitea Actions разворачивает dev на сервер скриптом infra/deploy/deploy.sh — его же можно запускать руками для любого хоста. Подробно — в docs/dev/DEPLOY.md.

Переменные окружения

Полный список с комментариями — .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-ключ бота
CI_PROVIDER, CI_TOKEN, CI_REPO нет CI репозитория загрузок, которого ждёт деплой (gitea); при none сервисы разворачиваются сразу после push
RUNNER_HOST, RUNNER_SSH_KEY_B64 нет tgit-runner на хосте; без него сервисы не разворачиваются
DATABASE_URL нет по умолчанию SQLite; в Compose — PostgreSQL
DOCKER_URL нет docker-socket-proxy; без него «Статус», «Логи» и алерты отвечают «Docker недоступен»
LLM_PROVIDER, LLM_BASE_URL, LLM_MODEL, LLM_API_KEY нет LLM для агента; при mock агент выключен

Проверки

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 репозитория (.gitea/workflows/check.yml).
  • В БД (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
SSH → tgit-runner деплой и перезапуск сервисов на хосте ssh подпроцессом, ключ с forced command
Gitea Actions API прогон CI по sha, упавший шаг, лог джоба httpx с ретраями, app/pipeline/gitea.py
SourceCraft REST API прогоны CI — на будущее возвращение (ADR-016) httpx, клиент готов, к наблюдателю не подключён

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 мин — счётчик, а не новое сообщение

Что пока не сделано (открытые задачи):

  • CI репозитория загрузок в SourceCraft: сейчас — Gitea Actions (ADR-016); SourceCraft подключится второй реализацией CIProvider, когда он примет ключ бота;
  • «🔕 Тишина на час» для алертов (P2);
  • prod-окружение: нужен отдельный бот, деплой main пропускается, пока его нет.

Структура репозитория

app/
  access/       приглашения, QR, проверка доступа
  bot/          диспетчер, меню, роутеры разделов, FSM в БД
  repo/         клон репозитория загрузок, валидация, черновики, push
  runtime/      Docker API, проверки, снапшоты
  logs/         grep-движок и журнал
  agent/        фильтр, страж, планировщик, схема LogQuery
  alerts/       детекторы, сервис алертов, рассылка, монитор
  pipeline/     наблюдатель CI/CD после push, клиент Gitea Actions
  runner/       SSH-клиент tgit-runner и деплой сервисов
  llm/          провайдеры: OpenAI-совместимый, mock
  sourcecraft/  клиент API прогонов
  db/           модели, миграции Alembic, очистка
config/         тексты интерфейса, пороги проверок и алертов, правила и промпты агента
tests/          pytest; фейки Telegram, Docker и LLM
examples/       демо-сервисы и сценарии
infra/          деплой бота, tgit-runner (песочница сервисов), шаблон репозитория загрузок
docs/           ТЗ, архитектура, ADR, процесс