Files
tgit/docs/SPEC.md
T

16 KiB
Raw Blame History

SPEC — техническое задание tgit

Статус: v1, рабочая версия (2026-10-12). Составлено по постановке преподавателя и согласованным расширениям; официальное ТЗ ещё не пришло — когда придёт, сверяем и фиксируем расхождения в DECISIONS.md. При конфликте ADR важнее этого документа.

1. Контекст

Что это: Telegram-бот «git в телеграме». Пользователь отправляет боту .py-файл — бот коммитит его в отдельный репозиторий на SourceCraft, там автоматически запускается CI (проверки) и CD (запуск кода на сервере). Бот сообщает результат CI, следит за запущенным кодом, присылает алерты о падениях и перегрузке, отдаёт логи и отвечает на вопросы по логам с помощью LLM.

Зачем: учебный проект курса Python (НИУ ВШЭ) — демонстрация асинхронного Python, интеграций по API, работы с git, контейнеризации, CI/CD и тестирования.

Исходная постановка (переписка с преподавателем, 2026-10-12):

  • бот загружает .py-файлы в репозиторий SourceCraft, автоматически запускается CI/CD;
  • репозиторий для загрузок — отдельный, не репозиторий с домашками; у бота свой ключ;
  • разворачивание — на VPS или локально;
  • вайбкодинг разрешён;
  • согласованы расширения: статус CI, алерты о падении и перегрузке, удобные логи, LLM-чат по логам;
  • опционально — настроить anytask.

2. Термины

Термин Значение
Репозиторий бота Gitea HSE/tgit (основной) → зеркало rogachevegorushka/tgit в SourceCraft, где работает CI/CD
Репозиторий загрузок rogachevegorushka/tgit-uploads — сюда бот коммитит файлы пользователей
Проект Папка projects/<name>/ в репозитории загрузок; единица запуска и мониторинга
Загрузка Один отправленный файл → один коммит
Прогон CI/CD-run SourceCraft, запущенный коммитом загрузки
Приложение Запущенный на сервере контейнер с кодом проекта
Хост Сервер, где работают бот и приложения (по умолчанию VPS vobot)

3. Роли

Роль Может
Гость /start, /help; видит, что доступа нет, и свой Telegram ID для запроса доступа
Пользователь Загружать файлы, видеть свои проекты, статусы CI, логи, алерты, спрашивать LLM — только по своим проектам
Администратор Всё + управление доступом (/allow, /deny), видит все проекты, получает системные алерты бота

Доступ — по белому списку Telegram ID (первичные администраторы задаются в .env).

4. Функциональные требования

Приоритеты: P0 — без этого нет сдачи; P1 — согласованные расширения; P2 — если останется время.

4.1 Загрузка кода — P0

# Требование
F-1 Бот принимает документ .py от пользователя из белого списка
F-2 Валидация до коммита: расширение .py, размер ≤ 100 КБ, кодировка UTF-8, синтаксис (ast.parse). Ошибка → понятное сообщение с номером строки, коммита нет
F-3 Выбор проекта: подпись к файлу project: <name>, иначе — инлайн-кнопки «существующий проект / новый». Имя проекта: ^[a-z][a-z0-9-]{1,30}$
F-4 Файл кладётся как projects/<name>/main.py (или под своим именем, если в проекте уже есть main.py); requirements.txt принимается так же отдельным документом
F-5 Коммит от имени бота с указанием автора: upload(<project>): <file> by @<username> и трейлером Telegram-User: <id>
F-6 Ответ: ссылка на коммит и сообщение «CI запущен», которое затем обновляется статусом
F-7 Повторная загрузка того же содержимого не создаёт пустой коммит — бот сообщает «изменений нет»

4.2 CI в репозитории загрузок — P0

# Требование
F-10 На каждый push в main репозитория загрузок запускается CI: py_compile, ruff check, pytest (если в проекте есть test_*.py), bandit (статический анализ безопасности)
F-11 Бот отслеживает прогон своего коммита и обновляет сообщение: ⏳ запущен → ✅ успех / ❌ падение (с указанием упавшего шага)
F-12 При падении бот прикладывает лог упавшего шага (текстом, если короткий, иначе файлом)
F-13 /runs [project] — последние 5 прогонов со статусами и ссылками

4.3 CD и запуск приложений — P1

# Требование
F-20 После успешного CI проект автоматически запускается на хосте в изолированном контейнере (см. §5.3)
F-21 Режимы запуска: service (долгоживущий процесс, перезапуск при падении, до 3 раз) и job (однократный запуск, результат — код выхода и вывод). Режим — в первой строке файла # tgit: mode=job или по умолчанию service
F-22 /status [project] — состояние приложения: запущено/остановлено/упало, аптайм, CPU, RAM, число рестартов, ревизия
F-23 /stop <project>, /restart <project> — управление своим приложением
F-24 Лимит: не больше 3 приложений на пользователя и 10 на хост

4.4 Алерты — P1

