English · Русский
🤖 Self-hosted AI code review для ваших Pull Requests.
- О проекте
- Ключевые возможности
- Технологии
- ToDo
- Требования
- Быстрый старт
- Ревью в пайплайне
- Разработка
- Автор
- Поддержка
- Лицензия
ReVu — это AI-ревью для Pull Request: self-hosted вебхук-сервис либо команда в пайплайне. В серверном режиме он принимает вебхуки от Git-провайдеров (сейчас поддерживаются GitHub, Gitea, GitLab и Bitbucket) и публикует результат прямо в PR; в режиме пайплайна делает то же самое там, где репозиторий уже склонирован, — без входящего адреса, постоянного хоста и отдельной учётной записи для бота.
Один инстанс обслуживает любое количество подключений: можно одновременно ревьюить репозитории на GitHub, в корпоративном GitLab и в Gitea, используя общий ИИ-провайдер или отдельный профиль для каждого.
Основная идея — разгрузить команду и повысить качество кода. Прежде чем ваш коллега возьмётся за ревью, PR уже проверит ИИ и укажет на потенциальные проблемы. ReVu — не сторонний сервис, а self-hosted инструмент, который вы разворачиваете у себя.
- Два способа запуска
- Сервер — вебхук от Git-провайдера, постоянный инстанс, отдельная учётка для бота.
- Пайплайн —
revu reviewпрямо в GitHub Actions, GitLab CI или Gitea Actions: тот же образ, тот же промпт, тот же результат, но без входящего адреса и без хоста. См. Ревью в пайплайне.
- Два режима работы
- Комментарий (
comment) — единый подробный отзыв по всему Pull Request. - Инлайн (
inline) — точечные комментарии к конкретным изменениям в коде.
- Комментарий (
- Повторное ревью помнит предыдущее
- Правки в PR проверяются с учётом того, что ReVu уже написал: исправленные замечания не повторяются, оставшиеся не дублируются комментариями, а попадают в итог. Подробности — в разделе Повторное ревью.
- Для первого и повторного ревью используются разные промпты.
- Управляемая глубина контекста
flat(по умолчанию) — ревью по диффу и комментариям PR.deep— модель дополнительно видит структуру репозитория, тела изменённых функций целиком, сигнатуры того, что этот код вызывает, и связанные тесты. Меньше выдуманных замечаний и «а вы не обработали случай X», который обработан двадцатью строками ниже.- Бюджеты контекста настраиваются, а каждая ревью пишет в лог, сколько это стоило: сколько файлов и символов было в диффе, сколько добавил контекст и во что это примерно превращается в токенах.
- Ревью, которое не теряется
- Перед публикацией комментарии сверяются с диффом: строка, которой в изменениях нет, не отменяет остальные замечания. Раньше одно выдуманное моделью число могло стоить всего ревью.
- Если ревью всё же не удалось, ReVu сообщает об этом в PR (
NOTIFY_ON_FAILURE), а не молчит: сервис, который ничего не написал, выглядит как сервис, который всё проверил. Подробности остаются в логах.
- Несколько провайдеров на одном инстансе
- Один контейнер обслуживает сколько угодно Git-подключений — GitHub, Gitea, GitLab и Bitbucket в любом сочетании.
- Для каждого подключения свой вебхук, свой токен и свой секрет.
- Общий ИИ-провайдер по умолчанию, с возможностью переопределить для конкретного подключения.
- Гибкая интеграция
- Поддержка GitHub, Gitea, GitLab (включая self-managed) и Bitbucket.
- Совместимость с OpenAI API и его альтернативами (OpenRouter, LocalAI, LM Studio и др.), а также с российскими ИИ-провайдерами: GigaChat и YandexGPT.
- Именованные профили ИИ: разные модели для разных репозиториев без второго контейнера.
- Полностью self-hosted
- Разворачивается на ваших серверах без передачи данных третьим лицам.
- Можно использовать локальные модели (например, DeepSeek, Qwen), запуская их отдельно в вашей инфраструктуре.
- Простая конфигурация
- Настройка через один YAML-файл; полный справочник — в
config/settings.yaml.example_full. - Ошибка в конфигурации останавливает запуск с указанием проблемного ключа, а не превращает каждый вебхук в молчаливый сбой.
- Таймауты, количество повторов и экспоненциальный backoff настраиваются; повторяются только запросы, которые нельзя задублировать.
- Ограничение одновременных ревью считается отдельно по каждому подключению, чтобы шумный репозиторий не блокировал остальные.
- Настройка через один YAML-файл; полный справочник — в
- Наблюдаемость
- Логи пишутся в консоль и в файл с ротацией; в каждой записи указано подключение, к которому она относится.
- Python 3.13+ и FastAPI — API и вебхук-обработчик
- HTTPX — единый HTTP-клиент для Git- и AI-провайдеров: таймауты, повторы и backoff в одном месте
- OpenAI SDK, GigaChat SDK, Yandex AI Studio SDK — интеграция с LLM
- Dishka — внедрение зависимостей; конфигурация и клиенты собираются в одном месте
- Dynaconf + pydantic — конфигурация и валидация вебхуков
- unidiff — разбор диффов и подготовка их к инлайн-комментариям
- Docker — для удобного развертывания
- Поддержка Git-провайдеров:
- GitHub
- Gitea
- GitLab (включая self-managed)
- Bitbucket (спасибо lanity_roshoose)
- Поддержка дополнительных AI-провайдеров:
- Gemini
- DeepSeek
- Anthropic
- Qween
- GigaChat
- YandexGPT
- Гибкая настройка кастомных промптов для ревью (через
SYSTEM_PROMPT) - Подробная документация по конфигурации и запуску
- Англоязычная версия README
- Покрытие тестами - Coverage 100%
- Больше документации и Docstrings во всём коде
- Replay-защита вебхуков GitLab по
webhook-timestamp - Проверка инлайн-комментариев на реальных PR всех поддерживаемых провайдеров
Для корректной работы ReVu потребуется:
-
Docker и Docker Compose — ReVu поставляется готовым образом, отдельная установка Python не нужна.
-
Доменное имя с настроенным SSL-сертификатом
- Обязательно для публичных сервисов (GitHub, GitLab.com): они доставляют вебхуки только на доступный извне HTTPS-адрес с валидным сертификатом.
- Если Git-сервер и ReVu работают в одной Docker-сети, публичный адрес и SSL не нужны.
-
Персональный токен доступа (PAT) от вашего Git-провайдера
- GitHub — PAT Classic с правами на
repo. - Gitea — токен с доступом к репозиторию и Pull Requests.
- GitLab — токен со scope
api: он нужен и на чтение диффа, и на публикацию комментариев. - Bitbucket — токен с доступом на чтение Pull Requests и публикацию комментариев.
- GitHub — PAT Classic с правами на
-
API-ключ от ИИ-провайдера
- OpenAI и совместимые сервисы (OpenRouter, LocalAI, LM Studio), GigaChat или YandexGPT.
Для того чтобы запустить проект, достаточно выполнить несколько шагов:
-
Подключитесь по SSH к серверу и в удобном месте создайте директорию для проекта:
mkdir revu && cd revu
-
Скачайте конфигурационный файл и пропишите настройки:
- Скачайте файл — упрощённый или полный, с комментариями по каждому параметру:
- Упрощённый:
wget -O settings.yaml https://raw.githubusercontent.com/proDreams/revu/refs/heads/main/config/settings.yaml.example
- Полный:
wget -O settings.yaml https://raw.githubusercontent.com/proDreams/revu/refs/heads/main/config/settings.yaml.example_full
- Упрощённый:
- Откройте файл для редактирования:
nano settings.yaml
- Заполните следующие параметры:
REVIEW_MODE- формат ревью по умолчанию. Доступные варианты:"comment"- один большой комментарий к Pull Request"inline"- точечные комментарии к конкретным участкам кода
DEFAULT_AI_PROVIDER- имя профиля изAI_PROVIDERS, который используется всеми Git-подключениями, если в подключении не указан свой.AI_PROVIDERS- именованные настройки ИИ. Один профиль может использоваться несколькими Git-подключениями. Для каждого профиля:AI_PROVIDER- используемый ИИ-провайдер. Доступные варианты:"openai"- официальный API от OpenAI"openai_compatible"- совместимый с OpenAI провайдер"gigachat"- официальный GigaChat"yandexgpt"- официальный YandexGPT
AI_PROVIDER_API_KEY- API Ключ от ИИ-провайдераAI_PROVIDER_MODEL- используемая модель (например,gpt-4o-mini)AI_PROVIDER_TIMEOUT- сколько секунд отпущено на ВЕСЬ запрос к модели, включая генерацию ответа (по умолчанию300; таймаут Git-хостов к ИИ не применяется, а таймаут httpx не подходит — он считает паузы между чанками, а не общее время)AI_PROVIDER_RETRIES- сколько раз повторить запрос, если модель не ответила (по умолчанию4, то есть пять попыток; повторяются только сбои без ответа модели); превышениеAI_PROVIDER_TIMEOUTповторяется только один разAI_PROVIDER_BASE_URL- обязателен дляopenai_compatible, URL от OpenAI-совместимого провайдераAI_PROVIDER_SCOPE- только для GigaChat. Определяет используемую версию GigaChat, указан в личном кабинете.AI_PROVIDER_FOLDER_ID- обязателен для YandexGPT. Идентификатор рабочей директории.
GIT_PROVIDERS- Git-подключения. Ключ записи становится частью URL вебхука:https://<ваш-хост>/webhooks/<ключ>. Ключ должен соответствовать^[a-z0-9][a-z0-9_-]{2,31}$и быть уникальным. Для каждого подключения:GIT_PROVIDER- используемый Git-провайдер. Доступные варианты:"github""gitea""gitlab""bitbucket"
GIT_PROVIDER_USER_TOKEN- персональный токен доступа (PAT) от вашего Git-провайдераGIT_PROVIDER_URL- URL Git-провайдера. Обязателен для Gitea и BitBucket. Для GitHub и GitLab необязателен: без него используютсяapi.github.comиhttps://gitlab.com. Для GitHub Enterprise Server укажите корень REST API вместе с суффиксом/api/v3(например,https://ghe.example.com/api/v3), для GitLab Self-Managed — корень инстанса:/api/v4ReVu добавляет сам.GIT_PROVIDER_SECRET_TOKEN- случайный набор символов. Используется для валидации входящего вебхука от Git-провайдера: в вебхуке провайдера должен быть указан тот же самый секрет. У каждого подключения свой секрет — подпись одного подключения не подходит к другому.- GitHub, Gitea, BitBucket — подпись тела запроса в заголовках
X-Hub-Signature-256,X-Gitea-SignatureилиX-Hub-Signature(форматsha256=<hex>). Для BitBucket подпись обязательна: нужен Bitbucket Data Center 8.9+ и заполненное поле Secret в настройках вебхука, иначе запросы будут отклоняться с403. - GitLab — либо Signing token (GitLab 19.0+, рекомендуется: подписывается и тело запроса),
либо legacy Secret token, который уходит открытым текстом в заголовке
X-Gitlab-Tokenи тело запроса не защищает.
- GitHub, Gitea, BitBucket — подпись тела запроса в заголовках
- (необязательно)
AI_PROVIDER- имя профиля изAI_PROVIDERS, чтобы это подключение ревьюилось другой моделью (например,"cheap"). Если не указано — берётсяDEFAULT_AI_PROVIDER. - (необязательно)
GIT_PROVIDER_REVIEWER- только для BitBucket. Username от имени которого будет производиться ревью. Ревью запускается на событияpr:openedиpr:from_ref_updated(новые коммиты в PR); изменение только метаданных PR (pr:modified) ревью не запускает. - (необязательно)
SYSTEM_PROMPT,REVIEW_MODE,MAX_CONCURRENT_REVIEWS,NOTIFY_ON_FAILURE,INCLUDE_PR_DISCUSSION- значения для этого подключения; если не указаны, берутся глобальные.
- (необязательно)
SYSTEM_PROMPT- дополнительные инструкции, которые добавляются к стандартному промпту (не заменяют его: формат ответа и критерии ревью сохраняются). - Логирование (все параметры необязательны):
LOG_LEVEL- уровень логирования (DEBUG,INFO,WARNING,ERROR,CRITICAL, по умолчаниюINFO)LOG_DIR/LOG_FILE- каталог и имя файла логов (по умолчаниюlogsиlogs.txt). Логи пишутся и в консоль, и в файл; в Docker каталог удобно вынести на хост (вdocker-compose.yamlэто уже сделано).MAX_SIZE_MB/BACKUP_COUNT- размер файла до ротации в мегабайтах и сколько ротаций хранить (по умолчанию10и5).- В каждой записи указано подключение, к которому она относится, — при нескольких подключениях это заметно экономит время при разборе инцидента.
- (необязательно)
MAX_CONCURRENT_REVIEWS- сколько ревью может выполняться одновременно на одно подключение (по умолчанию3). Остальные вебхуки ждут свободный слот, а не запускают неограниченное число запросов к ИИ. Лимит считается по каждому подключению отдельно, чтобы шумный репозиторий не блокировал остальные. - (необязательно)
HTTP_CLIENT_TIMEOUT- таймаут HTTP-запросов в секундах (по умолчанию30.0) - (необязательно)
HTTP_CLIENT_REQUEST_ATTEMPTS- сколько всего раз может быть отправлен повторяемый запрос (по умолчанию3). Повторяются только безопасные запросы: клиентские ошибки4xxне повторяются, а комментарий ревью (POST) не отправляется повторно после5xxили таймаута чтения — иначе в PR появился бы дубликат. - (необязательно)
HTTP_CLIENT_RETRY_BACKOFF- базовая задержка экспоненциального backoff между повторами в секундах (по умолчанию0.5, потолок — 8 секунд). ЗаголовокRetry-Afterот сервера имеет приоритет. - (необязательно)
APP_HOST/APP_PORT- адрес, который слушает встроенный сервер (по умолчанию0.0.0.0:8000) - (необязательно)
NOTIFY_ON_FAILURE- публиковать ли в PR короткое сообщение, если ревью не удалось (по умолчаниюtrue). В сообщение попадает только тип ошибки; подробности остаются в логах. Отключите, если не хотите шума при систематическом сбое. - (необязательно)
CONTEXT_DEPTH- сколько кода видит модель:"flat"(по умолчанию, только дифф) или"deep"(ещё структура репозитория, тела изменённых функций, сигнатуры зависимостей и связанные тесты). Режимdeepчитает файлы репозитория и передаёт их ИИ-провайдеру целиком, поэтому он выключен по умолчанию. - (необязательно)
CONTEXT_MAX_FILES(по умолчанию12) - сколько дополнительных файлов разрешено подтянуть: модули зависимостей и тесты. Изменённые файлы и сам дифф в это число не входят и не урезаются никогда. - (необязательно)
CONTEXT_MAX_CHARS(по умолчанию40000, примерно 10k токенов) - общий бюджет всего добавленного контекста. Когда он исчерпан, контекст обрезается, а в лог пишется строка об этом; дифф остаётся полным. - (необязательно)
INCLUDE_PR_DISCUSSION- передавать ли ИИ-провайдеру комментарии других участников PR как контекст повторного ревью (по умолчаниюtrue). Собственные замечания ReVu передаются всегда: это её же прошлый ответ, а не новые данные, покидающие вашу сеть. Выключите флаг, если обсуждение в PR не должно уходить ИИ-провайдеру.
Конфигурация проверяется при старте: опечатка в
REVIEW_MODE, неизвестное имя профиля вAI_PROVIDERили отсутствующий токен провайдера останавливают запуск с указанием проблемного ключа, а не превращают каждый вебхук в молчаливый сбой. 4. Сохраните и выйдите, нажав CTRL+S, затем CTRL+X. - Скачайте файл — упрощённый или полный, с комментариями по каждому параметру:
-
Скачайте docker-compose-файл:
wget -O docker-compose.yaml https://raw.githubusercontent.com/proDreams/revu/refs/heads/main/docker-compose.yaml
-
Запустите проект:
sudo docker compose up -d
-
Откройте ReVu наружу. В
docker-compose.yamlпорт намеренно не проброшен: по умолчанию сервис доступен только из своей compose-сети. Выберите один из двух вариантов — оба уже лежат в файле закомментированными, достаточно убрать#:- Есть Traefik — раскомментируйте блок
labelsу сервиса и блокnetworksв конце файла, заменивrevu.example.comна свой домен. Сертификат Traefik получит сам (resolverletsencrypt), контейнер при этом слушает обычный HTTP на порту 8000. - Прокси на хосте (Caddy, NGINX) или Git-сервер в одной сети с ReVu — раскомментируйте
ports: - "8000:8000"и направьте прокси на127.0.0.1:8000. Если Git-провайдер и ReVu в одной Docker-сети, прокси можно не использовать вовсе. HTTPS с валидным сертификатом обязателен, если Git-провайдер доставляет вебхуки извне.
- Есть Traefik — раскомментируйте блок
-
Пропишите вебхук в каждом Git-провайдере на URL своего подключения:
https://<ваш-хост>/webhooks/<ключ из GIT_PROVIDERS>Например, для подключения
mainиз примера конфигурации —https://revu.example.com/webhooks/main. Секрет вебхука должен совпадать сGIT_PROVIDER_SECRET_TOKENэтого подключения: подпись одного подключения не подходит к другому.- GitHub — включите событие Pull requests и укажите секрет в поле Secret.
- Gitea — включите Pull request events и укажите секрет в поле Secret.
- GitLab — включите Merge request events. В поле Signing token (GitLab 19.0+)
вставьте
GIT_PROVIDER_SECRET_TOKENэтого подключения, либо для более старых версий заполните Secret token. Ревью запускается наopen,reopenиupdate; правка заголовка или меток (updateбез новых коммитов) ревью не перезапускает — GitLab не различает эти события, поэтому ReVu сравнивает head-коммит. - Bitbucket — включите события Opened и Source branch updated и заполните Secret.
-
(необязательно) Проверьте, что сервис поднялся:
curl http://localhost:8000/healthдолжен ответить{"status":"ok"}. Команда сработает, если порт проброшен (шаг 5); без проброса то же самое изнутри контейнера —docker compose exec revu curl -fsS http://127.0.0.1:8000/health. Этот же эндпоинт используется вHEALTHCHECKDocker-образа. Список подключений, которые увидел сервис, печатается в лог при старте:configured git connections: gh-main (github), gitea-internal (gitea); default AI profile: strong
Один инстанс ReVu обслуживает сколько угодно Git-подключений: GitHub, Gitea, GitLab и Bitbucket в любом сочетании.
Каждое подключение — своя запись в GIT_PROVIDERS со своими токеном и секретом, и свой URL вебхука.
ИИ-провайдер при этом общий: все подключения по умолчанию используют профиль из DEFAULT_AI_PROVIDER.
Если для конкретного подключения нужна другая модель, добавьте ему AI_PROVIDER с именем другого профиля:
DEFAULT_AI_PROVIDER: "strong"
AI_PROVIDERS:
strong:
AI_PROVIDER: "openai"
AI_PROVIDER_API_KEY: "sk-..."
AI_PROVIDER_MODEL: "gpt-4.1"
cheap:
AI_PROVIDER: "openai_compatible"
AI_PROVIDER_BASE_URL: "https://openrouter.ai/api/v1"
AI_PROVIDER_API_KEY: "sk-or-..."
AI_PROVIDER_MODEL: "qwen-2.5-coder"
GIT_PROVIDERS:
main: # идёт на "strong" — как все
GIT_PROVIDER: "github"
GIT_PROVIDER_USER_TOKEN: "ghp_..."
GIT_PROVIDER_SECRET_TOKEN: "..."
side-project: # переопределение: идёт на "cheap"
GIT_PROVIDER: "gitea"
GIT_PROVIDER_URL: "https://gitea.example.com"
GIT_PROVIDER_USER_TOKEN: "..."
GIT_PROVIDER_SECRET_TOKEN: "..."
AI_PROVIDER: "cheap"
REVIEW_MODE: "comment" # необязательно
MAX_CONCURRENT_REVIEWS: 1 # необязательно
gitlab-internal: # Self-Managed: без URL ReVu пошёл бы на gitlab.com
GIT_PROVIDER: "gitlab"
GIT_PROVIDER_URL: "https://gitlab.example.com"
GIT_PROVIDER_USER_TOKEN: "glpat-..."
GIT_PROVIDER_SECRET_TOKEN: "whsec_..."Накладные расходы: один контейнер, один конфиг, один набор логов — вместо отдельного инстанса на каждого провайдера.
Когда автор отправляет правки, ReVu не начинает с чистого листа. Перед ревью она читает то, что уже есть в PR: собственные прошлые замечания и обсуждение вокруг них. Для такого прогона используется отдельный промпт, и результат отличается:
- исправленное не повторяется — замечание, которое автор закрыл, больше не поднимается;
- оставшееся не дублируется — вместо повторного комментария на ту же строку оно попадает в итог;
- инлайн-комментарии ставятся только на новое — на проблемы в свежих изменениях;
- итог начинается с прогресса —
📊 Из прошлых замечаний исправлено: 3 из 5. Осталось: 2.Общее число берётся из прошлых замечаний ReVu, поэтому занизить его нельзя.
Обсуждение учитывается: если участники объяснили решение или попросили не менять код, замечание не поднимется повторно.
Как ReVu узнаёт свои комментарии. Не по автору: ReVu обычно работает на вашем же токене, поэтому
«бот» и «вы» — один аккаунт, и по автору любой ваш комментарий выглядел бы как прошлое ревью. Вместо этого
ReVu подписывает то, что публикует: в конце текста остаётся невидимый маркер, а сверху — видимый
заголовок 🤖 ReVu (у повторного — 🤖 ReVu · повторное ревью). Инлайн-комментарии помечаются 🤖 ReVu:.
Так под одной учёткой видно, кто есть кто, а определение своих комментариев не зависит от аккаунта.
Рекомендуется всё же завести отдельную учётку для ReVu — не ради корректности (её обеспечивает подпись), а ради читаемости: в истории PR сразу видно, где бот, а где человек, и ваша переписка не смешивается с выводом модели.
Откуда берутся прошлые комментарии:
| Провайдер | Что читается |
|---|---|
| GitHub, Gitea | таймлайн PR, инлайн-комментарии и тела ревью |
| GitLab | заметки MR (системные пропускаются, состояние «resolved» сохраняется) |
| Bitbucket Data Center | комментарии с якорями и вложенными ответами |
Повторное ревью запускается по тому же вебхуку, что и первое: признак — наличие прошлых замечаний ReVu в PR. Если PR ещё не ревьюился, используется обычный промпт.
Приватность. Обсуждение в PR (комментарии коллег) уходит ИИ-провайдеру как контекст. Если это недопустимо, выключите
INCLUDE_PR_DISCUSSION— тогда передаются только замечания самой ReVu.
По умолчанию ReVu ревьюит по диффу: предсказуемо по цене и ничего лишнего не отправляет наружу. Режим
deep добавляет ровно четыре вещи:
-
структуру репозитория — по строке на каталог, по несколько имён в каждой, манифесты и lock-файлы первыми. По
uv.lockв этом списке видно, что проект на uv, поpackages/— где живут компоненты, по.gitea/workflows/— чем он собирается. Дерево всё равно читается для поиска тестов и зависимостей, так что лишних запросов это не стоит; -
тела изменённых функций целиком — дифф показывает три строки вокруг правки, а модель судит обо всём методе. Это самая частая причина и ложных замечаний, и пропусков;
-
сигнатуры того, что вызывает изменённый код — по импортам подтягиваются только объявления (и docstring), а не тела. Модель видит контракт и перестаёт его выдумывать;
-
связанные тесты — по путям и именам файлов, только имена тестовых функций.
Что важно знать:
- Дифф никогда не урезается. Бюджеты ограничивают только дополнительные файлы и общий объём
добавленного. PR из 15 файлов модель увидит целиком независимо от
CONTEXT_MAX_FILES. - Языки. Разбор идёт через tree-sitter; в поставку входят грамматики для Python, JavaScript, TypeScript, Go, Java, Kotlin, Ruby, Rust, C#, PHP, C, C++, Swift, Scala, Elixir, Lua, Perl, Dart, Bash, SQL, HTML, CSS, HCL, YAML, JSON, Make, Zig, Haskell, OCaml и Dockerfile. Для языка без грамматики режим не выключается: изменённый файл попадает в контекст целиком.
- Приватность. В
deepИИ-провайдеру уходят целые файлы, а не только изменения. Содержимое.env, ключей,credentials,node_modules/,vendor/,dist/, минифицированных файлов и lock-файлов не отправляется никогда. Имена — отдельный случай: структура проекта называетuv.lockиpyproject.tomlнамеренно (иначе модель не поймёт, чем собран проект), а имена секретов и сгенерированных каталогов из неё исключены полностью. Если это неприемлемо — оставьтеflat. - Деградация. Любой сбой сбора контекста (нет прав, хост недоступен, лимит) не ломает ревью: оно проходит по диффу, а в лог пишется причина.
- Комментарии — только по диффу. Модели прямо сказано не оставлять инлайн-комментарии к окружающему коду; если проблема видна только там, она уходит в общий комментарий.
Как подобрать бюджеты. Каждое ревью пишет в лог одну строку:
review context for connection main, owner/repo#7: depth=deep; diff=3 files/4821 chars (~1205 tokens);
context=8 entries/37120 chars (~9280 tokens): bodies=3 extra_files=4 signatures=7 tests=12 unparsed=1;
requests=9 truncated=False took=1.83s
По ней видно, во что обходится ревью: diff — размер самого PR, context — добавка, requests —
сколько запросов ушло хостингу, truncated — упёрлись ли в бюджет. Если truncated=True часто, а токенов
не жалко — поднимайте CONTEXT_MAX_CHARS; если context съедает больше, чем сам дифф, — опускайте.
Формат конфигурации изменился, и старый файл намеренно не принимается: приложение не стартует и подсказывает, что переехало. Значения переносятся так:
| Было (1.x) | Стало (2.0) |
|---|---|
GIT_PROVIDER_CONFIG.GIT_PROVIDER |
GIT_PROVIDERS.<ключ>.GIT_PROVIDER |
GIT_PROVIDER_CONFIG.GIT_PROVIDER_USER_TOKEN |
GIT_PROVIDERS.<ключ>.GIT_PROVIDER_USER_TOKEN |
GIT_PROVIDER_CONFIG.GIT_PROVIDER_URL |
GIT_PROVIDERS.<ключ>.GIT_PROVIDER_URL |
GIT_PROVIDER_CONFIG.GIT_PROVIDER_SECRET_TOKEN |
GIT_PROVIDERS.<ключ>.GIT_PROVIDER_SECRET_TOKEN |
GIT_PROVIDER_CONFIG.GIT_PROVIDER_REVIEWER |
GIT_PROVIDERS.<ключ>.GIT_PROVIDER_REVIEWER |
AI_PROVIDER_CONFIG.AI_PROVIDER |
AI_PROVIDERS.<имя>.AI_PROVIDER + DEFAULT_AI_PROVIDER: <имя> |
AI_PROVIDER_CONFIG.AI_PROVIDER_* |
AI_PROVIDERS.<имя>.AI_PROVIDER_* |
REVIEW_MODE, SYSTEM_PROMPT, MAX_CONCURRENT_REVIEWS |
остались глобальными, их можно переопределить в подключении |
HTTP_CLIENT_*, LOG_*, APP_* |
без изменений |
Дополнительно нужно поменять URL вебхука в настройках Git-провайдера: вместо /webhooks/github,
/webhooks/gitea или /webhooks/bitbucket теперь /webhooks/<ключ>. Старых маршрутов больше нет.
GitLab поддержан с версии 2.1.0, переносить для него ничего не нужно: добавьте подключение
с GIT_PROVIDER: "gitlab" и пропишите его URL вебхука.
Для обновления достаточно выполнить команду:
sudo docker compose pull && sudo docker compose up -dСерверный режим требует входящего HTTPS-адреса, постоянно работающего хоста и отдельной учётной записи для бота. Если этого нет или не хочется — ReVu запускается прямо в пайплайне: код уже склонирован, событие само сообщает, какой это PR, а токен уже выдан job'у.
Тот же образ, другая команда: revu review вместо сервера.
Самый короткий путь — готовый экшен: он делает чекаут сам (с fetch-depth: 0, без которого сравнивать
не с чем) и запускает образ.
name: ReVu
on:
pull_request:
types: [opened, synchronize, reopened]
paths-ignore: # экономия: документация ревью не требует
- "**/*.md"
- "docs/**"
concurrency: # новый push отменяет предыдущее ревью
group: revu-${{ github.event.pull_request.number }}
cancel-in-progress: true
jobs:
review:
runs-on: ubuntu-latest
permissions:
contents: read
pull-requests: write # без этого GITHUB_TOKEN не сможет комментировать
steps:
- uses: proDreams/revu@v2
with:
ai-api-key: ${{ secrets.REVU_AI_KEY }}
ai-model: gpt-4o-mini
# format: github-annotations # пометки ещё и прямо в «Files changed»
# context-depth: deep # читать окружение изменённых функций
# fail-on: error # падать, если ревью не состоялосьВходы экшена называются так же, как переменные из таблицы ниже (ai-model — это AI_PROVIDER_MODEL),
плюс image (по умолчанию ghcr.io/prodreams/revu:latest), checkout: false, fail-on, format
и args — для всего остального.
Если экшен не подходит (или хостинг не GitHub) — то же самое шагом docker run. В контейнере job'а
actions/checkout использовать нельзя: это JS-экшен, которому нужен Node, а в образе ReVu его нет.
Поэтому чекаут делает обычный actions/checkout, а образ запускается шагом:
name: ReVu
on:
pull_request:
types: [opened, synchronize, reopened]
paths-ignore: # экономия: документация ревью не требует
- "**/*.md"
- "docs/**"
concurrency: # новый push отменяет предыдущее ревью
group: revu-${{ github.event.pull_request.number }}
cancel-in-progress: true
jobs:
review:
runs-on: ubuntu-latest
permissions:
contents: read
pull-requests: write # без этого GITHUB_TOKEN не сможет комментировать
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0 # нужен общий предок: иначе сравнивать не с чем
- name: ReVu
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
AI_PROVIDER_API_KEY: ${{ secrets.REVU_AI_KEY }}
AI_PROVIDER_MODEL: gpt-4o-mini
run: |
docker run --rm -v "$PWD:/repo" -w /repo \
-e GITHUB_TOKEN -e AI_PROVIDER_API_KEY -e AI_PROVIDER_MODEL \
ghcr.io/prodreams/revu:2.5.0 reviewКонтейнер здесь родная модель, а CI_JOB_TOKEN доступен сразу:
revu:
stage: test
image: ghcr.io/prodreams/revu:2.5.0
rules:
- if: $CI_PIPELINE_SOURCE == "merge_request_event"
variables:
GIT_DEPTH: 0 # нужна история до общего предка
AI_PROVIDER_API_KEY: $REVU_AI_KEY
AI_PROVIDER_MODEL: gpt-4o-mini
# GIT_PROVIDER_USER_TOKEN: $REVU_PROJECT_TOKEN # чтобы писать комментарий в MR
script:
- revu review --format gitlab-codequality
artifacts:
when: always # отчёт нужен и когда job упал
reports:
codequality: revu-codequality.jsonПро токен: CI_JOB_TOKEN умеет читать merge request и его заметки, но не писать их. Поэтому либо
задайте GIT_PROVIDER_USER_TOKEN (project access token с правом api) — и ревью уйдёт ещё и
комментарием в MR, либо не задавайте: замечания попадут в отчёт Code Quality, который GitLab покажет
в виджете MR, а сам текст ревью — в лог пайплайна. Отчёт не создаёт ни одного комментария
и не требует прав на запись вообще.
Формат тот же, что у GitHub, отличается расположение воркфлоу и то, что хостинг лучше назвать явно: Gitea Actions повторяет переменные GitHub, и по ним Gitea не всегда отличим от github.com.
# .gitea/workflows/revu.yaml
name: ReVu
on:
pull_request:
jobs:
review:
runs-on: ubuntu-latest
permissions:
contents: read
pull-requests: write
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: ReVu
env:
GIT_PROVIDER: gitea
GIT_PROVIDER_URL: https://git.example.com
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
AI_PROVIDER_API_KEY: ${{ secrets.REVU_AI_KEY }}
AI_PROVIDER_MODEL: gpt-4o-mini
run: |
docker run --rm -v "$PWD:/repo" -w /repo \
-e GIT_PROVIDER -e GIT_PROVIDER_URL -e GITHUB_TOKEN \
-e AI_PROVIDER_API_KEY -e AI_PROVIDER_MODEL \
ghcr.io/prodreams/revu:2.5.0 reviewХостинг ReVu определяет сам по переменным GITHUB_ACTIONS, GITLAB_CI и GITEA_ACTIONS, а репозиторий,
номер PR и ревизии берёт из переменных платформы (GITHUB_EVENT_PATH, CI_MERGE_REQUEST_IID,
CI_MERGE_REQUEST_DIFF_BASE_SHA и им подобных). Явно задать нужно только ключ ИИ.
| Переменная | Что задаёт |
|---|---|
AI_PROVIDER |
вендор: openai (по умолчанию), openai_compatible, gigachat, yandexgpt |
AI_PROVIDER_API_KEY |
ключ провайдера (обязательно) |
AI_PROVIDER_MODEL |
модель (обязательно) |
AI_PROVIDER_BASE_URL |
свой endpoint для openai_compatible |
AI_PROVIDER_SCOPE, AI_PROVIDER_FOLDER_ID |
для GigaChat и YandexGPT |
GIT_PROVIDER |
хостинг: github, gitea, gitlab, bitbucket |
GIT_PROVIDER_URL |
адрес self-hosted хостинга |
GIT_PROVIDER_USER_TOKEN |
токен для чтения и комментирования; иначе берётся токен платформы |
REVIEW_MODE |
inline (по умолчанию) или comment |
CONTEXT_DEPTH, CONTEXT_MAX_FILES, CONTEXT_MAX_CHARS |
глубина контекста и её бюджеты |
INCLUDE_PR_DISCUSSION |
передавать ли ИИ комментарии других участников PR |
LOG_LEVEL |
DEBUG, INFO (по умолчанию), WARNING, ERROR |
Имена те же, что в settings.yaml, поэтому знание файла переносится без изменений. Валидация тоже общая:
опечатка в REVIEW_MODE останавливает job с тем же сообщением, с каким остановила бы сервер.
| Флаг | Что делает |
|---|---|
--diff-source git|api|file |
откуда брать изменения: git — из чекаута (по умолчанию), api — через хостинг, file — из --diff-file |
--diff-file PATH |
готовый diff; включает --diff-source file |
--base, --head |
ревизии для сравнения, если платформа их не сообщила |
--repo, --pr |
репозиторий и номер PR — для запуска вне CI |
--provider |
хостинг; вне CI обязателен |
--depth flat|deep |
переопределяет CONTEXT_DEPTH |
--comment / --stdout |
комментарий в PR или печать в лог; по умолчанию — комментарий, если токен это позволяет |
--format text|json|gitlab-codequality|github-annotations |
что добавить к выводу: text (по умолчанию), машиночитаемый json, отчёт Code Quality или аннотации в «Files changed» |
--output PATH |
куда писать отчёт (по умолчанию revu-codequality.json) |
--fail-on never|error|findings |
когда ронять job: никогда (по умолчанию), если ревью не состоялось, если есть замечания |
Коды возврата: 0 — всё хорошо, 1 — сработал --fail-on findings, 2 — ревью не удалось запустить
(в том числе из-за ошибки в аргументах).
Тот же CLI работает и на машине разработчика — без CI, вебхука и сервера:
export AI_PROVIDER_API_KEY=...
export AI_PROVIDER_MODEL=gpt-4o-mini
export GIT_PROVIDER=github
export GIT_PROVIDER_USER_TOKEN=...
revu review --base origin/main --head HEAD --stdoutfetch-depth: 0обязателен. При shallow-клоне общего предка нет,git diff base...headвернёт пустоту, и ревью выглядело бы так, будто PR ничего не меняет. ReVu это ловит и останавливается с подсказкой, а не ревьюит пустоту.- В
deepконтекст бесплатен. Файлы читаются с диска, дерево — одной командойgit ls-tree, запросов к хостингу нет вовсе; на сервере тот же режим стоил бы до 12 запросов. - PR из форков не поддерживаются. Ревью запускается для PR внутри репозитория: у PR из форка нет
ни секретов, ни права комментировать, а
pull_request_targetтребует отдельной осторожности. - Сервер и пайплайн на одном репозитории — два ревью на push. Выберите что-то одно.
--fail-onпо умолчаниюnever: пайплайн не краснеет из-за мнения модели. Нужно наоборот —--fail-on error(ревью не состоялось) или--fail-on findings(есть замечания).- Замечания можно показать, ничего не публикуя.
--format github-annotationsпечатает аннотации, которые GitHub показывает прямо у изменённых строк, а--format gitlab-codequalityпишет отчёт для виджета MR. Ни то, ни другое не создаёт комментариев и не требует прав на запись — это ответ на read-onlyCI_JOB_TOKEN. - О неудачном ревью ReVu скажет в PR.
NOTIFY_ON_FAILURE(по умолчанию включено) публикует короткий комментарий с типом ошибки, если есть токен на запись; подробности остаются в логе. Выключите, если не хотите шума. - Ревью всегда попадает в лог пайплайна. Если токен позволяет комментировать, оно уходит ещё и комментарием: лог читает тот, кто разбирается в пайплайне, а комментарий — автор изменения.
- Код репозитория не исполняется. Ревью читает файлы и разбирает их парсером: ни
make, ниnpm install, ниpip installиз ревьюируемого дерева не запускаются.
Понадобятся Python 3.13+, uv и Docker — для сборки образа.
make install_dev # зависимости проекта и git-хуки
make run # запуск приложения локально
make test # тесты с проверкой покрытия (сейчас 100%)
make lint # ruff, форматирование и pre-commit
make typecheck # mypy с зависимостями проектаmake typecheck — не дубль make lint: хук mypy в pre-commit работает в изолированном окружении без
зависимостей проекта и не видит ошибок на границе с FastAPI, pydantic и SDK провайдеров. Именно этот запуск
нашёл, например, что создание модели в Yandex AI Studio SDK синхронное, а await перед ним падал в рантайме.
Интеграционные тесты поднимают настоящее приложение и DI-контейнер, остальные работают на моках:
uv run pytest -m integration # только интеграционные
uv run pytest -m "not integration" # только быстрыеПрограмма написана в рамках проекта "Код на салфетке":
- Сайт: https://napkincode.ru
- Telegram-канал: https://t.me/napkincode
Если вам нравится этот проект и вы хотите поддержать его дальнейшее развитие, рассмотрите возможность доната:
Ваша поддержка помогает проекту развиваться и улучшать будущие функции!
Этот проект распространяется под лицензией MIT. Подробности можно найти в файле LICENSE.