Files
tgit/docs/DECISIONS.md
T

99 lines
10 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.
# Архитектурные решения (ADR)
Формат записи:
```
## ADR-NNN: <заголовок>
**Дата:** YYYY-MM-DD · **Статус:** принято | отменено | заменено ADR-MMM
**Контекст:** почему возник вопрос.
**Решение:** что выбрали.
**Альтернативы:** что отвергли и почему.
**Последствия:** что это меняет.
```
---
## ADR-001: Хостинг кода и задач — SourceCraft
**Дата:** 2026-10-12 · **Статус:** заменено ADR-010
**Контекст:** нужен репозиторий проекта, трекер задач и CI.
**Решение:** код бота, issues и CI — на SourceCraft. Загрузки пользователей — в отдельный репозиторий там же.
**Альтернативы:** GitHub/Gitea — не соответствует постановке преподавателя.
**Последствия:** CI описывается в формате SourceCraft; бот ходит в SourceCraft по своему deploy-ключу.
## ADR-002: Telegram — long polling, а не webhook
**Дата:** 2026-10-12 · **Статус:** принято
**Контекст:** бот должен легко разворачиваться «где угодно», включая ноутбук без белого IP.
**Решение:** long polling (aiogram `start_polling`).
**Альтернативы:** webhook — нужен домен, HTTPS и nginx-vhost; выигрыш в задержке для учебной нагрузки несущественен.
**Последствия:** входящих портов нет; на VPS не нужен vhost. Один экземпляр бота на токен.
## ADR-003: Статус CI — polling SourceCraft API
**Дата:** 2026-10-12 · **Статус:** принято
**Контекст:** нужно сообщать пользователю результат CI.
**Решение:** бот опрашивает `/cicd/runs` и сопоставляет прогон с коммитом по sha; опрос идёт только пока есть незавершённые загрузки.
**Альтернативы:** исходящие webhooks — у SourceCraft не задокументированы; «CI сам пишет в Telegram» — секрет бота в чужом CI и нет связи с исходным сообщением.
**Последствия:** задержка статуса до ~10 с; клиент API обязан переживать обрывы (ретраи).
## ADR-004: Образы собираются на хосте, реестр — опционально
**Дата:** 2026-10-12 · **Статус:** принято
**Контекст:** у SourceCraft есть container registry (`pkg.sourcecraft.tech`), но его создание требует платёжного аккаунта Yandex Cloud и роли администратора организации.
**Решение:** и бот, и приложения собираются `docker build` прямо на хосте (как meditron). Переменная `REGISTRY` в будущем переключит деплой на `pull` готового образа.
**Альтернативы:** registry SourceCraft — зависимость от биллинга; GHCR/Docker Hub — ещё один внешний аккаунт.
**Последствия:** сборка нагружает хост (на время деплоя); деплой не зависит от внешнего реестра.
## ADR-005: Приложения пользователей — на том же хосте, в песочнице
**Дата:** 2026-10-12 · **Статус:** принято (временно)
**Контекст:** единственный доступный сервер — VPS vobot, где работают прод и dev Vobot и meditron.
**Решение:** запускать на нём же, но в жёсткой песочнице (non-root, read-only, cap-drop ALL, лимиты CPU/RAM/PIDs, без сети по умолчанию) и с общим лимитом 10 приложений.
**Альтернативы:** отдельный VPS — правильнее по изоляции, но лишняя машина для учебного проекта; self-hosted worker SourceCraft — запуск внутри CI не даёт долгоживущих сервисов.
**Последствия:** риск побега из контейнера ложится на прод-Vobot. Хост приложений вынесен в конфиг (`RUNNER_HOST`) — переезд на отдельную машину без изменений кода.
## ADR-006: Запуск и управление приложениями — через `tgit-runner` с forced command
**Дата:** 2026-10-12 · **Статус:** принято
**Контекст:** и CI, и бот должны запускать/останавливать контейнеры; полный доступ к Docker равен root на хосте.
**Решение:** единая точка — скрипт `tgit-runner` на хосте; SSH-ключи CI и бота в `authorized_keys` пользователя `tgit-deploy` ограничены `command="tgit-runner"`; скрипт валидирует аргументы. Бот читает состояние через read-only docker-socket-proxy.
**Альтернативы:** смонтировать docker.sock в бота — компрометация бота = root на хосте.
**Последствия:** вся логика песочницы — в одном месте и тестируется отдельно.
## ADR-007: LLM строит структурированный запрос, а не shell-команду
**Дата:** 2026-10-12 · **Статус:** принято
**Контекст:** идея — «LLM формирует grep по логам». Логи содержат текст, подконтрольный пользователю (prompt injection).
**Решение:** LLM возвращает JSON `LogQuery` (источник, период, regex, уровень, лимит) по схеме; бот валидирует и выполняет поиск сам, в Python, с таймаутом regex. Пользователю показывается эквивалентный grep.
**Альтернативы:** выполнять `grep` от LLM в shell — инъекции команд, ReDoS, доступ к файлам хоста.
**Последствия:** поиск ограничен возможностями `LogQuery`, зато безопасен и тестируем.
## ADR-008: LLM-провайдер — за протоколом, по умолчанию OpenAI-совместимый API
**Дата:** 2026-10-12 · **Статус:** принято
**Контекст:** провайдер не выбран; нужны тесты без сети.
**Решение:** протокол `LLMProvider` + фабрика по `LLM_PROVIDER`: `openai_compat` (`LLM_BASE_URL`, `LLM_MODEL`, `LLM_API_KEY` — подходит YandexGPT, OpenAI, локальная модель) и `mock`.
**Альтернативы:** привязка к одному вендору — как в Vobot, правило «провайдер за протоколом».
**Последствия:** выбор конкретной модели — настройка, а не код.
## ADR-009: Стек
**Дата:** 2026-10-12 · **Статус:** принято
**Решение:** Python 3.12, uv, aiogram 3, httpx, pydantic-settings, SQLAlchemy 2 async + Alembic, PostgreSQL 16, `regex`, pytest (+ pytest-asyncio, respx), ruff, mypy. Контейнеризация — Docker Compose.
**Альтернативы:** python-telegram-bot — синхронное наследие в API; SQLite в проде — проще, но Postgres ближе к реальным сервисам и уже привычен по Vobot/meditron.
**Последствия:** тесты на SQLite через тот же SQLAlchemy.
## ADR-010: Gitea — основной репозиторий и трекер, SourceCraft — зеркало + CI/CD
**Дата:** 2026-10-12 · **Статус:** принято (заменяет ADR-001)
**Контекст:** веб-интерфейс SourceCraft работает нестабильно и часть функций отображает плохо; API периодически рвёт соединения. При этом CI/CD на SourceCraft — часть постановки преподавателя.
**Решение:** код, issues, PR и ревью — в Gitea [`HSE/tgit`](https://gitea.24studbank.ru/HSE/tgit) (как Vobot/meditron). Встроенный push-mirror Gitea синхронизирует репозиторий в SourceCraft на каждый push (+ раз в 8 ч); CI/CD бота и репозиторий загрузок `tgit-uploads` остаются в SourceCraft.
**Альтернативы:** только SourceCraft — неудобно работать; только Gitea (Gitea Actions) — уходим от постановки; зеркало по SSH — на этом Gitea SSH-зеркала отключены, поэтому HTTPS + токен SourceCraft.
**Последствия:**
- В SourceCraft ничего не пушим и не мержим — зеркало перезаписывает его целиком.
- Триггер `pull_request` в SourceCraft не срабатывает (PR живут в Gitea) → CI бота запускается на `push` во все ветки.
- Токен SourceCraft хранится в настройках зеркала Gitea → нужен отдельный токен с доступом только к `tgit`.
- Issues #1–#21 перенесены из SourceCraft с теми же номерами; в SourceCraft закрыты со ссылкой.