# Требование
F-30 Падение приложения (выход с ненулевым кодом, OOM-kill, исчерпаны рестарты) → алерт владельцу с хвостом лога (последние 30 строк)
F-31 Перегрузка: CPU > 80% лимита или RAM > 90% лимита дольше 1 минуты → алерт
F-32 Антиспам: один и тот же алерт по проекту — не чаще раза в 10 минут; повторные копятся счётчиком
F-33 Системные алерты администратору: бот не может достучаться до SourceCraft API / Docker / БД дольше 5 минут
F-34 `/alerts on

4.5 Логи — P1

# Требование
F-40 `/logs [N
F-41 Ответ длиннее лимита Telegram (4096 символов) — файлом .log
F-42 Логи CI-прогона — по кнопке под сообщением о прогоне

4.6 LLM-ассистент по логам — P1

# Требование
F-50 /ask <project> <вопрос> (или режим диалога после /ask <project>) — вопрос на естественном языке: «почему упал ночью?», «сколько было ошибок таймаута за час?»
F-51 LLM не выполняет команды: она возвращает структурированный запрос к логам (источник, период, регулярное выражение, уровень, лимит), бот валидирует и выполняет его сам, затем LLM формулирует ответ по найденным строкам
F-52 В ответе — краткий вывод + использованный запрос (чтобы пользователь видел, «какой grep» был сделан) + до 10 строк-доказательств
F-53 Перед отправкой в LLM логи проходят маскирование секретов (токены, ключи, пароли, e-mail)
F-54 Лимит: 20 запросов к LLM на пользователя в сутки

4.7 Прочее — P2

# Требование
F-60 /history [project] — история загрузок с откатом на ревизию (/rollback <project> <sha>)
F-61 Интеграция с anytask (формат уточнить у преподавателя)
F-62 Еженедельная сводка по проектам: загрузки, прогоны, падения

5. Нефункциональные требования

5.1 Развёртывание

  • Всё поднимается одной командой make up (Docker Compose) на любой Linux-машине с Docker — ноутбук или VPS.
  • Бот работает через long polling — входящий HTTPS и домен не нужны.
  • Конфигурация — только через .env (пример — .env.example); секреты в git не попадают.
  • Основная площадка — VPS vobot (/srv/tgit), деплой — автоматически из CI SourceCraft.

5.2 Качество кода

  • Python 3.12, asyncio; типизация (mypy --strict для app/), ruff (lint + format).
  • Тесты pytest, покрытие app/ ≥ 70%; внешние сервисы (Telegram, SourceCraft, Docker, LLM) в тестах подменяются фейками.
  • Комментарии — там, где неочевидно «почему»; докстринги у публичных интерфейсов модулей.

5.3 Безопасность (запуск чужого кода)

  • Приложения пользователей — в контейнере: непривилегированный пользователь, --cap-drop ALL, no-new-privileges, read-only FS + tmpfs /tmp, лимиты --memory 128m --cpus 0.25 --pids-limit 64, без доступа к Docker-сокету и к сети хоста. Сеть наружу — выключена по умолчанию, включается администратором на проект.
  • Бот не видит Docker-сокет напрямую — только через прокси с минимальным набором разрешённых вызовов.
  • SSH-ключ, которым CI запускает приложения на хосте, ограничен одной командой (command= в authorized_keys).
  • Секреты (токен бота, токен SourceCraft, ключи) не логируются и не отправляются в LLM.

5.4 Надёжность

  • SourceCraft API нестабилен (обрывы TLS) — все вызовы с ретраями и экспоненциальной задержкой; запись идемпотентна.
  • Падение бота → перезапуск Docker (restart: unless-stopped) + healthcheck.
  • Пуши в репозиторий загрузок сериализуются (одна очередь), при конфликте — pull --rebase и повтор.

5.5 Производительность

  • Ответ на загрузку (коммит + ссылка) — ≤ 10 с.
  • Обновление статуса CI — ≤ 30 с после смены статуса в SourceCraft.
  • Бот (без приложений) укладывается в 256 МБ RAM: на хосте свободно ~1,9 ГБ.

6. Ключевые сценарии

  1. Успешная загрузка. Пользователь отправляет bot.py с подписью project: weather → бот: «✅ Закоммичено a1b2c3d → ⏳ CI…» → через минуту сообщение меняется на «✅ CI пройден → 🚀 запущено» → /status weather показывает работающий сервис.
  2. Ошибка синтаксиса. Файл с def f(: → бот сразу отвечает «Синтаксическая ошибка, строка 1», коммита нет.
  3. Падение CI. ruff нашёл ошибки → «❌ CI упал на шаге lint» + лог шага файлом.
  4. Падение приложения. Сервис упал с KeyError → алерт с хвостом лога и кнопками «Логи», «Перезапустить», «Спросить ИИ».
  5. Вопрос к логам. /ask weather почему падает по ночам? → LLM строит запрос {since: 24h, pattern: "Traceback|Error"} → бот находит строки → ответ: «С 03:00 таймауты API погоды, 14 раз; см. строки…».
  6. Перегрузка. Скрипт ушёл в бесконечный цикл → через минуту алерт «CPU 100% лимита».

7. Критерии приёмки

  • Сценарии 1–6 проходят на развёрнутом боте (демо-видео или вживую).
  • make up с заполненным .env поднимает систему на чистой машине.
  • Push в main репозитория бота → CI зелёный → бот автоматически обновился на VPS.
  • make lint && make test зелёные, покрытие ≥ 70%.
  • README по «Требованиям к оформлению решений»: цели и задачи, что реализовано, развёртывание, входные/выходные данные и API, ограничения.
  • Структура каталогов: app/, tests/, config/, infra/, docs/, examples/ (демо-скрипты для загрузки).
  • Презентация (PDF): принцип работы, архитектура, демо.

8. Вне рамок

  • Веб-интерфейс, поддержка языков кроме Python, многофайловые проекты сложнее «скрипт + requirements + тесты».
  • Kubernetes, горизонтальное масштабирование, отдельный сервер под приложения (возможное развитие — ADR-005).