Files
tgit/docs/SPEC.md
T

172 lines
16 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# SPEC — техническое задание tgit
> Статус: **v1, рабочая версия** (2026-10-12). Составлено по постановке преподавателя и согласованным расширениям;
> официальное ТЗ ещё не пришло — когда придёт, сверяем и фиксируем расхождения в [`DECISIONS.md`](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`](https://gitea.24studbank.ru/HSE/tgit) (основной) → зеркало [`rogachevegorushka/tgit`](https://sourcecraft.dev/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|off [project]` — подписка на алерты |
### 4.5 Логи — P1
| # | Требование |
|---|---|
| F-40 | `/logs <project> [N|5m|1h] [grep]` — хвост логов приложения за период или 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).